diff options
author | bapt <bapt@FreeBSD.org> | 2017-08-08 11:35:27 +0000 |
---|---|---|
committer | bapt <bapt@FreeBSD.org> | 2017-08-08 11:35:27 +0000 |
commit | a6b39bb9e47f5d975d99196ea1862c50db1ea74f (patch) | |
tree | b43ac320442aa5ffbee199609e2f836acfa68914 /contrib/mdocml | |
parent | 1430ec935bcd8aa66595aeb68bbaf38339e6b807 (diff) | |
download | FreeBSD-src-a6b39bb9e47f5d975d99196ea1862c50db1ea74f.zip FreeBSD-src-a6b39bb9e47f5d975d99196ea1862c50db1ea74f.tar.gz |
MFC r313958, r319717, r321810
r313958:
Import mandoc 1.4.1rc2
r319717:
Import mandoc snapshot 2017-06-08
It implements missing man(7) macros used in base by kerberos/ntp and makes them
supported by mandoc.
This import should have been done before the removal of groff.
Reported by: gordon
r321810:
Update mandoc to 1.14.2
Diffstat (limited to 'contrib/mdocml')
114 files changed, 13067 insertions, 5844 deletions
diff --git a/contrib/mdocml/INSTALL b/contrib/mdocml/INSTALL index 115d160..d80e8e3 100644 --- a/contrib/mdocml/INSTALL +++ b/contrib/mdocml/INSTALL @@ -1,22 +1,24 @@ -$Id: INSTALL,v 1.17 2016/07/19 22:40:33 schwarze Exp $ +$Id: INSTALL,v 1.20 2017/07/28 14:57:56 schwarze Exp $ + +About the portable mandoc distribution +-------------------------------------- +The mandoc manpage compiler toolset (formerly called "mdocml") +is a suite of tools compiling mdoc(7), the roff(7) macro language +of choice for BSD manual pages, and man(7), the predominant +historical language for UNIX manuals. -About mdocml, the portable mandoc distribution ----------------------------------------------- -The mandoc manpage compiler toolset is a suite of tools compiling -mdoc(7), the roff(7) macro language of choice for BSD manual pages, -and man(7), the predominant historical language for UNIX manuals. It includes a man(1) manual viewer and additional tools. -For general information, see <http://mdocml.bsd.lv/>. +For general information, see <http://mandoc.bsd.lv/>. In case you have questions or want to provide feedback, read -<http://mdocml.bsd.lv/contact.html>. Consider subscribing to the +<http://mandoc.bsd.lv/contact.html>. Consider subscribing to the discuss@ mailing list mentioned on that page. If you intend to help with the development of mandoc, consider subscribing to the tech@ mailing list, too. Enjoy using the mandoc toolset! -Ingo Schwarze, Karlsruhe, July 2016 +Ingo Schwarze, Karlsruhe, July 2017 Installation @@ -25,21 +27,27 @@ Before manually installing mandoc on your system, please check whether the newest version of mandoc is already installed by default or available via a binary package or a ports system. A list of the latest bundled and ported versions of mandoc for various operating -systems is maintained at <http://mdocml.bsd.lv/ports.html>. +systems is maintained at <http://mandoc.bsd.lv/ports.html>. Regarding how packages and ports are maintained for your operating system, please consult your operating system documentation. To install mandoc manually, the following steps are needed: -1. If you want to build the CGI program, man.cgi(8), too, run the -command "echo BUILD_CGI=1 > configure.local". Then run "cp -cgi.h.examples cgi.h" and edit cgi.h as desired. +1. If you want to build the CGI program, man.cgi(8), too, +run the command "echo BUILD_CGI=1 >> configure.local". +Then run "cp cgi.h.example cgi.h" and edit cgi.h as desired. + +2. If you also want to build the catman(8) utility, run the +command "echo BUILD_CATMAN=1 >> configure.local". Note that it +is unlikely to be a drop-in replacement providing the same +functionality as your system's "catman", if your operating +system contains one. -2. Define MANPATH_DEFAULT in configure.local +3. Define MANPATH_DEFAULT in configure.local if /usr/share/man:/usr/X11R6/man:/usr/local/man is not appropriate for your operating system. -3. Run "./configure". +4. Run "./configure". This script attempts autoconfiguration of mandoc for your system. Read both its standard output and the file "Makefile.local" it generates. If anything looks wrong or different from what you @@ -49,27 +57,31 @@ result seems right to you. On Solaris 10 and earlier, you may have to run "ksh ./configure" because the native /bin/sh lacks some POSIX features. -4. Run "make". +5. Run "make". Any POSIX-compatible make, in particular both BSD make and GNU make, should work. If the build fails, look at "configure.local.example" and go back to step 2. -5. Run "make -n install" and check whether everything will be +6. Run "make -n install" and check whether everything will be installed to the intended places. Otherwise, put some *DIR or *NM* -variables into "configure.local" and go back to step 3. +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. -6. Run "sudo make install". If you intend to build a binary +8. Run "sudo make install". If you intend to build a binary package using some kind of fake root mechanism, you may need a command like "make DESTDIR=... install". Read the *-install targets in the "Makefile" to understand how DESTDIR is used. -7. Run the command "sudo -makewhatis" to build mandoc.db(5) databases in all the directory -trees configured in step 6. Whenever installing new manual pages, -re-run makewhatis(8) to update the databases, or apropos(1) will -not find the new pages. +9. Run the command "sudo makewhatis" to build mandoc.db(5) databases +in all the directory trees configured in step 3. Whenever installing +new manual pages, re-run makewhatis(8) to update the databases, or +apropos(1) will not find the new pages. -8. To set up a man.cgi(8) server, read its manual page. +10. To set up a man.cgi(8) server, read its manual page. Note that some man(7) pages may contain low-level roff(7) markup that mandoc does not yet understand. On some BSD systems using @@ -87,9 +99,10 @@ The following libraries are required: 2. The fts(3) directory traversion functions. If your system does not have them, the bundled compatibility version -will be used, so you need not worry in that case. But be careful: the -glibc version of fts(3) is known to be broken on 32bit platforms, -see <https://sourceware.org/bugzilla/show_bug.cgi?id=15838>. +will be used, so you need not worry in that case. But be careful: old +glibc versions of fts(3) were known to be broken on 32bit platforms, +see <https://sourceware.org/bugzilla/show_bug.cgi?id=11460>. +That was presumably fixed in glibc-2.23. If you run into that problem, set "HAVE_FTS=0" in configure.local. 3. Marc Espie's ohash(3) library. diff --git a/contrib/mdocml/LICENSE b/contrib/mdocml/LICENSE index 1dec04b..aca7ee7 100644 --- a/contrib/mdocml/LICENSE +++ b/contrib/mdocml/LICENSE @@ -1,18 +1,19 @@ -$Id: LICENSE,v 1.13 2016/10/18 14:15:33 schwarze Exp $ +$Id: LICENSE,v 1.17 2017/06/23 15:58:14 schwarze Exp $ With the exceptions noted below, all code and documentation -contained in the mdocml toolkit is protected by the Copyright +contained in the mandoc toolkit is protected by the Copyright of the following developers: Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> -Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org> +Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> Copyright (c) 2009, 2010, 2011, 2012 Joerg Sonnenberger <joerg@netbsd.org> Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> Copyright (c) 2014 Baptiste Daroussin <bapt@freebsd.org> Copyright (c) 2016 Ed Maste <emaste@freebsd.org> +Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> Copyright (c) 1998, 2004, 2010 Todd C. Miller <Todd.Miller@courtesan.com> -Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> +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> @@ -21,7 +22,7 @@ See the individual source files for information about who contributed to which file during which years. -The mdocml distribution as a whole is distributed by its developers +The mandoc distribution as a whole is distributed by its developers under the following license: Permission to use, copy, modify, and distribute this software for any diff --git a/contrib/mdocml/Makefile b/contrib/mdocml/Makefile index 5c2657c..9507d99 100644 --- a/contrib/mdocml/Makefile +++ b/contrib/mdocml/Makefile @@ -1,7 +1,7 @@ -# $Id: Makefile,v 1.493 2016/11/19 15:24:51 schwarze Exp $ +# $Id: Makefile,v 1.516 2017/07/20 16:24:53 schwarze Exp $ # # Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> -# Copyright (c) 2011, 2013-2016 Ingo Schwarze <schwarze@openbsd.org> +# Copyright (c) 2011, 2013-2017 Ingo Schwarze <schwarze@openbsd.org> # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -15,11 +15,12 @@ # 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.0 +VERSION = 1.14.2 # === LIST OF FILES ==================================================== TESTSRCS = test-be32toh.c \ + test-cmsg.c \ test-dirent-namlen.c \ test-EFTYPE.c \ test-err.c \ @@ -30,11 +31,14 @@ TESTSRCS = test-be32toh.c \ test-mkdtemp.c \ test-nanosleep.c \ test-ntohl.c \ + test-O_DIRECTORY.c \ test-ohash.c \ test-PATH_MAX.c \ test-pledge.c \ test-progname.c \ + test-recvmsg.c \ test-reallocarray.c \ + test-recallocarray.c \ test-rewb-bsd.c \ test-rewb-sysv.c \ test-sandbox_init.c \ @@ -49,6 +53,7 @@ TESTSRCS = test-be32toh.c \ test-wchar.c SRCS = att.c \ + catman.c \ cgi.c \ chars.c \ compat_err.c \ @@ -60,6 +65,7 @@ SRCS = att.c \ compat_ohash.c \ compat_progname.c \ compat_reallocarray.c \ + compat_recallocarray.c \ compat_strcasestr.c \ compat_stringlist.c \ compat_strlcat.c \ @@ -81,7 +87,6 @@ SRCS = att.c \ lib.c \ main.c \ man.c \ - man_hash.c \ man_html.c \ man_macro.c \ man_term.c \ @@ -89,16 +94,17 @@ SRCS = att.c \ mandoc.c \ mandoc_aux.c \ mandoc_ohash.c \ + mandoc_xr.c \ + mandocd.c \ mandocdb.c \ - manpage.c \ manpath.c \ mansearch.c \ mdoc.c \ mdoc_argv.c \ - mdoc_hash.c \ mdoc_html.c \ mdoc_macro.c \ mdoc_man.c \ + mdoc_markdown.c \ mdoc_state.c \ mdoc_term.c \ mdoc_validate.c \ @@ -107,6 +113,9 @@ SRCS = att.c \ preconv.c \ read.c \ roff.c \ + roff_html.c \ + roff_term.c \ + roff_validate.c \ soelim.c \ st.c \ tag.c \ @@ -119,6 +128,7 @@ SRCS = att.c \ term.c \ term_ascii.c \ term_ps.c \ + term_tab.c \ tree.c DISTFILES = INSTALL \ @@ -128,6 +138,7 @@ DISTFILES = INSTALL \ NEWS \ TODO \ apropos.1 \ + catman.8 \ cgi.h.example \ compat_fts.h \ compat_ohash.h \ @@ -156,6 +167,7 @@ DISTFILES = INSTALL \ man.cgi.8 \ man.conf.5 \ man.h \ + man.options.1 \ manconf.h \ mandoc.1 \ mandoc.3 \ @@ -169,6 +181,8 @@ DISTFILES = INSTALL \ mandoc_html.3 \ mandoc_malloc.3 \ mandoc_ohash.h \ + mandoc_xr.h \ + mandocd.8 \ mansearch.3 \ mansearch.h \ mchars_alloc.3 \ @@ -190,7 +204,6 @@ DISTFILES = INSTALL \ $(TESTSRCS) LIBMAN_OBJS = man.o \ - man_hash.o \ man_macro.o \ man_validate.o @@ -198,7 +211,6 @@ LIBMDOC_OBJS = att.o \ lib.o \ mdoc.o \ mdoc_argv.o \ - mdoc_hash.o \ mdoc_macro.o \ mdoc_state.o \ mdoc_validate.o \ @@ -206,6 +218,7 @@ LIBMDOC_OBJS = att.o \ LIBROFF_OBJS = eqn.o \ roff.o \ + roff_validate.o \ tbl.o \ tbl_data.o \ tbl_layout.o \ @@ -218,6 +231,7 @@ LIBMANDOC_OBJS = $(LIBMAN_OBJS) \ mandoc.o \ mandoc_aux.o \ mandoc_ohash.o \ + mandoc_xr.o \ msec.o \ preconv.o \ read.o @@ -231,6 +245,7 @@ COMPAT_OBJS = compat_err.o \ compat_ohash.o \ compat_progname.o \ compat_reallocarray.o \ + compat_recallocarray.o \ compat_strcasestr.o \ compat_strlcat.o \ compat_strlcpy.o \ @@ -242,16 +257,17 @@ MANDOC_HTML_OBJS = eqn_html.o \ html.o \ man_html.o \ mdoc_html.o \ + roff_html.o \ tbl_html.o -MANDOC_MAN_OBJS = mdoc_man.o - MANDOC_TERM_OBJS = eqn_term.o \ man_term.o \ mdoc_term.o \ + roff_term.o \ term.o \ term_ascii.o \ term_ps.o \ + term_tab.o \ tbl_term.o DBM_OBJS = dbm.o \ @@ -271,6 +287,8 @@ MAIN_OBJS = $(MANDOC_HTML_OBJS) \ $(DBA_OBJS) \ main.o \ manpath.o \ + mdoc_man.o \ + mdoc_markdown.o \ out.o \ tag.o \ tree.o @@ -280,9 +298,11 @@ CGI_OBJS = $(MANDOC_HTML_OBJS) \ cgi.o \ out.o -MANPAGE_OBJS = $(DBM_OBJS) \ - manpage.o \ - manpath.o +MANDOCD_OBJS = $(MANDOC_HTML_OBJS) \ + $(MANDOC_TERM_OBJS) \ + mandocd.o \ + out.o \ + tag.o DEMANDOC_OBJS = demandoc.o @@ -298,6 +318,7 @@ WWW_MANS = apropos.1.html \ man.1.html \ mandoc.1.html \ soelim.1.html \ + man.cgi.3.html \ mandoc.3.html \ mandoc_escape.3.html \ mandoc_headers.3.html \ @@ -311,11 +332,12 @@ 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.3.html \ man.cgi.8.html \ man.h.html \ manconf.h.html \ @@ -325,24 +347,17 @@ WWW_MANS = apropos.1.html \ mdoc.h.html \ roff.h.html -WWW_OBJS = mdocml.tar.gz \ - mdocml.sha256 - # === USER CONFIGURATION =============================================== include Makefile.local # === DEPENDENCY HANDLING ============================================== -all: base-build $(BUILD_TARGETS) Makefile.local - -base-build: mandoc demandoc soelim - -cgi-build: man.cgi +all: mandoc demandoc soelim $(BUILD_TARGETS) Makefile.local install: base-install $(INSTALL_TARGETS) -www: $(WWW_OBJS) $(WWW_MANS) +www: $(WWW_MANS) $(WWW_MANS): mandoc @@ -360,13 +375,13 @@ clean: rm -f libmandoc.a $(LIBMANDOC_OBJS) $(COMPAT_OBJS) rm -f mandoc $(MAIN_OBJS) rm -f man.cgi $(CGI_OBJS) - rm -f manpage $(MANPAGE_OBJS) + rm -f mandocd catman catman.o $(MANDOCD_OBJS) rm -f demandoc $(DEMANDOC_OBJS) rm -f soelim $(SOELIM_OBJS) - rm -f $(WWW_MANS) $(WWW_OBJS) + rm -f $(WWW_MANS) mandoc.tar.gz mandoc.sha256 rm -rf *.dSYM -base-install: base-build +base-install: mandoc demandoc soelim mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(SBINDIR) mkdir -p $(DESTDIR)$(MANDIR)/man1 @@ -375,29 +390,28 @@ base-install: base-build mkdir -p $(DESTDIR)$(MANDIR)/man8 $(INSTALL_PROGRAM) mandoc demandoc $(DESTDIR)$(BINDIR) $(INSTALL_PROGRAM) soelim $(DESTDIR)$(BINDIR)/$(BINM_SOELIM) - ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_MAN) - ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_APROPOS) - ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_WHATIS) - ln -f $(DESTDIR)$(BINDIR)/mandoc \ - $(DESTDIR)$(SBINDIR)/$(BINM_MAKEWHATIS) + cd $(DESTDIR)$(BINDIR) && $(LN) mandoc $(BINM_MAN) + cd $(DESTDIR)$(BINDIR) && $(LN) mandoc $(BINM_APROPOS) + cd $(DESTDIR)$(BINDIR) && $(LN) mandoc $(BINM_WHATIS) + cd $(DESTDIR)$(SBINDIR) && \ + $(LN) ${BIN_FROM_SBIN}/mandoc $(BINM_MAKEWHATIS) $(INSTALL_MAN) mandoc.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1 $(INSTALL_MAN) soelim.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_SOELIM).1 $(INSTALL_MAN) man.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_MAN).1 $(INSTALL_MAN) apropos.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 - ln -f $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 \ - $(DESTDIR)$(MANDIR)/man1/$(BINM_WHATIS).1 - $(INSTALL_MAN) man.conf.5 $(DESTDIR)$(MANDIR)/man5/${MANM_MANCONF}.5 + cd $(DESTDIR)$(MANDIR)/man1 && $(LN) $(BINM_APROPOS).1 $(BINM_WHATIS).1 + $(INSTALL_MAN) man.conf.5 $(DESTDIR)$(MANDIR)/man5/$(MANM_MANCONF).5 $(INSTALL_MAN) mandoc.db.5 $(DESTDIR)$(MANDIR)/man5 - $(INSTALL_MAN) man.7 $(DESTDIR)$(MANDIR)/man7/${MANM_MAN}.7 - $(INSTALL_MAN) mdoc.7 $(DESTDIR)$(MANDIR)/man7/${MANM_MDOC}.7 - $(INSTALL_MAN) roff.7 $(DESTDIR)$(MANDIR)/man7/${MANM_ROFF}.7 - $(INSTALL_MAN) eqn.7 $(DESTDIR)$(MANDIR)/man7/${MANM_EQN}.7 - $(INSTALL_MAN) tbl.7 $(DESTDIR)$(MANDIR)/man7/${MANM_TBL}.7 + $(INSTALL_MAN) man.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_MAN).7 + $(INSTALL_MAN) mdoc.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_MDOC).7 + $(INSTALL_MAN) roff.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_ROFF).7 + $(INSTALL_MAN) eqn.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_EQN).7 + $(INSTALL_MAN) tbl.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_TBL).7 $(INSTALL_MAN) mandoc_char.7 $(DESTDIR)$(MANDIR)/man7 $(INSTALL_MAN) makewhatis.8 \ $(DESTDIR)$(MANDIR)/man8/$(BINM_MAKEWHATIS).8 -lib-install: base-build +lib-install: libmandoc.a mkdir -p $(DESTDIR)$(LIBDIR) mkdir -p $(DESTDIR)$(INCLUDEDIR) mkdir -p $(DESTDIR)$(MANDIR)/man3 @@ -407,13 +421,70 @@ lib-install: base-build $(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \ mansearch.3 mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3 -cgi-install: cgi-build +cgi-install: man.cgi mkdir -p $(DESTDIR)$(CGIBINDIR) mkdir -p $(DESTDIR)$(HTDOCDIR) $(INSTALL_PROGRAM) man.cgi $(DESTDIR)$(CGIBINDIR) $(INSTALL_DATA) mandoc.css $(DESTDIR)$(HTDOCDIR) -Makefile.local config.h: configure ${TESTSRCS} +catman-install: mandocd catman + mkdir -p $(DESTDIR)$(SBINDIR) + mkdir -p $(DESTDIR)$(MANDIR)/man8 + $(INSTALL_PROGRAM) mandocd $(DESTDIR)$(SBINDIR) + $(INSTALL_PROGRAM) catman $(DESTDIR)$(SBINDIR)/$(BINM_CATMAN) + $(INSTALL_MAN) mandocd.8 $(DESTDIR)$(MANDIR)/man8 + $(INSTALL_MAN) catman.8 $(DESTDIR)$(MANDIR)/man8/$(BINM_CATMAN).8 + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/mandoc + rm -f $(DESTDIR)$(BINDIR)/demandoc + rm -f $(DESTDIR)$(BINDIR)/$(BINM_SOELIM) + rm -f $(DESTDIR)$(BINDIR)/$(BINM_MAN) + rm -f $(DESTDIR)$(BINDIR)/$(BINM_APROPOS) + rm -f $(DESTDIR)$(BINDIR)/$(BINM_WHATIS) + rm -f $(DESTDIR)$(SBINDIR)/$(BINM_MAKEWHATIS) + rm -f $(DESTDIR)$(MANDIR)/man1/mandoc.1 + rm -f $(DESTDIR)$(MANDIR)/man1/demandoc.1 + rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_SOELIM).1 + rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_MAN).1 + rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 + rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_WHATIS).1 + rm -f $(DESTDIR)$(MANDIR)/man5/$(MANM_MANCONF).5 + rm -f $(DESTDIR)$(MANDIR)/man5/mandoc.db.5 + rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_MAN).7 + rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_MDOC).7 + rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_ROFF).7 + rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_EQN).7 + rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_TBL).7 + rm -f $(DESTDIR)$(MANDIR)/man7/mandoc_char.7 + rm -f $(DESTDIR)$(MANDIR)/man8/$(BINM_MAKEWHATIS).8 + rm -f $(DESTDIR)$(CGIBINDIR)/man.cgi + rm -f $(DESTDIR)$(HTDOCDIR)/mandoc.css + rm -f $(DESTDIR)$(SBINDIR)/mandocd + rm -f $(DESTDIR)$(SBINDIR)/$(BINM_CATMAN) + rm -f $(DESTDIR)$(MANDIR)/man8/mandocd.8 + rm -f $(DESTDIR)$(MANDIR)/man8/$(BINM_CATMAN).8 + rm -f $(DESTDIR)$(LIBDIR)/libmandoc.a + rm -f $(DESTDIR)$(MANDIR)/man3/mandoc.3 + rm -f $(DESTDIR)$(MANDIR)/man3/mandoc_escape.3 + rm -f $(DESTDIR)$(MANDIR)/man3/mandoc_malloc.3 + 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)/man.h + rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc.h + rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc_aux.h + rm -f $(DESTDIR)$(INCLUDEDIR)/mdoc.h + rm -f $(DESTDIR)$(INCLUDEDIR)/roff.h + [ ! -e $(DESTDIR)$(INCLUDEDIR) ] || rmdir $(DESTDIR)$(INCLUDEDIR) + +regress: all + cd regress && ./regress.pl + +regress-clean: + cd regress && ./regress.pl . clean + +Makefile.local config.h: configure $(TESTSRCS) @echo "$@ is out of date; please run ./configure" @exit 1 @@ -423,12 +494,15 @@ libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS) mandoc: $(MAIN_OBJS) libmandoc.a $(CC) -o $@ $(LDFLAGS) $(MAIN_OBJS) libmandoc.a $(LDADD) -manpage: $(MANPAGE_OBJS) libmandoc.a - $(CC) -o $@ $(LDFLAGS) $(MANPAGE_OBJS) libmandoc.a $(LDADD) - man.cgi: $(CGI_OBJS) libmandoc.a $(CC) $(STATIC) -o $@ $(LDFLAGS) $(CGI_OBJS) libmandoc.a $(LDADD) +mandocd: $(MANDOCD_OBJS) libmandoc.a + $(CC) -o $@ $(LDFLAGS) $(MANDOCD_OBJS) libmandoc.a $(LDADD) + +catman: catman.o libmandoc.a + $(CC) -o $@ $(LDFLAGS) catman.o libmandoc.a $(LDADD) + demandoc: $(DEMANDOC_OBJS) libmandoc.a $(CC) -o $@ $(LDFLAGS) $(DEMANDOC_OBJS) libmandoc.a $(LDADD) @@ -438,13 +512,7 @@ soelim: $(SOELIM_OBJS) # --- maintainer targets --- www-install: www - mkdir -p $(HTDOCDIR)/snapshots $(INSTALL_DATA) $(WWW_MANS) mandoc.css $(HTDOCDIR) - $(INSTALL_DATA) $(WWW_OBJS) $(HTDOCDIR)/snapshots - $(INSTALL_DATA) mdocml.tar.gz \ - $(HTDOCDIR)/snapshots/mdocml-$(VERSION).tar.gz - $(INSTALL_DATA) mdocml.sha256 \ - $(HTDOCDIR)/snapshots/mdocml-$(VERSION).sha256 depend: config.h mkdep -f Makefile.depend $(CFLAGS) $(SRCS) @@ -453,16 +521,43 @@ depend: config.h Makefile.depend > Makefile.tmp mv Makefile.tmp Makefile.depend -dist: mdocml.sha256 - -mdocml.sha256: mdocml.tar.gz - sha256 mdocml.tar.gz > $@ - -mdocml.tar.gz: $(DISTFILES) - mkdir -p .dist/mdocml-$(VERSION)/ - $(INSTALL) -m 0644 $(DISTFILES) .dist/mdocml-$(VERSION) - chmod 755 .dist/mdocml-$(VERSION)/configure - ( cd .dist/ && tar zcf ../$@ mdocml-$(VERSION) ) +regress-distclean: + @find regress \ + -name '.#*' -o \ + -name '*.orig' -o \ + -name '*.rej' -o \ + -name '*.core' \ + -exec rm -i {} \; + +regress-distcheck: + @find regress ! -type d ! -type f + @find regress -type f \ + ! -path '*/CVS/*' \ + ! -name Makefile \ + ! -name Makefile.inc \ + ! -name '*.in' \ + ! -name '*.out_ascii' \ + ! -name '*.out_utf8' \ + ! -name '*.out_html' \ + ! -name '*.out_markdown' \ + ! -name '*.out_lint' \ + ! -path regress/regress.pl \ + ! -path regress/regress.pl.1 + +dist: mandoc.sha256 + +mandoc.sha256: mandoc.tar.gz + sha256 mandoc.tar.gz > $@ + +mandoc.tar.gz: $(DISTFILES) + ls regress/*/*/*.mandoc_* && exit 1 || true + mkdir -p .dist/mandoc-$(VERSION)/ + $(INSTALL) -m 0644 $(DISTFILES) .dist/mandoc-$(VERSION) + cp -pR regress .dist/mandoc-$(VERSION) + find .dist/mandoc-$(VERSION)/regress \ + -type d -name CVS -print0 | xargs -0 rm -rf + chmod 755 .dist/mandoc-$(VERSION)/configure + ( cd .dist/ && tar zcf ../$@ mandoc-$(VERSION) ) rm -rf .dist/ # === SUFFIX RULES ===================================================== diff --git a/contrib/mdocml/Makefile.depend b/contrib/mdocml/Makefile.depend index af1255d..4f5dc80 100644 --- a/contrib/mdocml/Makefile.depend +++ b/contrib/mdocml/Makefile.depend @@ -1,4 +1,5 @@ -att.o: att.c config.h roff.h mdoc.h libmdoc.h +att.o: att.c config.h mandoc.h roff.h mdoc.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 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 @@ -10,6 +11,7 @@ compat_mkdtemp.o: compat_mkdtemp.c config.h compat_ohash.o: compat_ohash.c config.h compat_ohash.h compat_progname.o: compat_progname.c config.h compat_reallocarray.o: compat_reallocarray.c config.h +compat_recallocarray.o: compat_recallocarray.c config.h compat_strcasestr.o: compat_strcasestr.c config.h compat_stringlist.o: compat_stringlist.c config.h compat_stringlist.h compat_strlcat.o: compat_strlcat.c config.h @@ -23,42 +25,45 @@ 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 roff.h man.h mdoc.h mandoc.h -eqn.o: eqn.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.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 -html.o: html.c config.h mandoc.h mandoc_aux.h out.h html.h manconf.h main.h -lib.o: lib.c config.h roff.h mdoc.h libmdoc.h lib.in -main.o: main.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h tag.h main.h manconf.h mansearch.h +html.o: html.c config.h mandoc_aux.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 man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h -man_hash.o: man_hash.c config.h mandoc.h roff.h man.h libmandoc.h libman.h -man_html.o: man_html.c config.h mandoc_aux.h roff.h man.h out.h html.h main.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_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.h mandoc_aux.h libmandoc.h +mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.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 -manpage.o: manpage.c config.h manconf.h mansearch.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 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_hash.o: mdoc_hash.c config.h mandoc.h roff.h mdoc.h libmandoc.h libmdoc.h -mdoc_html.o: mdoc_html.c config.h mandoc_aux.h roff.h mdoc.h out.h html.h main.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_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.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_int.h -roff.o: roff.c config.h mandoc.h mandoc_aux.h roff.h libmandoc.h roff_int.h libroff.h predefs.in +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 +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 roff.h mdoc.h libmdoc.h st.in +st.o: st.c config.h mandoc.h roff.h mdoc.h libmdoc.h st.in 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 @@ -69,4 +74,5 @@ tbl_term.o: tbl_term.c config.h mandoc.h out.h term.h term.o: term.c config.h mandoc.h mandoc_aux.h out.h term.h main.h term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h out.h term.h 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 diff --git a/contrib/mdocml/NEWS b/contrib/mdocml/NEWS index 7f5625a..d62ac4b 100644 --- a/contrib/mdocml/NEWS +++ b/contrib/mdocml/NEWS @@ -1,6 +1,220 @@ -$Id: NEWS,v 1.12 2016/07/14 11:09:06 schwarze Exp $ +$Id: NEWS,v 1.26 2017/07/28 14:57:56 schwarze Exp $ -This file lists the most important changes in the mdocml.bsd.lv distribution. +This file lists the most important changes in the mandoc.bsd.lv distribution. + +Changes in version 1.14.2, released on July 28, 2017 + + --- MAJOR NEW FEATURES --- + * New mdoc(7) -Tmarkdown output mode. + * For -Thtml, implement internal hyperlinks pointing to authoritative + definitions of various syntax elements, similar to the ctags(1)-like + less(1) :t internal searching in terminal mode. + * Provide a superset of the functionality of the former mdoclint(1) + utility and a new -Wstyle message level with several new messages, + including validity checking of .Xr cross references. + * tbl(7): Implement automatic line breaking inside individual table + cells, and several other formatting improvements. + * eqn(7): Complete rewrite of the lexer, resulting in several bugfixes. + * Continue parser unification, in particular allowing generation + of syntax tree nodes on the roff(7) level, allowing implementation + of many additional roff requests. + --- REMOVED FUNCTIONALITY --- + * Delete the manpage(1) utility. It was never enabled in any release. + * Delete the -Txhtml command line option. It has been an obsolete + alias for the -Thtml output mode for more than two years. + --- MINOR NEW FEATURES --- + * -Tlint now puts parser messages on stdout instead of stderr, + making commands like "man -l -Tlint *.1" useful. + * mdoc(7): Various .Lk formatting improvements. + * mdoc(7) -Thtml: Better CSS for .Bl lists. + * man(7): Implement the .MT/.ME block macro (mailto hyperlink). + * man(7): Implement the .DT macro (restore default tab positions). + * man(7): Improved support for manuals generated with reStructuredText + by partial support for the \n[an-margin] number register. + * man(7) -Thtml: Support deep linking to .SH and .SS headers. + * tbl(7): Implement the "allbox" table option. + * tbl(7): Implement the column spacing and the 'w' (minimum column + width) layout modifiers. + * tbl(7): Significant improvements of the manual page. + * eqn(7): Much improved font selection, including recognition of + well-known function names, and a few other formatting improvements. + * eqn(7) -Thtml: Use <mn> and <mo> in addition to <mi>. + * roff(7): Implement the .ce (centering), .mc (margin character), + .rj (right justify), .ta (define tab stops), .ti (temporary indent), + .als (macro alias), .ec and .eo (escape character control), + .po (page offset), and .rn (macro rename) requests. + * roff(7) .am: Implement appending to mdoc(7) and man(7) macros. + * roff(7): implement the \h (horizontol motion), \l (horizontal + line drawing), and \p (break output line) escape sequences, + and also several additional character escape sequences. + * roff(7): Implement the 'd' conditional (macro or string defined). + * man.cgi(8) now uses pledge(2), too. + * regress.pl(1): simpler user interface, better summary output, + simpler code, and no more recursion. + --- THANKS TO --- + * Anthony Bentley (OpenBSD) for the implementation of .MT/.ME, + reports of many bugs and missing features, and suggestions + for a number of feature and documentation improvements. + * Sebastien Marie (OpenBSD) for two source code patches and + for some useful discussions. + * Florian Obser (OpenBSD) for a bugfix patch and a bug report. + * Jonathan Gray (OpenBSD) for several bug reports from afl(1) + and several more from static analysis tools. + * Theo Buehler (OpenBSD) for several bug reports, most from afl(1). + * Jason McIntyre (OpenBSD) for many useful discussions about a + wide variety of topics, lots of continuous testing, a number of + bug reports, and some suggestions for messages and documentation. + * Thomas Klausner (NetBSD) for lots of help while migrating + mdoclint(1) functionality to mandoc -Tlint, for suggesting + several useful new messages, and for release testing. + * Reyk Floeter (OpenBSD) and Vsevolod Stakhov (FreeBSD) for + suggesting a markdown output mode. + * Thomas Guettler for suggesting -Thtml internal hyperlinks. + * Yuri Pankov (Illumos) for inspiring new warning messages and + for extensive release testing. + * Anton Lindqvist and TJ Townsend (both OpenBSD) and Jan Stary + for multiple bug reports. + * Leah Neukirchen (Void Linux) for bug reports and release testing. + * Michael Stapelberg (Debian) for suggesting feature improvements + and for release testing. + * Martin Natano and Theo de Raadt (both OpenBSD), Andreas Voegele, + Gabriel Guzman, Gonzalo Tornaria, Markus Waldeck, and Raf Czlonka + for bug reports. + * Antoine Jacoutot (OpenBSD) and Steffen Nurpmeso for suggesting + feature improvements. + * Dag-Erling Smoergrav (FreeBSD) for inspiring new warning messages. + * Ted Unangst and Marc Espie (OpenBSD) for providing useful ideas. + * Svyatoslav Mishyn (Crux Linux) for release testing. + * Carsten Kunze (Heirloom roff) for help keeping mandoc and groff + compatible and for committing some of my patches to groff. + +Changes in version 1.14.1, released on February 21, 2017 + + --- MAJOR NEW FEATURES --- + * apropos(1): Reimplement complete semantic search functionality + without the dependency on SQLite3, using only POSIX APIs. + This comes with a completely new mandoc.db(5) file format. + * man(1): Support more than one tag entry for the same search term, + plus some minor improvements to the less(1) :t support. + * -Thtml: Use real macro names for CSS classes. + Systematic cleanup of and many improvements to mandoc.css. + * -Thtml: Produce human readable HTML code by using indentation + and better line breaks. Improve various HTML elements, + and trim several useless ones. + * New catman(8) utility, still somewhat experimental. + * Now includes a portable version of the OpenBSD mandoc regression + suite, see regress/regress.pl.1 for details. + --- REMOVED FUNCTIONALITY --- + * Operating systems that don't provide mmap(3) are no longer supported. + * Drop support for manpath(1). Even if your system has manpath(1), + it is simpler to use MANPATH_DEFAULT in configure.local for + operating system defaults, man.conf(5) for machine-specific + modifications, and ${MANPATH}, -m, and -M for user preferences + than to bother with the complexity of manpath(1). + * makewhatis(8) -p: No longer warn about missing MLINKS since these + are no longer needed for anything. + --- MINOR NEW FEATURES --- + * mdoc(7): Warn about invalid punctuation and content below NAME. + * mdoc(7): Warn about .Xr lacking the second argument (section). + * mdoc(7): Warn about violations of the rule "new sentence, new line". + * roff(7): Warn about trailing whitespace at the end of comments. + * mdoc(7): Improve rendering of double quotes. + * mdoc(7): Always do text production in the validator, never in the + formatters. Cleaner, simpler, shorter, helps NetBSD apropos(1) + and also makes -Ttree output more useful. + * -Ttree: Show metadata and some additional node flags. + New -Onoval output option to show the unvalidated tree. + --- RELIABILITY BUGFIXES --- + * man(1): Make "man -l" work with standard input from a pipe or file, + as long as standard output is a terminal. + * man(7): Fix out of bounds read access if a text node immediately + preceded the first .SH header. + * mdoc(7): Fix out of bounds read access for .Bl without a type + but with a width. + * mdoc(7): Fix out of bounds read access for .Bl -column starting + with a tab character instead of a child .It macro. + * mdoc(7): Fix syntax tree corruption leading to segfaults caused + by stray block end macros in nested blocks of mismatching type. + * man(1): Fix NULL dereference when the first of multiple pages + shown was preformatted. + * mdoc(7): Fix syntax tree corruption leading to NULL dereference + caused by partial implicit macros inside .Bl -column table cells. + * mdoc(7): Fix syntax tree corruption leading to NULL dereference + for macro sequences like .Bl .Bl .It Bo .El .It. + * mdoc(7): Fix syntax tree corruption leading to NULL dereference + caused by .Ta following a nested .Bl -column breaking another block. + * mdoc(7): Fix syntax tree corruption sometimes leading to NULL + dereference caused by indirectly broken .Nd or .Nm blocks. + * mdoc(7) -Thtml: Fix a NULL dereference for .Bl -column with 0 columns. + * mdoc(7): Fix NULL dereference in some specific cases of a + block-end macro calling another block-end macro. + * mdoc(7): Fix NULL dereference if the only child of the head + of the first .Sh was an empty in-line macro. + * eqn(7): Fix NULL dereference in the terminal formatter + for empty matrices and empty square roots. + * mdoc(7): Fix an assertion failure for a .Bd without a type that + breaks another block. + * mdoc(7): Fix an assertion failure that happened for some .Bl -column + lists containing a column width of "-4n", "-3n", or "-2n". + * mdoc(7): Fix an assertion failure caused by .Bl -column without .It + but containing eqn(7) or tbl(7) code. + * roff(7): Fix an assertion failure caused by \z\[u00FF] with -Tps/-Tpdf. + * roff(7): Fix an assertion failures caused by whitespace inside \o'' + (overstrike) sequences. + * -Thtml: Fix an assertion failure caused by -Oman or -Oincludes of + excessive length. + --- PORTABILITY IMPROVEMENTS --- + * man(1): Do not mix stdio narrow and wide stream orientation + on stdout, which could cause output corruption on glibc. + * mandoc(1): Autodetect a suitable locale for -Tutf8 mode. + * ./configure: Autodetect whether PATH_MAX and O_DIRECTORY are defined. + * ./configure: Autodetect if nanosleep(3) needs -lrt. + * ./configure: Provide an ${LN} configuration variable. + * ./configure: Put compiler arguments that may contain -l at the end. + --- MINOR BUGFIXES --- + * mdoc(7): Fix SYNOPSIS output if the first child of .Nm is a macro. + * mdoc(7) -Thtml: Improve formatting of .Bl -tag with short tags. + * man(7) -Thtml: Preserve whitespace in .nf (nofill) mode. + * mandoc(1): Error out on invalid output options on the command line. + --- STRUCTURAL CHANGES, no functional change --- + * Redesign part of the mandoc_html(3) interfaces, making them much + easier to use and reducing the amount of code by a few hundred lines. + --- THANKS TO --- + * Michael Stapelberg (Debian) for designing the new mandocd(8) + and parts of the new catman(8), for release testing, and for a + number of patches and bug reports. + * Baptiste Daroussin (FreeBSD) for profiling the new makewhatis(8) + implementation and suggesting an algorithmic improvement which + more than doubled performance, and for a few bug reports. + * Ed Maste (FreeBSD) for an important patch improving reproducibility + of builds in makewhatis(8), and for a few bug reports. + * Theo Buehler (OpenBSD) for almost twenty important bug reports, + most of them found by systematic afl(1) fuzzing. + * Benny Lofgren, David Dahlberg, and in particular Vadim Zhukov + for crucial help in getting .Bl -tag CSS formatting fixed. + * Svyatoslav Mishyn (Crux Linux) for an initial version of the + patch to autodetect a suitable locale for -Tutf8 mode + and for release testing. + * Jason McIntyre (OpenBSD) for multiple useful discussions + and a number of bug reports. + * Sevan Janiyan (NetBSD) for extensive release testing and multiple + bug reports. + * Thomas Klausner and Christos Zoulas (NetBSD), Yuri Pankov (illumos), + and Leah Neukirchen (Void Linux) for release testing and bug reports. + * Ulrich Spoerlein (FreeBSD) for release testing. + * Alexander Bluhm, Andrew Fresh, Antoine Jacoutot, Antony Bentley, + Christian Weisgerber, Jonathan Gray, Marc Espie, Martijn van Duren, + Stuart Henderson, Ted Unangst, Theo de Raadt (OpenBSD), Abhinav + Upadhyay, Kamil Rytarowski (NetBSD), Aaron M. Ucko, Bdale Garbee, + Reiner Herrmann, Shane Kerr (Debian), Daniel Sabogal (Alpine Linux), + Carsten Kunze (Heirloom roff), Kristaps Dzonsons (bsd.lv), + Anton Lindqvist, Jan Stary, Jeremy A. Mates, Mark Patruck, + Pavan Maddamsetti, Sean Levy <attila@stalphonsos.com>, and + Tiago Silva for bug reports. + * Brent Cook, Marc Espie, Philip Guenther, Todd Miller (OpenBSD) + and Markus Waldeck for useful discussions. + * And as usual, OpenCSW for providing me with a Solaris 9/10/11 + testing environment. Changes in version 1.13.4, released on July 14, 2016 @@ -109,7 +323,7 @@ Changes in version 1.13.4, released on July 14, 2016 again resulting in more than half a dozen important bug reports. * Svyatoslav Mishyn (Crux Linux) for some patches, several bug reports, and extensive release testing. - * Christian Neukirchen (void Linux) for a number of compatibility + * Leah Neukirchen (Void Linux) for a number of compatibility patches and suggestions and several bug reports. * Christos Zoulas (NetBSD) for a bug fix patch and some useful suggestions for cleanup. @@ -146,11 +360,11 @@ Changes in version 1.13.3, released on March 13, 2015 * New -Wunsupp message level. --- POTENTIONALLY SECURITY RELEVANT BUGFIXES --- * Fix a potential write buffer overrun on incomplete string conditionals. - http://mdocml.bsd.lv/cgi-bin/cvsweb/roff.c#rev1.241 + http://mandoc.bsd.lv/cgi-bin/cvsweb/roff.c#rev1.241 * Fix a potential write buffer overrun on backslash at EOF in a conditional. - http://mdocml.bsd.lv/cgi-bin/cvsweb/roff.c#rev1.247 + http://mandoc.bsd.lv/cgi-bin/cvsweb/roff.c#rev1.247 * Fix a use after free sometimes hit when validation deletes a block. - http://mdocml.bsd.lv/cgi-bin/cvsweb/mdoc_macro.c#rev1.180 + http://mandoc.bsd.lv/cgi-bin/cvsweb/mdoc_macro.c#rev1.180 --- MAJOR FUNCTIONALLY RELEVANT BUGFIXES --- * Let man(1) show manuals for the current architecture by default, and support the MACHINE environment variable. @@ -761,4 +975,4 @@ Changes in version 1.9.15, released on February 18, 2010 * and column lengths handled correctly. For older releases, see the ChangeLog files -in http://mdocml.bsd.lv/snapshots/ . +in http://mandoc.bsd.lv/snapshots/ . diff --git a/contrib/mdocml/TODO b/contrib/mdocml/TODO index 181c431..70371a0 100644 --- a/contrib/mdocml/TODO +++ b/contrib/mdocml/TODO @@ -1,6 +1,6 @@ ************************************************************************ * Official mandoc TODO. -* $Id: TODO,v 1.223 2017/01/17 15:32:43 schwarze Exp $ +* $Id: TODO,v 1.246 2017/07/24 11:15:12 schwarze Exp $ ************************************************************************ Many issues are annotated for difficulty as follows: @@ -61,37 +61,14 @@ are mere guesses, and some may be wrong. reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 loc *** exist *** algo *** size ** imp * -- .ta (tab settings) - #1 most important issue naddy@ Mon, 16 Feb 2015 20:59:17 +0100 - ircbug(1) gnats(1) reported by brad@ Sat, 15 Jan 2011 15:50:51 -0500 - also Tcl_NewStringObj(3) via wiz@ Wed, 5 Mar 2014 22:27:43 +0100 - also posix2time(3) Carsten Kunze Mon, 1 Dec 2014 13:03:10 +0100 - loc ** exist *** algo ** size ** imp *** - -- .ti (temporary indent) - found by naddy@ in xloadimage(1) [devel/libvstr] vstr(3) - found by bentley@ in nmh(1) Mon, 23 Apr 2012 13:38:28 -0600 - loc ** exist ** algo ** size * imp ** (parser reorg helps a lot) - - .while and .shift found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200 loc * exist ** algo ** size ** imp ** -- \h horizontal move - #2 most important issue naddy@ Mon, 16 Feb 2015 20:59:17 +0100 - found in cclive(1) nasm(1) bogofilter(1) asciidoc/DocBook output - bentley@ on discuss@ Sat, 21 Sep 2013 22:29:34 -0600 - naddy@ Thu, 4 Dec 2014 16:26:41 +0100 - loc ** exist ** algo ** size * imp *** (parser reorg helps a lot) - - \n+ and \n- numerical register increment and decrement found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700 loc * exist * algo * size * imp ** -- \n(.$ macro argument count number register; ocserv(8) by autogen - found by sthen@ Thu, 19 Feb 2015 22:03:01 +0000 - 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 @@ -193,28 +170,8 @@ are mere guesses, and some may be wrong. uqs@ Thu, 2 Jun 2011 11:33:35 +0200 loc * exist ** algo *** size * imp ** ---- missing man features ----------------------------------------------- - -- -T[x]html doesn't stipulate non-collapsing spaces in literal mode - --- missing tbl features ----------------------------------------------- -- horizontal lines in the layout still consume data cells - and can be mixed with actual data on the same table line - synaptics(4) found by tedu@ Mon, 17 Aug 2015 21:17:42 -0400 - loc ** exist ** algo ** size ** imp *** - -- break long text into lines inside cells - net/lftp(1) from jirib via bentley@ Sep 13, 2016 - -- layout l1 for a column of max text width 3 reduces the following - inter-column spacing for groff, but not for mandoc - net/lftp(1) from jirib via bentley@ Sep 13, 2016 - -- the "w" layout option is ignored - synaptics(4) found by tedu@ Mon, 17 Aug 2015 21:17:42 -0400 - loc * exist * algo * size * imp ** - - 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 @@ -223,13 +180,16 @@ are mere guesses, and some may be wrong. - 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; - informed by bapt@ that FreeBSD needs this. + informed by bapt@ that FreeBSD needs this: 3 Jan 2015 23:32:23 +0100 loc *** exist ** algo *** size ** imp *** - look at the POSIX manuals in the books/man-pages-posix port, they use some unsupported tbl(7) features. 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 @@ -253,6 +213,16 @@ are mere guesses, and some may be wrong. see User's Guide (Second Edition) page 5 section 15. loc ** exist ** algo ** size ** imp ** +- GNU eqn converts some operators to special characters, for example, + input HYPHEN-MINUS becomes output \(mi, unless it is part of a + quoted word. mandoc(1) only does this when the operator is + surrounded by blanks, not when it is part of an unquoted word. + Also, check whether there are more such cases (e.g., +?). + reported by bentley@ 20 Jun 2017 02:04:29 -0600 + +- Primes, opprime, and ' + bentley@ Thu, 13 Jul 2017 23:14:20 -0600 + --- missing misc features ---------------------------------------------- - italic correction (\/) in PostScript mode @@ -263,15 +233,6 @@ are mere guesses, and some may be wrong. even for apropos title line output; req by bapt@ loc * exist * algo * size * imp *** -- makewhatis(8) for preformatted pages: - parse the section number from the header line - and compare to the section number from the directory name - loc * exist * algo * size * imp ** - -- Does makewhatis(8) detect missing NAME sections, missing names, - and missing descriptions in all the file formats? - loc * exist * algo * size * imp *** - - clean up escape sequence handling, creating three classes: (1) fully implemented, or parsed and ignored without loss of content (2) unimplemented, potentially causing loss of content @@ -283,12 +244,6 @@ are mere guesses, and some may be wrong. - kettenis wants base roff, ms, and me Fri, 1 Jan 2010 22:13:15 +0100 (CET) loc ** exist ** algo ** size *** imp * -- Vsevolod Stakhov (FreeBSD) needs either a markdown output formatter - for mandoc -mdoc or a markdown to mdoc converter because they - have to maintain manuals needed both in markdown and mdoc format. - Look at the libsoldout (markdown -> whatever) - loc * exist * algo * size ** imp ** - --- compatibility checks ----------------------------------------------- - is .Bk implemented correctly in modern groff? @@ -300,6 +255,8 @@ are mere guesses, and some may be wrong. - look at AT&T DWB http://www2.research.att.com/sw/download Carsten Kunze <carsten dot kunze at arcor dot de> has patches Mon, 4 Aug 2014 17:01:28 +0200 + ported version: https://github.com/n-t-roff/DWB3.3 + Carsten Kunze Wed, 22 Apr 2015 11:21:43 +0200 - look at pages generated from reStructeredText, e.g. devel/mercurial hg(1) These are a weird mixture of man(7) and custom autogenerated low-level @@ -334,6 +291,9 @@ are mere guesses, and some may be wrong. https://github.com/schmonz/ikiwiki/compare/mandoc Amitai Schlair Mon, 19 May 2014 14:05:53 -0400 +- check features of the Slackware man.conf(5) format + Carsten Kunze Wed, 11 Mar 2015 17:57:24 +0100 + ************************************************************************ * formatting issues: ugly output ************************************************************************ @@ -382,6 +342,12 @@ are mere guesses, and some may be wrong. .Vt vs .Vt/.Va vs .Ft/.Va vs .Ft/.Fa ... from kristaps@ Tue, 08 Jun 2010 11:13:32 +0200 +- implicit whitespace around inline equations + example code: where '$times$' denotes matrix multiplication + must not have an HTML line break, nor a blank, before <math> + partial solution: html.c {"math", HTML_NLINSIDE | HTML_INDENT}, + bentley@ Thu, 13 Jul 2017 19:00:59 -0600 + - in enclosures, mandoc sometimes fancies a bogus end of sentence reminded by jmc@ Thu, 23 Sep 2010 18:13:39 +0059 loc * exist ** algo *** size * imp *** @@ -394,7 +360,7 @@ are mere guesses, and some may be wrong. reveals lots of bugs both in groff and mandoc... reported by bentley@ Wed, 22 May 2013 23:49:30 -0600 ---- PDF issues --------------------------------------------------------- +--- PostScript and PDF issues ------------------------------------------ - PDF output doesn't use a monospaced font for .Bd -literal Example: "mandoc -Tpdf afterboot.8 > output.pdf && pdfviewer output.pdf". @@ -404,21 +370,11 @@ are mere guesses, and some may be wrong. instructions from juanfra@ Wed, 11 Jun 2014 02:21:01 +0200 add a new <</Type /Font>> block to the PDF files with /BaseFont /Courier and change the /Name from /F0 to the new font (/F5 (?)). + re-reported by tb@ Mon, 16 Mar 2015 16:47:21 +0100 loc * exist ** algo ** size * imp ** --- HTML issues -------------------------------------------------------- -- <dl><dt><dd> formatting is ugly - hints are easy to find on the web, e.g. - http://stackoverflow.com/questions/1713048/ - see also matthew@ Fri, 18 Jul 2014 19:25:12 -0700 - loc * exist * algo ** size * imp *** - -- In -man -Thtml, .nf does not preserve indentation. - It should either convert blanks to - or use <pre> rather than <div> (like .Bd -literal does). - Reported by afresh1@ 12 Apr 2016 14:35:45 -0700 - - .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 @@ -427,6 +383,9 @@ are mere guesses, and some may be wrong. - jsg on icb, Nov 3, 2014: try to guess Xr in man(7) for hyperlinking + and render them with <a class="Xr" href=...> + https://github.com/Debian/debiman/issues/15 + loc * exist * algo ** size ** imp ** - The tables used to render the three-part page headers actually force the width of the <body> to the max-width given for <html>. @@ -435,9 +394,6 @@ are mere guesses, and some may be wrong. http://undeadly.org/cgi?action=article&sid=20140925064244&pid=1 loc * exist * algo ** size * imp *** -- consider whether <var> can be used for Ar Dv Er Ev Fa Va. - from bentley@ Wed, 13 Aug 2014 09:17:55 -0600 - - generate <img> tags in HTML idea from florian@ Tue, 7 Apr 2015 00:26:28 +0000 may be possible to implement with .Lk img://something.png alt_text @@ -480,16 +436,6 @@ are mere guesses, and some may be wrong. Probably, this should be fixed somewhere in termp_it_pre(), not sure. loc * exist ** algo ** size * imp ** -- .Nx 1.0a - should be "NetBSD 1.0A", not "NetBSD 1.0a", - see OpenBSD ccdconfig(8). - loc * exist * algo * size * imp ** - -- In .Bl -tag, if a tag exceeds the right margin and must be continued - on the next line, it must be indented by -width, not width+1; - see "rule block|pass" in OpenBSD ifconfig(8). - loc * exist *** algo ** size * imp ** - - When the -width string contains macros, the macros must be rendered before measuring the width, for example .Bl -tag -width ".Dv message" @@ -498,6 +444,7 @@ are mere guesses, and some may be wrong. The same applies to .Bl -column column widths; reported again by Nicolas Joly Thu, 1 Mar 2012 13:41:26 +0100 via wiz@ 5 Mar reported again by Franco Fichtner Fri, 27 Sep 2013 21:02:28 +0200 + reported again by Bruce Evans Fri, 17 Feb 2017 21:22:44 +0100 via bapt@ loc *** exist *** algo *** size ** imp *** An easy partial fix would be to just skip the first word if it starts with a dot, including any following white space, when measuring. @@ -508,12 +455,6 @@ are mere guesses, and some may be wrong. we want three blank lines, not two as in mandoc. loc ** exist ** algo ** size * imp ** -- Header lines of excessive length: - Port OpenBSD man_term.c rev. 1.25 to mdoc_term.c - and document it in mdoc(7) and man(7) COMPATIBILITY - found while talking to Chris Bennett - loc * exist * algo * size * imp * - - Sequences of multiple man(7) paragraphs (.PP, .IP) interspersed with .ps and .nf/.fi produce execessive blank lines, see libJudy and graphics/dcmtk. The parser reorg may help with this. @@ -530,25 +471,23 @@ are mere guesses, and some may be wrong. * warning issues ************************************************************************ -- provide a way in mandoc(1) to warn about broken .Xr links; - probably cannot be on by default in -Tlint because it needs - to access the manpath and mandoc.db(3) after parsing. - asked for by jmc@ Fri, 4 Dec 2015 22:39:40 +0000 +- style message about macros inside .Bd -literal and .Dl, in particular + font changing macros like .Cm, .Ar, .Fa (from the mdoclint TODO) -- Report errors in -O suboption parsing. - loc * exist * algo * size * imp ** +- style message about mismatches between the section number in the + file name (if it is known) and the section number in .Dt + (from the mdoclint TODO) + +- style message about NULL without .Dv (from the mdoclint TODO) + +- style message about error constants without .Er (from the mdoclint TODO) - warn when .Sh or .Ss contain other macros Steffen Nurpmeso, savannah.gnu.org/bugs/index.php?45034 loc * exist * algo * size * imp ** -- check that MANDOCERR_BADTAB is thrown in the right cases, - i.e. when finding a literal tab character in fill mode, - and possibly change the wording of the warning message - to refer to fill mode, not literal mode - See the mail from Werner LEMBERG on the groff list, - Fri, 14 Feb 2014 18:54:42 +0100 (CET) - loc * exist ** algo ** size * imp ** +- style message about violations of the convention + .An name Aq Mt localpart@domain in AUTHORS (from the mdoclint TODO) - warn about attempts to call non-callable macros Steffen Nurpmeso Tue, 11 Nov 2014 22:55:16 +0100 @@ -558,34 +497,24 @@ are mere guesses, and some may be wrong. all over mdoc_macro.c and all subtly different. loc ** exist ** algo ** size ** imp ** -- warn about "new sentence, new line" - 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 ** -- integrate mdoclint into mandoc ("end-of-line whitespace" thread) - from jmc@ Mon, 13 Jul 2009 17:12:09 +0100 - from kristaps@ Mon, 13 Jul 2009 18:34:53 +0200 - from jmc@ Mon, 13 Jul 2009 17:45:37 +0059 - from kristaps@ Mon, 13 Jul 2009 19:02:03 +0200 - (mostly done, check what remains) - -- -Tlint parser errors and warnings to stdout - to tech@mdocml, naddy@ Wed, 28 Sep 2011 11:21:46 +0200 - wait! kristaps@ Sun, 02 Oct 2011 17:12:52 +0200 - ************************************************************************ * documentation issues ************************************************************************ -- mention hyphenation rules: - breaking at letter-letter in text mode (not macro args) - proper hyphenation is unimplemented - -- talk about spacing around delimiters - to jmc@, kristaps@ Sat, 23 Apr 2011 17:41:27 +0200 +- 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? @@ -599,21 +528,8 @@ are mere guesses, and some may be wrong. * performance issues ************************************************************************ -- Why are we using MAP_SHARED, not MAP_PRIVATE for mmap(2)? - from kristaps@ Sat, 09 Aug 2014 13:51:36 +0200 - -Several areas can be cleaned up to make mandoc even faster. These are - -- improve hashing mechanism for macros (quite important: performance) - -- improve hashing mechanism for characters (not as important) - - the PDF file is HUGE: this can be reduced by using relative offsets -- instead of re-initialising the roff predefined-strings set before each - parse, create a read-only version the first time and copy it - loc * exist ** algo ** size * imp ** - ************************************************************************ * structural issues ************************************************************************ @@ -629,13 +545,13 @@ Several areas can be cleaned up to make mandoc even faster. These are 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 in roff(7) macro and string expansion. -- Finish cleanup of date handling. - Decide which formats should be recognized where. - Update both mdoc(7) and man(7) documentation. - Triggered by Tim van der Molen Tue, 22 Feb 2011 20:30:45 +0100 +- make buffers for parsing functions const + christos@ via wiz@ Fri, 18 Dec 2015 17:10:01 +0100 - struct mparse refactoring Steffen Nurpmeso Thu, 04 Sep 2014 12:50:00 +0200 @@ -648,9 +564,6 @@ Several areas can be cleaned up to make mandoc even faster. These are output through libz. - Sandbox (see OpenSSH). - Enable caching support via HTTP 304 and If-Modified-Since. - - Allow for cgi.h to be overridden by CGI environment variables. - Otherwise, binary distributions will inherit the compile-time - behaviour, which is not optimal. - Have Mac OSX systems automatically disable -static compilation of the CGI: -static isn't supported. @@ -660,3 +573,10 @@ Several areas can be cleaned up to make mandoc even faster. These are - use uname(1) to set doc-default-operating-system at install time tobimensch Mon, 1 Dec 2014 00:25:07 +0100 + +- apostrophe (39), circumflex (94), grave (96), tilde (126) + in manuals: \(aq, \(ha, \`, \(ti + Re: [Groff] ASCII Minus Sign in man Pages. + bentley@ 26 Apr 2017 10:02:06 -0600 + Do we need to fix existing manuals? + Do we need to fix the definition of the mdoc(7) language? diff --git a/contrib/mdocml/apropos.1 b/contrib/mdocml/apropos.1 index 10ba3c6..16cefbd 100644 --- a/contrib/mdocml/apropos.1 +++ b/contrib/mdocml/apropos.1 @@ -1,7 +1,7 @@ -.\" $Id: apropos.1,v 1.39 2015/04/03 08:46:17 schwarze Exp $ +.\" $Id: apropos.1,v 1.46 2017/07/04 23:40:01 schwarze Exp $ .\" .\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2011, 2012, 2014, 2017 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 3 2015 $ +.Dd $Mdocdate: July 4 2017 $ .Dt APROPOS 1 .Os .Sh NAME @@ -24,7 +24,7 @@ .Nd search manual page databases .Sh SYNOPSIS .Nm -.Op Fl acfhklw +.Op Fl afk .Op Fl C Ar file .Op Fl M Ar path .Op Fl m Ar path @@ -89,12 +89,6 @@ Specify an alternative configuration in .Xr man.conf 5 format. -.It Fl c -In -.Fl a -mode, copy the formatted manual pages to the standard output without using -.Xr more 1 -to paginate them. .It Fl f Search for all words in .Ar expression @@ -102,37 +96,12 @@ in manual page names only. The search is case insensitive and matches whole words only. In this mode, macro keys, comparison operators, and logical operators are not available. -This overrides any earlier -.Fl k -and -.Fl l -options. -.It Fl h -Instead of showing the title lines, show the SYNOPSIS sections, just like -.Xr man 1 -.Fl h -would. .It Fl k Support the full .Ar expression syntax. -This overrides any earlier -.Fl f -and -.Fl l -options. It is the default for .Nm . -.It Fl l -An alias for -.Xr mandoc 1 -.Fl a . -This overrides any earlier -.Fl f , -.Fl k , -and -.Fl w -options. .It Fl M Ar path Use the colon-separated path instead of the default list of paths searched for @@ -162,14 +131,16 @@ By default, pages from all sections are shown. See .Xr man 1 for a listing of sections. -.It Fl w -Instead of showing title lines, show the pathnames of the matching -manual pages, just like -.Xr man 1 -.Fl w -would. .El .Pp +The options +.Fl chlw +are also supported and are documented in +.Xr man 1 . +The options +.Fl fkl +are mutually exclusive and override each other. +.Pp An .Ar expression consists of search terms joined by logical operators @@ -237,7 +208,28 @@ is evaluated case-insensitively. Has no effect on substring terms. .El .Pp -Results are sorted by manual sections and names, with output formatted as +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 +.Xr ascii 7 +alphabetical order, case-insensitive. +.El +.Pp +Each output line is formatted as .Pp .D1 name[, name...](sec) \- description .Pp @@ -341,25 +333,25 @@ Text production: .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER -will be used instead of the standard pagination program, -.Xr more 1 . +is used instead of the standard pagination program, +.Xr more 1 ; +see +.Xr man 1 +for details. +Only used if +.Fl a +or +.Fl l +is specified. .It Ev MANPATH -The standard search path used by +A colon-separated list of directories to search for manual pages; see .Xr man 1 -may be changed by specifying a path in the -.Ev MANPATH -environment variable. -Invalid paths, or paths without manual databases, are ignored. +for details. Overridden by -.Fl M . -If -.Ev MANPATH -begins with a colon, it is appended to the default list; -if it ends with a colon, it is prepended to the default list; -or if it contains two adjacent colons, -the standard search path is inserted between the colons. -If none of these conditions are met, it overrides the -standard search path. +.Fl M , +ignored if +.Fl l +is specified. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER @@ -367,7 +359,12 @@ is not defined. If neither PAGER nor MANPAGER is defined, .Xr more 1 .Fl s -will be used. +is used. +Only used if +.Fl a +or +.Fl l +is specified. .El .Sh FILES .Bl -tag -width "/etc/man.conf" -compact @@ -410,7 +407,7 @@ variables: .Dl $ apropos \-s 3 Va=optind \-a Va=optarg .Pp Do exactly the same as calling -.Xr whatis 1 +.Nm whatis with the argument .Qq ssh : .Pp @@ -468,6 +465,10 @@ and in .Ox 5.6 for .Nm whatis . +The options +.Fl acfhIKklOTWw +appeared in +.Ox 5.7 . .Sh AUTHORS .An -nosplit .An Bill Joy diff --git a/contrib/mdocml/att.c b/contrib/mdocml/att.c index 872f982..dd7f2a0 100644 --- a/contrib/mdocml/att.c +++ b/contrib/mdocml/att.c @@ -1,4 +1,4 @@ -/* $Id: att.c,v 1.15 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: att.c,v 1.16 2017/06/24 14:38:32 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -19,6 +19,7 @@ #include <sys/types.h> #include <string.h> +#include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "libmdoc.h" diff --git a/contrib/mdocml/catman.8 b/contrib/mdocml/catman.8 new file mode 100644 index 0000000..903fa1f --- /dev/null +++ b/contrib/mdocml/catman.8 @@ -0,0 +1,186 @@ +.\" $Id: catman.8,v 1.8 2017/03/18 19:56:01 schwarze Exp $ +.\" +.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 18 2017 $ +.Dt CATMAN 8 +.Os +.Sh NAME +.Nm catman +.Nd format all manual pages below a directory +.Sh SYNOPSIS +.Nm catman +.Op Fl I Cm os Ns = Ns Ar name +.Op Fl T Ar output +.Ar srcdir dstdir +.Sh DESCRIPTION +The +.Nm +utility assumes that all files below +.Ar srcdir +are manual pages in +.Xr mdoc 7 +and +.Xr man 7 +format and formats all of them, storing the formatted versions in +the same relative paths below +.Ar dstdir . +Subdirectories of +.Ar dstdir +are created as needed. +Existing files are not explicitly deleted, but possibly overwritten. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl I Cm os Ns = Ns Ar name +Override the default operating system +.Ar name +for the +.Xr mdoc 7 +.Ic \&Os +and for the +.Xr man 7 +.Ic TH +macro. +.It Fl T Ar output +Output format. +The +.Ar output +argument can be +.Cm ascii , +.Cm utf8 , +or +.Cm html ; +see +.Xr mandoc 1 . +In +.Cm html +output mode, the +.Cm fragment +output option is implied. +Other output options are not supported. +.El +.Sh IMPLEMENTATION NOTES +Since this version avoids +.Xr fork 2 +and +.Xr exec 3 +overhead and uses the much faster +.Sy mandoc +parsers and formatters rather than +.Sy groff , +it may be about one order of magnitude faster than other +.Nm +implementations. +.Sh EXIT STATUS +.Ex -std +.Pp +Possible errors include: +.Bl -bullet +.It +missing, invalid, or excessive command line arguments +.It +failure to change the current working directory to +.Ar srcdir +.It +failure to open +.Ar dstdir +.It +communication failure with +.Xr mandocd 8 +.It +resource exhaustion, for example file descriptor, process table, +or memory exhaustion +.El +.Pp +Except for memory exhaustion and similar system-level failures, +failures while trying to open, read, parse, or format individual +manual pages, to save individual formatted files to the file system, +or even to create directories do not cause +.Nm +to return an error exit status. +In such cases, +.Nm +will simply continue with the next file or subdirectory. +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr mandocd 8 +.Sh HISTORY +A +.Nm +utility first appeared in +.Fx 1.0 . +Other, incompatible implementations appeared in +.Nx 1.0 +and in +.Sy man-db No 2.2 . +.Pp +This version appeared in version 1.14.1 of the +.Sy mandoc +toolkit. +.Sh AUTHORS +.An -nosplit +The first +.Nm +implementation was a short shell script by +.An Christoph Robitschko +in July 1993. +.Pp +The +.Nx +implementations were written by +.An J. T. Conklin Aq Mt jtc@netbsd.org +in 1993, +.An Christian E. Hopps Aq Mt chopps@netbsd.org +in 1994, +and +.An Dante Profeta Aq Mt dante@netbsd.org +in 1999; the +.Sy man-db +implementation by +.An Graeme W. Wilford +in 1994; and the +.Fx +implementations by +.An Wolfram Schneider Aq Mt wosch@freebsd.org +in 1995 and +.An John Rochester Aq Mt john@jrochester.org +in 2002. +.Pp +The concept of the present version was designed and implemented by +.An Michael Stapelberg Aq Mt stapelberg@debian.org +in 2017. +Option and argument handling and directory iteration was added by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +.Sh CAVEATS +All versions of +.Nm +are incompatible with each other because each caters to the needs +of a specific operating system, for example regarding directory +structures and file naming conventions. +.Pp +This version is more flexible than the others in so far as it does +not assume any particular directory structure or naming convention. +That flexibility comes at the price of not being able to change the +names and relative paths of the source files when reusing them to +store the formatted files, of not supporting any configuration file +formats or environment variables, and of being unable to scan for +and remove junk files in +.Ar dstdir . +.Pp +Currently, +.Nm +always reformats each page, even if the formatted version is newer +than the source version. diff --git a/contrib/mdocml/catman.c b/contrib/mdocml/catman.c new file mode 100644 index 0000000..7d62c0e --- /dev/null +++ b/contrib/mdocml/catman.c @@ -0,0 +1,260 @@ +/* $Id: catman.c,v 1.21 2017/02/18 12:24:24 schwarze Exp $ */ +/* + * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> + * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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" + +#if HAVE_CMSG_XPG42 +#define _XPG4_2 +#endif + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#if HAVE_ERR +#include <err.h> +#endif +#include <errno.h> +#include <fcntl.h> +#if HAVE_FTS +#include <fts.h> +#else +#include "compat_fts.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +int process_manpage(int, int, const char *); +int process_tree(int, int); +void run_mandocd(int, const char *, const char *) + __attribute__((__noreturn__)); +ssize_t sock_fd_write(int, int, int, int); +void usage(void) __attribute__((__noreturn__)); + + +void +run_mandocd(int sockfd, const char *outtype, const char* defos) +{ + char sockfdstr[10]; + + if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1) + err(1, "snprintf"); + if (defos == NULL) + execlp("mandocd", "mandocd", "-T", outtype, + sockfdstr, (char *)NULL); + else + execlp("mandocd", "mandocd", "-T", outtype, + "-I", defos, sockfdstr, (char *)NULL); + err(1, "exec"); +} + +ssize_t +sock_fd_write(int fd, int fd0, int fd1, int fd2) +{ + const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */ + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(3 * sizeof(int))]; + } cmsgu; + struct cmsghdr *cmsg; + int *walk; + ssize_t sz; + unsigned char dummy[1] = {'\0'}; + + iov.iov_base = dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + walk = (int *)CMSG_DATA(cmsg); + *(walk++) = fd0; + *(walk++) = fd1; + *(walk++) = fd2; + + /* + * It appears that on some systems, sendmsg(3) + * may return EAGAIN even in blocking mode. + * Seen for example on Oracle Solaris 11.2. + * The sleeping time was chosen by experimentation, + * to neither cause more than a handful of retries + * in normal operation nor unnecessary delays. + */ + for (;;) { + if ((sz = sendmsg(fd, &msg, 0)) != -1 || + errno != EAGAIN) + break; + nanosleep(&timeout, NULL); + } + return sz; +} + +int +process_manpage(int srv_fd, int dstdir_fd, const char *path) +{ + int in_fd, out_fd; + int irc; + + if ((in_fd = open(path, O_RDONLY)) == -1) { + warn("open(%s)", path); + return 0; + } + + if ((out_fd = openat(dstdir_fd, path, + O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { + warn("openat(%s)", path); + close(in_fd); + return 0; + } + + irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO); + + close(in_fd); + close(out_fd); + + if (irc < 0) { + warn("sendmsg"); + return -1; + } + return 0; +} + +int +process_tree(int srv_fd, int dstdir_fd) +{ + FTS *ftsp; + FTSENT *entry; + const char *argv[2]; + const char *path; + + argv[0] = "."; + argv[1] = (char *)NULL; + + if ((ftsp = fts_open((char * const *)argv, + FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) { + warn("fts_open"); + return -1; + } + + while ((entry = fts_read(ftsp)) != NULL) { + path = entry->fts_path + 2; + switch (entry->fts_info) { + case FTS_F: + if (process_manpage(srv_fd, dstdir_fd, path) == -1) { + fts_close(ftsp); + return -1; + } + break; + case FTS_D: + if (*path != '\0' && + mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP | + S_IXGRP | S_IROTH | S_IXOTH) == -1 && + errno != EEXIST) { + warn("mkdirat(%s)", path); + (void)fts_set(ftsp, entry, FTS_SKIP); + } + break; + case FTS_DP: + break; + default: + warnx("%s: not a regular file", path); + break; + } + } + + fts_close(ftsp); + return 0; +} + +int +main(int argc, char **argv) +{ + const char *defos, *outtype; + int srv_fds[2]; + int dstdir_fd; + int opt; + pid_t pid; + + defos = NULL; + outtype = "ascii"; + while ((opt = getopt(argc, argv, "I:T:")) != -1) { + switch (opt) { + case 'I': + defos = optarg; + break; + case 'T': + outtype = optarg; + break; + default: + usage(); + } + } + + if (argc > 0) { + argc -= optind; + argv += optind; + } + if (argc != 2) + usage(); + + if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1) + err(1, "socketpair"); + + pid = fork(); + switch (pid) { + case -1: + err(1, "fork"); + case 0: + close(srv_fds[0]); + run_mandocd(srv_fds[1], outtype, defos); + default: + break; + } + close(srv_fds[1]); + + if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1) + err(1, "open(%s)", argv[1]); + + if (chdir(argv[0]) == -1) + err(1, "chdir(%s)", argv[0]); + + return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0; +} + +void +usage(void) +{ + fprintf(stderr, "usage: %s [-I os=name] [-T output] " + "srcdir dstdir\n", BINM_CATMAN); + exit(1); +} diff --git a/contrib/mdocml/cgi.c b/contrib/mdocml/cgi.c index 06beca3..746c481 100644 --- a/contrib/mdocml/cgi.c +++ b/contrib/mdocml/cgi.c @@ -1,4 +1,4 @@ -/* $Id: cgi.c,v 1.144 2017/01/21 01:20:31 schwarze Exp $ */ +/* $Id: cgi.c,v 1.156 2017/06/24 14:38:32 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@usta.de> @@ -21,7 +21,9 @@ #include <sys/time.h> #include <ctype.h> +#if HAVE_ERR #include <err.h> +#endif #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -74,11 +76,12 @@ static void pg_error_badrequest(const char *); static void pg_error_internal(void); static void pg_index(const struct req *); static void pg_noresult(const struct req *, const char *); +static void pg_redirect(const struct req *, const char *); static void pg_search(const struct req *); static void pg_searchres(const struct req *, struct manpage *, size_t); static void pg_show(struct req *, const char *); -static void resp_begin_html(int, const char *); +static void resp_begin_html(int, const char *, const char *); static void resp_begin_http(int, const char *); static void resp_catman(const struct req *, const char *); static void resp_copy(const char *); @@ -113,7 +116,7 @@ static const char *const sec_names[] = { static const int sec_MAX = sizeof(sec_names) / sizeof(char *); static const char *const arch_names[] = { - "amd64", "alpha", "armv7", + "amd64", "alpha", "armv7", "arm64", "hppa", "i386", "landisk", "loongson", "luna88k", "macppc", "mips64", "octeon", "sgi", "socppc", "sparc64", @@ -137,16 +140,16 @@ html_putchar(char c) { switch (c) { - case ('"'): + case '"': printf("""); break; - case ('&'): + case '&': printf("&"); break; - case ('>'): + case '>': printf(">"); break; - case ('<'): + case '<': printf("<"); break; default: @@ -343,8 +346,9 @@ resp_copy(const char *filename) } static void -resp_begin_html(int code, const char *msg) +resp_begin_html(int code, const char *msg, const char *file) { + char *cp; resp_begin_http(code, msg); @@ -354,10 +358,20 @@ resp_begin_html(int code, const char *msg) " <meta charset=\"UTF-8\"/>\n" " <link rel=\"stylesheet\" href=\"%s/mandoc.css\"" " type=\"text/css\" media=\"all\">\n" - " <title>%s</title>\n" + " <title>", + CSS_DIR); + if (file != NULL) { + if ((cp = strrchr(file, '/')) != NULL) + file = cp + 1; + if ((cp = strrchr(file, '.')) != NULL) { + printf("%.*s(%s) - ", (int)(cp - file), file, cp + 1); + } else + printf("%s - ", file); + } + printf("%s</title>\n" "</head>\n" "<body>\n", - CSS_DIR, CUSTOMIZE_TITLE); + CUSTOMIZE_TITLE); resp_copy(MAN_DIR "/header.html"); } @@ -490,7 +504,7 @@ static void pg_index(const struct req *req) { - resp_begin_html(200, NULL); + resp_begin_html(200, NULL, NULL); resp_searchform(req, FOCUS_QUERY); printf("<p>\n" "This web interface is documented in the\n" @@ -507,7 +521,7 @@ pg_index(const struct req *req) static void pg_noresult(const struct req *req, const char *msg) { - resp_begin_html(200, NULL); + resp_begin_html(200, NULL, NULL); resp_searchform(req, FOCUS_QUERY); puts("<p>"); puts(msg); @@ -519,7 +533,7 @@ static void pg_error_badrequest(const char *msg) { - resp_begin_html(400, "Bad Request"); + resp_begin_html(400, "Bad Request", NULL); puts("<h1>Bad Request</h1>\n" "<p>\n"); puts(msg); @@ -532,12 +546,29 @@ pg_error_badrequest(const char *msg) static void pg_error_internal(void) { - resp_begin_html(500, "Internal Server Error"); + resp_begin_html(500, "Internal Server Error", NULL); puts("<p>Internal Server Error</p>"); resp_end_html(); } static void +pg_redirect(const struct req *req, const char *name) +{ + printf("Status: 303 See Other\r\n" + "Location: /"); + if (*scriptname != '\0') + printf("%s/", scriptname); + if (strcmp(req->q.manpath, req->p[0])) + 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); + printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n"); +} + +static void pg_searchres(const struct req *req, struct manpage *r, size_t sz) { char *arch, *archend; @@ -560,47 +591,25 @@ pg_searchres(const struct req *req, struct manpage *r, size_t sz) * If we have just one result, then jump there now * without any delay. */ - printf("Status: 303 See Other\r\n"); - printf("Location: http://%s/%s%s%s/%s", - HTTP_HOST, scriptname, - *scriptname == '\0' ? "" : "/", - req->q.manpath, r[0].file); - printf("\r\n" - "Content-Type: text/html; charset=utf-8\r\n" - "\r\n"); + printf("Status: 303 See Other\r\n" + "Location: /"); + if (*scriptname != '\0') + printf("%s/", scriptname); + if (strcmp(req->q.manpath, req->p[0])) + printf("%s/", req->q.manpath); + printf("%s\r\n" + "Content-Type: text/html; charset=utf-8\r\n\r\n", + r[0].file); return; } - resp_begin_html(200, NULL); - resp_searchform(req, - req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY); - - if (sz > 1) { - puts("<table class=\"results\">"); - for (i = 0; i < sz; i++) { - printf(" <tr>\n" - " <td>" - "<a class=\"Xr\" href=\"/%s%s%s/%s\">", - scriptname, *scriptname == '\0' ? "" : "/", - req->q.manpath, r[i].file); - html_print(r[i].names); - printf("</a></td>\n" - " <td><span class=\"Nd\">"); - html_print(r[i].output); - puts("</span></td>\n" - " </tr>"); - } - puts("</table>"); - } - /* * In man(1) mode, show one of the pages * even if more than one is found. */ + iuse = 0; if (req->q.equal || sz == 1) { - puts("<hr>"); - iuse = 0; priouse = 20; archpriouse = 3; for (i = 0; i < sz; i++) { @@ -633,6 +642,36 @@ pg_searchres(const struct req *req, struct manpage *r, size_t sz) priouse = prio; iuse = i; } + resp_begin_html(200, NULL, r[iuse].file); + } else + resp_begin_html(200, NULL, NULL); + + resp_searchform(req, + req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY); + + if (sz > 1) { + puts("<table class=\"results\">"); + for (i = 0; i < sz; i++) { + printf(" <tr>\n" + " <td>" + "<a class=\"Xr\" href=\"/"); + if (*scriptname != '\0') + printf("%s/", scriptname); + if (strcmp(req->q.manpath, req->p[0])) + printf("%s/", req->q.manpath); + printf("%s\">", r[i].file); + html_print(r[i].names); + printf("</a></td>\n" + " <td><span class=\"Nd\">"); + html_print(r[i].output); + puts("</span></td>\n" + " </tr>"); + } + puts("</table>"); + } + + if (req->q.equal || sz == 1) { + puts("<hr>"); resp_show(req, r[iuse].file); } @@ -793,14 +832,16 @@ resp_format(const struct req *req, const char *file) mchars_alloc(); mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1, - MANDOCLEVEL_BADARG, NULL, req->q.manpath); + MANDOCERR_MAX, NULL, MANDOC_OS_OTHER, req->q.manpath); mparse_readfd(mp, fd, file); close(fd); memset(&conf, 0, sizeof(conf)); conf.fragment = 1; + conf.style = mandoc_strdup(CSS_DIR "/mandoc.css"); usepath = strcmp(req->q.manpath, req->p[0]); - mandoc_asprintf(&conf.man, "/%s%s%%N.%%S", + mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S", + scriptname, *scriptname == '\0' ? "" : "/", usepath ? req->q.manpath : "", usepath ? "/" : ""); mparse_result(mp, &man, NULL); @@ -826,6 +867,7 @@ resp_format(const struct req *req, const char *file) mparse_free(mp); mchars_free(); free(conf.man); + free(conf.style); } static void @@ -882,7 +924,7 @@ pg_show(struct req *req, const char *fullpath) return; } - resp_begin_html(200, NULL); + resp_begin_html(200, NULL, file); resp_searchform(req, FOCUS_NONE); resp_show(req, file); resp_end_html(); @@ -952,9 +994,13 @@ pg_search(const struct req *req) } } - if (0 == mansearch(&search, &paths, argc, argv, &res, &ressz)) + res = NULL; + ressz = 0; + if (req->isquery && req->q.equal && argc == 1) + pg_redirect(req, argv[0]); + else if (mansearch(&search, &paths, argc, argv, &res, &ressz) == 0) pg_noresult(req, "You entered an invalid query."); - else if (0 == ressz) + else if (ressz == 0) pg_noresult(req, "No results found."); else pg_searchres(req, res, ressz); @@ -974,6 +1020,22 @@ main(void) const char *querystring; int i; +#if HAVE_PLEDGE + /* + * The "rpath" pledge could be revoked after mparse_readfd() + * if the file desciptor to "/footer.html" would be opened + * up front, but it's probably not worth the complication + * of the code it would cause: it would require scattering + * pledge() calls in multiple low-level resp_*() functions. + */ + + if (pledge("stdio rpath", NULL) == -1) { + warn("pledge"); + pg_error_internal(); + return EXIT_FAILURE; + } +#endif + /* Poor man's ReDoS mitigation. */ itimer.it_value.tv_sec = 2; @@ -1011,7 +1073,8 @@ main(void) if (*path != '\0') { parse_path_info(&req, path); - if (req.q.manpath == NULL || access(path, F_OK) == -1) + if (req.q.manpath == NULL || req.q.sec == NULL || + *req.q.query == '\0' || access(path, F_OK) == -1) path = ""; } else if ((querystring = getenv("QUERY_STRING")) != NULL) parse_query_string(&req, querystring); diff --git a/contrib/mdocml/cgi.h.example b/contrib/mdocml/cgi.h.example index 7e5f391..2ccbe25 100644 --- a/contrib/mdocml/cgi.h.example +++ b/contrib/mdocml/cgi.h.example @@ -1,6 +1,5 @@ /* Example compile-time configuration file for man.cgi(8). */ -#define HTTP_HOST "mdocml.bsd.lv" #define SCRIPT_NAME "cgi-bin/man.cgi" #define MAN_DIR "/man" #define CSS_DIR "" diff --git a/contrib/mdocml/chars.c b/contrib/mdocml/chars.c index c2cfaf8..039e6dc 100644 --- a/contrib/mdocml/chars.c +++ b/contrib/mdocml/chars.c @@ -1,7 +1,7 @@ -/* $Id: chars.c,v 1.68 2015/10/13 22:59:54 schwarze Exp $ */ +/* $Id: chars.c,v 1.71 2017/06/14 20:57:07 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,6 +61,7 @@ static struct ln lines[] = { { "ba", "|", 0x007c }, { "br", "|", 0x2502 }, { "ul", "_", 0x005f }, + { "ru", "_", 0x005f }, { "rn", "-", 0x203e }, { "bb", "|", 0x00a6 }, { "sl", "/", 0x002f }, @@ -81,6 +82,10 @@ static struct ln lines[] = { { "sh", "#", 0x0023 }, { "CR", "_|", 0x21b5 }, { "OK", "\\/", 0x2713 }, + { "CL", "<club>", 0x2663 }, + { "SP", "<spade>", 0x2660 }, + { "HE", "<heart>", 0x2665 }, + { "DI", "<diamond>", 0x2666 }, /* Legal symbols. */ { "co", "(C)", 0x00a9 }, @@ -101,8 +106,8 @@ static struct ln lines[] = { { "bq", ",", 0x201a }, { "lq", "\"", 0x201c }, { "rq", "\"", 0x201d }, - { "Lq", "``", 0x201c }, - { "Rq", "''", 0x201d }, + { "Lq", "\"", 0x201c }, + { "Rq", "\"", 0x201d }, { "oq", "`", 0x2018 }, { "cq", "\'", 0x2019 }, { "aq", "\'", 0x0027 }, @@ -161,6 +166,7 @@ static struct ln lines[] = { { "uA", "=\b^", 0x21d1 }, { "dA", "=\bv", 0x21d3 }, { "vA", "^=v", 0x21d5 }, + { "an", "-", 0x23af }, /* Logic. */ { "AN", "^", 0x2227 }, @@ -234,11 +240,20 @@ static struct ln lines[] = { { "Ah", "N", 0x2135 }, { "Im", "I", 0x2111 }, { "Re", "R", 0x211c }, + { "wp", "P", 0x2118 }, { "pd", "a", 0x2202 }, { "-h", "/h", 0x210f }, + { "hbar", "/h", 0x210f }, { "12", "1/2", 0x00bd }, { "14", "1/4", 0x00bc }, { "34", "3/4", 0x00be }, + { "18", "1/8", 0x215B }, + { "38", "3/8", 0x215C }, + { "58", "5/8", 0x215D }, + { "78", "7/8", 0x215E }, + { "S1", "1", 0x00B9 }, + { "S2", "2", 0x00B2 }, + { "S3", "3", 0x00B3 }, /* Ligatures. */ { "ff", "ff", 0xfb00 }, @@ -354,6 +369,8 @@ static struct ln lines[] = { { "fm", "\'", 0x2032 }, { "sd", "''", 0x2033 }, { "mc", ",\bu", 0x00b5 }, + { "Of", "_\ba", 0x00aa }, + { "Om", "_\bo", 0x00ba }, /* Greek characters. */ { "*A", "A", 0x0391 }, diff --git a/contrib/mdocml/compat_fts.c b/contrib/mdocml/compat_fts.c index c2cc957..3859111 100644 --- a/contrib/mdocml/compat_fts.c +++ b/contrib/mdocml/compat_fts.c @@ -6,7 +6,7 @@ int dummy; #else -/* $Id: compat_fts.c,v 1.12 2016/10/18 23:58:12 schwarze Exp $ */ +/* $Id: compat_fts.c,v 1.14 2017/02/18 12:24:24 schwarze Exp $ */ /* $OpenBSD: fts.c,v 1.56 2016/09/21 04:38:56 guenther Exp $ */ /*- @@ -63,15 +63,9 @@ static FTSENT *fts_sort(FTS *, FTSENT *, int); static unsigned short fts_stat(FTS *, FTSENT *); #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) -#ifndef O_DIRECTORY -#define O_DIRECTORY 0 -#endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif #define CLR(opt) (sp->fts_options &= ~(opt)) #define ISSET(opt) (sp->fts_options & (opt)) @@ -84,7 +78,7 @@ fts_open(char * const *argv, int options, FTS *sp; FTSENT *p, *root; int nitems; - FTSENT *parent, *tmp; + FTSENT *parent, *prev; /* Options check. */ if (options & ~FTS_OPTIONMASK) { @@ -117,7 +111,7 @@ fts_open(char * const *argv, int options, parent->fts_level = FTS_ROOTPARENTLEVEL; /* Allocate/initialize root(s). */ - for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + for (root = prev = NULL, nitems = 0; *argv; ++argv, ++nitems) { if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL) goto mem3; p->fts_level = FTS_ROOTLEVEL; @@ -139,11 +133,10 @@ fts_open(char * const *argv, int options, } else { p->fts_link = NULL; if (root == NULL) - tmp = root = p; - else { - tmp->fts_link = p; - tmp = p; - } + root = p; + else + prev->fts_link = p; + prev = p; } } if (compar && nitems > 1) diff --git a/contrib/mdocml/compat_recallocarray.c b/contrib/mdocml/compat_recallocarray.c new file mode 100644 index 0000000..d86dc50 --- /dev/null +++ b/contrib/mdocml/compat_recallocarray.c @@ -0,0 +1,108 @@ +#include "config.h" + +#if HAVE_RECALLOCARRAY + +int dummy; + +#else + +/* $Id: compat_recallocarray.c,v 1.1 2017/06/12 19:05:47 schwarze Exp $ */ +/* $OpenBSD: malloc.c,v 1.225 2017/05/13 07:11:29 otto Exp $ */ +/* + * Copyright (c) 2017 Otto Moerbeek <otto@drijf.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +/* + * Even though specified in POSIX, the PAGESIZE and PAGE_SIZE + * macros have very poor portability. Since we only use this + * to avoid free() overhead for small shrinking, simply pick + * an arbitrary number. + */ +#define MALLOC_PAGESIZE (1UL << 12) + + +void * +recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) +{ + size_t oldsize, newsize; + void *newptr; + + if (ptr == NULL) + return calloc(newnmemb, size); + + if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + newnmemb > 0 && SIZE_MAX / newnmemb < size) { + errno = ENOMEM; + return NULL; + } + newsize = newnmemb * size; + + if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { + errno = EINVAL; + return NULL; + } + oldsize = oldnmemb * size; + + /* + * Don't bother too much if we're shrinking just a bit, + * we do not shrink for series of small steps, oh well. + */ + if (newsize <= oldsize) { + size_t d = oldsize - newsize; + + if (d < oldsize / 2 && d < MALLOC_PAGESIZE) { + memset((char *)ptr + newsize, 0, d); + return ptr; + } + } + + newptr = malloc(newsize); + if (newptr == NULL) + return NULL; + + if (newsize > oldsize) { + memcpy(newptr, ptr, oldsize); + memset((char *)newptr + oldsize, 0, newsize - oldsize); + } else + memcpy(newptr, ptr, newsize); + + /* + * At this point, the OpenBSD implementation calls + * explicit_bzero() on the old memory before it is + * freed. Since explicit_bzero() is hard to implement + * portably and we don't handle confidential data in + * mandoc in the first place, simply free the memory + * without clearing it. + */ + + free(ptr); + + return newptr; +} + +#endif /* !HAVE_RECALLOCARRAY */ diff --git a/contrib/mdocml/config.h b/contrib/mdocml/config.h index 22b9a38d..57d3f31 100644 --- a/contrib/mdocml/config.h +++ b/contrib/mdocml/config.h @@ -10,14 +10,18 @@ #define _GNU_SOURCE /* See test-*.c what needs this. */ #endif +#include <sys/types.h> #define MAN_CONF_FILE "/etc/man.conf" +#define MANPATH_BASE "/usr/share/man" #define MANPATH_DEFAULT "/usr/share/man:/usr/local/man" #define UTF8_LOCALE "en_US.UTF-8" +#define HAVE_CMSG_XPG42 0 #define HAVE_DIRENT_NAMLEN 1 #define HAVE_ENDIAN 0 #define HAVE_ERR 1 #define HAVE_FTS 1 +#define HAVE_FTS_COMPARE_CONST 1 #define HAVE_GETLINE 1 #define HAVE_GETSUBOPT 1 #define HAVE_ISBLANK 1 @@ -26,6 +30,7 @@ #define HAVE_PLEDGE 0 #define HAVE_PROGNAME 1 #define HAVE_REALLOCARRAY 1 +#define HAVE_RECALLOCARRAY 0 #define HAVE_REWB_BSD 1 #define HAVE_REWB_SYSV 1 #define HAVE_SANDBOX_INIT 0 @@ -40,10 +45,11 @@ #define HAVE_VASPRINTF 1 #define HAVE_WCHAR 1 #define HAVE_OHASH 1 -#define HAVE_FTS_COMPARE_CONST 1 #define BINM_APROPOS "apropos" #define BINM_MAKEWHATIS "makewhatis" #define BINM_MAN "man" #define BINM_SOELIM "soelim" #define BINM_WHATIS "whatis" + +extern void *recallocarray(void *, size_t, size_t, size_t); diff --git a/contrib/mdocml/configure b/contrib/mdocml/configure index 1b81015..f9416ce 100755 --- a/contrib/mdocml/configure +++ b/contrib/mdocml/configure @@ -1,8 +1,8 @@ #!/bin/sh # -# $Id: configure,v 1.55 2017/01/12 15:45:05 schwarze Exp $ +# $Id: configure,v 1.64 2017/07/01 09:47:30 schwarze Exp $ # -# Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> +# Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -33,6 +33,9 @@ echo "config.log: writing..." # Initialize all variables here, # such that nothing can leak in from the environment. +SOURCEDIR=`dirname "$0"` + +MANPATH_BASE="/usr/share/man:/usr/X11R6/man" MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man" OSNAME= UTF8_LOCALE= @@ -44,11 +47,15 @@ LDADD= LDFLAGS= LD_NANOSLEEP= LD_OHASH= +LD_RECVMSG= STATIC="-static" BUILD_CGI=0 +BUILD_CATMAN=0 INSTALL_LIBMANDOC=0 +HAVE_CMSG= +HAVE_CMSG_XPG42=0 HAVE_DIRENT_NAMLEN= HAVE_EFTYPE= HAVE_ENDIAN= @@ -61,11 +68,14 @@ HAVE_ISBLANK= HAVE_MKDTEMP= HAVE_NANOSLEEP= HAVE_NTOHL= +HAVE_O_DIRECTORY= HAVE_OHASH= HAVE_PATH_MAX= HAVE_PLEDGE= HAVE_PROGNAME= HAVE_REALLOCARRAY= +HAVE_RECALLOCARRAY= +HAVE_RECVMSG= HAVE_REWB_BSD= HAVE_REWB_SYSV= HAVE_SANDBOX_INIT= @@ -83,6 +93,7 @@ HAVE_WCHAR= PREFIX="/usr/local" BINDIR= SBINDIR= +BIN_FROM_SBIN= INCLUDEDIR= LIBDIR= MANDIR= @@ -93,6 +104,7 @@ HTDOCDIR= CGIBINDIR= BINM_APROPOS="apropos" +BINM_CATMAN="catman" BINM_MAKEWHATIS="makewhatis" BINM_MAN="man" BINM_SOELIM="soelim" @@ -109,6 +121,7 @@ INSTALL_PROGRAM= INSTALL_LIB= INSTALL_MAN= INSTALL_DATA= +LN="ln -f" # --- manual settings from configure.local ----------------------------- @@ -148,7 +161,8 @@ ${1}${3}: testing... ${COMP} -o test-${1} test-${1}.c ${3} __HEREDOC__ - if ${COMP} -o "test-${1}" "test-${1}.c" ${3} 1>&3 2>&3; then + if ${COMP} -o "test-${1}" "${SOURCEDIR}/test-${1}.c" ${3} 1>&3 2>&3 + then echo "${1}${3}: ${CC} succeeded" 1>&3 else echo "${1}${3}: ${CC} failed with $?" 1>&3 @@ -211,11 +225,13 @@ runtest getsubopt GETSUBOPT || true runtest isblank ISBLANK || true runtest mkdtemp MKDTEMP || true runtest ntohl NTOHL || true +runtest O_DIRECTORY O_DIRECTORY || true 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 runtest rewb-bsd REWB_BSD || true runtest rewb-sysv REWB_SYSV || true runtest strcasestr STRCASESTR || true @@ -266,6 +282,36 @@ if [ "${HAVE_NANOSLEEP}" -eq 0 ]; then exit 1 fi +if [ ${BUILD_CATMAN} -gt 0 ]; then + # --- recvmsg --- + if [ -n "${LD_RECVMSG}" ]; then + runtest recvmsg RECVMSG "${LD_RECVMSG}" || true + elif singletest recvmsg RECVMSG; then + : + elif runtest recvmsg RECVMSG "-lsocket"; then + LD_RECVMSG="-lsocket" + fi + if [ "${HAVE_RECVMSG}" -eq 0 ]; then + echo "FATAL: recvmsg: no" 1>&2 + echo "FATAL: recvmsg: no" 1>&3 + echo "Without recvmsg(2), you cannot BUILD_CATMAN." 1>&2 + exit 1 + fi + + # --- cmsg --- + if singletest cmsg CMSG; then + : + elif runtest cmsg CMSG "-D_XPG4_2"; then + HAVE_CMSG_XPG42=1 + fi + if [ "${HAVE_CMSG}" -eq 0 ]; then + echo "FATAL: cmsg: no" 1>&2 + echo "FATAL: cmsg: no" 1>&3 + echo "Without CMSG_FIRSTHDR(3), you cannot BUILD_CATMAN." 1>&2 + exit 1 + fi +fi + # --- ohash --- if ismanual ohash OHASH "${HAVE_OHASH}"; then : @@ -281,7 +327,7 @@ if [ "${HAVE_OHASH}" -eq 0 ]; then fi # --- LDADD --- -LDADD="${LDADD} ${LD_NANOSLEEP} ${LD_OHASH} -lz" +LDADD="${LDADD} ${LD_NANOSLEEP} ${LD_RECVMSG} ${LD_OHASH} -lz" echo "LDADD=\"${LDADD}\"" 1>&2 echo "LDADD=\"${LDADD}\"" 1>&3 echo 1>&3 @@ -305,7 +351,8 @@ cat << __HEREDOC__ __HEREDOC__ -[ ${HAVE_GETLINE} -eq 0 -o ${HAVE_REALLOCARRAY} -eq 0 -o \ +[ ${HAVE_GETLINE} -eq 0 -o \ + ${HAVE_REALLOCARRAY} -eq 0 -o ${HAVE_RECALLOCARRAY} -eq 0 -o \ ${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 ] \ && echo "#include <sys/types.h>" [ ${HAVE_VASPRINTF} -eq 0 ] && echo "#include <stdarg.h>" @@ -313,11 +360,13 @@ __HEREDOC__ echo echo "#define MAN_CONF_FILE \"/etc/${MANM_MANCONF}\"" +echo "#define MANPATH_BASE \"${MANPATH_BASE}\"" echo "#define MANPATH_DEFAULT \"${MANPATH_DEFAULT}\"" [ -n "${OSNAME}" ] && echo "#define OSNAME \"${OSNAME}\"" [ -n "${UTF8_LOCALE}" ] && echo "#define UTF8_LOCALE \"${UTF8_LOCALE}\"" [ -n "${HOMEBREWDIR}" ] && echo "#define HOMEBREWDIR \"${HOMEBREWDIR}\"" [ ${HAVE_EFTYPE} -eq 0 ] && echo "#define EFTYPE EINVAL" +[ ${HAVE_O_DIRECTORY} -eq 0 ] && echo "#define O_DIRECTORY 0" [ ${HAVE_PATH_MAX} -eq 0 ] && echo "#define PATH_MAX 4096" if [ ${HAVE_ENDIAN} -eq 0 -a ${HAVE_SYS_ENDIAN} -eq 0 ]; then echo "#define be32toh ntohl" @@ -325,6 +374,7 @@ if [ ${HAVE_ENDIAN} -eq 0 -a ${HAVE_SYS_ENDIAN} -eq 0 ]; then fi cat << __HEREDOC__ +#define HAVE_CMSG_XPG42 ${HAVE_CMSG_XPG42} #define HAVE_DIRENT_NAMLEN ${HAVE_DIRENT_NAMLEN} #define HAVE_ENDIAN ${HAVE_ENDIAN} #define HAVE_ERR ${HAVE_ERR} @@ -338,6 +388,7 @@ cat << __HEREDOC__ #define HAVE_PLEDGE ${HAVE_PLEDGE} #define HAVE_PROGNAME ${HAVE_PROGNAME} #define HAVE_REALLOCARRAY ${HAVE_REALLOCARRAY} +#define HAVE_RECALLOCARRAY ${HAVE_RECALLOCARRAY} #define HAVE_REWB_BSD ${HAVE_REWB_BSD} #define HAVE_REWB_SYSV ${HAVE_REWB_SYSV} #define HAVE_SANDBOX_INIT ${HAVE_SANDBOX_INIT} @@ -354,6 +405,7 @@ cat << __HEREDOC__ #define HAVE_OHASH ${HAVE_OHASH} #define BINM_APROPOS "${BINM_APROPOS}" +#define BINM_CATMAN "${BINM_CATMAN}" #define BINM_MAKEWHATIS "${BINM_MAKEWHATIS}" #define BINM_MAN "${BINM_MAN}" #define BINM_SOELIM "${BINM_SOELIM}" @@ -388,6 +440,9 @@ fi [ ${HAVE_REALLOCARRAY} -eq 0 ] && \ echo "extern void *reallocarray(void *, size_t, size_t);" +[ ${HAVE_RECALLOCARRAY} -eq 0 ] && \ + echo "extern void *recallocarray(void *, size_t, size_t, size_t);" + [ ${HAVE_STRCASESTR} -eq 0 ] && \ echo "extern char *strcasestr(const char *, const char *);" @@ -413,14 +468,15 @@ echo "config.h: written" 1>&3 exec > Makefile.local -[ -z "${BINDIR}" ] && BINDIR="${PREFIX}/bin" -[ -z "${SBINDIR}" ] && SBINDIR="${PREFIX}/sbin" -[ -z "${INCLUDEDIR}" ] && INCLUDEDIR="${PREFIX}/include/mandoc" -[ -z "${LIBDIR}" ] && LIBDIR="${PREFIX}/lib/mandoc" -[ -z "${MANDIR}" ] && MANDIR="${PREFIX}/man" +[ -z "${BINDIR}" ] && BINDIR="${PREFIX}/bin" +[ -z "${SBINDIR}" ] && SBINDIR="${PREFIX}/sbin" +[ -z "${BIN_FROM_SBIN}" ] && BIN_FROM_SBIN="../bin" +[ -z "${INCLUDEDIR}" ] && INCLUDEDIR="${PREFIX}/include/mandoc" +[ -z "${LIBDIR}" ] && LIBDIR="${PREFIX}/lib/mandoc" +[ -z "${MANDIR}" ] && MANDIR="${PREFIX}/man" -[ -z "${HTDOCDIR}" ] && HTDOCDIR="${WWWPREFIX}/htdocs" -[ -z "${CGIBINDIR}" ] && CGIBINDIR="${WWWPREFIX}/cgi-bin" +[ -z "${HTDOCDIR}" ] && HTDOCDIR="${WWWPREFIX}/htdocs" +[ -z "${CGIBINDIR}" ] && CGIBINDIR="${WWWPREFIX}/cgi-bin" [ -z "${INSTALL_PROGRAM}" ] && INSTALL_PROGRAM="${INSTALL} -m 0555" [ -z "${INSTALL_LIB}" ] && INSTALL_LIB="${INSTALL} -m 0444" @@ -428,10 +484,14 @@ exec > Makefile.local [ -z "${INSTALL_DATA}" ] && INSTALL_DATA="${INSTALL} -m 0444" BUILD_TARGETS= -[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="cgi-build" +[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="man.cgi" +[ ${BUILD_CATMAN} -gt 0 ] && \ + BUILD_TARGETS="${BUILD_TARGETS} mandocd catman" INSTALL_TARGETS= [ ${INSTALL_LIBMANDOC} -gt 0 ] && INSTALL_TARGETS="lib-install" [ ${BUILD_CGI} -gt 0 ] && INSTALL_TARGETS="${INSTALL_TARGETS} cgi-install" +[ ${BUILD_CATMAN} -gt 0 ] && \ + INSTALL_TARGETS="${INSTALL_TARGETS} catman-install" cat << __HEREDOC__ BUILD_TARGETS = ${BUILD_TARGETS} @@ -444,6 +504,7 @@ STATIC = ${STATIC} PREFIX = ${PREFIX} BINDIR = ${BINDIR} SBINDIR = ${SBINDIR} +BIN_FROM_SBIN = ${BIN_FROM_SBIN} INCLUDEDIR = ${INCLUDEDIR} LIBDIR = ${LIBDIR} MANDIR = ${MANDIR} @@ -451,6 +512,7 @@ WWWPREFIX = ${WWWPREFIX} HTDOCDIR = ${HTDOCDIR} CGIBINDIR = ${CGIBINDIR} BINM_APROPOS = ${BINM_APROPOS} +BINM_CATMAN = ${BINM_CATMAN} BINM_MAKEWHATIS = ${BINM_MAKEWHATIS} BINM_MAN = ${BINM_MAN} BINM_SOELIM = ${BINM_SOELIM} @@ -466,6 +528,7 @@ INSTALL_PROGRAM = ${INSTALL_PROGRAM} INSTALL_LIB = ${INSTALL_LIB} INSTALL_MAN = ${INSTALL_MAN} INSTALL_DATA = ${INSTALL_DATA} +LN = ${LN} __HEREDOC__ echo "Makefile.local: written" 1>&2 diff --git a/contrib/mdocml/configure.local.example b/contrib/mdocml/configure.local.example index d5799a5..4a583ad 100644 --- a/contrib/mdocml/configure.local.example +++ b/contrib/mdocml/configure.local.example @@ -1,6 +1,6 @@ -# $Id: configure.local.example,v 1.22 2016/11/19 15:24:51 schwarze Exp $ +# $Id: configure.local.example,v 1.33 2017/07/20 16:24:53 schwarze Exp $ # -# Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> +# Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -20,7 +20,7 @@ # and put any of these settings into it if ./configure autodetection # fails or if you want to make different choices for other reasons. -# If autodetection fails, please tell <tech@mdocml.bsd.lv>. +# If autodetection fails, please tell <tech@mandoc.bsd.lv>. # We recommend that you write ./configure.local from scratch and # only put the lines there you need. This file contains examples. @@ -62,6 +62,11 @@ UTF8_LOCALE=en_US.UTF-8 MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man" +# Validation of cross references with mandoc -Tlint only looks +# for manual pages in the following directories: + +MANPATH_BASE="/usr/share/man:/usr/X11R6/man" + # 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. @@ -85,6 +90,13 @@ BINDIR="${PREFIX}/bin" SBINDIR="${PREFIX}/sbin" MANDIR="${PREFIX}/man" +# If BINDIR and SBINDIR are not subdirectories of the same parent +# directory or if the basename(1) of BINDIR differs from "bin", +# the relative path from SBINDIR to BINDIR is also needed. +# The default is: + +BIN_FROM_SBIN="../bin" + # Some distributions may want to avoid naming conflicts # with the configuration files of other man(1) implementations. # This changes the name of the installed section 5 manual page as well. @@ -119,6 +131,15 @@ BINM_WHATIS=mwhatis # default is "whatis" BINM_MAKEWHATIS=mandocdb # default is "makewhatis" BINM_SOELIM=msoelim # default is "soelim" +# Some distributions do not want hardlinks +# between installed binary programs. +# Set the following variable to use symbolic links instead. +# It is also used for links between manual pages. +# It is only used by the install* targets. +# When using this, DESTDIR must be empty or an absolute path. + +LN="ln -sf" # default is "ln -f" + # Before falling back to the bundled version of the ohash(3) hashing # library, autoconfiguration tries the following linker flag to # link against your system version. If you do have ohash(3) on @@ -140,6 +161,12 @@ CFLAGS="${CFLAGS} -I/usr/local/include" LD_NANOSLEEP="-lrt" +# Some platforms may need an additional linker flag for recvmsg(2). +# If none is needed or it is -lsocket, it is autodetected. +# Otherwise, set the following variable. + +LD_RECVMSG="-lsocket" + # Some platforms might need additional linker flags to link against # libmandoc that are not autodetected, though no such cases are # currently known. @@ -212,6 +239,20 @@ WWWPREFIX="/var/www" HTDOCDIR="${WWWPREFIX}/htdocs" CGIBINDIR="${WWWPREFIX}/cgi-bin" +# --- user settings related to catman ---------------------------------- + +# By default, building mandocd(8) and catman(8) is disabled. +# To enable it, use the following line. +# It does not work on SunOS 5.10 because there is no mkdirat(2) +# nor on SunOS 5.9 which also lacks CMSG_LEN(3) and CMSG_SPACE(3). + +BUILD_CATMAN=1 + +# Install catman(8) with a different name. +# See BINM_MAN above for details of how this works. + +BINM_CATMAN=mcatman # default is "catman" + # --- settings that rarely need to be touched -------------------------- # Do not set these variables unless you really need to. @@ -250,11 +291,13 @@ HAVE_GETSUBOPT=0 HAVE_ISBLANK=0 HAVE_MKDTEMP=0 HAVE_NTOHL=0 +HAVE_O_DIRECTORY=0 HAVE_OHASH=0 HAVE_PATH_MAX=0 HAVE_PLEDGE=0 HAVE_PROGNAME=0 HAVE_REALLOCARRAY=0 +HAVE_RECALLOCARRAY=0 HAVE_REWB_BSD=0 HAVE_REWB_SYSV=0 HAVE_STRCASESTR=0 diff --git a/contrib/mdocml/dba.c b/contrib/mdocml/dba.c index bb1539b..ee43933 100644 --- a/contrib/mdocml/dba.c +++ b/contrib/mdocml/dba.c @@ -1,4 +1,4 @@ -/* $Id: dba.c,v 1.9 2017/01/15 15:28:55 schwarze Exp $ */ +/* $Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp $ */ /* * Copyright (c) 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -315,8 +315,8 @@ compare_names(const void *vp1, const void *vp2) const char *cp1, *cp2; int diff; - cp1 = *(char **)vp1; - cp2 = *(char **)vp2; + cp1 = *(const char * const *)vp1; + cp2 = *(const char * const *)vp2; return (diff = *cp2 - *cp1) ? diff : strcasecmp(cp1 + 1, cp2 + 1); } @@ -326,8 +326,8 @@ compare_strings(const void *vp1, const void *vp2) { const char *cp1, *cp2; - cp1 = *(char **)vp1; - cp2 = *(char **)vp2; + cp1 = *(const char * const *)vp1; + cp2 = *(const char * const *)vp2; return strcmp(cp1, cp2); } @@ -502,7 +502,7 @@ compare_entries(const void *vp1, const void *vp2) { const struct macro_entry *ep1, *ep2; - ep1 = *(struct macro_entry **)vp1; - ep2 = *(struct macro_entry **)vp2; + ep1 = *(const struct macro_entry * const *)vp1; + ep2 = *(const struct macro_entry * const *)vp2; return strcmp(ep1->value, ep2->value); } diff --git a/contrib/mdocml/dbm_map.c b/contrib/mdocml/dbm_map.c index d158302..87c085d 100644 --- a/contrib/mdocml/dbm_map.c +++ b/contrib/mdocml/dbm_map.c @@ -1,4 +1,4 @@ -/* $Id: dbm_map.c,v 1.7 2016/10/22 10:09:27 schwarze Exp $ */ +/* $Id: dbm_map.c,v 1.8 2017/02/17 14:43:54 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -175,7 +175,7 @@ dbm_getint(int32_t offset) int32_t dbm_addr(const void *p) { - return htobe32((char *)p - dbm_base); + return htobe32((const char *)p - dbm_base); } int diff --git a/contrib/mdocml/demandoc.c b/contrib/mdocml/demandoc.c index acddf5a..8b202ab 100644 --- a/contrib/mdocml/demandoc.c +++ b/contrib/mdocml/demandoc.c @@ -1,4 +1,4 @@ -/* $Id: demandoc.c,v 1.28 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: demandoc.c,v 1.29 2017/06/24 14:38:32 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -25,10 +25,10 @@ #include <string.h> #include <unistd.h> +#include "mandoc.h" #include "roff.h" #include "man.h" #include "mdoc.h" -#include "mandoc.h" static void pline(int, int *, int *, int); static void pman(const struct roff_node *, int *, int *, int); @@ -78,7 +78,8 @@ main(int argc, char *argv[]) argv += optind; mchars_alloc(); - mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_BADARG, NULL, NULL); + mp = mparse_alloc(MPARSE_SO, MANDOCERR_MAX, NULL, + MANDOC_OS_OTHER, NULL); assert(mp); if (argc < 1) diff --git a/contrib/mdocml/eqn.7 b/contrib/mdocml/eqn.7 index e3aad72..33d509b 100644 --- a/contrib/mdocml/eqn.7 +++ b/contrib/mdocml/eqn.7 @@ -1,4 +1,4 @@ -.\" $Id: eqn.7,v 1.35 2015/03/30 16:06:14 schwarze Exp $ +.\" $Id: eqn.7,v 1.36 2017/07/20 11:07:27 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 30 2015 $ +.Dd $Mdocdate: July 20 2017 $ .Dt EQN 7 .Os .Sh NAME @@ -125,7 +125,7 @@ int (integral), sum (summation), grad (gradient), del (vector differential), times (multiply), cdot (center-dot), nothing (zero-width space), approx (approximately equals), prime (prime), half (one-half), partial (partial differential), inf (infinity), >> (much greater), << -(much less), \-> (left arrow), <\- (right arrow), +\- (plus-minus), != +(much less), <\- (left arrow), \-> (right arrow), +\- (plus-minus), != (not equal), == (equivalence), <= (less-than-equal), and >= (more-than-equal). The character escape sequences documented in diff --git a/contrib/mdocml/eqn.c b/contrib/mdocml/eqn.c index e9fbdec..01601a7 100644 --- a/contrib/mdocml/eqn.c +++ b/contrib/mdocml/eqn.c @@ -1,7 +1,7 @@ -/* $Id: eqn.c,v 1.61 2016/01/08 00:50:45 schwarze Exp $ */ +/* $Id: eqn.c,v 1.78 2017/07/15 16:26:17 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,14 +20,16 @@ #include <sys/types.h> #include <assert.h> +#include <ctype.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> -#include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc.h" +#include "roff.h" #include "libmandoc.h" #include "libroff.h" @@ -80,9 +82,12 @@ enum eqn_tok { EQN_TOK_TDEFINE, EQN_TOK_NDEFINE, EQN_TOK_UNDEF, - EQN_TOK_EOF, EQN_TOK_ABOVE, - EQN_TOK__MAX + EQN_TOK__MAX, + EQN_TOK_FUNC, + EQN_TOK_QUOTED, + EQN_TOK_SYM, + EQN_TOK_EOF }; static const char *eqn_toks[EQN_TOK__MAX] = { @@ -130,12 +135,18 @@ static const char *eqn_toks[EQN_TOK__MAX] = { "tdefine", /* EQN_TOK_TDEFINE */ "ndefine", /* EQN_TOK_NDEFINE */ "undef", /* EQN_TOK_UNDEF */ - NULL, /* EQN_TOK_EOF */ "above", /* EQN_TOK_ABOVE */ }; +static const char *const eqn_func[] = { + "acos", "acsc", "and", "arc", "asec", "asin", "atan", + "cos", "cosh", "coth", "csc", "det", "exp", "for", + "if", "lim", "ln", "log", "max", "min", + "sec", "sin", "sinh", "tan", "tanh", "Im", "Re", +}; + enum eqn_symt { - EQNSYM_alpha, + EQNSYM_alpha = 0, EQNSYM_beta, EQNSYM_chi, EQNSYM_delta, @@ -266,261 +277,195 @@ static const struct eqnsym eqnsyms[EQNSYM__MAX] = { { "-", "mi" }, /* EQNSYM_minus */ }; +enum parse_mode { + MODE_QUOTED, + MODE_NOSUB, + MODE_SUB, + MODE_TOK +}; + static struct eqn_box *eqn_box_alloc(struct eqn_node *, struct eqn_box *); -static void eqn_box_free(struct eqn_box *); static struct eqn_box *eqn_box_makebinary(struct eqn_node *, - enum eqn_post, struct eqn_box *); + struct eqn_box *); static void eqn_def(struct eqn_node *); -static struct eqn_def *eqn_def_find(struct eqn_node *, const char *, size_t); +static struct eqn_def *eqn_def_find(struct eqn_node *); static void eqn_delim(struct eqn_node *); -static const char *eqn_next(struct eqn_node *, char, size_t *, int); -static const char *eqn_nextrawtok(struct eqn_node *, size_t *); -static const char *eqn_nexttok(struct eqn_node *, size_t *); -static enum rofferr eqn_parse(struct eqn_node *, struct eqn_box *); -static enum eqn_tok eqn_tok_parse(struct eqn_node *, char **); +static enum eqn_tok eqn_next(struct eqn_node *, enum parse_mode); static void eqn_undef(struct eqn_node *); -enum rofferr -eqn_read(struct eqn_node **epp, int ln, - const char *p, int pos, int *offs) +struct eqn_node * +eqn_alloc(struct mparse *parse) { - size_t sz; - struct eqn_node *ep; - enum rofferr er; - - ep = *epp; - - /* - * If we're the terminating mark, unset our equation status and - * validate the full equation. - */ - - if (0 == strncmp(p, ".EN", 3)) { - er = eqn_end(epp); - p += 3; - while (' ' == *p || '\t' == *p) - p++; - if ('\0' == *p) - return er; - mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse, - ln, pos, "EN %s", p); - return er; - } - - /* - * Build up the full string, replacing all newlines with regular - * whitespace. - */ - - sz = strlen(p + pos) + 1; - ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1); + struct eqn_node *ep; - /* First invocation: nil terminate the string. */ - - if (0 == ep->sz) - *ep->data = '\0'; - - ep->sz += sz; - strlcat(ep->data, p + pos, ep->sz + 1); - strlcat(ep->data, " ", ep->sz + 1); - return ROFF_IGN; + ep = mandoc_calloc(1, sizeof(*ep)); + ep->parse = parse; + ep->gsize = EQN_DEFSIZE; + return ep; } -struct eqn_node * -eqn_alloc(int pos, int line, struct mparse *parse) +void +eqn_reset(struct eqn_node *ep) { - struct eqn_node *p; - - p = mandoc_calloc(1, sizeof(struct eqn_node)); + free(ep->data); + ep->data = ep->start = ep->end = NULL; + ep->sz = ep->toksz = 0; +} - p->parse = parse; - p->eqn.ln = line; - p->eqn.pos = pos; - p->gsize = EQN_DEFSIZE; +void +eqn_read(struct eqn_node *ep, const char *p) +{ + char *cp; - return p; + if (ep->data == NULL) { + ep->sz = strlen(p); + ep->data = mandoc_strdup(p); + } else { + ep->sz = mandoc_asprintf(&cp, "%s %s", ep->data, p); + free(ep->data); + ep->data = cp; + } + ep->sz += 1; } /* * Find the key "key" of the give size within our eqn-defined values. */ static struct eqn_def * -eqn_def_find(struct eqn_node *ep, const char *key, size_t sz) +eqn_def_find(struct eqn_node *ep) { int i; for (i = 0; i < (int)ep->defsz; i++) if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, - ep->defs[i].keysz, key, sz)) + ep->defs[i].keysz, ep->start, ep->toksz)) return &ep->defs[i]; return NULL; } /* - * Get the next token from the input stream using the given quote - * character. - * Optionally make any replacements. + * Parse a token from the input text. The modes are: + * MODE_QUOTED: Use *ep->start as the delimiter; the token ends + * before its next occurence. Do not interpret the token in any + * way and return EQN_TOK_QUOTED. All other modes behave like + * MODE_QUOTED when *ep->start is '"'. + * MODE_NOSUB: If *ep->start is a curly brace, the token ends after it; + * otherwise, it ends before the next whitespace or brace. + * Do not interpret the token and return EQN_TOK__MAX. + * MODE_SUB: Like MODE_NOSUB, but try to interpret the token as an + * alias created with define. If it is an alias, replace it with + * its string value and reparse. + * MODE_TOK: Like MODE_SUB, but also check the token against the list + * of tokens, and if there is a match, return that token. Otherwise, + * if the token matches a symbol, return EQN_TOK_SYM; if it matches + * a function name, EQN_TOK_FUNC, or else EQN_TOK__MAX. Except for + * a token match, *ep->start is set to an allocated string that the + * caller is expected to free. + * All modes skip whitespace following the end of the token. */ -static const char * -eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl) +static enum eqn_tok +eqn_next(struct eqn_node *ep, enum parse_mode mode) { - char *start, *next; - int q, diff, lim; - size_t ssz, dummy; - struct eqn_def *def; - - if (NULL == sz) - sz = &dummy; - - lim = 0; - ep->rew = ep->cur; -again: - /* Prevent self-definitions. */ - - if (lim >= EQN_NEST_MAX) { - mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, - ep->eqn.ln, ep->eqn.pos, NULL); - return NULL; - } - - ep->cur = ep->rew; - start = &ep->data[(int)ep->cur]; - q = 0; + static int last_len, lim; - if ('\0' == *start) - return NULL; - - if (quote == *start) { - ep->cur++; - q = 1; - } - - start = &ep->data[(int)ep->cur]; - - if ( ! q) { - if ('{' == *start || '}' == *start) - ssz = 1; - else - ssz = strcspn(start + 1, " ^~\"{}\t") + 1; - next = start + (int)ssz; - if ('\0' == *next) - next = NULL; - } else - next = strchr(start, quote); - - if (NULL != next) { - *sz = (size_t)(next - start); - ep->cur += *sz; - if (q) - ep->cur++; - while (' ' == ep->data[(int)ep->cur] || - '\t' == ep->data[(int)ep->cur] || - '^' == ep->data[(int)ep->cur] || - '~' == ep->data[(int)ep->cur]) - ep->cur++; - } else { - if (q) - mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, - ep->eqn.ln, ep->eqn.pos, NULL); - next = strchr(start, '\0'); - *sz = (size_t)(next - start); - ep->cur += *sz; - } - - /* Quotes aren't expanded for values. */ - - if (q || ! repl) - return start; + struct eqn_def *def; + size_t start; + int diff, i, quoted; + enum eqn_tok tok; - if (NULL != (def = eqn_def_find(ep, start, *sz))) { - diff = def->valsz - *sz; + /* + * Reset the recursion counter after advancing + * beyond the end of the previous substitution. + */ + if (ep->end - ep->data >= last_len) + lim = 0; + + ep->start = ep->end; + quoted = mode == MODE_QUOTED; + for (;;) { + switch (*ep->start) { + case '\0': + ep->toksz = 0; + return EQN_TOK_EOF; + case '"': + quoted = 1; + break; + default: + break; + } + if (quoted) { + ep->end = strchr(ep->start + 1, *ep->start); + ep->start++; /* Skip opening quote. */ + if (ep->end == NULL) { + mandoc_msg(MANDOCERR_ARG_QUOTE, ep->parse, + ep->node->line, ep->node->pos, NULL); + ep->end = strchr(ep->start, '\0'); + } + } else { + ep->end = ep->start + 1; + if (*ep->start != '{' && *ep->start != '}') + ep->end += strcspn(ep->end, " ^~\"{}\t"); + } + ep->toksz = ep->end - ep->start; + if (quoted && *ep->end != '\0') + ep->end++; /* Skip closing quote. */ + while (*ep->end != '\0' && strchr(" \t^~", *ep->end) != NULL) + ep->end++; + if (quoted) /* Cannot return, may have to strndup. */ + break; + if (mode == MODE_NOSUB) + return EQN_TOK__MAX; + if ((def = eqn_def_find(ep)) == NULL) + break; + if (++lim > EQN_NEST_MAX) { + mandoc_msg(MANDOCERR_ROFFLOOP, ep->parse, + ep->node->line, ep->node->pos, NULL); + return EQN_TOK_EOF; + } - if (def->valsz > *sz) { + /* Replace a defined name with its string value. */ + if ((diff = def->valsz - ep->toksz) > 0) { + start = ep->start - ep->data; ep->sz += diff; ep->data = mandoc_realloc(ep->data, ep->sz + 1); - ep->data[ep->sz] = '\0'; - start = &ep->data[(int)ep->rew]; + ep->start = ep->data + start; } - - diff = def->valsz - *sz; - memmove(start + *sz + diff, start + *sz, - (strlen(start) - *sz) + 1); - memcpy(start, def->val, def->valsz); - lim++; - goto again; + if (diff) + memmove(ep->start + def->valsz, ep->start + ep->toksz, + strlen(ep->start + ep->toksz) + 1); + memcpy(ep->start, def->val, def->valsz); + last_len = ep->start - ep->data + def->valsz; } - - return start; -} - -/* - * Get the next delimited token using the default current quote - * character. - */ -static const char * -eqn_nexttok(struct eqn_node *ep, size_t *sz) -{ - - return eqn_next(ep, '"', sz, 1); -} - -/* - * Get next token without replacement. - */ -static const char * -eqn_nextrawtok(struct eqn_node *ep, size_t *sz) -{ - - return eqn_next(ep, '"', sz, 0); -} - -/* - * Parse a token from the stream of text. - * A token consists of one of the recognised eqn(7) strings. - * Strings are separated by delimiting marks. - * This returns EQN_TOK_EOF when there are no more tokens. - * If the token is an unrecognised string literal, then it returns - * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated - * string. - * This must be later freed with free(3). - */ -static enum eqn_tok -eqn_tok_parse(struct eqn_node *ep, char **p) -{ - const char *start; - size_t i, sz; - int quoted; - - if (NULL != p) - *p = NULL; - - quoted = ep->data[ep->cur] == '"'; - - if (NULL == (start = eqn_nexttok(ep, &sz))) - return EQN_TOK_EOF; - + if (mode != MODE_TOK) + return quoted ? EQN_TOK_QUOTED : EQN_TOK__MAX; if (quoted) { - if (p != NULL) - *p = mandoc_strndup(start, sz); - return EQN_TOK__MAX; + ep->start = mandoc_strndup(ep->start, ep->toksz); + return EQN_TOK_QUOTED; } - - for (i = 0; i < EQN_TOK__MAX; i++) { - if (NULL == eqn_toks[i]) - continue; - if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i]))) - break; + for (tok = 0; tok < EQN_TOK__MAX; tok++) + if (STRNEQ(ep->start, ep->toksz, + eqn_toks[tok], strlen(eqn_toks[tok]))) + return tok; + + for (i = 0; i < EQNSYM__MAX; i++) { + if (STRNEQ(ep->start, ep->toksz, + eqnsyms[i].str, strlen(eqnsyms[i].str))) { + mandoc_asprintf(&ep->start, + "\\[%s]", eqnsyms[i].sym); + return EQN_TOK_SYM; + } } - - if (i == EQN_TOK__MAX && NULL != p) - *p = mandoc_strndup(start, sz); - - return i; + ep->start = mandoc_strndup(ep->start, ep->toksz); + for (i = 0; i < (int)(sizeof(eqn_func)/sizeof(*eqn_func)); i++) + if (STRNEQ(ep->start, ep->toksz, + eqn_func[i], strlen(eqn_func[i]))) + return EQN_TOK_FUNC; + return EQN_TOK__MAX; } -static void +void eqn_box_free(struct eqn_box *bp) { @@ -549,6 +494,7 @@ eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) bp->parent = parent; bp->parent->args++; bp->expectargs = UINT_MAX; + bp->font = bp->parent->font; bp->size = ep->gsize; if (NULL != parent->first) { @@ -568,8 +514,7 @@ eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) * The new EQN_SUBEXPR will have a two-child limit. */ static struct eqn_box * -eqn_box_makebinary(struct eqn_node *ep, - enum eqn_post pos, struct eqn_box *parent) +eqn_box_makebinary(struct eqn_node *ep, struct eqn_box *parent) { struct eqn_box *b, *newb; @@ -581,7 +526,6 @@ eqn_box_makebinary(struct eqn_node *ep, parent->last = b->prev; b->prev = NULL; newb = eqn_box_alloc(ep, parent); - newb->pos = pos; newb->type = EQN_SUBEXPR; newb->expectargs = 2; newb->args = 1; @@ -597,20 +541,21 @@ eqn_box_makebinary(struct eqn_node *ep, static void eqn_delim(struct eqn_node *ep) { - const char *start; - size_t sz; - - if ((start = eqn_nextrawtok(ep, &sz)) == NULL) + if (ep->end[0] == '\0' || ep->end[1] == '\0') { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, "delim"); - else if (strncmp(start, "off", 3) == 0) + ep->node->line, ep->node->pos, "delim"); + if (ep->end[0] != '\0') + ep->end++; + } else if (strncmp(ep->end, "off", 3) == 0) { ep->delim = 0; - else if (strncmp(start, "on", 2) == 0) { + ep->end += 3; + } else if (strncmp(ep->end, "on", 2) == 0) { if (ep->odelim && ep->cdelim) ep->delim = 1; - } else if (start[1] != '\0') { - ep->odelim = start[0]; - ep->cdelim = start[1]; + ep->end += 2; + } else { + ep->odelim = *ep->end++; + ep->cdelim = *ep->end++; ep->delim = 1; } } @@ -621,16 +566,14 @@ eqn_delim(struct eqn_node *ep) static void eqn_undef(struct eqn_node *ep) { - const char *start; struct eqn_def *def; - size_t sz; - if ((start = eqn_nextrawtok(ep, &sz)) == NULL) { + if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, "undef"); + ep->node->line, ep->node->pos, "undef"); return; } - if ((def = eqn_def_find(ep, start, sz)) == NULL) + if ((def = eqn_def_find(ep)) == NULL) return; free(def->key); free(def->val); @@ -641,14 +584,12 @@ eqn_undef(struct eqn_node *ep) static void eqn_def(struct eqn_node *ep) { - const char *start; - size_t sz; struct eqn_def *def; int i; - if ((start = eqn_nextrawtok(ep, &sz)) == NULL) { + if (eqn_next(ep, MODE_NOSUB) == EQN_TOK_EOF) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, "define"); + ep->node->line, ep->node->pos, "define"); return; } @@ -656,7 +597,7 @@ eqn_def(struct eqn_node *ep) * Search for a key that already exists. * Create a new key if none is found. */ - if (NULL == (def = eqn_def_find(ep, start, sz))) { + if ((def = eqn_def_find(ep)) == NULL) { /* Find holes in string array. */ for (i = 0; i < (int)ep->defsz; i++) if (0 == ep->defs[i].keysz) @@ -671,14 +612,13 @@ eqn_def(struct eqn_node *ep) def = ep->defs + i; free(def->key); - def->key = mandoc_strndup(start, sz); - def->keysz = sz; + def->key = mandoc_strndup(ep->start, ep->toksz); + def->keysz = ep->toksz; } - start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0); - if (start == NULL) { + if (eqn_next(ep, MODE_QUOTED) == EQN_TOK_EOF) { mandoc_vmsg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, "define %s", def->key); + ep->node->line, ep->node->pos, "define %s", def->key); free(def->key); free(def->val); def->key = def->val = NULL; @@ -686,25 +626,21 @@ eqn_def(struct eqn_node *ep) return; } free(def->val); - def->val = mandoc_strndup(start, sz); - def->valsz = sz; + def->val = mandoc_strndup(ep->start, ep->toksz); + def->valsz = ep->toksz; } -/* - * Recursively parse an eqn(7) expression. - */ -static enum rofferr -eqn_parse(struct eqn_node *ep, struct eqn_box *parent) +void +eqn_parse(struct eqn_node *ep) { - char sym[64]; - struct eqn_box *cur; - const char *start; + struct eqn_box *cur, *nbox, *parent, *split; + const char *cp, *cpn; char *p; - size_t i, sz; - enum eqn_tok tok, subtok; - enum eqn_post pos; + enum eqn_tok tok; + enum { CCL_LET, CCL_DIG, CCL_PUN } ccl, ccln; int size; + parent = ep->node->eqn; assert(parent != NULL); /* @@ -713,119 +649,99 @@ eqn_parse(struct eqn_node *ep, struct eqn_box *parent) */ if (ep->data == NULL) - return ROFF_IGN; + return; -next_tok: - tok = eqn_tok_parse(ep, &p); + ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); -this_tok: +next_tok: + tok = eqn_next(ep, MODE_TOK); switch (tok) { - case (EQN_TOK_UNDEF): + case EQN_TOK_UNDEF: eqn_undef(ep); break; - case (EQN_TOK_NDEFINE): - case (EQN_TOK_DEFINE): + case EQN_TOK_NDEFINE: + case EQN_TOK_DEFINE: eqn_def(ep); break; - case (EQN_TOK_TDEFINE): - if (eqn_nextrawtok(ep, NULL) == NULL || - eqn_next(ep, ep->data[(int)ep->cur], NULL, 0) == NULL) + 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, - ep->eqn.ln, ep->eqn.pos, "tdefine"); + ep->node->line, ep->node->pos, "tdefine"); break; - case (EQN_TOK_DELIM): + case EQN_TOK_DELIM: eqn_delim(ep); break; - case (EQN_TOK_GFONT): - if (eqn_nextrawtok(ep, NULL) == NULL) + case EQN_TOK_GFONT: + if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); break; - case (EQN_TOK_MARK): - case (EQN_TOK_LINEUP): + case EQN_TOK_MARK: + case EQN_TOK_LINEUP: /* Ignore these. */ break; - case (EQN_TOK_DYAD): - case (EQN_TOK_VEC): - case (EQN_TOK_UNDER): - case (EQN_TOK_BAR): - case (EQN_TOK_TILDE): - case (EQN_TOK_HAT): - case (EQN_TOK_DOT): - case (EQN_TOK_DOTDOT): + case EQN_TOK_DYAD: + case EQN_TOK_VEC: + case EQN_TOK_UNDER: + case EQN_TOK_BAR: + case EQN_TOK_TILDE: + case EQN_TOK_HAT: + case EQN_TOK_DOT: + case EQN_TOK_DOTDOT: if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); } - parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent); - parent->type = EQN_LISTONE; + parent = eqn_box_makebinary(ep, parent); + parent->type = EQN_LIST; parent->expectargs = 1; + parent->font = EQNFONT_ROMAN; switch (tok) { - case (EQN_TOK_DOTDOT): - strlcpy(sym, "\\[ad]", sizeof(sym)); + case EQN_TOK_DOTDOT: + parent->top = mandoc_strdup("\\[ad]"); break; - case (EQN_TOK_VEC): - strlcpy(sym, "\\[->]", sizeof(sym)); + case EQN_TOK_VEC: + parent->top = mandoc_strdup("\\[->]"); break; - case (EQN_TOK_DYAD): - strlcpy(sym, "\\[<>]", sizeof(sym)); + case EQN_TOK_DYAD: + parent->top = mandoc_strdup("\\[<>]"); break; - case (EQN_TOK_TILDE): - strlcpy(sym, "\\[a~]", sizeof(sym)); + case EQN_TOK_TILDE: + parent->top = mandoc_strdup("\\[a~]"); break; - case (EQN_TOK_UNDER): - strlcpy(sym, "\\[ul]", sizeof(sym)); + case EQN_TOK_UNDER: + parent->bottom = mandoc_strdup("\\[ul]"); break; - case (EQN_TOK_BAR): - strlcpy(sym, "\\[rl]", sizeof(sym)); + case EQN_TOK_BAR: + parent->top = mandoc_strdup("\\[rn]"); break; - case (EQN_TOK_DOT): - strlcpy(sym, "\\[a.]", sizeof(sym)); + case EQN_TOK_DOT: + parent->top = mandoc_strdup("\\[a.]"); break; - case (EQN_TOK_HAT): - strlcpy(sym, "\\[ha]", sizeof(sym)); - break; - default: - abort(); - } - - switch (tok) { - case (EQN_TOK_DOTDOT): - case (EQN_TOK_VEC): - case (EQN_TOK_DYAD): - case (EQN_TOK_TILDE): - case (EQN_TOK_BAR): - case (EQN_TOK_DOT): - case (EQN_TOK_HAT): - parent->top = mandoc_strdup(sym); - break; - case (EQN_TOK_UNDER): - parent->bottom = mandoc_strdup(sym); + case EQN_TOK_HAT: + parent->top = mandoc_strdup("\\[ha]"); break; default: abort(); } parent = parent->parent; break; - case (EQN_TOK_FWD): - case (EQN_TOK_BACK): - case (EQN_TOK_DOWN): - case (EQN_TOK_UP): - subtok = eqn_tok_parse(ep, NULL); - if (subtok != EQN_TOK__MAX) { + case EQN_TOK_FWD: + case EQN_TOK_BACK: + 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->eqn.ln, ep->eqn.pos, eqn_toks[tok]); - tok = subtok; - goto this_tok; - } + ep->node->line, ep->node->pos, eqn_toks[tok]); break; - case (EQN_TOK_FAT): - case (EQN_TOK_ROMAN): - case (EQN_TOK_ITALIC): - case (EQN_TOK_BOLD): + case EQN_TOK_FAT: + case EQN_TOK_ROMAN: + case EQN_TOK_ITALIC: + case EQN_TOK_BOLD: while (parent->args == parent->expectargs) parent = parent->parent; /* @@ -834,52 +750,54 @@ this_tok: * exactly one of those. */ parent = eqn_box_alloc(ep, parent); - parent->type = EQN_LISTONE; + parent->type = EQN_LIST; parent->expectargs = 1; switch (tok) { - case (EQN_TOK_FAT): + case EQN_TOK_FAT: parent->font = EQNFONT_FAT; break; - case (EQN_TOK_ROMAN): + case EQN_TOK_ROMAN: parent->font = EQNFONT_ROMAN; break; - case (EQN_TOK_ITALIC): + case EQN_TOK_ITALIC: parent->font = EQNFONT_ITALIC; break; - case (EQN_TOK_BOLD): + case EQN_TOK_BOLD: parent->font = EQNFONT_BOLD; break; default: abort(); } break; - case (EQN_TOK_SIZE): - case (EQN_TOK_GSIZE): + case EQN_TOK_SIZE: + case EQN_TOK_GSIZE: /* Accept two values: integral size and a single. */ - if (NULL == (start = eqn_nexttok(ep, &sz))) { + if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); break; } - size = mandoc_strntoi(start, sz, 10); + size = mandoc_strntoi(ep->start, ep->toksz, 10); if (-1 == size) { mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); break; } if (EQN_TOK_GSIZE == tok) { ep->gsize = size; break; } + while (parent->args == parent->expectargs) + parent = parent->parent; parent = eqn_box_alloc(ep, parent); - parent->type = EQN_LISTONE; + parent->type = EQN_LIST; parent->expectargs = 1; parent->size = size; break; - case (EQN_TOK_FROM): - case (EQN_TOK_TO): - case (EQN_TOK_SUB): - case (EQN_TOK_SUP): + case EQN_TOK_FROM: + case EQN_TOK_TO: + case EQN_TOK_SUB: + case EQN_TOK_SUP: /* * We have a left-right-associative expression. * Repivot under a positional node, open a child scope @@ -887,41 +805,53 @@ this_tok: */ if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); } - /* Handle the "subsup" and "fromto" positions. */ - if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) { + while (parent->expectargs == 1 && parent->args == 1) + parent = parent->parent; + if (tok == EQN_TOK_FROM || tok == EQN_TOK_TO) { + for (cur = parent; cur != NULL; cur = cur->parent) + if (cur->pos == EQNPOS_SUB || + cur->pos == EQNPOS_SUP || + cur->pos == EQNPOS_SUBSUP || + cur->pos == EQNPOS_SQRT || + cur->pos == EQNPOS_OVER) + break; + if (cur != NULL) + parent = cur->parent; + } + if (tok == EQN_TOK_SUP && parent->pos == EQNPOS_SUB) { parent->expectargs = 3; parent->pos = EQNPOS_SUBSUP; break; } - if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) { + if (tok == EQN_TOK_TO && parent->pos == EQNPOS_FROM) { parent->expectargs = 3; parent->pos = EQNPOS_FROMTO; break; } + parent = eqn_box_makebinary(ep, parent); switch (tok) { - case (EQN_TOK_FROM): - pos = EQNPOS_FROM; + case EQN_TOK_FROM: + parent->pos = EQNPOS_FROM; break; - case (EQN_TOK_TO): - pos = EQNPOS_TO; + case EQN_TOK_TO: + parent->pos = EQNPOS_TO; break; - case (EQN_TOK_SUP): - pos = EQNPOS_SUP; + case EQN_TOK_SUP: + parent->pos = EQNPOS_SUP; break; - case (EQN_TOK_SUB): - pos = EQNPOS_SUB; + case EQN_TOK_SUB: + parent->pos = EQNPOS_SUB; break; default: abort(); } - parent = eqn_box_makebinary(ep, pos, parent); break; - case (EQN_TOK_SQRT): + case EQN_TOK_SQRT: while (parent->args == parent->expectargs) parent = parent->parent; /* @@ -934,7 +864,7 @@ this_tok: parent->pos = EQNPOS_SQRT; parent->expectargs = 1; break; - case (EQN_TOK_OVER): + case EQN_TOK_OVER: /* * We have a right-left-associative fraction. * Close out anything that's currently open, then @@ -942,17 +872,20 @@ this_tok: */ if (parent->last == NULL) { mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; cur->text = mandoc_strdup(""); } + while (parent->args == parent->expectargs) + parent = parent->parent; while (EQN_SUBEXPR == parent->type) parent = parent->parent; - parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent); + parent = eqn_box_makebinary(ep, parent); + parent->pos = EQNPOS_OVER; break; - case (EQN_TOK_RIGHT): - case (EQN_TOK_BRACE_CLOSE): + case EQN_TOK_RIGHT: + case EQN_TOK_BRACE_CLOSE: /* * Close out the existing brace. * FIXME: this is a shitty sentinel: we should really @@ -960,31 +893,31 @@ this_tok: */ for (cur = parent; cur != NULL; cur = cur->parent) if (cur->type == EQN_LIST && + cur->expectargs > 1 && (tok == EQN_TOK_BRACE_CLOSE || cur->left != NULL)) break; if (cur == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); break; } parent = cur; if (EQN_TOK_RIGHT == tok) { - if (NULL == (start = eqn_nexttok(ep, &sz))) { + if (eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { mandoc_msg(MANDOCERR_REQ_EMPTY, - ep->parse, ep->eqn.ln, - ep->eqn.pos, eqn_toks[tok]); + ep->parse, ep->node->line, + ep->node->pos, eqn_toks[tok]); break; } /* Handling depends on right/left. */ - if (STRNEQ(start, sz, "ceiling", 7)) { - strlcpy(sym, "\\[rc]", sizeof(sym)); - parent->right = mandoc_strdup(sym); - } else if (STRNEQ(start, sz, "floor", 5)) { - strlcpy(sym, "\\[rf]", sizeof(sym)); - parent->right = mandoc_strdup(sym); - } else - parent->right = mandoc_strndup(start, sz); + if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) + parent->right = mandoc_strdup("\\[rc]"); + else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) + parent->right = mandoc_strdup("\\[rf]"); + else + parent->right = + mandoc_strndup(ep->start, ep->toksz); } parent = parent->parent; if (tok == EQN_TOK_BRACE_CLOSE && @@ -992,12 +925,13 @@ this_tok: parent->type == EQN_MATRIX)) parent = parent->parent; /* Close out any "singleton" lists. */ - while (parent->type == EQN_LISTONE && - parent->args == parent->expectargs) + while (parent->type == EQN_LIST && + parent->expectargs == 1 && + parent->args == 1) parent = parent->parent; break; - case (EQN_TOK_BRACE_OPEN): - case (EQN_TOK_LEFT): + case EQN_TOK_BRACE_OPEN: + case EQN_TOK_LEFT: /* * If we already have something in the stack and we're * in an expression, then rewind til we're not any more @@ -1006,65 +940,63 @@ this_tok: while (parent->args == parent->expectargs) parent = parent->parent; if (EQN_TOK_LEFT == tok && - (start = eqn_nexttok(ep, &sz)) == NULL) { + eqn_next(ep, MODE_SUB) == EQN_TOK_EOF) { mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); break; } parent = eqn_box_alloc(ep, parent); parent->type = EQN_LIST; if (EQN_TOK_LEFT == tok) { - if (STRNEQ(start, sz, "ceiling", 7)) { - strlcpy(sym, "\\[lc]", sizeof(sym)); - parent->left = mandoc_strdup(sym); - } else if (STRNEQ(start, sz, "floor", 5)) { - strlcpy(sym, "\\[lf]", sizeof(sym)); - parent->left = mandoc_strdup(sym); - } else - parent->left = mandoc_strndup(start, sz); + if (STRNEQ(ep->start, ep->toksz, "ceiling", 7)) + parent->left = mandoc_strdup("\\[lc]"); + else if (STRNEQ(ep->start, ep->toksz, "floor", 5)) + parent->left = mandoc_strdup("\\[lf]"); + else + parent->left = + mandoc_strndup(ep->start, ep->toksz); } break; - case (EQN_TOK_PILE): - case (EQN_TOK_LPILE): - case (EQN_TOK_RPILE): - case (EQN_TOK_CPILE): - case (EQN_TOK_CCOL): - case (EQN_TOK_LCOL): - case (EQN_TOK_RCOL): + case EQN_TOK_PILE: + case EQN_TOK_LPILE: + case EQN_TOK_RPILE: + case EQN_TOK_CPILE: + case EQN_TOK_CCOL: + case EQN_TOK_LCOL: + case EQN_TOK_RCOL: while (parent->args == parent->expectargs) parent = parent->parent; parent = eqn_box_alloc(ep, parent); parent->type = EQN_PILE; parent->expectargs = 1; break; - case (EQN_TOK_ABOVE): + case EQN_TOK_ABOVE: for (cur = parent; cur != NULL; cur = cur->parent) if (cur->type == EQN_PILE) break; if (cur == NULL) { mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, - ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + ep->node->line, ep->node->pos, eqn_toks[tok]); break; } parent = eqn_box_alloc(ep, cur); parent->type = EQN_LIST; break; - case (EQN_TOK_MATRIX): + case EQN_TOK_MATRIX: while (parent->args == parent->expectargs) parent = parent->parent; parent = eqn_box_alloc(ep, parent); parent->type = EQN_MATRIX; parent->expectargs = 1; break; - case (EQN_TOK_EOF): - /* - * End of file! - * TODO: make sure we're not in an open subexpression. - */ - return ROFF_EQN; - default: - assert(tok == EQN_TOK__MAX); - assert(NULL != p); + case EQN_TOK_EOF: + return; + case EQN_TOK__MAX: + case EQN_TOK_FUNC: + case EQN_TOK_QUOTED: + case EQN_TOK_SYM: + p = ep->start; + assert(p != NULL); /* * If we already have something in the stack and we're * in an expression, then rewind til we're not any more. @@ -1073,48 +1005,93 @@ this_tok: parent = parent->parent; cur = eqn_box_alloc(ep, parent); cur->type = EQN_TEXT; - for (i = 0; i < EQNSYM__MAX; i++) - if (0 == strcmp(eqnsyms[i].str, p)) { - (void)snprintf(sym, sizeof(sym), - "\\[%s]", eqnsyms[i].sym); - cur->text = mandoc_strdup(sym); - free(p); + cur->text = p; + switch (tok) { + case EQN_TOK_FUNC: + cur->font = EQNFONT_ROMAN; + break; + case EQN_TOK_QUOTED: + if (cur->font == EQNFONT_NONE) + cur->font = EQNFONT_ITALIC; + break; + case EQN_TOK_SYM: + break; + default: + if (cur->font != EQNFONT_NONE || *p == '\0') break; + cpn = p - 1; + ccln = CCL_LET; + split = NULL; + for (;;) { + /* Advance to next character. */ + cp = cpn++; + ccl = ccln; + ccln = isalpha((unsigned char)*cpn) ? CCL_LET : + isdigit((unsigned char)*cpn) || + (*cpn == '.' && (ccl == CCL_DIG || + isdigit((unsigned char)cpn[1]))) ? + CCL_DIG : CCL_PUN; + /* No boundary before first character. */ + if (cp < p) + continue; + cur->font = ccl == CCL_LET ? + EQNFONT_ITALIC : EQNFONT_ROMAN; + if (*cp == '\\') + mandoc_escape(&cpn, NULL, NULL); + /* No boundary after last character. */ + if (*cpn == '\0') + break; + if (ccln == ccl && *cp != ',' && *cpn != ',') + continue; + /* Boundary found, split the text. */ + if (parent->args == parent->expectargs) { + /* Remove the text from the tree. */ + if (cur->prev == NULL) + parent->first = cur->next; + else + cur->prev->next = NULL; + parent->last = cur->prev; + parent->args--; + /* Set up a list instead. */ + split = eqn_box_alloc(ep, parent); + split->type = EQN_LIST; + /* Insert the word into the list. */ + split->first = split->last = cur; + cur->parent = split; + cur->prev = NULL; + parent = split; + } + /* Append a new text box. */ + nbox = eqn_box_alloc(ep, parent); + nbox->type = EQN_TEXT; + nbox->text = mandoc_strdup(cpn); + /* Truncate the old box. */ + p = mandoc_strndup(cur->text, + cpn - cur->text); + free(cur->text); + cur->text = p; + /* Setup to process the new box. */ + cur = nbox; + p = nbox->text; + cpn = p - 1; + ccln = CCL_LET; } - - if (i == EQNSYM__MAX) - cur->text = p; - /* - * Post-process list status. - */ - while (parent->type == EQN_LISTONE && - parent->args == parent->expectargs) - parent = parent->parent; + if (split != NULL) + parent = split->parent; + break; + } break; + default: + abort(); } goto next_tok; } -enum rofferr -eqn_end(struct eqn_node **epp) -{ - struct eqn_node *ep; - - ep = *epp; - *epp = NULL; - - ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); - ep->eqn.root->expectargs = UINT_MAX; - return eqn_parse(ep, ep->eqn.root); -} - void eqn_free(struct eqn_node *p) { int i; - eqn_box_free(p->eqn.root); - for (i = 0; i < (int)p->defsz; i++) { free(p->defs[i].key); free(p->defs[i].val); diff --git a/contrib/mdocml/eqn_html.c b/contrib/mdocml/eqn_html.c index b6e7d91..51f1442 100644 --- a/contrib/mdocml/eqn_html.c +++ b/contrib/mdocml/eqn_html.c @@ -1,4 +1,4 @@ -/* $Id: eqn_html.c,v 1.11 2017/01/17 01:47:51 schwarze Exp $ */ +/* $Id: eqn_html.c,v 1.17 2017/07/14 13:32:35 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -20,6 +20,7 @@ #include <sys/types.h> #include <assert.h> +#include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -33,7 +34,10 @@ eqn_box(struct html *p, const struct eqn_box *bp) { struct tag *post, *row, *cell, *t; const struct eqn_box *child, *parent; + const char *cp; size_t i, j, rows; + enum htmltag tag; + enum eqn_fontt font; if (NULL == bp) return; @@ -47,7 +51,8 @@ eqn_box(struct html *p, const struct eqn_box *bp) if (EQN_MATRIX == bp->type) { if (NULL == bp->first) goto out; - if (EQN_LIST != bp->first->type) { + if (bp->first->type != EQN_LIST || + bp->first->expectargs == 1) { eqn_box(p, bp->first); goto out; } @@ -87,28 +92,28 @@ eqn_box(struct html *p, const struct eqn_box *bp) } switch (bp->pos) { - case (EQNPOS_TO): + case EQNPOS_TO: post = print_otag(p, TAG_MOVER, ""); break; - case (EQNPOS_SUP): + case EQNPOS_SUP: post = print_otag(p, TAG_MSUP, ""); break; - case (EQNPOS_FROM): + case EQNPOS_FROM: post = print_otag(p, TAG_MUNDER, ""); break; - case (EQNPOS_SUB): + case EQNPOS_SUB: post = print_otag(p, TAG_MSUB, ""); break; - case (EQNPOS_OVER): + case EQNPOS_OVER: post = print_otag(p, TAG_MFRAC, ""); break; - case (EQNPOS_FROMTO): + case EQNPOS_FROMTO: post = print_otag(p, TAG_MUNDEROVER, ""); break; - case (EQNPOS_SUBSUP): + case EQNPOS_SUBSUP: post = print_otag(p, TAG_MSUBSUP, ""); break; - case (EQNPOS_SQRT): + case EQNPOS_SQRT: post = print_otag(p, TAG_MSQRT, ""); break; default: @@ -127,18 +132,68 @@ eqn_box(struct html *p, const struct eqn_box *bp) if (EQN_PILE == bp->type) { assert(NULL == post); - if (bp->first != NULL && bp->first->type == EQN_LIST) + if (bp->first != NULL && + bp->first->type == EQN_LIST && + bp->first->expectargs > 1) post = print_otag(p, TAG_MTABLE, ""); - } else if (bp->type == EQN_LIST && + } else if (bp->type == EQN_LIST && bp->expectargs > 1 && bp->parent && bp->parent->type == EQN_PILE) { assert(NULL == post); post = print_otag(p, TAG_MTR, ""); print_otag(p, TAG_MTD, ""); } - if (NULL != bp->text) { - assert(NULL == post); - post = print_otag(p, TAG_MI, ""); + if (bp->text != NULL) { + assert(post == NULL); + tag = TAG_MI; + cp = bp->text; + if (isdigit((unsigned char)cp[0]) || + (cp[0] == '.' && isdigit((unsigned char)cp[1]))) { + tag = TAG_MN; + while (*++cp != '\0') { + if (*cp != '.' && + isdigit((unsigned char)*cp) == 0) { + tag = TAG_MI; + break; + } + } + } else if (*cp != '\0' && isalpha((unsigned char)*cp) == 0) { + tag = TAG_MO; + while (*cp != '\0') { + if (cp[0] == '\\' && cp[1] != '\0') { + cp++; + mandoc_escape(&cp, NULL, NULL); + } else if (isalnum((unsigned char)*cp)) { + tag = TAG_MI; + break; + } else + cp++; + } + } + font = bp->font; + if (bp->text[0] != '\0' && + (((tag == TAG_MN || tag == TAG_MO) && + font == EQNFONT_ROMAN) || + (tag == TAG_MI && font == (bp->text[1] == '\0' ? + EQNFONT_ITALIC : EQNFONT_ROMAN)))) + font = EQNFONT_NONE; + switch (font) { + case EQNFONT_NONE: + post = print_otag(p, tag, ""); + break; + case EQNFONT_ROMAN: + post = print_otag(p, tag, "?", "fontstyle", "normal"); + break; + case EQNFONT_BOLD: + case EQNFONT_FAT: + post = print_otag(p, tag, "?", "fontweight", "bold"); + break; + case EQNFONT_ITALIC: + post = print_otag(p, tag, "?", "fontstyle", "italic"); + break; + default: + abort(); + } print_text(p, bp->text); } else if (NULL == post) { if (NULL != bp->left || NULL != bp->right) @@ -172,14 +227,17 @@ out: } void -print_eqn(struct html *p, const struct eqn *ep) +print_eqn(struct html *p, const struct eqn_box *bp) { struct tag *t; + if (bp->first == NULL) + return; + t = print_otag(p, TAG_MATH, "c", "eqn"); p->flags |= HTML_NONOSPACE; - eqn_box(p, ep->root); + eqn_box(p, bp); p->flags &= ~HTML_NONOSPACE; print_tagq(p, t); diff --git a/contrib/mdocml/eqn_term.c b/contrib/mdocml/eqn_term.c index 5f2818b..08f4a99 100644 --- a/contrib/mdocml/eqn_term.c +++ b/contrib/mdocml/eqn_term.c @@ -1,7 +1,7 @@ -/* $Id: eqn_term.c,v 1.8 2015/01/01 15:36:08 schwarze Exp $ */ +/* $Id: eqn_term.c,v 1.13 2017/07/08 14:51:04 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,10 +40,10 @@ static void eqn_box(struct termp *, const struct eqn_box *); void -term_eqn(struct termp *p, const struct eqn *ep) +term_eqn(struct termp *p, const struct eqn_box *bp) { - eqn_box(p, ep->root); + eqn_box(p, bp); p->flags &= ~TERMP_NOSPACE; } @@ -51,25 +51,46 @@ static void eqn_box(struct termp *p, const struct eqn_box *bp) { const struct eqn_box *child; + int delim; - if (bp->type == EQN_LIST || + /* Delimiters around this box? */ + + if ((bp->type == EQN_LIST && bp->expectargs > 1) || (bp->type == EQN_PILE && (bp->prev || bp->next)) || - (bp->parent != NULL && bp->parent->pos == EQNPOS_SQRT)) { + (bp->parent != NULL && (bp->parent->pos == EQNPOS_SQRT || + /* Diacritic followed by ^ or _. */ + ((bp->top != NULL || bp->bottom != NULL) && + bp->parent->type == EQN_SUBEXPR && + bp->parent->pos != EQNPOS_OVER && bp->next != NULL) || + /* Nested over, sub, sup, from, to. */ + (bp->type == EQN_SUBEXPR && bp->pos != EQNPOS_SQRT && + ((bp->parent->type == EQN_LIST && bp->expectargs == 1) || + (bp->parent->type == EQN_SUBEXPR && + bp->pos != EQNPOS_SQRT)))))) { if (bp->parent->type == EQN_SUBEXPR && bp->prev != NULL) p->flags |= TERMP_NOSPACE; term_word(p, bp->left != NULL ? bp->left : "("); p->flags |= TERMP_NOSPACE; - } + delim = 1; + } else + delim = 0; + + /* Handle Fonts and text. */ + if (bp->font != EQNFONT_NONE) term_fontpush(p, fontmap[(int)bp->font]); if (bp->text != NULL) term_word(p, bp->text); + /* Special box types. */ + if (bp->pos == EQNPOS_SQRT) { term_word(p, "sqrt"); - p->flags |= TERMP_NOSPACE; - eqn_box(p, bp->first); + if (bp->first != NULL) { + p->flags |= TERMP_NOSPACE; + eqn_box(p, bp->first); + } } else if (bp->type == EQN_SUBEXPR) { child = bp->first; eqn_box(p, child); @@ -93,29 +114,26 @@ eqn_box(struct termp *p, const struct eqn_box *bp) } } else { child = bp->first; - if (bp->type == EQN_MATRIX && child->type == EQN_LIST) + if (bp->type == EQN_MATRIX && + child != NULL && + child->type == EQN_LIST && + child->expectargs > 1) child = child->first; while (child != NULL) { eqn_box(p, bp->type == EQN_PILE && child->type == EQN_LIST && + child->expectargs > 1 && child->args == 1 ? child->first : child); child = child->next; } } + /* Handle Fonts and diacritics. */ + if (bp->font != EQNFONT_NONE) term_fontpop(p); - if (bp->type == EQN_LIST || - (bp->type == EQN_PILE && (bp->prev || bp->next)) || - (bp->parent != NULL && bp->parent->pos == EQNPOS_SQRT)) { - p->flags |= TERMP_NOSPACE; - term_word(p, bp->right != NULL ? bp->right : ")"); - if (bp->parent->type == EQN_SUBEXPR && bp->next != NULL) - p->flags |= TERMP_NOSPACE; - } - if (bp->top != NULL) { p->flags |= TERMP_NOSPACE; term_word(p, bp->top); @@ -124,4 +142,13 @@ eqn_box(struct termp *p, const struct eqn_box *bp) p->flags |= TERMP_NOSPACE; term_word(p, "_"); } + + /* Right delimiter after this box? */ + + if (delim) { + p->flags |= TERMP_NOSPACE; + term_word(p, bp->right != NULL ? bp->right : ")"); + if (bp->parent->type == EQN_SUBEXPR && bp->next != NULL) + p->flags |= TERMP_NOSPACE; + } } diff --git a/contrib/mdocml/gmdiff b/contrib/mdocml/gmdiff index 8d24fa7..d5b1f2a 100644 --- a/contrib/mdocml/gmdiff +++ b/contrib/mdocml/gmdiff @@ -19,7 +19,7 @@ if [ `id -u` -eq 0 ]; then fi if [ $# -eq 0 ]; then - echo "usage: $0 -h manual_source_file ..." + echo "usage: $0 [-h] manual_source_file ..." exit 1 fi @@ -29,21 +29,24 @@ if [ "X$1" = "X-h" ]; then EQN="neqn" ROFF="nroff" MOPT="-Omdoc $MOPT" +elif [ "X$1" = "X-u" ]; then + shift + ROFF="groff -ket -ww -Tutf8 -P -c" + MOPT="-Werror -Tutf8 $MOPT" else - EQN="eqn -Tascii" - ROFF="groff -ww -Tascii -P -c" + ROFF="groff -et -ww -Tascii -P -c" + MOPT="-Werror -Tascii $MOPT" fi -MOPT="-Werror $MOPT" while [ -n "$1" ]; do file=$1 shift echo " ========== $file ========== " - tbl $file | $EQN | $ROFF -mandoc 2> /tmp/roff.err > /tmp/roff.out - ${MANDOC:=mandoc} -Ios='OpenBSD ports' $MOPT $file \ + $ROFF -mandoc $file 2> /tmp/roff.err > /tmp/roff.out + ${MANDOC:=mandoc} -Ios=OpenBSD $MOPT $file \ 2> /tmp/mandoc.err > /tmp/mandoc.out for i in roff mandoc; do - [[ -s /tmp/$i.err ]] && echo "$i errors:" && cat /tmp/$i.err + [ -s /tmp/$i.err ] && echo "$i errors:" && cat /tmp/$i.err done diff -au /tmp/roff.out /tmp/mandoc.out 2>&1 done diff --git a/contrib/mdocml/html.c b/contrib/mdocml/html.c index 24fd6f4..fc55e88 100644 --- a/contrib/mdocml/html.c +++ b/contrib/mdocml/html.c @@ -1,4 +1,4 @@ -/* $Id: html.c,v 1.200 2017/01/21 02:29:57 schwarze Exp $ */ +/* $Id: html.c,v 1.219 2017/07/15 17:57:51 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -28,8 +28,9 @@ #include <string.h> #include <unistd.h> -#include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc.h" +#include "roff.h" #include "out.h" #include "html.h" #include "manconf.h" @@ -65,7 +66,7 @@ static const struct htmldata htmltags[TAG_MAX] = { {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"a", 0}, {"table", HTML_NLALL | HTML_INDENT}, - {"tbody", HTML_NLALL | HTML_INDENT}, + {"colgroup", HTML_NLALL | HTML_INDENT}, {"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"tr", HTML_NLALL | HTML_INDENT}, {"td", HTML_NLAROUND}, @@ -76,6 +77,8 @@ static const struct htmldata htmltags[TAG_MAX] = { {"dt", HTML_NLAROUND}, {"dd", HTML_NLAROUND | HTML_INDENT}, {"pre", HTML_NLALL | HTML_NOINDENT}, + {"var", 0}, + {"cite", 0}, {"b", 0}, {"i", 0}, {"code", 0}, @@ -84,6 +87,7 @@ static const struct htmldata htmltags[TAG_MAX] = { {"math", HTML_NLALL | HTML_INDENT}, {"mrow", 0}, {"mi", 0}, + {"mn", 0}, {"mo", 0}, {"msup", 0}, {"msub", 0}, @@ -114,7 +118,6 @@ static const char *const roffscales[SCALE_MAX] = { static void a2width(const char *, struct roffsu *); static void print_byte(struct html *, char); -static void print_endline(struct html *); static void print_endword(struct html *); static void print_indent(struct html *); static void print_word(struct html *, const char *); @@ -133,7 +136,7 @@ html_alloc(const struct manoutput *outopts) h = mandoc_calloc(1, sizeof(struct html)); - h->tags.head = NULL; + h->tag = NULL; h->style = outopts->style; h->base_man = outopts->man; h->base_includes = outopts->includes; @@ -151,8 +154,8 @@ html_free(void *p) h = (struct html *)p; - while ((tag = h->tags.head) != NULL) { - h->tags.head = tag->next; + while ((tag = h->tag) != NULL) { + h->tag = tag->next; free(tag); } @@ -235,6 +238,28 @@ print_metaf(struct html *h, enum mandoc_esc deco) } } +char * +html_make_id(const struct roff_node *n) +{ + const struct roff_node *nch; + char *buf, *cp; + + for (nch = n->child; nch != NULL; nch = nch->next) + if (nch->type != ROFFT_TEXT) + return NULL; + + buf = NULL; + deroff(&buf, n); + + /* http://www.w3.org/TR/html5/dom.html#the-id-attribute */ + + for (cp = buf; *cp != '\0'; cp++) + if (*cp == ' ') + *cp = '_'; + + return buf; +} + int html_strlen(const char *cp) { @@ -321,16 +346,18 @@ static int print_encode(struct html *h, const char *p, const char *pend, int norecurse) { char numbuf[16]; - size_t sz; - int c, len, nospace; + struct tag *t; const char *seq; + size_t sz; + int c, len, breakline, nospace; enum mandoc_esc esc; - static const char rejs[9] = { '\\', '<', '>', '&', '"', + static const char rejs[10] = { ' ', '\\', '<', '>', '&', '"', ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; if (pend == NULL) pend = strchr(p, '\0'); + breakline = 0; nospace = 0; while (p < pend) { @@ -341,14 +368,28 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) } for (sz = strcspn(p, rejs); sz-- && p < pend; p++) - if (*p == ' ') - print_endword(h); - else - print_byte(h, *p); + print_byte(h, *p); + + if (breakline && + (p >= pend || *p == ' ' || *p == ASCII_NBRSP)) { + t = print_otag(h, TAG_DIV, ""); + print_text(h, "\\~"); + print_tagq(h, t); + breakline = 0; + while (p < pend && (*p == ' ' || *p == ASCII_NBRSP)) + p++; + continue; + } if (p >= pend) break; + if (*p == ' ') { + print_endword(h); + p++; + continue; + } + if (print_escape(h, *p++)) continue; @@ -393,6 +434,9 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) if (c <= 0) continue; break; + case ESCAPE_BREAK: + breakline = 1; + continue; case ESCAPE_NOSPACE: if ('\0' == *p) nospace = 1; @@ -409,7 +453,7 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) (c > 0x7E && c < 0xA0)) c = 0xFFFD; if (c > 0x7E) { - (void)snprintf(numbuf, sizeof(numbuf), "&#%d;", c); + (void)snprintf(numbuf, sizeof(numbuf), "&#x%.4X;", c); print_word(h, numbuf); } else if (print_escape(h, c) == 0) print_byte(h, c); @@ -450,19 +494,19 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) char numbuf[16]; struct tag *t; const char *attr; - char *s; + char *arg1, *arg2; double v; int i, have_style, tflags; tflags = htmltags[tag].flags; - /* Push this tags onto the stack of open scopes. */ + /* Push this tag onto the stack of open scopes. */ if ((tflags & HTML_NOSTACK) == 0) { t = mandoc_malloc(sizeof(struct tag)); t->tag = tag; - t->next = h->tags.head; - h->tags.head = t; + t->next = h->tag; + h->tag = t; } else t = NULL; @@ -472,7 +516,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) print_indent(h); else if ((h->flags & HTML_NOSPACE) == 0) { if (h->flags & HTML_KEEP) - print_word(h, " "); + print_word(h, " "); else { if (h->flags & HTML_PREKEEP) h->flags |= HTML_KEEP; @@ -495,12 +539,14 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) have_style = 0; while (*fmt != '\0') { if (*fmt == 's') { - print_word(h, " style=\""); have_style = 1; fmt++; break; } - s = va_arg(ap, char *); + + /* Parse a non-style attribute and its arguments. */ + + arg1 = va_arg(ap, char *); switch (*fmt++) { case 'c': attr = "class"; @@ -512,31 +558,46 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) attr = "id"; break; case '?': - attr = s; - s = va_arg(ap, char *); + attr = arg1; + arg1 = va_arg(ap, char *); break; default: abort(); } + arg2 = NULL; + if (*fmt == 'M') + arg2 = va_arg(ap, char *); + if (arg1 == NULL) + continue; + + /* Print the non-style attributes. */ + print_byte(h, ' '); print_word(h, attr); print_byte(h, '='); print_byte(h, '"'); switch (*fmt) { - case 'M': - print_href(h, s, va_arg(ap, char *), 1); + case 'I': + print_href(h, arg1, NULL, 0); fmt++; break; - case 'I': - print_href(h, s, NULL, 0); + case 'M': + print_href(h, arg1, arg2, 1); fmt++; break; case 'R': print_byte(h, '#'); + print_encode(h, arg1, NULL, 1); fmt++; - /* FALLTHROUGH */ + break; + case 'T': + print_encode(h, arg1, NULL, 1); + print_word(h, "\" title=\""); + print_encode(h, arg1, NULL, 1); + fmt++; + break; default: - print_encode(h, s, NULL, 1); + print_encode(h, arg1, NULL, 1); break; } print_byte(h, '"'); @@ -544,30 +605,49 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) /* Print out styles. */ - s = NULL; - su = &mysu; while (*fmt != '\0') { + arg1 = NULL; + su = NULL; /* First letter: input argument type. */ switch (*fmt++) { case 'h': i = va_arg(ap, int); + su = &mysu; SCALE_HS_INIT(su, i); break; case 's': - s = va_arg(ap, char *); + arg1 = va_arg(ap, char *); break; case 'u': su = va_arg(ap, struct roffsu *); break; - case 'v': - i = va_arg(ap, int); - SCALE_VS_INIT(su, i); - break; case 'w': - s = va_arg(ap, char *); - a2width(s, su); + if ((arg2 = va_arg(ap, char *)) != NULL) { + su = &mysu; + a2width(arg2, su); + } + if (*fmt == '*') { + if (su != NULL && su->unit == SCALE_EN && + su->scale > 5.9 && su->scale < 6.1) + su = NULL; + fmt++; + } + if (*fmt == '+') { + if (su != NULL) { + /* Make even bold text fit. */ + su->scale *= 1.2; + /* Add padding. */ + su->scale += 3.0; + } + fmt++; + } + if (*fmt == '-') { + if (su != NULL) + su->scale *= -1.0; + fmt++; + } break; default: abort(); @@ -576,9 +656,6 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) /* Second letter: style name. */ switch (*fmt++) { - case 'b': - attr = "margin-bottom"; - break; case 'h': attr = "height"; break; @@ -588,9 +665,6 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) case 'l': attr = "margin-left"; break; - case 't': - attr = "margin-top"; - break; case 'w': attr = "width"; break; @@ -598,33 +672,37 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) attr = "min-width"; break; case '?': - print_word(h, s); - print_byte(h, ':'); - print_byte(h, ' '); - print_word(h, va_arg(ap, char *)); - print_byte(h, ';'); - if (*fmt != '\0') - print_byte(h, ' '); - continue; + attr = arg1; + arg1 = va_arg(ap, char *); + break; default: abort(); } - v = su->scale; - if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) - v = 1.0; - else if (su->unit == SCALE_BU) - v /= 24.0; + if (su == NULL && arg1 == NULL) + continue; + + if (have_style == 1) + print_word(h, " style=\""); + else + print_byte(h, ' '); print_word(h, attr); print_byte(h, ':'); print_byte(h, ' '); - (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); - print_word(h, numbuf); - print_word(h, roffscales[su->unit]); + if (su != NULL) { + v = su->scale; + if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) + v = 1.0; + else if (su->unit == SCALE_BU) + v /= 24.0; + (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); + print_word(h, numbuf); + print_word(h, roffscales[su->unit]); + } else + print_word(h, arg1); print_byte(h, ';'); - if (*fmt != '\0') - print_byte(h, ' '); + have_style = 2; } - if (have_style) + if (have_style == 2) print_byte(h, '"'); va_end(ap); @@ -679,7 +757,7 @@ print_ctag(struct html *h, struct tag *tag) if (tflags & HTML_NLAFTER) print_endline(h); - h->tags.head = tag->next; + h->tag = tag->next; free(tag); } @@ -699,7 +777,7 @@ print_text(struct html *h, const char *word) h->flags |= HTML_KEEP; print_endword(h); } else - print_word(h, " "); + print_word(h, " "); } assert(NULL == h->metaf); @@ -740,7 +818,7 @@ print_tagq(struct html *h, const struct tag *until) { struct tag *tag; - while ((tag = h->tags.head) != NULL) { + while ((tag = h->tag) != NULL) { print_ctag(h, tag); if (until && tag == until) return; @@ -752,7 +830,7 @@ print_stagq(struct html *h, const struct tag *suntil) { struct tag *tag; - while ((tag = h->tags.head) != NULL) { + while ((tag = h->tag) != NULL) { if (suntil && tag == suntil) return; print_ctag(h, tag); @@ -809,7 +887,7 @@ print_byte(struct html *h, char c) * If something was printed on the current output line, end it. * Not to be called right after print_indent(). */ -static void +void print_endline(struct html *h) { if (h->col == 0) @@ -890,7 +968,10 @@ print_word(struct html *h, const char *cp) static void a2width(const char *p, struct roffsu *su) { - if (a2roffsu(p, su, SCALE_MAX) < 2) { + const char *end; + + end = a2roffsu(p, su, SCALE_MAX); + if (end == NULL || *end != '\0') { su->unit = SCALE_EN; su->scale = html_strlen(p); } else if (su->scale < 0.0) diff --git a/contrib/mdocml/html.h b/contrib/mdocml/html.h index 19532c4..c727eac 100644 --- a/contrib/mdocml/html.h +++ b/contrib/mdocml/html.h @@ -1,4 +1,4 @@ -/* $Id: html.h,v 1.78 2017/01/19 16:59:30 schwarze Exp $ */ +/* $Id: html.h,v 1.87 2017/07/08 14:51:04 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -30,7 +30,7 @@ enum htmltag { TAG_BR, TAG_A, TAG_TABLE, - TAG_TBODY, + TAG_COLGROUP, TAG_COL, TAG_TR, TAG_TD, @@ -41,6 +41,8 @@ enum htmltag { TAG_DT, TAG_DD, TAG_PRE, + TAG_VAR, + TAG_CITE, TAG_B, TAG_I, TAG_CODE, @@ -49,6 +51,7 @@ enum htmltag { TAG_MATH, TAG_MROW, TAG_MI, + TAG_MN, TAG_MO, TAG_MSUP, TAG_MSUB, @@ -78,10 +81,6 @@ struct tag { enum htmltag tag; }; -struct tagq { - struct tag *head; -}; - struct html { int flags; #define HTML_NOSPACE (1 << 0) /* suppress next space */ @@ -100,7 +99,7 @@ struct html { size_t col; /* current output byte position */ size_t bufcol; /* current buf byte position */ char buf[80]; /* output buffer */ - struct tagq tags; /* stack of open tags */ + 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 */ @@ -114,8 +113,11 @@ struct html { }; +struct roff_node; struct tbl_span; -struct eqn; +struct eqn_box; + +void roff_html_pre(struct html *, const struct roff_node *); void print_gen_decls(struct html *); void print_gen_head(struct html *); @@ -125,7 +127,9 @@ void print_stagq(struct html *, const struct tag *); 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 *); +void print_eqn(struct html *, const struct eqn_box *); void print_paragraph(struct html *); +void print_endline(struct html *); +char *html_make_id(const struct roff_node *); int html_strlen(const char *); diff --git a/contrib/mdocml/lib.c b/contrib/mdocml/lib.c index 5295950..0474924 100644 --- a/contrib/mdocml/lib.c +++ b/contrib/mdocml/lib.c @@ -1,4 +1,4 @@ -/* $Id: lib.c,v 1.13 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: lib.c,v 1.14 2017/06/24 14:38:32 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -20,6 +20,7 @@ #include <string.h> +#include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "libmdoc.h" diff --git a/contrib/mdocml/libman.h b/contrib/mdocml/libman.h index 6584960..312093d 100644 --- a/contrib/mdocml/libman.h +++ b/contrib/mdocml/libman.h @@ -1,4 +1,4 @@ -/* $Id: libman.h,v 1.79 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: libman.h,v 1.81 2017/04/29 12:45:41 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -17,7 +17,7 @@ */ #define MACRO_PROT_ARGS struct roff_man *man, \ - int tok, \ + enum roff_tok tok, \ int line, \ int ppos, \ int *pos, \ @@ -35,7 +35,6 @@ struct man_macro { extern const struct man_macro *const man_macros; -int man_hash_find(const char *); void man_node_validate(struct roff_man *); void man_state(struct roff_man *, struct roff_node *); void man_unscope(struct roff_man *, const struct roff_node *); diff --git a/contrib/mdocml/libmandoc.h b/contrib/mdocml/libmandoc.h index 96e726c..2cf0762 100644 --- a/contrib/mdocml/libmandoc.h +++ b/contrib/mdocml/libmandoc.h @@ -1,7 +1,7 @@ -/* $Id: libmandoc.h,v 1.64 2016/07/19 13:36:13 schwarze Exp $ */ +/* $Id: libmandoc.h,v 1.70 2017/07/08 17:52:49 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,8 +23,6 @@ enum rofferr { ROFF_REPARSE, /* re-run main parser on the result */ ROFF_SO, /* include another file */ ROFF_IGN, /* ignore current line */ - ROFF_TBL, /* a table row was successfully parsed */ - ROFF_EQN /* an equation was successfully parsed */ }; struct buf { @@ -34,8 +32,6 @@ struct buf { struct mparse; -struct tbl_span; -struct eqn; struct roff; struct roff_man; @@ -43,23 +39,21 @@ 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))); + __attribute__((__format__ (__printf__, 5, 6))); char *mandoc_getarg(struct mparse *, char **, int, int *); -char *mandoc_normdate(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); const char *mandoc_a2msec(const char*); -void mdoc_hash_init(void); int mdoc_parseln(struct roff_man *, int, char *, int); void mdoc_endparse(struct roff_man *); -void man_hash_init(void); int man_parseln(struct roff_man *, int, char *, int); void man_endparse(struct roff_man *); int preconv_cue(const struct buf *, size_t); -int preconv_encode(struct buf *, size_t *, +int preconv_encode(const struct buf *, size_t *, struct buf *, size_t *, int *); void roff_free(struct roff *); @@ -77,6 +71,3 @@ char *roff_strdup(const struct roff *, const char *); int roff_getcontrol(const struct roff *, const char *, int *); int roff_getformat(const struct roff *); - -const struct tbl_span *roff_span(const struct roff *); -const struct eqn *roff_eqn(const struct roff *); diff --git a/contrib/mdocml/libmdoc.h b/contrib/mdocml/libmdoc.h index 5a6cc3e..57dff61 100644 --- a/contrib/mdocml/libmdoc.h +++ b/contrib/mdocml/libmdoc.h @@ -1,7 +1,7 @@ -/* $Id: libmdoc.h,v 1.108 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: libmdoc.h,v 1.112 2017/05/30 16:22:03 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,7 +17,7 @@ */ #define MACRO_PROT_ARGS struct roff_man *mdoc, \ - int tok, \ + enum roff_tok tok, \ int line, \ int ppos, \ int *pos, \ @@ -39,7 +39,6 @@ enum margserr { ARGS_EOLN, /* end-of-line */ ARGS_WORD, /* normal word */ ARGS_PUNCT, /* series of punctuation */ - ARGS_QWORD, /* quoted word */ ARGS_PHRASE /* Bl -column phrase */ }; @@ -65,24 +64,24 @@ extern const struct mdoc_macro *const mdoc_macros; void mdoc_macro(MACRO_PROT_ARGS); void mdoc_elem_alloc(struct roff_man *, int, int, - int, struct mdoc_arg *); + enum roff_tok, struct mdoc_arg *); struct roff_node *mdoc_block_alloc(struct roff_man *, int, int, - int, struct mdoc_arg *); -void mdoc_tail_alloc(struct roff_man *, int, int, int); -struct roff_node *mdoc_endbody_alloc(struct roff_man *, int, int, int, - struct roff_node *, enum mdoc_endbody); + enum roff_tok, struct mdoc_arg *); +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 *); -int mdoc_hash_find(const char *); const char *mdoc_a2arch(const char *); const char *mdoc_a2att(const char *); const char *mdoc_a2lib(const char *); enum roff_sec mdoc_a2sec(const char *); const char *mdoc_a2st(const char *); -void mdoc_argv(struct roff_man *, int, int, +void mdoc_argv(struct roff_man *, int, enum roff_tok, struct mdoc_arg **, int *, char *); enum margserr mdoc_args(struct roff_man *, int, - int *, char *, int, char **); + int *, char *, enum roff_tok, char **); enum mdelim mdoc_isdelim(const char *); diff --git a/contrib/mdocml/libroff.h b/contrib/mdocml/libroff.h index 897a55a..b6a026d 100644 --- a/contrib/mdocml/libroff.h +++ b/contrib/mdocml/libroff.h @@ -1,7 +1,7 @@ -/* $Id: libroff.h,v 1.39 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: libroff.h,v 1.42 2017/07/08 17:52:49 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -38,15 +38,15 @@ struct tbl_node { }; struct eqn_node { - struct eqn eqn; /* syntax tree of this equation */ struct mparse *parse; /* main parser, for error reporting */ - struct eqn_node *next; /* singly linked list of equations */ + 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 cur; /* parse point in the source code */ - size_t rew; /* beginning of the current token */ + 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 */ @@ -65,15 +65,16 @@ struct tbl_node *tbl_alloc(int, int, struct mparse *); void tbl_restart(int, int, struct tbl_node *); void tbl_free(struct tbl_node *); void tbl_reset(struct tbl_node *); -enum rofferr tbl_read(struct tbl_node *, int, const char *, int); +void tbl_read(struct tbl_node *, int, const char *, int); void tbl_option(struct tbl_node *, int, const char *, int *); void tbl_layout(struct tbl_node *, int, const char *, int); void tbl_data(struct tbl_node *, int, const char *, int); -int tbl_cdata(struct tbl_node *, int, const char *, int); +void tbl_cdata(struct tbl_node *, int, const char *, int); const struct tbl_span *tbl_span(struct tbl_node *); -int tbl_end(struct tbl_node **); -struct eqn_node *eqn_alloc(int, int, struct mparse *); -enum rofferr eqn_end(struct eqn_node **); +int tbl_end(struct tbl_node *); +struct eqn_node *eqn_alloc(struct mparse *); +void eqn_box_free(struct eqn_box *); void eqn_free(struct eqn_node *); -enum rofferr eqn_read(struct eqn_node **, int, - const char *, int, int *); +void eqn_parse(struct eqn_node *); +void eqn_read(struct eqn_node *, const char *); +void eqn_reset(struct eqn_node *); diff --git a/contrib/mdocml/main.c b/contrib/mdocml/main.c index b64b3be..7f1411a 100644 --- a/contrib/mdocml/main.c +++ b/contrib/mdocml/main.c @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.279 2017/01/09 17:49:57 schwarze Exp $ */ +/* $Id: main.c,v 1.301 2017/07/26 10:21:55 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -43,6 +43,7 @@ #include "mandoc_aux.h" #include "mandoc.h" +#include "mandoc_xr.h" #include "roff.h" #include "mdoc.h" #include "man.h" @@ -56,7 +57,6 @@ enum outmode { OUTMODE_FLN, OUTMODE_LST, OUTMODE_ALL, - OUTMODE_INT, OUTMODE_ONE }; @@ -67,6 +67,7 @@ enum outt { OUTT_TREE, /* -Ttree */ OUTT_MAN, /* -Tman */ OUTT_HTML, /* -Thtml */ + OUTT_MARKDOWN, /* -Tmarkdown */ OUTT_LINT, /* -Tlint */ OUTT_PS, /* -Tps */ OUTT_PDF /* -Tpdf */ @@ -74,25 +75,28 @@ enum outt { struct curparse { struct mparse *mp; - enum mandoclevel wlevel; /* ignore messages below this */ + struct manoutput *outopts; /* output options */ + 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 */ - void *outdata; /* data for output */ - struct manoutput *outopts; /* output options */ }; int mandocdb(int, char *[]); +static void check_xr(const char *); static int fs_lookup(const struct manpaths *, size_t ipath, const char *, const char *, const char *, struct manpage **, size_t *); -static void fs_search(const struct mansearch *, +static int fs_search(const struct mansearch *, const struct manpaths *, int, char**, struct manpage **, size_t *); static int koptions(int *, char *); -static int moptions(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 *); @@ -100,29 +104,28 @@ static void parse(struct curparse *, int, const char *); static void passthrough(const char *, int, int); static pid_t spawn_pager(struct tag_files *); static int toptions(struct curparse *, char *); -static void usage(enum argmode) __attribute__((noreturn)); +static void usage(enum argmode) __attribute__((__noreturn__)); static int woptions(struct curparse *, char *); static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static char help_arg[] = "help"; static char *help_argv[] = {help_arg, NULL}; static enum mandoclevel rc; +static FILE *mmsg_stream; int main(int argc, char *argv[]) { struct manconf conf; - struct curparse curp; struct mansearch search; + struct curparse curp; struct tag_files *tag_files; - const char *progname; - char *auxpaths; - char *defos; - unsigned char *uc; struct manpage *res, *resp; - char *conf_file, *defpaths; - const char *sec; + const char *progname, *sec, *thisarg; + char *conf_file, *defpaths, *auxpaths; + char *oarg; + unsigned char *uc; size_t i, sz; int prio, best_prio; enum outmode outmode; @@ -151,7 +154,7 @@ main(int argc, char *argv[]) return mandocdb(argc, argv); #if HAVE_PLEDGE - if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1) + if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); #endif @@ -168,6 +171,7 @@ main(int argc, char *argv[]) memset(&search, 0, sizeof(struct mansearch)); search.outkey = "Nd"; + oarg = NULL; if (strcmp(progname, BINM_MAN) == 0) search.argmode = ARG_NAME; @@ -184,18 +188,22 @@ main(int argc, char *argv[]) memset(&curp, 0, sizeof(struct curparse)); curp.outtype = OUTT_LOCALE; - curp.wlevel = MANDOCLEVEL_BADARG; + curp.mmin = MANDOCERR_MAX; curp.outopts = &conf.output; options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; - defos = NULL; + mmsg_stream = stderr; use_pager = 1; tag_files = NULL; show_usage = 0; outmode = OUTMODE_DEF; - while (-1 != (c = getopt(argc, argv, - "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) { + while ((c = getopt(argc, argv, + "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { + if (c == 'i' && search.argmode == ARG_EXPR) { + optind--; + break; + } switch (c) { case 'a': outmode = OUTMODE_ALL; @@ -219,14 +227,11 @@ main(int argc, char *argv[]) warnx("-I %s: Bad argument", optarg); return (int)MANDOCLEVEL_BADARG; } - if (defos) { + if (curp.os_s != NULL) { warnx("-I %s: Duplicate argument", optarg); return (int)MANDOCLEVEL_BADARG; } - defos = mandoc_strdup(optarg + 3); - break; - case 'i': - outmode = OUTMODE_INT; + curp.os_s = mandoc_strdup(optarg + 3); break; case 'K': if ( ! koptions(&options, optarg)) @@ -246,10 +251,7 @@ main(int argc, char *argv[]) auxpaths = optarg; break; case 'O': - search.outkey = optarg; - while (optarg != NULL) - manconf_output(&conf.output, - strsep(&optarg, ",")); + oarg = optarg; break; case 'S': search.arch = optarg; @@ -294,6 +296,21 @@ main(int argc, char *argv[]) } } + if (oarg != NULL) { + if (outmode == OUTMODE_LST) + search.outkey = oarg; + else { + while (oarg != NULL) { + thisarg = oarg; + if (manconf_output(&conf.output, + strsep(&oarg, ","), 0) == 0) + continue; + warnx("-O %s: Bad argument", thisarg); + return (int)MANDOCLEVEL_BADARG; + } + } + } + if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST || !isatty(STDOUT_FILENO)) @@ -301,7 +318,7 @@ main(int argc, char *argv[]) #if HAVE_PLEDGE if (!use_pager) - if (pledge("stdio rpath flock", NULL) == -1) + if (pledge("stdio rpath", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); #endif @@ -430,11 +447,12 @@ main(int argc, char *argv[]) } #endif - if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths)) - return (int)MANDOCLEVEL_BADARG; + if (search.argmode == ARG_FILE) + moptions(&options, auxpaths); mchars_alloc(); - curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos); + curp.mp = mparse_alloc(options, curp.mmin, mmsg, + curp.os_e, curp.os_s); /* * Conditionally start up the lookaside buffer before parsing. @@ -460,7 +478,7 @@ main(int argc, char *argv[]) parse(&curp, fd, *argv); else if (resp->form == FORM_SRC) { /* For .so only; ignore failure. */ - chdir(conf.manpath.paths[resp->ipath]); + (void)chdir(conf.manpath.paths[resp->ipath]); parse(&curp, fd, resp->file); } else passthrough(resp->file, fd, @@ -503,6 +521,7 @@ main(int argc, char *argv[]) break; } } + mandoc_xr_free(); mparse_free(curp.mp); mchars_free(); @@ -512,7 +531,7 @@ out: mansearch_free(res, sz); } - free(defos); + free(curp.os_s); /* * When using a pager, finish writing both temporary files, @@ -580,24 +599,22 @@ usage(enum argmode argmode) switch (argmode) { case ARG_FILE: - fputs("usage: mandoc [-acfhkl] [-I os=name] " - "[-K encoding] [-mformat] [-O option]\n" + fputs("usage: mandoc [-ac] [-I os=name] " + "[-K encoding] [-mdoc | -man] [-O options]\n" "\t [-T output] [-W level] [file ...]\n", stderr); break; case ARG_NAME: - fputs("usage: man [-acfhklw] [-C file] [-I os=name] " - "[-K encoding] [-M path] [-m path]\n" - "\t [-O option=value] [-S subsection] [-s section] " - "[-T output] [-W level]\n" - "\t [section] name ...\n", stderr); + fputs("usage: man [-acfhklw] [-C file] [-M path] " + "[-m path] [-S subsection]\n" + "\t [[-s] section] name ...\n", stderr); break; case ARG_WORD: - fputs("usage: whatis [-acfhklw] [-C file] " + fputs("usage: whatis [-afk] [-C file] " "[-M path] [-m path] [-O outkey] [-S arch]\n" "\t [-s section] name ...\n", stderr); break; case ARG_EXPR: - fputs("usage: apropos [-acfhklw] [-C file] " + fputs("usage: apropos [-afk] [-C file] " "[-M path] [-m path] [-O outkey] [-S arch]\n" "\t [-s section] expression ...\n", stderr); break; @@ -648,12 +665,23 @@ fs_lookup(const struct manpaths *paths, size_t ipath, if (globres == 0) file = mandoc_strdup(*globinfo.gl_pathv); globfree(&globinfo); - if (globres != 0) + if (globres == 0) + goto found; + if (res != NULL || ipath + 1 != paths->sz) return 0; + mandoc_asprintf(&file, "%s.%s", name, sec); + globres = access(file, R_OK); + free(file); + return globres != -1; + found: warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); + if (res == NULL) { + free(file); + return 1; + } *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); page = *res + (*ressz - 1); page->file = file; @@ -666,7 +694,7 @@ found: return 1; } -static void +static int fs_search(const struct mansearch *cfg, const struct manpaths *paths, int argc, char **argv, struct manpage **res, size_t *ressz) { @@ -678,7 +706,8 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths, assert(cfg->argmode == ARG_NAME); - *res = NULL; + if (res != NULL) + *res = NULL; *ressz = lastsz = 0; while (argc) { for (ipath = 0; ipath < paths->sz; ipath++) { @@ -686,19 +715,20 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths, if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, *argv, res, ressz) && cfg->firstmatch) - return; + return 1; } else for (isec = 0; isec < nsec; isec++) if (fs_lookup(paths, ipath, sections[isec], cfg->arch, *argv, res, ressz) && cfg->firstmatch) - return; + return 1; } - if (*ressz == lastsz) + if (res != NULL && *ressz == lastsz) warnx("No entry for %s in the manual.", *argv); lastsz = *ressz; argv++; argc--; } + return 0; } static void @@ -735,8 +765,10 @@ parse(struct curparse *curp, int fd, const char *file) if (man == NULL) return; + mandoc_xr_reset(); if (man->macroset == MACROSET_MDOC) { - mdoc_validate(man); + if (curp->outtype != OUTT_TREE || !curp->outopts->noval) + mdoc_validate(man); switch (curp->outtype) { case OUTT_HTML: html_mdoc(curp->outdata, man); @@ -754,12 +786,16 @@ parse(struct curparse *curp, int fd, const char *file) case OUTT_PS: terminal_mdoc(curp->outdata, man); break; + case OUTT_MARKDOWN: + markdown_mdoc(curp->outdata, man); + break; default: break; } } if (man->macroset == MACROSET_MAN) { - man_validate(man); + if (curp->outtype != OUTT_TREE || !curp->outopts->noval) + man_validate(man); switch (curp->outtype) { case OUTT_HTML: html_man(curp->outdata, man); @@ -781,10 +817,47 @@ parse(struct curparse *curp, int fd, const char *file) break; } } + if (curp->mmin < MANDOCERR_STYLE) + check_xr(file); mparse_updaterc(curp->mp, &rc); } static void +check_xr(const char *file) +{ + static struct manpaths paths; + struct mansearch search; + struct mandoc_xr *xr; + char *cp; + size_t sz; + + if (paths.sz == 0) + manpath_base(&paths); + + for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { + if (xr->line == -1) + continue; + search.arch = NULL; + search.sec = xr->sec; + search.outkey = NULL; + search.argmode = ARG_NAME; + search.firstmatch = 1; + if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) + continue; + 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); + else + mandoc_asprintf(&cp, "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); + } +} + +static void outdata_alloc(struct curparse *curp) { switch (curp->outtype) { @@ -902,24 +975,16 @@ koptions(int *options, char *arg) return 1; } -static int +static void moptions(int *options, char *arg) { if (arg == NULL) - /* nothing to do */; - else if (0 == strcmp(arg, "doc")) + return; + if (strcmp(arg, "doc") == 0) *options |= MPARSE_MDOC; - else if (0 == strcmp(arg, "andoc")) - /* nothing to do */; - else if (0 == strcmp(arg, "an")) + else if (strcmp(arg, "an") == 0) *options |= MPARSE_MAN; - else { - warnx("-m %s: Bad argument", arg); - return 0; - } - - return 1; } static int @@ -930,19 +995,20 @@ toptions(struct curparse *curp, char *arg) curp->outtype = OUTT_ASCII; else if (0 == strcmp(arg, "lint")) { curp->outtype = OUTT_LINT; - curp->wlevel = MANDOCLEVEL_WARNING; + curp->mmin = MANDOCERR_BASE; + mmsg_stream = stdout; } else if (0 == strcmp(arg, "tree")) curp->outtype = OUTT_TREE; else if (0 == strcmp(arg, "man")) curp->outtype = OUTT_MAN; else if (0 == strcmp(arg, "html")) curp->outtype = OUTT_HTML; + else if (0 == strcmp(arg, "markdown")) + curp->outtype = OUTT_MARKDOWN; else if (0 == strcmp(arg, "utf8")) curp->outtype = OUTT_UTF8; else if (0 == strcmp(arg, "locale")) curp->outtype = OUTT_LOCALE; - else if (0 == strcmp(arg, "xhtml")) - curp->outtype = OUTT_HTML; else if (0 == strcmp(arg, "ps")) curp->outtype = OUTT_PS; else if (0 == strcmp(arg, "pdf")) @@ -959,15 +1025,19 @@ static int woptions(struct curparse *curp, char *arg) { char *v, *o; - const char *toks[7]; + const char *toks[11]; toks[0] = "stop"; toks[1] = "all"; - toks[2] = "warning"; - toks[3] = "error"; - toks[4] = "unsupp"; - toks[5] = "fatal"; - toks[6] = NULL; + toks[2] = "base"; + toks[3] = "style"; + toks[4] = "warning"; + toks[5] = "error"; + toks[6] = "unsupp"; + toks[7] = "fatal"; + toks[8] = "openbsd"; + toks[9] = "netbsd"; + toks[10] = NULL; while (*arg) { o = arg; @@ -977,23 +1047,36 @@ woptions(struct curparse *curp, char *arg) break; case 1: case 2: - curp->wlevel = MANDOCLEVEL_WARNING; + curp->mmin = MANDOCERR_BASE; break; case 3: - curp->wlevel = MANDOCLEVEL_ERROR; + curp->mmin = MANDOCERR_STYLE; break; case 4: - curp->wlevel = MANDOCLEVEL_UNSUPP; + curp->mmin = MANDOCERR_WARNING; break; case 5: - curp->wlevel = MANDOCLEVEL_BADARG; + curp->mmin = MANDOCERR_ERROR; + break; + case 6: + curp->mmin = MANDOCERR_UNSUPP; + break; + case 7: + curp->mmin = MANDOCERR_MAX; + break; + case 8: + curp->mmin = MANDOCERR_BASE; + curp->os_e = MANDOC_OS_OPENBSD; + break; + case 9: + curp->mmin = MANDOCERR_BASE; + curp->os_e = MANDOC_OS_NETBSD; break; default: warnx("-W %s: Bad argument", o); return 0; } } - return 1; } @@ -1003,21 +1086,21 @@ mmsg(enum mandocerr t, enum mandoclevel lvl, { const char *mparse_msg; - fprintf(stderr, "%s: %s:", getprogname(), + fprintf(mmsg_stream, "%s: %s:", getprogname(), file == NULL ? "<stdin>" : file); if (line) - fprintf(stderr, "%d:%d:", line, col + 1); + fprintf(mmsg_stream, "%d:%d:", line, col + 1); - fprintf(stderr, " %s", mparse_strlevel(lvl)); + fprintf(mmsg_stream, " %s", mparse_strlevel(lvl)); - if (NULL != (mparse_msg = mparse_strerror(t))) - fprintf(stderr, ": %s", mparse_msg); + if ((mparse_msg = mparse_strerror(t)) != NULL) + fprintf(mmsg_stream, ": %s", mparse_msg); if (msg) - fprintf(stderr, ": %s", msg); + fprintf(mmsg_stream, ": %s", msg); - fputc('\n', stderr); + fputc('\n', mmsg_stream); } static pid_t diff --git a/contrib/mdocml/main.h b/contrib/mdocml/main.h index f12f3e4..f9b8f13 100644 --- a/contrib/mdocml/main.h +++ b/contrib/mdocml/main.h @@ -1,4 +1,4 @@ -/* $Id: main.h,v 1.26 2016/07/15 19:33:01 schwarze Exp $ */ +/* $Id: main.h,v 1.27 2017/03/03 14:23:23 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -49,3 +49,5 @@ void pspdf_free(void *); void terminal_mdoc(void *, const struct roff_man *); void terminal_man(void *, const struct roff_man *); void terminal_sepline(void *); + +void markdown_mdoc(void *, const struct roff_man *); diff --git a/contrib/mdocml/makewhatis.8 b/contrib/mdocml/makewhatis.8 index 945c053..9f307a3 100644 --- a/contrib/mdocml/makewhatis.8 +++ b/contrib/mdocml/makewhatis.8 @@ -1,7 +1,7 @@ -.\" $Id: makewhatis.8,v 1.4 2016/07/19 22:40:33 schwarze Exp $ +.\" $Id: makewhatis.8,v 1.6 2017/05/17 22:27:12 schwarze Exp $ .\" .\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2011, 2012, 2014, 2017 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 19 2016 $ +.Dd $Mdocdate: May 17 2017 $ .Dt MAKEWHATIS 8 .Os .Sh NAME @@ -74,6 +74,8 @@ and .Sm on in that directory. Existing databases are replaced. +If a directory contains no manual pages, no database is created in that +directory. If .Ar dir is not provided, @@ -130,11 +132,22 @@ Remove .Ar from the database in .Ar dir . +If that causes the database to become empty, also delete the database file. .El .Pp If fatal parse errors are encountered while parsing, the offending file is printed to stderr, omitted from the index, and the parse continues with the next input file. +.Sh ENVIRONMENT +.Bl -tag -width MANPATH +.It Ev MANPATH +A colon-separated list of directories to create databases in. +Ignored if a +.Ar dir +argument or the +.Fl t +option is specified. +.El .Sh FILES .Bl -tag -width Ds .It Pa mandoc.db diff --git a/contrib/mdocml/man.1 b/contrib/mdocml/man.1 index 191bc95..075497a 100644 --- a/contrib/mdocml/man.1 +++ b/contrib/mdocml/man.1 @@ -1,9 +1,9 @@ -.\" $Id: man.1,v 1.20 2017/01/06 01:34:57 schwarze Exp $ +.\" $Id: man.1,v 1.29 2017/05/17 23:23:00 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, 2015 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010, 2011, 2014-2017 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: January 6 2017 $ +.Dd $Mdocdate: May 17 2017 $ .Dt MAN 1 .Os .Sh NAME @@ -41,16 +41,10 @@ .Nm man .Op Fl acfhklw .Op Fl C Ar file -.Op Fl I Cm os Ns = Ns Ar name -.Op Fl K Ar encoding .Op Fl M Ar path .Op Fl m Ar path -.Op Fl O Ar option Ns = Ns Ar value .Op Fl S Ar subsection -.Op Fl s Ar section -.Op Fl T Ar output -.Op Fl W Ar level -.Op Ar section +.Op Oo Fl s Oc Ar section .Ar name ... .Sh DESCRIPTION The @@ -91,39 +85,12 @@ It searches for .Ar name in manual page names and displays the header lines from all matching pages. The search is case insensitive and matches whole words only. -This overrides any earlier -.Fl k -and -.Fl l -options. .It Fl h Display only the SYNOPSIS lines of the requested manual pages. Implies .Fl a and .Fl c . -.It Fl I Cm os Ns = Ns Ar name -Override the default operating system -.Ar name -for the -.Xr mdoc 7 -.Ic \&Os -and for the -.Xr man 7 -.Ic \&TH -macro. -.It Fl K Ar encoding -Specify the input encoding. -The supported -.Ar encoding -arguments are -.Cm us-ascii , -.Cm iso-8859-1 , -and -.Cm utf-8 . -By default, the encoding is automatically detected as described in the -.Xr mandoc 1 -manual. .It Fl k A synonym for .Xr apropos 1 . @@ -133,11 +100,6 @@ an expression can be provided using the syntax described in the .Xr apropos 1 manual. By default, it displays the header lines of all matching pages. -This overrides any earlier -.Fl f -and -.Fl l -options. .It Fl l A synonym for .Xr mandoc 1 @@ -149,15 +111,10 @@ No search is done and .Ar file , .Ar path , .Ar section , -and -.Ar subsection -are ignored. -This overrides any earlier -.Fl f , -.Fl k , +.Ar subsection , and .Fl w -options. +are ignored. .It Fl M Ar path Override the list of standard directories which .Nm @@ -184,15 +141,8 @@ the directories specified using the option or the .Ev MANPATH environment variable. -.It Fl O Ar option Ns = Ns Ar value -Comma-separated output options. -For each output format, the available options are described in the -.Xr mandoc 1 -manual. .It Fl S Ar subsection -Restricts the directories that -.Nm -will search to those of a specific +Only show pages for the specified .Xr machine 1 architecture. .Ar subsection @@ -234,53 +184,23 @@ System maintenance and operation commands. .It 9 Kernel internals. .El -.It Fl T Ar output -Select the output format. -The default is -.Cm locale . -The other output modes -.Cm ascii , -.Cm html , -.Cm lint , -.Cm man , -.Cm pdf , -.Cm ps , -.Cm tree , -and -.Cm utf8 -are described in the -.Xr mandoc 1 -manual. -.It Fl W Ar level -Specify the minimum message -.Ar level -to be reported on the standard error output and to affect the exit status. -The -.Ar level -can be -.Cm warning , -.Cm error , -or -.Cm unsupp ; -.Cm all -is an alias for -.Cm warning . -By default, -.Nm -is silent. -See the -.Xr mandoc 1 -manual for details. +.Pp +If not specified and a match is found in more than one section, +the first match is selected from the following list: +1, 8, 6, 2, 3, 5, 7, 4, 9, 3p. .It Fl w -List the pathnames of the manual pages which -.Nm -would display for the specified -.Ar section -and -.Ar name -combination. +List the pathnames of all matching manual pages instead of displaying +any of them. .El .Pp +The options +.Fl IKOTW +are also supported and are documented in +.Xr mandoc 1 . +The options +.Fl fkl +are mutually exclusive and override each other. +.Pp Guidelines for writing man pages can be found in .Xr mdoc 7 . @@ -290,13 +210,7 @@ for example .Pa cat1/foo.0 and .Pa man1/foo.1 , -exist in the same directory, and at least one of them is selected, -only the newer one is used. -However, if both the -.Fl a -and the -.Fl w -options are specified, both file names are printed. +exist in the same directory, only the unformatted version is used. .Sh ENVIRONMENT .Bl -tag -width MANPATHX .It Ev MACHINE @@ -318,7 +232,7 @@ is case insensitive. .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER -will be used instead of the standard pagination program, +is used instead of the standard pagination program, .Xr more 1 . If .Xr less 1 @@ -342,13 +256,27 @@ information about the term last searched for with .It Ev MANPATH The standard search path used by .Nm -may be overridden by specifying a path in the +may be changed by specifying a path in the .Ev MANPATH -environment -variable. +environment variable. The format of the path is a colon .Pq Ql \&: separated list of directories. +Invalid paths are ignored. +Overridden by +.Fl M , +ignored if +.Fl l +is specified. +.Pp +If +.Ev MANPATH +begins with a colon, it is appended to the default list; +if it ends with a colon, it is prepended to the default list; +or if it contains two adjacent colons, +the standard search path is inserted between the colons. +If none of these conditions are met, it overrides the +standard search path. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER @@ -356,7 +284,12 @@ is not defined. If neither PAGER nor MANPAGER is defined, .Xr more 1 .Fl s -will be used. +is used. +Only used if +.Fl a +or +.Fl l +is specified. .El .Sh FILES .Bl -tag -width /etc/man.conf -compact @@ -365,10 +298,12 @@ default man configuration file .El .Sh EXIT STATUS .Ex -std man +See +.Xr mandoc 1 +for details. .Sh SEE ALSO .Xr apropos 1 , .Xr intro 1 , -.Xr whatis 1 , .Xr whereis 1 , .Xr intro 2 , .Xr intro 3 , @@ -427,9 +362,23 @@ in .Fl C in .Nx 1.0 ; -and .Fl s and .Fl S in -.Ox 2.3 . +.Ox 2.3 ; +and +.Fl I , +.Fl K , +.Fl l , +.Fl O , +and +.Fl W +in +.Ox 5.7 . +The +.Fl T +option first appeared in +.At III +and was also added in +.Ox 5.7 . diff --git a/contrib/mdocml/man.7 b/contrib/mdocml/man.7 index bfeec51..570e7ac 100644 --- a/contrib/mdocml/man.7 +++ b/contrib/mdocml/man.7 @@ -1,4 +1,4 @@ -.\" $Id: man.7,v 1.132 2015/01/29 00:33:57 schwarze Exp $ +.\" $Id: man.7,v 1.136 2017/06/25 11:42:02 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org> @@ -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: January 29 2015 $ +.Dd $Mdocdate: June 25 2017 $ .Dt MAN 7 .Os .Sh NAME @@ -266,8 +266,6 @@ in the alphabetical reference below. .It Sx TP Ta tagged paragraph: Op Ar width .It Sx HP Ta hanged paragraph: Op Ar width .It Sx PD Ta set vertical paragraph distance: Op Ar height -.It Sx \&br Ta force output line break in text mode (no arguments) -.It Sx \&sp Ta force vertical space: Op Ar height .It Sx fi , nf Ta fill mode and no-fill mode (no arguments) .It Sx in Ta additional indent: Op Ar width .El @@ -350,8 +348,12 @@ See also and .Sx \&IR . .Ss \&DT -Has no effect. -Included for compatibility. +Restore the default tabulator positions. +They are at intervals of 0.5 inches. +This has no effect unless the tabulator positions were changed with the +.Xr roff 7 +.Ic \&ta +request. .Ss \&EE This is a non-standard GNU extension, included only for compatibility. In @@ -464,6 +466,20 @@ See also .Sx \&PP , and .Sx \&TP . +.Ss \&ME +End a mailto block. +This is a non-standard GNU extension, included only for compatibility. +See +.Sx \&MT . +.Ss \&MT +Begin a mailto block. +This is a non-standard GNU extension, included only for compatibility. +It has the following syntax: +.Bd -literal -offset indent +.Pf \. Sx \&MT Ar address +link description to be shown +.Pf \. Sx ME +.Ed .Ss \&OP Optional command-line argument. This is a non-standard GNU extension, included only for compatibility. @@ -708,12 +724,6 @@ It has the following syntax: link description to be shown .Pf \. Sx UE .Ed -.Ss \&br -Breaks the current line. -Consecutive invocations have no further effect. -.Pp -See also -.Sx \&sp . .Ss \&fi End literal mode begun by .Sx \&nf . @@ -736,24 +746,6 @@ Literal mode is implicitly ended by .Sx \&SH or .Sx \&SS . -.Ss \&sp -Insert vertical spaces into output with the following syntax: -.Bd -filled -offset indent -.Pf \. Sx \&sp -.Op Ar height -.Ed -.Pp -The -.Ar height -argument is a scaling width as described in -.Xr roff 7 . -If 0, this is equivalent to the -.Sx \&br -macro. -Defaults to 1, if unspecified. -.Pp -See also -.Sx \&br . .Sh MACRO SYNTAX The .Nm @@ -777,10 +769,7 @@ is equivalent to .Sq \&.I foo . If next-line macros are invoked consecutively, only the last is used. If a next-line macro is followed by a non-next-line macro, an error is -raised, except for -.Sx \&br -and -.Sx \&sp . +raised. .Pp The syntax is as follows: .Bd -literal -offset indent @@ -808,11 +797,9 @@ The syntax is as follows: .It Sx \&SM Ta n Ta next-line Ta \& .It Sx \&TH Ta >1, <6 Ta current Ta \& .It Sx \&UC Ta <=1 Ta current Ta \& -.It Sx \&br Ta 0 Ta current Ta compat .It Sx \&fi Ta 0 Ta current Ta compat .It Sx \&in Ta 1 Ta current Ta compat .It Sx \&nf Ta 0 Ta current Ta compat -.It Sx \&sp Ta 1 Ta current Ta compat .El .Pp Macros marked as diff --git a/contrib/mdocml/man.c b/contrib/mdocml/man.c index a2db05f..7a2bcc9 100644 --- a/contrib/mdocml/man.c +++ b/contrib/mdocml/man.c @@ -1,7 +1,7 @@ -/* $Id: man.c,v 1.167 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: man.c,v 1.176 2017/06/28 12:52:45 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -35,21 +35,6 @@ #include "roff_int.h" #include "libman.h" -const char *const __man_macronames[MAN_MAX] = { - "br", "TH", "SH", "SS", - "TP", "LP", "PP", "P", - "IP", "HP", "SM", "SB", - "BI", "IB", "BR", "RB", - "R", "B", "I", "IR", - "RI", "sp", "nf", - "fi", "RE", "RS", "DT", - "UC", "PD", "AT", "in", - "ft", "OP", "EX", "EE", - "UR", "UE", "ll" - }; - -const char * const *man_macronames = __man_macronames; - static void man_descope(struct roff_man *, int, int); static int man_ptext(struct roff_man *, int, char *, int); static int man_pmacro(struct roff_man *, int, char *, int); @@ -91,6 +76,8 @@ 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. */ @@ -104,17 +91,36 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs) /* Skip leading whitespace. */ ; /* - * Blank lines are ignored right after headings + * Blank lines are ignored in next line scope + * and right after headings and cancel preceding \c, * but add a single vertical space elsewhere. */ if (buf[i] == '\0') { - /* Allocate a blank entry. */ - if (man->last->tok != MAN_SH && - man->last->tok != MAN_SS) { - roff_elem_alloc(man, line, offs, MAN_sp); - man->next = ROFF_NEXT_SIBLING; + if (man->flags & (MAN_ELINE | MAN_BLINE)) { + mandoc_msg(MANDOCERR_BLK_BLANK, man->parse, + 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; + *ep = '\0'; + return 1; + default: + break; } + roff_elem_alloc(man, line, offs, ROFF_sp); + man->next = ROFF_NEXT_SIBLING; return 1; } @@ -160,26 +166,19 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) { struct roff_node *n; const char *cp; - int tok; - int i, ppos; + size_t sz; + enum roff_tok tok; + int ppos; int bline; - char mac[5]; - - ppos = offs; - - /* - * Copy the first word into a nil-terminated buffer. - * Stop when a space, tab, escape, or eoln is encountered. - */ - - i = 0; - while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) - mac[i++] = buf[offs++]; - - mac[i] = '\0'; - tok = (i > 0 && i < 4) ? man_hash_find(mac) : TOKEN_NONE; + /* Determine the line macro. */ + ppos = offs; + tok = TOKEN_NONE; + for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) + 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); @@ -203,7 +202,7 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) /* Jump to the next non-whitespace word. */ - while (buf[offs] && buf[offs] == ' ') + while (buf[offs] == ' ') offs++; /* @@ -223,6 +222,20 @@ man_pmacro(struct roff_man *man, int ln, char *buf, int offs) man_breakscope(man, tok); bline = man->flags & MAN_BLINE; + /* + * If the line in next-line scope ends with \c, keep the + * next-line scope open for the subsequent input line. + * That is not at all portable, only groff >= 1.22.4 + * does it, but *if* this weird idiom occurs in a manual + * 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; + } + /* Call to handler... */ assert(man_macros[tok].fp); @@ -266,17 +279,18 @@ man_breakscope(struct roff_man *man, int tok) * Delete the element that is being broken. */ - if (man->flags & MAN_ELINE && (tok == TOKEN_NONE || + if (man->flags & MAN_ELINE && (tok < MAN_TH || ! (man_macros[tok].flags & MAN_NSCOPED))) { n = man->last; - assert(n->type != ROFFT_TEXT); - if (man_macros[n->tok].flags & MAN_NSCOPED) + if (n->type == ROFFT_TEXT) + n = n->parent; + if (n->tok < MAN_TH || + man_macros[n->tok].flags & MAN_NSCOPED) n = n->parent; mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line, n->pos, "%s breaks %s", - tok == TOKEN_NONE ? "TS" : man_macronames[tok], - man_macronames[n->tok]); + roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); man->flags &= ~MAN_ELINE; @@ -302,12 +316,13 @@ man_breakscope(struct roff_man *man, int tok) * Delete the block that is being broken. */ - if (man->flags & MAN_BLINE && (tok == TOKEN_NONE || + if (man->flags & MAN_BLINE && (tok < MAN_TH || man_macros[tok].flags & MAN_BSCOPE)) { n = man->last; if (n->type == ROFFT_TEXT) n = n->parent; - if ( ! (man_macros[n->tok].flags & MAN_BSCOPE)) + if (n->tok < MAN_TH || + (man_macros[n->tok].flags & MAN_BSCOPE) == 0) n = n->parent; assert(n->type == ROFFT_HEAD); @@ -317,8 +332,7 @@ man_breakscope(struct roff_man *man, int tok) mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line, n->pos, "%s breaks %s", - tok == TOKEN_NONE ? "TS" : man_macronames[tok], - man_macronames[n->tok]); + roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); man->flags &= ~MAN_BLINE; diff --git a/contrib/mdocml/man.cgi.3 b/contrib/mdocml/man.cgi.3 index e7c1d2f..e83248c 100644 --- a/contrib/mdocml/man.cgi.3 +++ b/contrib/mdocml/man.cgi.3 @@ -1,6 +1,6 @@ -.\" $Id: man.cgi.3,v 1.2 2016/07/07 19:19:01 schwarze Exp $ +.\" $Id: man.cgi.3,v 1.4 2017/03/15 13:18:53 schwarze Exp $ .\" -.\" Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -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: July 7 2016 $ +.Dd $Mdocdate: March 15 2017 $ .Dt MAN.CGI 3 .Os .Sh NAME @@ -126,12 +126,30 @@ contains a search query in short format or when is empty and a .Ev QUERY_STRING is provided. -It changes into the manpath and calls +If possible, requests using +.Ev QUERY_STRING +are redirected to URIs using +.Ev PATH_INFO +by calling +.Fn pg_redirect . +Otherwise, it changes into the manpath and calls .Xr mansearch 3 . Depending on the result, it calls either .Fn pg_noresult or .Fn pg_searchres . +.It Ft void Fn pg_redirect "const struct req *req" "const char *name" +This function is special in so far as it does not print an HTML page, +but only an HTTP 303 response with a Location: of the form: +.Sm off +.No http:// +.Ar host Ns / +.Op Ar scriptname Ns / +.Op Ar manpath Ns / +.Op Ar arch Ns / +.Fa name +.Op Pf . Ar sec +.Sm on .It Ft void Fn pg_noresult "const struct req *req" "const char *msg" This function calls .Fn resp_begin_html , @@ -219,13 +237,18 @@ and are used. The highest level result generators are: .Bl -tag -width 1n -.It Ft void Fn resp_begin_html "int code" "const char *msg" +.It Ft void Fn resp_begin_html "int code" "const char *msg" "const char *file" This generator calls .Fn resp_begin_http to print the HTTP headers, then prints the HTML header up to the opening tag of the <body> element, then copies the file .Pa header.html to the output, if it exists and is readable. +If +.Fa file +is not +.Dv NULL , +it is used for the <title> element. .It Ft void Fn resp_searchform "const struct req *req" "enum focus focus" This generator prints a search form, filling it with data from the provided request object. diff --git a/contrib/mdocml/man.cgi.8 b/contrib/mdocml/man.cgi.8 index 0e1b60d..eba3cec 100644 --- a/contrib/mdocml/man.cgi.8 +++ b/contrib/mdocml/man.cgi.8 @@ -1,4 +1,4 @@ -.\" $Id: man.cgi.8,v 1.20 2016/07/11 22:48:37 schwarze Exp $ +.\" $Id: man.cgi.8,v 1.22 2017/03/18 16:48:24 schwarze Exp $ .\" .\" Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 11 2016 $ +.Dd $Mdocdate: March 18 2017 $ .Dt MAN.CGI 8 .Os .Sh NAME @@ -186,11 +186,6 @@ Otherwise, a leading slash is needed. This is used in generated HTML code. .It Dv CUSTOMIZE_TITLE An ASCII string to be used for the HTML <TITLE> element. -.It Dv HTTP_HOST -The FQDN of the (possibly virtual) host the HTTP server is running on. -This is used for -.Ic Location: -headers in HTTP 303 responses. .It Dv MAN_DIR A file system path to the .Nm @@ -411,15 +406,16 @@ A version of based on .Xr mandoc 1 first appeared in mdocml-1.12.1 (March 2012). -The current SQLite3-based version first appeared in -.Ox 5.6 . +The current +.Xr mandoc.db 5 +database format first appeared in +.Ox 6.1 . .Sh AUTHORS .An -nosplit The .Nm program was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv -and ported to the SQLite3-based -.Xr mandoc.db 5 -backend by -.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +and is maintained by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org , +who also designed and implemented the database format. diff --git a/contrib/mdocml/man.h b/contrib/mdocml/man.h index 8f63f3b..d671f9a 100644 --- a/contrib/mdocml/man.h +++ b/contrib/mdocml/man.h @@ -1,4 +1,4 @@ -/* $Id: man.h,v 1.77 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: man.h,v 1.78 2017/04/24 23:06:18 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -16,50 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define MAN_br 0 -#define MAN_TH 1 -#define MAN_SH 2 -#define MAN_SS 3 -#define MAN_TP 4 -#define MAN_LP 5 -#define MAN_PP 6 -#define MAN_P 7 -#define MAN_IP 8 -#define MAN_HP 9 -#define MAN_SM 10 -#define MAN_SB 11 -#define MAN_BI 12 -#define MAN_IB 13 -#define MAN_BR 14 -#define MAN_RB 15 -#define MAN_R 16 -#define MAN_B 17 -#define MAN_I 18 -#define MAN_IR 19 -#define MAN_RI 20 -#define MAN_sp 21 -#define MAN_nf 22 -#define MAN_fi 23 -#define MAN_RE 24 -#define MAN_RS 25 -#define MAN_DT 26 -#define MAN_UC 27 -#define MAN_PD 28 -#define MAN_AT 29 -#define MAN_in 30 -#define MAN_ft 31 -#define MAN_OP 32 -#define MAN_EX 33 -#define MAN_EE 34 -#define MAN_UR 35 -#define MAN_UE 36 -#define MAN_ll 37 -#define MAN_MAX 38 - -/* Names of macros. */ -extern const char *const *man_macronames; - - struct roff_man; const struct mparse *man_mparse(const struct roff_man *); diff --git a/contrib/mdocml/man.options.1 b/contrib/mdocml/man.options.1 new file mode 100644 index 0000000..d8c790f --- /dev/null +++ b/contrib/mdocml/man.options.1 @@ -0,0 +1,1332 @@ +.\" $Id: man.options.1,v 1.7 2017/07/04 23:40:01 schwarze Exp $ +.\" +.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: July 4 2017 $ +.Dt MAN.OPTIONS 1 +.Os +.Sh NAME +.Nm man.options +.Nd assignment of option letters in manual page utilities +.\" +.\" Sources that occur repeatedly. +.\" Only use if the precise implementation time is unknown. +.\" +.de PWB +.No PWB/UNIX 1.0 Pq July 1, 1977 \\$1 +.. +.de At7 +.At v7 Pq January 1979 \\$1 +.. +.de At3 +.At III Pq June 1980 \\$1 +.. +.de Bx4 +.Bx 4 Pq November 16, 1980 \\$1 +.. +.de At5 +.At V Pq January 1983 \\$1 +.. +.de Bx43 +.Bx 4.3 Pq June 1986 \\$1 +.. +.\" option was present in groff-1.01 as contained in 4.3BSD-Net/2 +.\" and no mention of it could be found in the ChangeLog, +.\" so it's probably older than groff-0.4, where the log started +.de g04 +.No probably before groff-0.4 Pq before July 14, 1990 \\$1 +.. +.de Eaton +.No Eaton Pq before July 7, 1993; 1990/91? \\$1 +.. +.\" man-1.5e was released on July 11, 1998. +.de man15e +.No man-1.5e Pq not before 1993, not after 1998 \\$1 +.. +.\" man-1.5g was released on April 7, 1999. +.de man15g +.No man-1.5g Pq not before 1993, not after 1999 \\$1 +.. +.\" code first seen in the initial import of man-db into CVS , +.\" which was more or less debian man-db-2.3.17 +.\" Colin Watson's first release was 2.3.18 on May 14, 2001 +.\" no clue about it found in ChangeLog-2013, +.\" so it was probably already present before man-db-2.2a4 +.de dbI +.No man-db probably before 2.2a4 Pq before Nov 8, 1994 \\$1 +.. +.\" +.\" -------------------------------------------------------------------- +.\" +.Sh DESCRIPTION +This manual page lists option letters used in many different versions +of the +.Nm man , +.Nm apropos , +.Nm whatis , +.Nm mandoc , +.Nm makewhatis , +.Nm mandb , +.Nm makemandb , +.Nm catman , +and +.Nm manpath +utilities. +Option letters used by +.Nm groff , +.Nm nroff , +.Nm troff , +and +.Nm roff +are also included because beginning with +.At v7 , +many versions of +.Xr man 1 +pass on unrecognized options to these programs. +.Pp +For each option letter, information is first grouped into paragraphs, +each paragraph describing similar functionality and starting with +one line briefly summarizing that functionality. +.Pp +For each program using the letter for that functionality, one line +is provided, giving the name of the program, a colon, the system +where this letter first appeared for this functionality in this +program, optionally a comma and a list of other system versions +introducing the same, a semicolon, and a list of current systems +supporting it. +If a system appears before the semicolon, it is not repeated +afterwards. +.Pp +Entries are sorted by historical precedence, except that obsolete +options are moved to the end. +Dates are commit dates where known, and release dates otherwise. +.Bl -tag -width 3n +.It Fl a +display all matching manual pages +.br +.Nm man : +.Bx 4.3 Tahoe Pq June 1988 , +.Eaton ; +.Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +only display items that match all keywords +.br +.Nm apropos : +.No man-db Pq Aug 29, 2007 +.Pp +use all directories and files for +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.Pp +.Bq superseded by Fl T Cm ascii +ASCII output mode +.br +.Nm troff : +.At7 +.br +.Nm groff : +.g04 +.It Fl B +use specified browser +.br +.Nm man : +.No man-1.6 Pq June 24, 2005 +.It Fl b +print a backtrace with each warning or error message +.br +.Nm groff : +.g04 +.Pp +.Bq obsolete hardware +report whether the phototypesetter is busy +.br +.Nm troff : +.At7 +.It Fl C +alternate configuration file +.br +.Nm apropos , whatis : +.Bx 4.4 Lite1 Pq April 22, 1994 , +.No man-db Pq Feb 22, 2003 ; +.Ox , Nx +.br +.Nm man : +.Nx 1.0 Pq Oct 26, 1994 , +.man15e ; +.Ox +.br +.Nm mandb , catman , manpath : +.No man-db Pq Feb 22, 2003 +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +.Bq obsolete +enable compatibility mode +.br +.Nm groff : +.No before groff-0.5 Pq before August 3, 1990 +.It Fl c +do not use a pager +.br +.Nm man : +.Bx 4.3 Reno Pq June 1990 ; +.Ox , Nx +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +process given catpath +.br +.Nm makewhatis : +.Pq not before 1992, not after 1995 +.Pp +recreate databases from scratch +.br +.Nm mandb : +.dbI +.Pp +produce a catpath as opposed to a manpath +.br +.Nm manpath : +.dbI +.Pp +internal option for use by +.Xr catman 1 +.br +.Nm man : +.dbI +.Pp +reformat source page even if cat page exists +.br +.Nm man : +.man15e +.Pp +disable terminal color output in +.Xr grotty 1 +.br +.Nm groff : +.No groff-1.18.0 Pq Oct 4, 2001 +.Pp +recreate nroff versions from SGML sources +.br +.Nm catman : +.No Solaris 9-11 +.Pp +.Bq obsolete +postprocess with +.Xr col 1 +.br +.Nm man : +.At3 , +.At5 +.It Fl D +reset whatever was set with +.Ev MANOPT +.br +.Nm man : +.dbI +.Pp +print debugging info in addition to manual page +.br +.Nm man : +.man15e +.Pp +set default input encoding for +.Xr preconv 1 +.br +.Nm groff : +.No groff-1.20 Pq August 20, 2008 +.Pp +display all files added to +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.It Fl d +define a user-defined string +.br +.Nm groff : +.g04 +.Pp +print debugging information +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm apropos , whatis : +.dbI ; +.Fx +.br +.Nm mandb , catman : +.dbI +.Pp +remove and re-add a file to +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 2.7 Pq Feb 3, 2000 +.Pp +.Bq superseded by Fl l +interpret arguments as file names +.br +.Nm man : +.At3 , +.At5 +.It Fl E +inhibit all error messages +.br +.Nm groff : +.g04 +.Pp +select output encoding +.br +.Nm man : +.No man-db Pq Dec 23, 2001 +.It Fl e +preprocess with +.Xr eqn 7 +.br +.Nm man : +.At7 +.br +.Nm groff : +.g04 +.Pp +adjust text to left and right margins +.br +.Nm nroff : +.At7 +.Pp +use exact matching +.br +.Nm apropos , whatis : +.dbI +.Pp +restrict search by section extension +.br +.Nm man : +.No man-db-2.3.5 Pq April 21, 1995 +.It Fl F +use alternate font directory +.br +.Nm troff : +.Bx 4.2 Pq September 1983 +.br +.Nm groff : +.g04 +.Pp +preformat only, do not display +.br +.Nm man : +.No man-1.5g Pq April 7, 1999 +.Pp +force searching dirs, do not use index (default) +.br +.Nm man : +.No illumos , Solaris 9-11 +.It Fl f +.Xr whatis 1 +mode +.br +.Nm man : +.Bx4 , +.Eaton ; +.Ox , Fx , No man-db , man-1.6 +.br +.Nm apropos , whatis : +.No man-db Pq Dec 2, 2010 , +.Ox 5.7 Pq August 27, 2014 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +set the default font family +.br +.Nm groff : +.g04 +.Pp +force formatting even if cat page is newer +.br +.Nm catman : +.Fx Pq March 15, 1995 +.Pp +update only the entries for the given file +.br +.Nm mandb : +.No man-db Pq Feb 21, 2003 +.Pp +force rebuilding the database from scratch +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.Pp +locate manual page related to given file name +.br +.Nm man : +.No illumos , Solaris 9-11 +.Pp +.Bq obsolete hardware +do not feed out paper nor stop phototypesetter +.br +.Nm troff : +.At7 +.It Fl G +preprocess with +.Xr grap 1 +.br +.Nm groff : +.No groff-1.16 Pq May 1, 2000 +.It Fl g +produce a global manpath +.br +.Nm manpath : +.No man-db-2.2a7 Pq Nov 16, 1994 +.Pp +preprocess with +.Xr grn 1 +.br +.Nm groff : +.No groff-1.16 Pq Feb 20, 2000 +.Pp +.Bq obsolete hardware +output to a GCOS phototypesetter +.br +.Nm troff : +.At7 +.Pp +.Bq obsolete hardware +output to a DASI 300 terminal in 12-pitch mode +.br +.Nm man : +.PWB +.It Fl H +read hyphenation patterns from the given file +.br +.Nm groff : +.g04 +.Pp +produce HTML output +.br +.Nm man : +.No man-db-1.3.12 to 1.3.17 Pq not before 1996, not after 2001 +.Pp +use program to render HTML files as text +.br +.Nm man : +.No man-1.6 Pq June 24, 2005 +.It Fl h +print a help message and exit +.br +.Nm groff : +.g04 +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.br +.Nm manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm apropos , whatis , mandb , catman : +.dbI +.Pp +display the SYNOPSIS lines only +.br +.Nm man : +.Bx 4.3 Net/2 Pq August 20, 1991 ; +.Ox , Nx +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq Sep 3, 2014 +.Pp +turn on HTML formatting +.br +.Nm apropos : +.Nx Pq Apr 2, 2013 +.Pp +.Bq obsolete +replace spaces by tabs in the output +.br +.Nm roff , nroff : +.At7 +.It Fl I +input file search path for +.Xr soelim 1 +.br +.Nm groff : +.No groff-1.12 Pq Sep 11, 1999 +.Pp +respect case when matching manual page names +.br +.Nm man , catman : +.No man-db Pq Apr 21, 2002 +.Pp +input options, in particular default operating system name +.br +.Nm mandoc : +.Ox 5.2 Pq May 24, 2012 +.br +.Nm man , apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.It Fl i +read standard input after the input files are exhausted +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +ignore case when matching manual page names +.br +.Nm man , catman : +.No man-db Pq Apr 21, 2002 +.Pp +turn on terminal escape code formatting +.br +.Nm apropos : +.Nx Pq March 29, 2013 +.It Fl J +preprocess with +.Xr gideal 1 +.br +.Nm groff : +.No groff-1.22.3 Pq June 17, 2014 +.It Fl j +preprocess with +.Xr chem 1 +.br +.Nm groff : +.No groff-1.22 Pq Jan 22, 2011 +.It Fl K +source code full text search +.br +.Nm man : +.man15e , +.No man-db Pq June 28, 2009 ; +.No Solaris 11 +.Pp +input encoding +.br +.Nm groff : +.No groff-1.20 Pq Dec 31, 2005 +.br +.Nm man , apropos , whatis , mandoc : +.Ox 5.7 Pq Oct 30, 2014 +.It Fl k +.Xr apropos 1 +mode +.br +.Nm man : +.Bx4 , +.Eaton ; +.No POSIX , Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +ignore formatting errors +.br +.Nm catman : +.Nx Pq April 26, 1994 +.Pp +preprocess with +.Xr preconv 1 +.br +.Nm groff : +.No groff-1.20 Pq Dec 31, 2005 +.Pp +.Bq obsolete hardware +display on a Tektronix 4014 terminal +.br +.Nm man : +.At7 +.It Fl L +pass argument to the spooler +.br +.Nm groff : +.No groff-0.6 Pq Sep 14, 1990 +.Pp +use alternate +.Xr locale 1 +.br +.Nm man , apropos , whatis : +.No before man-db-2.2a13 Pq before Dec 15, 1994 +.Pp +print list of locales +.br +.Nm manpath : +.Fx Pq Nov 23, 1999 +.Pp +use +.Xr locale 1 +specified in the environment +.br +.Nm catman : +.Fx Pq May 18, 2002 +.It Fl l +spool the output +.br +.Nm groff : +.g04 +.Pp +interpret arguments as file names +.br +.Nm man : +.No before man-2.2a7 Pq before Nov 16, 1994 , +.Ox 5.7 Pq Aug 30, 2014 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq Aug 30, 2014 +.Pp +do not trim output to the terminal width +.br +.Nm apropos , whatis : +.No man-db Pq Aug 19, 2007 +.Pp +only parse NAME sections +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.Pp +legacy mode: search Nm,Nd, no context or formatting +.br +.Nm apropos : +.Nx Pq March 29, 2013 +.Pp +list all manual pages matching name within the search path +.br +.Nm man : +.No illumos , Solaris 9-11 +.It Fl M +override manual page search path +.br +.Nm man : +.Bx43 , +.Eaton ; +.Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm apropos , whatis : +.Bx43 , +.No before man-db-2.2a14 Pq before Dec 16, 1994 ; +.Ox , No illumos +.br +.Nm catman : +.dbI ; +.Nx Pq July 27, 1993 , +.No Solaris 9-11 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +prepend to macro file search path +.br +.Nm groff : +.g04 +.Pp +do not show the context of the match +.br +.Nm apropos : +.Nx Pq May 22, 2016 +.It Fl m +specify input macro language +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.br +.Nm mandoc : +.Ox 4.8 Pq April 6, 2009 +.Pp +augment manual page search path +.br +.Nm man , apropos , whatis : +.Bx 4.3 Reno Pq June 1990 ; +.Ox , Nx +.br +.Nm catman : +.Nx Pq Apr 4, 1999 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +override operating system +.br +.Nm man : +.Eaton ; +.No man-db , man-1.6 +.br +.Nm apropos , whatis , manpath : +.dbI +.Pp +override architecture +.br +.Nm man : +.Fx Pq Jan 11, 2002 +.Pp +show the context of the match +.br +.Nm apropos : +.Nx Pq May 22, 2016 +.It Fl N +do not allow newlines between +.Xr eqn 7 +delimiters +.br +.Nm groff : +.No groff-1.01 Pq Feb 21, 1991 +.It Fl n +specify a page number for the first page +.br +.Nm troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +.Xr nroff 1 +output mode +.br +.Nm man : +.At7 +.Pp +do not create the +.Xr whatis 1 +database +.br +.Nm catman : +.Nx Pq July 27, 1993 +.Pp +print commands instead of executing them +.br +.Nm catman : +.Fx Pq May 18, 2002 , +.No Solaris 9-11 +.Pp +limit the number of results +.br +.Nm apropos : +.Nx Pq Feb 7, 2012 +.Pp +dry run simulating +.Xr mandoc.db 5 +creation +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.It Fl O +output options +.br +.Nm mandoc : +.Ox 4.8 Pq Oct 27, 2009 +.br +.Nm man , apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.It Fl o +select pages by numbers +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +force use of non-localized manual pages +.br +.Nm man : +.Fx Pq June 7, 1999 +.Pp +optimize index for speed and disk space +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.It Fl P +pass argument to postprocessor +.br +.Nm groff : +.No groff-0.6 Pq Sep 14, 1990 +.Pp +use specified pager +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.Pp +turn on pager formatting +.br +.Nm apropos : +.Nx Pq Apr 2, 2013 +.It Fl p +preprocess with +.Xr pic 1 +.br +.Nm groff : +.g04 +.Pp +use the given list of preprocessors +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.Pp +dry run, display commands instead of executing them +.br +.Nm catman : +.Nx Pq July 27, 1993 , +.Fx Pq March 15, 1995 to May 18, 2002 , +.No Solaris 9-11 +.Pp +print warnings when building +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 2.7 Pq April 23, 2000 +.Pp +do not look for deleted manual pages +.br +.Nm mandb : +.No man-db Pq June 28, 2001 +.Pp +print the search path for manual pages +.br +.Nm man : +.Nx Pq June 14 , 2011 +.Pp +turn on pager formatting and pipe through pager +.br +.Nm apropos : +.Nx Pq Feb 7, 2012 +.Pp +.Bq obsolete hardware +set phototypesetter point size +.br +.Nm troff : +.At7 +.It Fl Q +print only fatal error messages +.br +.Nm makemandb : +.Nx Pq Aug 29, 2012 +.Pp +quick mode of +.Xr mandoc.db 5 +creation +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.It Fl q +invoke the simultaneous input-output mode of the .rd request +.br +.Nm nroff , troff : +.At7 +.Pp +issue no warnings +.br +.Nm manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm mandb : +.dbI +.Pp +print only warnings and errors, no status updates +.br +.Nm makemandb : +.Nx Pq Aug 29, 2012 +.It Fl R +postprocess with +.Xr refer 1 +.br +.Nm groff : +.No groff-1.02 Pq June 2, 1991 +.Pp +recode to the specified encoding +.br +.Nm man : +.No man-db Pq Dec 31, 2007 +.It Fl r +set number register +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +scan for and remove junk files +.br +.Nm catman : +.Fx Pq March 31, 1995 +.Pp +set +.Xr less 1 +prompt +.br +.Nm man : +.No man-db-2.3.5 Pq April 21, 1995 +.Pp +use regular expression matching +.br +.Nm apropos , whatis : +.No man-db-2.3.5 Pq April 21, 1995 +.Pp +turn off formatting +.br +.Nm apropos : +.Nx Pq Feb 10, 2013 +.Pp +check for formatting errors, do not display +.br +.Nm man : +.No illumos , Solaris 9-11 +.It Fl S +manual section search list +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.Pp +safer mode +.br +.Nm groff : +.No groff-1.10 Pq May 17, 1994 +.Pp +restrict architecture +.br +.Nm man : +.Ox 2.3 Pq March 9, 1998 , +.Nx Pq May 27, 2000 +.br +.Nm apropos : +.Ox 4.5 Pq Dec 24, 2008 , +.Nx Pq May 8, 2009 +.br +.Nm whatis : +.Ox 5.6 Pq April 18, 2014 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.It Fl s +preprocess with +.Xr soelim 1 +.br +.Nm groff : +.g04 +.Pp +silent mode, do not echo commands +.br +.Nm catman : +.Nx Pq April 26, 1994 +.Pp +restrict section +.br +.Nm makewhatis : +.man15g +.br +.Nm man : +.Ox 2.3 Pq March 9, 1998 , +.Nx Pq June 12, 2000 ; +.No illumos , Solaris 9-11 +.br +.Nm apropos : +.No man-db Pq Nov 16, 2003 , +.Ox 4.5 Pq Dec 24, 2008 , +.Nx Pq May 8, 2009 ; +.No illumos +.br +.Nm whatis : +.No man-db Pq Nov 16, 2003 , +.Ox 5.6 Pq April 18, 2014 ; +.No illumos +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +do not look for stray cats +.br +.Nm mandb : +.dbI +.Pp +.Bq SysV compat, recommends Fl S +manual section search list +.br +.Nm man : +.No man-db Pq Jan 1, 2008 +.Pp +.Bq superseded by Fl h +display the SYNOPSIS lines only +.br +.Nm man : +.PWB +.Pp +.Bq obsolete hardware +pause before each page for paper manipulation +.br +.Nm roff : +.At7 +.Pp +.Bq obsolete hardware +.Xr troff 1 +output mode, small format +.br +.Nm man : +.At3 , +.At5 +.It Fl T +select terminal output format +.br +.Nm nroff : +.At7 +.br +.Nm man : +.At3 , +.At5 , +.dbI , +.Ox 5.7 Pq August 27, 2014 +.br +.Nm groff : +.g04 +.br +.Nm mandoc : +.Ox 4.8 Pq April 6, 2009 +.br +.Nm apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.Pp +use UTF-8 for +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.Pp +.Bq superseded by Fl m +use other macro package +.br +.Nm man , catman : +.No Solaris 9-11 +.It Fl t +.Xr troff 1 +output mode +.br +.Nm man : +.PWB , +.At7 , +.Bx 2 Pq May 10, 1979 , +.At3 , +.At5 , +.Eaton ; +.Fx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm catman : +.No Solaris 9-11 +.Pp +preprocess with +.Xr tbl 7 +.br +.Nm groff : +.g04 +.Pp +check manual pages in the hierarchy +.br +.Nm mandb : +.No man-db-1.3.12 to 1.3.17 Pq not before 1996, not after 2001 +.Pp +check files for problems related to +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 2.7 Pq April 23, 2000 +.It Fl U +unsafe mode +.br +.Nm groff : +.No groff-1.12 Pq Dec 13, 1999 +.It Fl u +update database +.br +.Nm makewhatis : +.Pq not before 1992, not after 1995 +.Pp +create user databases only +.br +.Nm mandb : +.dbI +.Pp +update database cache (requires suid) +.br +.Nm man : +.No before man-db-2.2a10 Pq before Dec 6, 1994 +.Pp +remove files from +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 3.4 Pq July 9, 2003 +.It Fl V +print the pipeline on stdout instead of executing it +.br +.Nm groff : +.No groff-0.6 Pq Sep 2, 1990 +.Pp +print version information +.br +.Nm man , apropos , whatis , mandb , catman , manpath : +.dbI +.It Fl v +print version number +.br +.Nm groff : +.g04 +.Pp +verbose mode +.br +.Nm catman : +.Fx Pq March 15, 1995 +.br +.Nm makewhatis : +.man15g +.br +.Nm apropos , whatis : +.No man-db Pq Dec 29, 2002 +.Pp +print the name of every parsed file +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.Pp +.Bq obsolete hardware +produce output on the Versatec printer +.br +.Nm man : +.PWB +.It Fl W +disable the named warning +.br +.Nm groff : +.No groff-0.5 Pq August 14, 1990 +.Pp +list pathnames without additional information +.br +.Nm man : +.man15e +.Pp +list pathnames of cat files +.br +.Nm man : +.No man-db Pq Aug 13, 2002 +.Pp +minimum message level to display +.br +.Nm mandoc : +.Ox 4.8 Pq April 6, 2009 +.br +.Nm man , apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.It Fl w +list pathnames +.br +.Nm man : +.At7 , +.At3 , +.At5 , +.Eaton ; +.Ox , Fx , Nx , No man-db , man-1.6 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +enable the named warning +.br +.Nm groff : +.No groff-0.5 Pq August 14, 1990 +.Pp +only create the +.Xr whatis 1 +database +.br +.Nm catman : +.Nx Pq July 27, 1993 , +.No Solaris 9-11 +.Pp +use wildcard matching +.br +.Nm apropos , whatis : +.No man-db-2.3.5 Pq April 21, 1995 +.Pp +use manpath obtained from man --path +.br +.Nm makewhatis : +.man15g +.Pp +update the +.Xr whatis 1 +database +.br +.Nm man : +.No illumos +.Pp +.Bq obsolete hardware +wait until the phototypesetter is available +.br +.Nm troff : +.At7 +.It Fl X +display with +.Xr gxditview 1 +.br +.Nm groff : +.No groff-1.06 Pq Sep 1, 1992 +.br +.Nm man : +.dbI +.It Fl y +use the non-compacted version of the macros +.br +.Nm man : +.At3 , +.At5 +.It Fl Z +do not run preprocessors +.br +.Nm groff : +.g04 +.br +.Nm man : +.No man-db-2.2a5 Pq Nov 10, 1994 +.It Fl z +suppress formatted output from +.Xr troff 1 , +print only error messages +.br +.Nm groff : +.g04 +.It Fl 7 +ASCII output mode +.br +.Nm man : +.No man-db-2.3.5 Pq April 21, 1995 +.It Fl \&? +print a help message and exit +.br +.Nm groff : +.g04 +.br +.Nm man , manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm apropos , whatis , mandb , catman : +.dbI +.El +.Pp +Multi-letter options: +.Bl -tag -width Ds +.It Fl hp +.Bq obsolete hardware +output to a Hewlett Packard terminal +.br +.Nm man : +.PWB +.It Fl 12 +.Bq obsolete hardware +use 12-pitch for certain terminals +.br +.Nm man : +.At3 , +.At5 +.It Fl 450 +.Bq obsolete hardware +output to a DASI 450 terminal +.br +.Nm man : +.PWB +.El +.Pp +In +.At v3 , +.Xr man 1 +had no options. +.br +The syntax was: +.Sy man Ar name Op Ar section +.Pp +In +.At v4 , +.br +the syntax changed to: +.Sy man Oo Ar section Oc Op Ar name ... +.Sh AUTHORS +This information was assembled by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org +using +.Bl -bullet -compact +.It +the Unix Archive of the Unix Heritage Society +.It +the CSRG Archive CD-ROMs +.It +the +.Fx +SVN repository +.It +the +.Ox +CVS repository +.It +the +.Nx +CVS repository +.It +the GNU roff (groff) git repository +.It +the 4.3BSD-Net/2 groff CHANGES file (Oct 1990 to March 1991) +.It +the 4.3BSD-Net/2 groff ChangeLog file (July 1990 to March 1991) +.It +the man-db CVS and git repositories (since April 2001) +.It +the man-db NEWS file (April 1995 to Dec 2016) +.It +the man-db ChangeLog-2013 file (Nov 1994 to Dec 2013) +.It +release tarballs man-1.5g (July 1998) to man-1.5p (Jan 2005), +man-1.6 (June 2005), and man-1.6a to man-1.6g (Dec 2010) +.It +a makewhatis release tarball without version number from 1995 +.It +the illumos manual pages on the WWW +.It +and Solaris 11, SunOS 5.10, and SunOS 5.9 machines at opencsw.org. +.El diff --git a/contrib/mdocml/man_hash.c b/contrib/mdocml/man_hash.c deleted file mode 100644 index bb7b466..0000000 --- a/contrib/mdocml/man_hash.c +++ /dev/null @@ -1,103 +0,0 @@ -/* $Id: man_hash.c,v 1.35 2016/07/15 18:03:45 schwarze Exp $ */ -/* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> - * - * 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 <sys/types.h> - -#include <assert.h> -#include <ctype.h> -#include <limits.h> -#include <string.h> - -#include "mandoc.h" -#include "roff.h" -#include "man.h" -#include "libmandoc.h" -#include "libman.h" - -#define HASH_DEPTH 6 - -#define HASH_ROW(x) do { \ - if (isupper((unsigned char)(x))) \ - (x) -= 65; \ - else \ - (x) -= 97; \ - (x) *= HASH_DEPTH; \ - } while (/* CONSTCOND */ 0) - -/* - * Lookup table is indexed first by lower-case first letter (plus one - * for the period, which is stored in the last row), then by lower or - * uppercase second letter. Buckets correspond to the index of the - * macro (the integer value of the enum stored as a char to save a bit - * of space). - */ -static unsigned char table[26 * HASH_DEPTH]; - - -void -man_hash_init(void) -{ - int i, j, x; - - if (*table != '\0') - return; - - memset(table, UCHAR_MAX, sizeof(table)); - - for (i = 0; i < (int)MAN_MAX; i++) { - x = man_macronames[i][0]; - - assert(isalpha((unsigned char)x)); - - HASH_ROW(x); - - for (j = 0; j < HASH_DEPTH; j++) - if (UCHAR_MAX == table[x + j]) { - table[x + j] = (unsigned char)i; - break; - } - - assert(j < HASH_DEPTH); - } -} - -int -man_hash_find(const char *tmp) -{ - int x, y, i; - int tok; - - if ('\0' == (x = tmp[0])) - return TOKEN_NONE; - if ( ! (isalpha((unsigned char)x))) - return TOKEN_NONE; - - HASH_ROW(x); - - for (i = 0; i < HASH_DEPTH; i++) { - if (UCHAR_MAX == (y = table[x + i])) - return TOKEN_NONE; - - tok = y; - if (0 == strcmp(tmp, man_macronames[tok])) - return tok; - } - - return TOKEN_NONE; -} diff --git a/contrib/mdocml/man_html.c b/contrib/mdocml/man_html.c index 641e0e3..a304b3e 100644 --- a/contrib/mdocml/man_html.c +++ b/contrib/mdocml/man_html.c @@ -1,4 +1,4 @@ -/* $Id: man_html.c,v 1.129 2017/01/21 01:20:32 schwarze Exp $ */ +/* $Id: man_html.c,v 1.145 2017/06/25 11:42:02 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -26,27 +26,21 @@ #include <string.h> #include "mandoc_aux.h" +#include "mandoc.h" #include "roff.h" #include "man.h" #include "out.h" #include "html.h" #include "main.h" -/* TODO: preserve ident widths. */ /* FIXME: have PD set the default vspace width. */ #define INDENT 5 #define MAN_ARGS const struct roff_meta *man, \ const struct roff_node *n, \ - struct mhtml *mh, \ struct html *h -struct mhtml { - int fl; -#define MANH_LITERAL (1 << 0) /* literal context */ -}; - struct htmlman { int (*pre)(MAN_ARGS); int (*post)(MAN_ARGS); @@ -57,6 +51,7 @@ static void print_bvspace(struct html *, static void print_man_head(MAN_ARGS); static void print_man_nodelist(MAN_ARGS); static void print_man_node(MAN_ARGS); +static int fillmode(struct html *, int); static int a2width(const struct roff_node *, struct roffsu *); static int man_B_pre(MAN_ARGS); @@ -71,15 +66,12 @@ static int man_SM_pre(MAN_ARGS); static int man_SS_pre(MAN_ARGS); static int man_UR_pre(MAN_ARGS); static int man_alt_pre(MAN_ARGS); -static int man_br_pre(MAN_ARGS); static int man_ign_pre(MAN_ARGS); static int man_in_pre(MAN_ARGS); -static int man_literal_pre(MAN_ARGS); static void man_root_post(MAN_ARGS); static void man_root_pre(MAN_ARGS); -static const struct htmlman mans[MAN_MAX] = { - { man_br_pre, NULL }, /* br */ +static const struct htmlman __mans[MAN_MAX - MAN_TH] = { { NULL, NULL }, /* TH */ { man_SH_pre, NULL }, /* SH */ { man_SS_pre, NULL }, /* SS */ @@ -100,9 +92,8 @@ static const struct htmlman mans[MAN_MAX] = { { man_I_pre, NULL }, /* I */ { man_alt_pre, NULL }, /* IR */ { man_alt_pre, NULL }, /* RI */ - { man_br_pre, NULL }, /* sp */ - { man_literal_pre, NULL }, /* nf */ - { man_literal_pre, NULL }, /* fi */ + { NULL, NULL }, /* nf */ + { NULL, NULL }, /* fi */ { NULL, NULL }, /* RE */ { man_RS_pre, NULL }, /* RS */ { man_ign_pre, NULL }, /* DT */ @@ -110,14 +101,15 @@ static const struct htmlman mans[MAN_MAX] = { { man_ign_pre, NULL }, /* PD */ { man_ign_pre, NULL }, /* AT */ { man_in_pre, NULL }, /* in */ - { man_ign_pre, NULL }, /* ft */ { man_OP_pre, NULL }, /* OP */ - { man_literal_pre, NULL }, /* EX */ - { man_literal_pre, NULL }, /* EE */ + { NULL, NULL }, /* EX */ + { NULL, NULL }, /* EE */ { man_UR_pre, NULL }, /* UR */ { NULL, NULL }, /* UE */ - { man_ign_pre, NULL }, /* ll */ + { man_UR_pre, NULL }, /* MT */ + { NULL, NULL }, /* ME */ }; +static const struct htmlman *const mans = __mans - MAN_TH; /* @@ -146,27 +138,25 @@ print_bvspace(struct html *h, const struct roff_node *n) void html_man(void *arg, const struct roff_man *man) { - struct mhtml mh; struct html *h; struct tag *t; - memset(&mh, 0, sizeof(mh)); h = (struct html *)arg; if ((h->oflags & HTML_FRAGMENT) == 0) { print_gen_decls(h); print_otag(h, TAG_HTML, ""); t = print_otag(h, TAG_HEAD, ""); - print_man_head(&man->meta, man->first, &mh, h); + print_man_head(&man->meta, man->first, h); print_tagq(h, t); print_otag(h, TAG_BODY, ""); } - man_root_pre(&man->meta, man->first, &mh, h); + man_root_pre(&man->meta, man->first, h); t = print_otag(h, TAG_DIV, "c", "manual-text"); - print_man_nodelist(&man->meta, man->first->child, &mh, h); + print_man_nodelist(&man->meta, man->first->child, h); print_tagq(h, t); - man_root_post(&man->meta, man->first, &mh, h); + man_root_post(&man->meta, man->first, h); print_tagq(h, NULL); } @@ -187,7 +177,7 @@ print_man_nodelist(MAN_ARGS) { while (n != NULL) { - print_man_node(man, n, mh, h); + print_man_node(man, n, h); n = n->next; } } @@ -195,25 +185,97 @@ print_man_nodelist(MAN_ARGS) static void print_man_node(MAN_ARGS) { - int child; + static int want_fillmode = MAN_fi; + static int save_fillmode; + struct tag *t; + int child; - child = 1; - t = h->tags.head; + /* + * Handle fill mode switch requests up front, + * they would just cause trouble in the subsequent code. + */ + + switch (n->tok) { + case MAN_nf: + case MAN_EX: + want_fillmode = MAN_nf; + return; + case MAN_fi: + case MAN_EE: + want_fillmode = MAN_fi; + if (fillmode(h, 0) == MAN_fi) + print_otag(h, TAG_BR, ""); + return; + default: + break; + } + + /* Set up fill mode for the upcoming node. */ switch (n->type) { - case ROFFT_TEXT: - if ('\0' == *n->string) { - print_paragraph(h); - return; + 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; } - if (n->flags & NODE_LINE && (*n->string == ' ' || - (n->prev != NULL && mh->fl & MANH_LITERAL && - ! (h->flags & HTML_NONEWLINE)))) + 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, ""); - print_text(h, n->string); + if (*n->string != '\0') + break; + print_paragraph(h); return; + default: + break; + } + + /* Produce output for this node. */ + + child = 1; + switch (n->type) { + case ROFFT_TEXT: + t = h->tag; + print_text(h, n->string); + break; case ROFFT_EQN: + t = h->tag; print_eqn(h, n->eqn); break; case ROFFT_TBL: @@ -239,41 +301,70 @@ print_man_node(MAN_ARGS) * the "meta" table state. This will be reopened on the * next table element. */ - if (h->tblt) { + if (h->tblt) print_tblclose(h); - t = h->tags.head; + + t = h->tag; + if (n->tok < ROFF_MAX) { + roff_html_pre(h, n); + child = 0; + break; } + + assert(n->tok >= MAN_TH && n->tok < MAN_MAX); if (mans[n->tok].pre) - child = (*mans[n->tok].pre)(man, n, mh, h); + child = (*mans[n->tok].pre)(man, n, h); + + /* Some block macros resume .nf in the body. */ + if (save_fillmode && n->type == ROFFT_BODY) + want_fillmode = save_fillmode; + break; } if (child && n->child) - print_man_nodelist(man, n->child, mh, h); + print_man_nodelist(man, n->child, h); /* This will automatically close out any font scope. */ print_stagq(h, t); - switch (n->type) { - case ROFFT_EQN: - break; - default: - if (mans[n->tok].post) - (*mans[n->tok].post)(man, n, mh, h); - break; + if (fillmode(h, 0) == MAN_nf && + n->next != NULL && n->next->flags & NODE_LINE) + print_endline(h); +} + +/* + * MAN_nf switches to no-fill mode, MAN_fi to fill mode. + * Other arguments do not switch. + * The old mode is returned. + */ +static int +fillmode(struct html *h, int want) +{ + struct tag *pre; + int had; + + for (pre = h->tag; pre != NULL; pre = pre->next) + if (pre->tag == TAG_PRE) + break; + + had = pre == NULL ? MAN_fi : MAN_nf; + + if (want && want != had) { + if (want == MAN_nf) + print_otag(h, TAG_PRE, ""); + else + print_tagq(h, pre); } + return had; } static int a2width(const struct roff_node *n, struct roffsu *su) { - if (n->type != ROFFT_TEXT) return 0; - if (a2roffsu(n->string, su, SCALE_EN)) - return 1; - - return 0; + return a2roffsu(n->string, su, SCALE_EN) != NULL; } static void @@ -287,7 +378,6 @@ man_root_pre(MAN_ARGS) mandoc_asprintf(&title, "%s(%s)", man->title, man->msec); t = print_otag(h, TAG_TABLE, "c", "head"); - print_otag(h, TAG_TBODY, ""); tt = print_otag(h, TAG_TR, ""); print_otag(h, TAG_TD, "c", "head-ltitle"); @@ -323,39 +413,18 @@ man_root_post(MAN_ARGS) print_tagq(h, t); } - -static int -man_br_pre(MAN_ARGS) -{ - struct roffsu su; - - SCALE_VS_INIT(&su, 1); - - if (MAN_sp == n->tok) { - if (NULL != (n = n->child)) - if ( ! a2roffsu(n->string, &su, SCALE_VS)) - su.scale = 1.0; - } else - su.scale = 0.0; - - print_otag(h, TAG_DIV, "suh", &su); - - /* So the div isn't empty: */ - print_text(h, "\\~"); - - return 0; -} - static int man_SH_pre(MAN_ARGS) { - if (n->type == ROFFT_BLOCK) { - mh->fl &= ~MANH_LITERAL; - return 1; - } else if (n->type == ROFFT_BODY) - return 1; - - print_otag(h, TAG_H1, "c", "Sh"); + char *id; + + if (n->type == ROFFT_HEAD) { + id = html_make_id(n); + print_otag(h, TAG_H1, "cTi", "Sh", id); + if (id != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + free(id); + } return 1; } @@ -363,17 +432,11 @@ static int man_alt_pre(MAN_ARGS) { const struct roff_node *nn; - int i, savelit; + int i; enum htmltag fp; struct tag *t; - if ((savelit = mh->fl & MANH_LITERAL)) - print_otag(h, TAG_BR, ""); - - mh->fl &= ~MANH_LITERAL; - for (i = 0, nn = n->child; nn; nn = nn->next, i++) { - t = NULL; switch (n->tok) { case MAN_BI: fp = i % 2 ? TAG_I : TAG_B; @@ -400,18 +463,14 @@ man_alt_pre(MAN_ARGS) if (i) h->flags |= HTML_NOSPACE; - if (TAG_MAX != fp) + if (fp != TAG_MAX) t = print_otag(h, fp, ""); - print_man_node(man, nn, mh, h); + print_text(h, nn->string); - if (t) + if (fp != TAG_MAX) print_tagq(h, t); } - - if (savelit) - mh->fl |= MANH_LITERAL; - return 0; } @@ -427,13 +486,15 @@ man_SM_pre(MAN_ARGS) static int man_SS_pre(MAN_ARGS) { - if (n->type == ROFFT_BLOCK) { - mh->fl &= ~MANH_LITERAL; - return 1; - } else if (n->type == ROFFT_BODY) - return 1; - - print_otag(h, TAG_H2, "c", "Ss"); + char *id; + + if (n->type == ROFFT_HEAD) { + id = html_make_id(n); + print_otag(h, TAG_H2, "cTi", "Ss", id); + if (id != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + free(id); + } return 1; } @@ -469,7 +530,7 @@ man_IP_pre(MAN_ARGS) /* For IP, only print the first header element. */ if (MAN_IP == n->tok && n->child) - print_man_node(man, n->child, mh, h); + print_man_node(man, n->child, h); /* For TP, only print next-line header elements. */ @@ -478,7 +539,7 @@ man_IP_pre(MAN_ARGS) while (NULL != nn && 0 == (NODE_LINE & nn->flags)) nn = nn->next; while (NULL != nn) { - print_man_node(man, nn, mh, h); + print_man_node(man, nn, h); nn = nn->next; } } @@ -552,19 +613,6 @@ man_I_pre(MAN_ARGS) } static int -man_literal_pre(MAN_ARGS) -{ - - if (MAN_fi == n->tok || MAN_EE == n->tok) { - print_otag(h, TAG_BR, ""); - mh->fl &= ~MANH_LITERAL; - } else - mh->fl |= MANH_LITERAL; - - return 0; -} - -static int man_in_pre(MAN_ARGS) { print_otag(h, TAG_BR, ""); @@ -599,18 +647,24 @@ man_RS_pre(MAN_ARGS) 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); - print_otag(h, TAG_A, "ch", "Lk", n->child->string); + if (n->tok == MAN_MT) { + mandoc_asprintf(&cp, "mailto:%s", n->child->string); + print_otag(h, TAG_A, "cTh", "Mt", cp); + free(cp); + } else + print_otag(h, TAG_A, "cTh", "Lk", n->child->string); } assert(n->next->type == ROFFT_BODY); if (n->next->child != NULL) n = n->next; - print_man_nodelist(man, n->child, mh, h); + print_man_nodelist(man, n->child, h); return 0; } diff --git a/contrib/mdocml/man_macro.c b/contrib/mdocml/man_macro.c index 7fd17c5..aa8b200 100644 --- a/contrib/mdocml/man_macro.c +++ b/contrib/mdocml/man_macro.c @@ -1,7 +1,7 @@ -/* $Id: man_macro.c,v 1.115 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: man_macro.c,v 1.123 2017/06/25 11:45:37 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> * * Permission to use, copy, modify, and distribute this software for any @@ -38,10 +38,9 @@ static void blk_imp(MACRO_PROT_ARGS); static void in_line_eoln(MACRO_PROT_ARGS); static int man_args(struct roff_man *, int, int *, char *, char **); -static void rew_scope(struct roff_man *, int); +static void rew_scope(struct roff_man *, enum roff_tok); -const struct man_macro __man_macros[MAN_MAX] = { - { in_line_eoln, MAN_NSCOPED }, /* br */ +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 */ @@ -62,7 +61,6 @@ const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* I */ { in_line_eoln, 0 }, /* IR */ { in_line_eoln, 0 }, /* RI */ - { in_line_eoln, MAN_NSCOPED }, /* sp */ { in_line_eoln, MAN_NSCOPED }, /* nf */ { in_line_eoln, MAN_NSCOPED }, /* fi */ { blk_close, MAN_BSCOPE }, /* RE */ @@ -71,17 +69,16 @@ const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, 0 }, /* UC */ { in_line_eoln, MAN_NSCOPED }, /* PD */ { in_line_eoln, 0 }, /* AT */ - { in_line_eoln, 0 }, /* in */ - { in_line_eoln, 0 }, /* ft */ + { in_line_eoln, MAN_NSCOPED }, /* in */ { 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 */ - { in_line_eoln, 0 }, /* ll */ + { blk_exp, MAN_BSCOPE }, /* MT */ + { blk_close, MAN_BSCOPE }, /* ME */ }; - -const struct man_macro * const man_macros = __man_macros; +const struct man_macro *const man_macros = __man_macros - MAN_TH; void @@ -100,8 +97,7 @@ man_unscope(struct roff_man *man, const struct roff_node *to) man_macros[n->tok].flags & MAN_SCOPED) { mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line, n->pos, - "EOF breaks %s", - man_macronames[n->tok]); + "EOF breaks %s", roff_name[n->tok]); if (man->flags & MAN_ELINE) man->flags &= ~MAN_ELINE; else { @@ -118,7 +114,7 @@ man_unscope(struct roff_man *man, const struct roff_node *to) man_macros[n->tok].fp == blk_exp) mandoc_msg(MANDOCERR_BLK_NOEND, man->parse, n->line, n->pos, - man_macronames[n->tok]); + roff_name[n->tok]); } /* @@ -150,7 +146,7 @@ man_unscope(struct roff_man *man, const struct roff_node *to) * scopes. When a scope is closed, it must be validated and actioned. */ static void -rew_scope(struct roff_man *man, int tok) +rew_scope(struct roff_man *man, enum roff_tok tok) { struct roff_node *n; @@ -193,7 +189,7 @@ rew_scope(struct roff_man *man, int tok) void blk_close(MACRO_PROT_ARGS) { - int ntok; + enum roff_tok ntok; const struct roff_node *nn; char *p; int nrew, target; @@ -223,6 +219,9 @@ blk_close(MACRO_PROT_ARGS) case MAN_UE: ntok = MAN_UR; break; + case MAN_ME: + ntok = MAN_MT; + break; default: abort(); } @@ -233,7 +232,7 @@ blk_close(MACRO_PROT_ARGS) if (nn == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse, - line, ppos, man_macronames[tok]); + line, ppos, roff_name[tok]); rew_scope(man, MAN_PP); } else { line = man->last->line; @@ -241,6 +240,10 @@ blk_close(MACRO_PROT_ARGS) 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) { @@ -262,13 +265,21 @@ blk_exp(MACRO_PROT_ARGS) head = roff_head_alloc(man, line, ppos, tok); la = *pos; - if (man_args(man, line, pos, buf, &p)) + if (man_args(man, line, pos, buf, &p)) { roff_word_alloc(man, line, la, p); + if (tok == MAN_RS) { + if (roff_getreg(man->roff, "an-margin") == 0) + roff_setreg(man->roff, "an-margin", + 7 * 24, '='); + if ((head->aux = strtod(p, NULL) * 24.0) > 0) + roff_setreg(man->roff, "an-margin", + head->aux, '+'); + } + } if (buf[*pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, - man->parse, line, *pos, "%s ... %s", - man_macronames[tok], buf + *pos); + mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, line, + *pos, "%s ... %s", roff_name[tok], buf + *pos); man_unscope(man, head); roff_body_alloc(man, line, ppos, tok); @@ -331,18 +342,16 @@ in_line_eoln(MACRO_PROT_ARGS) n = man->last; for (;;) { - if (buf[*pos] != '\0' && (tok == MAN_br || - tok == MAN_fi || tok == MAN_nf)) { + if (buf[*pos] != '\0' && (tok == MAN_fi || tok == MAN_nf)) { mandoc_vmsg(MANDOCERR_ARG_SKIP, man->parse, line, *pos, "%s %s", - man_macronames[tok], buf + *pos); + roff_name[tok], buf + *pos); break; } - if (buf[*pos] != '\0' && man->last != n && - (tok == MAN_PD || tok == MAN_ft || tok == MAN_sp)) { + if (buf[*pos] != '\0' && man->last != n && tok == MAN_PD) { mandoc_vmsg(MANDOCERR_ARG_EXCESS, man->parse, line, *pos, "%s ... %s", - man_macronames[tok], buf + *pos); + roff_name[tok], buf + *pos); break; } la = *pos; diff --git a/contrib/mdocml/man_term.c b/contrib/mdocml/man_term.c index 672ab41..fcdb45d 100644 --- a/contrib/mdocml/man_term.c +++ b/contrib/mdocml/man_term.c @@ -1,7 +1,7 @@ -/* $Id: man_term.c,v 1.188 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: man_term.c,v 1.208 2017/06/25 11:42:02 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -68,6 +68,7 @@ static void print_bvspace(struct termp *, const struct roff_node *, int); static int pre_B(DECL_ARGS); +static int pre_DT(DECL_ARGS); static int pre_HP(DECL_ARGS); static int pre_I(DECL_ARGS); static int pre_IP(DECL_ARGS); @@ -80,12 +81,9 @@ static int pre_SS(DECL_ARGS); static int pre_TP(DECL_ARGS); static int pre_UR(DECL_ARGS); static int pre_alternate(DECL_ARGS); -static int pre_ft(DECL_ARGS); static int pre_ign(DECL_ARGS); static int pre_in(DECL_ARGS); static int pre_literal(DECL_ARGS); -static int pre_ll(DECL_ARGS); -static int pre_sp(DECL_ARGS); static void post_IP(DECL_ARGS); static void post_HP(DECL_ARGS); @@ -95,8 +93,7 @@ static void post_SS(DECL_ARGS); static void post_TP(DECL_ARGS); static void post_UR(DECL_ARGS); -static const struct termact termacts[MAN_MAX] = { - { pre_sp, NULL, MAN_NOTEXT }, /* br */ +static const struct termact __termacts[MAN_MAX - MAN_TH] = { { NULL, NULL, 0 }, /* TH */ { pre_SH, post_SH, 0 }, /* SH */ { pre_SS, post_SS, 0 }, /* SS */ @@ -117,24 +114,24 @@ static const struct termact termacts[MAN_MAX] = { { pre_I, NULL, 0 }, /* I */ { pre_alternate, NULL, 0 }, /* IR */ { pre_alternate, NULL, 0 }, /* RI */ - { pre_sp, NULL, MAN_NOTEXT }, /* sp */ { pre_literal, NULL, 0 }, /* nf */ { pre_literal, NULL, 0 }, /* fi */ { NULL, NULL, 0 }, /* RE */ { pre_RS, post_RS, 0 }, /* RS */ - { pre_ign, NULL, 0 }, /* DT */ + { pre_DT, NULL, 0 }, /* DT */ { pre_ign, NULL, MAN_NOTEXT }, /* UC */ { pre_PD, NULL, MAN_NOTEXT }, /* PD */ { pre_ign, NULL, 0 }, /* AT */ { pre_in, NULL, MAN_NOTEXT }, /* in */ - { pre_ft, NULL, MAN_NOTEXT }, /* ft */ { pre_OP, NULL, 0 }, /* OP */ { pre_literal, NULL, 0 }, /* EX */ { pre_literal, NULL, 0 }, /* EE */ { pre_UR, post_UR, 0 }, /* UR */ { NULL, NULL, 0 }, /* UE */ - { pre_ll, NULL, MAN_NOTEXT }, /* ll */ + { pre_UR, post_UR, 0 }, /* MT */ + { NULL, NULL, 0 }, /* ME */ }; +static const struct termact *termacts = __termacts - MAN_TH; void @@ -143,11 +140,16 @@ terminal_man(void *arg, const struct roff_man *man) struct termp *p; struct roff_node *n; struct mtermp mt; + size_t save_defindent; p = (struct termp *)arg; - p->overstep = 0; - p->rmargin = p->maxrmargin = p->defrmargin; - p->tabwidth = term_len(p, 5); + save_defindent = p->defindent; + if (p->synopsisonly == 0 && p->defindent == 0) + p->defindent = 7; + p->tcol->rmargin = p->maxrmargin = p->defrmargin; + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); memset(&mt, 0, sizeof(struct mtermp)); mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); @@ -170,14 +172,13 @@ terminal_man(void *arg, const struct roff_man *man) n = n->next; } } else { - if (p->defindent == 0) - p->defindent = 7; term_begin(p, print_man_head, print_man_foot, &man->meta); p->flags |= TERMP_NOSPACE; if (n != NULL) print_man_nodelist(p, &mt, n, &man->meta); term_end(p); } + p->defindent = save_defindent; } /* @@ -216,14 +217,6 @@ pre_ign(DECL_ARGS) } static int -pre_ll(DECL_ARGS) -{ - - term_setwidth(p, n->child != NULL ? n->child->string : NULL); - return 0; -} - -static int pre_I(DECL_ARGS) { @@ -237,7 +230,7 @@ pre_literal(DECL_ARGS) term_newln(p); - if (MAN_nf == n->tok || MAN_EX == n->tok) + if (n->tok == MAN_nf || n->tok == MAN_EX) mt->fl |= MANT_LITERAL; else mt->fl &= ~MANT_LITERAL; @@ -247,9 +240,9 @@ pre_literal(DECL_ARGS) * So in case a second call to term_flushln() is needed, * indentation has to be set up explicitly. */ - if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; + if (n->parent->tok == MAN_HP && p->tcol->rmargin < p->maxrmargin) { + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); p->flags |= TERMP_NOSPACE; @@ -269,7 +262,7 @@ pre_PD(DECL_ARGS) return 0; } assert(n->type == ROFFT_TEXT); - if (a2roffsu(n->string, &su, SCALE_VS)) + if (a2roffsu(n->string, &su, SCALE_VS) != NULL) mt->pardist = term_vspan(p, &su); return 0; } @@ -359,41 +352,6 @@ pre_OP(DECL_ARGS) } static int -pre_ft(DECL_ARGS) -{ - const char *cp; - - if (NULL == n->child) { - term_fontlast(p); - return 0; - } - - cp = n->child->string; - switch (*cp) { - case '4': - case '3': - case 'B': - term_fontrepl(p, TERMFONT_BOLD); - break; - case '2': - case 'I': - term_fontrepl(p, TERMFONT_UNDER); - break; - case 'P': - term_fontlast(p); - break; - case '1': - case 'C': - case 'R': - term_fontrepl(p, TERMFONT_NONE); - break; - default: - break; - } - return 0; -} - -static int pre_in(DECL_ARGS) { struct roffsu su; @@ -403,8 +361,8 @@ pre_in(DECL_ARGS) term_newln(p); - if (NULL == n->child) { - p->offset = mt->offset; + if (n->child == NULL) { + p->tcol->offset = mt->offset; return 0; } @@ -418,71 +376,29 @@ pre_in(DECL_ARGS) else cp--; - if ( ! a2roffsu(++cp, &su, SCALE_EN)) + if (a2roffsu(++cp, &su, SCALE_EN) == NULL) return 0; - v = (term_hspan(p, &su) + 11) / 24; + v = term_hen(p, &su); if (less < 0) - p->offset -= p->offset > v ? v : p->offset; + p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset; else if (less > 0) - p->offset += v; + p->tcol->offset += v; else - p->offset = v; - if (p->offset > SHRT_MAX) - p->offset = term_len(p, p->defindent); + p->tcol->offset = v; + if (p->tcol->offset > SHRT_MAX) + p->tcol->offset = term_len(p, p->defindent); return 0; } static int -pre_sp(DECL_ARGS) +pre_DT(DECL_ARGS) { - struct roffsu su; - int i, len; - - if ((NULL == n->prev && n->parent)) { - switch (n->parent->tok) { - case MAN_SH: - case MAN_SS: - case MAN_PP: - case MAN_LP: - case MAN_P: - return 0; - default: - break; - } - } - - if (n->tok == MAN_br) - len = 0; - else if (n->child == NULL) - len = 1; - else { - if ( ! a2roffsu(n->child->string, &su, SCALE_VS)) - su.scale = 1.0; - len = term_vspan(p, &su); - } - - if (len == 0) - term_newln(p); - else if (len < 0) - p->skipvsp -= len; - else - for (i = 0; i < len; i++) - term_vspace(p); - - /* - * Handle an explicit break request in the same way - * as an overflowing line. - */ - - if (p->flags & TERMP_BRIND) { - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; - p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); - } - + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); return 0; } @@ -511,8 +427,8 @@ pre_HP(DECL_ARGS) /* Calculate offset. */ if ((nn = n->parent->head->child) != NULL && - a2roffsu(nn->string, &su, SCALE_EN)) { - len = term_hspan(p, &su) / 24; + a2roffsu(nn->string, &su, SCALE_EN) != NULL) { + len = term_hen(p, &su); if (len < 0 && (size_t)(-len) > mt->offset) len = -mt->offset; else if (len > SHRT_MAX) @@ -521,8 +437,8 @@ pre_HP(DECL_ARGS) } else len = mt->lmargin[mt->lmargincur]; - p->offset = mt->offset; - p->rmargin = mt->offset + len; + p->tcol->offset = mt->offset; + p->tcol->rmargin = mt->offset + len; return 1; } @@ -546,8 +462,8 @@ post_HP(DECL_ARGS) p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); p->trailspace = 0; - p->offset = mt->offset; - p->rmargin = p->maxrmargin; + p->tcol->offset = mt->offset; + p->tcol->rmargin = p->maxrmargin; break; default: break; @@ -564,7 +480,7 @@ pre_PP(DECL_ARGS) print_bvspace(p, n, mt->pardist); break; default: - p->offset = mt->offset; + p->tcol->offset = mt->offset; break; } @@ -596,8 +512,8 @@ pre_IP(DECL_ARGS) /* Calculate the offset from the optional second argument. */ if ((nn = n->parent->head->child) != NULL && (nn = nn->next) != NULL && - a2roffsu(nn->string, &su, SCALE_EN)) { - len = term_hspan(p, &su) / 24; + a2roffsu(nn->string, &su, SCALE_EN) != NULL) { + len = term_hen(p, &su); if (len < 0 && (size_t)(-len) > mt->offset) len = -mt->offset; else if (len > SHRT_MAX) @@ -608,8 +524,8 @@ pre_IP(DECL_ARGS) switch (n->type) { case ROFFT_HEAD: - p->offset = mt->offset; - p->rmargin = mt->offset + len; + p->tcol->offset = mt->offset; + p->tcol->rmargin = mt->offset + len; savelit = MANT_LITERAL & mt->fl; mt->fl &= ~MANT_LITERAL; @@ -622,8 +538,8 @@ pre_IP(DECL_ARGS) return 0; case ROFFT_BODY: - p->offset = mt->offset + len; - p->rmargin = p->maxrmargin; + p->tcol->offset = mt->offset + len; + p->tcol->rmargin = p->maxrmargin; break; default: break; @@ -641,11 +557,11 @@ post_IP(DECL_ARGS) term_flushln(p); p->flags &= ~TERMP_NOBREAK; p->trailspace = 0; - p->rmargin = p->maxrmargin; + p->tcol->rmargin = p->maxrmargin; break; case ROFFT_BODY: term_newln(p); - p->offset = mt->offset; + p->tcol->offset = mt->offset; break; default: break; @@ -678,8 +594,8 @@ pre_TP(DECL_ARGS) if ((nn = n->parent->head->child) != NULL && nn->string != NULL && ! (NODE_LINE & nn->flags) && - a2roffsu(nn->string, &su, SCALE_EN)) { - len = term_hspan(p, &su) / 24; + a2roffsu(nn->string, &su, SCALE_EN) != NULL) { + len = term_hen(p, &su); if (len < 0 && (size_t)(-len) > mt->offset) len = -mt->offset; else if (len > SHRT_MAX) @@ -690,8 +606,8 @@ pre_TP(DECL_ARGS) switch (n->type) { case ROFFT_HEAD: - p->offset = mt->offset; - p->rmargin = mt->offset + len; + p->tcol->offset = mt->offset; + p->tcol->rmargin = mt->offset + len; savelit = MANT_LITERAL & mt->fl; mt->fl &= ~MANT_LITERAL; @@ -710,8 +626,8 @@ pre_TP(DECL_ARGS) mt->fl |= MANT_LITERAL; return 0; case ROFFT_BODY: - p->offset = mt->offset + len; - p->rmargin = p->maxrmargin; + p->tcol->offset = mt->offset + len; + p->tcol->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP); break; @@ -732,7 +648,7 @@ post_TP(DECL_ARGS) break; case ROFFT_BODY: term_newln(p); - p->offset = mt->offset; + p->tcol->offset = mt->offset; break; default: break; @@ -767,14 +683,14 @@ pre_SS(DECL_ARGS) break; case ROFFT_HEAD: term_fontrepl(p, TERMFONT_BOLD); - p->offset = term_len(p, 3); - p->rmargin = mt->offset; + p->tcol->offset = term_len(p, 3); + p->tcol->rmargin = mt->offset; p->trailspace = mt->offset; p->flags |= TERMP_NOBREAK | TERMP_BRIND; break; case ROFFT_BODY: - p->offset = mt->offset; - p->rmargin = p->maxrmargin; + p->tcol->offset = mt->offset; + p->tcol->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); break; @@ -819,7 +735,8 @@ pre_SH(DECL_ARGS) do { n = n->prev; - } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT); + } while (n != NULL && n->tok != TOKEN_NONE && + termacts[n->tok].flags & MAN_NOTEXT); if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL)) break; @@ -828,14 +745,14 @@ pre_SH(DECL_ARGS) break; case ROFFT_HEAD: term_fontrepl(p, TERMFONT_BOLD); - p->offset = 0; - p->rmargin = mt->offset; + p->tcol->offset = 0; + p->tcol->rmargin = mt->offset; p->trailspace = mt->offset; p->flags |= TERMP_NOBREAK | TERMP_BRIND; break; case ROFFT_BODY: - p->offset = mt->offset; - p->rmargin = p->maxrmargin; + p->tcol->offset = mt->offset; + p->tcol->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); break; @@ -881,16 +798,16 @@ pre_RS(DECL_ARGS) n->aux = SHRT_MAX + 1; if (n->child == NULL) n->aux = mt->lmargin[mt->lmargincur]; - else if (a2roffsu(n->child->string, &su, SCALE_EN)) - n->aux = term_hspan(p, &su) / 24; + else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL) + n->aux = term_hen(p, &su); if (n->aux < 0 && (size_t)(-n->aux) > mt->offset) n->aux = -mt->offset; else if (n->aux > SHRT_MAX) n->aux = term_len(p, p->defindent); mt->offset += n->aux; - p->offset = mt->offset; - p->rmargin = p->maxrmargin; + p->tcol->offset = mt->offset; + p->tcol->rmargin = p->maxrmargin; if (++mt->lmarginsz < MAXMARGINS) mt->lmargincur = mt->lmarginsz; @@ -914,7 +831,7 @@ post_RS(DECL_ARGS) } mt->offset -= n->parent->head->aux; - p->offset = mt->offset; + p->tcol->offset = mt->offset; if (--mt->lmarginsz < MAXMARGINS) mt->lmargincur = mt->lmarginsz; @@ -947,7 +864,6 @@ post_UR(DECL_ARGS) static void print_man_node(DECL_ARGS) { - size_t rm, rmax; int c; switch (n->type) { @@ -957,10 +873,14 @@ print_man_node(DECL_ARGS) * If we have a space as the first character, break * before printing the line's data. */ - if ('\0' == *n->string) { - term_vspace(p); + if (*n->string == '\0') { + if (p->flags & TERMP_NONEWLINE) + term_newln(p); + else + term_vspace(p); return; - } else if (' ' == *n->string && NODE_LINE & n->flags) + } else if (*n->string == ' ' && n->flags & NODE_LINE && + (p->flags & TERMP_NONEWLINE) == 0) term_newln(p); term_word(p, n->string); @@ -982,6 +902,12 @@ print_man_node(DECL_ARGS) break; } + if (n->tok < ROFF_MAX) { + roff_term_pre(p, n); + return; + } + + assert(n->tok >= MAN_TH && n->tok <= MAN_MAX); if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) term_fontrepl(p, TERMFONT_NONE); @@ -1008,20 +934,17 @@ out: if (mt->fl & MANT_LITERAL && ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) && (n->next == NULL || n->next->flags & NODE_LINE)) { - rm = p->rmargin; - rmax = p->maxrmargin; - p->rmargin = p->maxrmargin = TERM_MAXMARGIN; - p->flags |= TERMP_NOSPACE; + p->flags |= TERMP_BRNEVER | TERMP_NOSPACE; if (n->string != NULL && *n->string != '\0') term_flushln(p); else term_newln(p); - if (rm < rmax && n->parent->tok == MAN_HP) { - p->offset = rm; - p->rmargin = rmax; - } else - p->rmargin = rm; - p->maxrmargin = rmax; + p->flags &= ~TERMP_BRNEVER; + if (p->tcol->rmargin < p->maxrmargin && + n->parent->tok == MAN_HP) { + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; + } } if (NODE_EOS & n->flags) p->flags |= TERMP_SENTENCE; @@ -1077,8 +1000,8 @@ print_man_foot(struct termp *p, const struct roff_meta *meta) p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; p->trailspace = 1; - p->offset = 0; - p->rmargin = p->maxrmargin > datelen ? + p->tcol->offset = 0; + p->tcol->rmargin = p->maxrmargin > datelen ? (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0; if (meta->os) @@ -1087,9 +1010,10 @@ print_man_foot(struct termp *p, const struct roff_meta *meta) /* At the bottom in the middle: manual date. */ - p->offset = p->rmargin; + p->tcol->offset = p->tcol->rmargin; titlen = term_strlen(p, title); - p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0; + p->tcol->rmargin = p->maxrmargin > titlen ? + p->maxrmargin - titlen : 0; p->flags |= TERMP_NOSPACE; term_word(p, meta->date); @@ -1100,8 +1024,8 @@ print_man_foot(struct termp *p, const struct roff_meta *meta) p->flags &= ~TERMP_NOBREAK; p->flags |= TERMP_NOSPACE; p->trailspace = 0; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; term_word(p, title); term_flushln(p); @@ -1128,8 +1052,8 @@ print_man_head(struct termp *p, const struct roff_meta *meta) p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; p->trailspace = 1; - p->offset = 0; - p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? + p->tcol->offset = 0; + p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? (p->maxrmargin - vollen + term_len(p, 1)) / 2 : vollen < p->maxrmargin ? p->maxrmargin - vollen : 0; @@ -1139,9 +1063,9 @@ print_man_head(struct termp *p, const struct roff_meta *meta) /* At the top in the middle: manual volume. */ p->flags |= TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->offset + vollen + titlen < p->maxrmargin ? - p->maxrmargin - titlen : p->maxrmargin; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->tcol->offset + vollen + titlen < + p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin; term_word(p, volume); term_flushln(p); @@ -1150,17 +1074,17 @@ print_man_head(struct termp *p, const struct roff_meta *meta) p->flags &= ~TERMP_NOBREAK; p->trailspace = 0; - if (p->rmargin + titlen <= p->maxrmargin) { + if (p->tcol->rmargin + titlen <= p->maxrmargin) { p->flags |= TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; term_word(p, title); term_flushln(p); } p->flags &= ~TERMP_NOSPACE; - p->offset = 0; - p->rmargin = p->maxrmargin; + p->tcol->offset = 0; + p->tcol->rmargin = p->maxrmargin; /* * Groff prints three blank lines before the content. diff --git a/contrib/mdocml/man_validate.c b/contrib/mdocml/man_validate.c index 16d9963..b3356cc 100644 --- a/contrib/mdocml/man_validate.c +++ b/contrib/mdocml/man_validate.c @@ -1,7 +1,7 @@ /* $OpenBSD$ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -47,15 +47,14 @@ static void check_text(CHKARGS); static void post_AT(CHKARGS); static void post_IP(CHKARGS); -static void post_vs(CHKARGS); -static void post_ft(CHKARGS); static void post_OP(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 v_check man_valids[MAN_MAX] = { - post_vs, /* br */ +static const v_check __man_valids[MAN_MAX - MAN_TH] = { post_TH, /* TH */ NULL, /* SH */ NULL, /* SS */ @@ -76,7 +75,6 @@ static v_check man_valids[MAN_MAX] = { NULL, /* I */ NULL, /* IR */ NULL, /* RI */ - post_vs, /* sp */ NULL, /* nf */ NULL, /* fi */ NULL, /* RE */ @@ -85,22 +83,23 @@ static v_check man_valids[MAN_MAX] = { post_UC, /* UC */ NULL, /* PD */ post_AT, /* AT */ - NULL, /* in */ - post_ft, /* ft */ + post_in, /* in */ post_OP, /* OP */ NULL, /* EX */ NULL, /* EE */ post_UR, /* UR */ NULL, /* UE */ - NULL, /* ll */ + post_UR, /* MT */ + NULL, /* ME */ }; +static const v_check *man_valids = __man_valids - MAN_TH; void man_node_validate(struct roff_man *man) { struct roff_node *n; - v_check *cp; + const v_check *cp; n = man->last; man->last = man->last->child; @@ -125,6 +124,19 @@ man_node_validate(struct roff_man *man) case ROFFT_TBL: 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; + } + break; + } + assert(n->tok >= MAN_TH && n->tok < MAN_MAX); cp = man_valids + n->tok; if (*cp) (*cp)(man, n); @@ -158,8 +170,14 @@ check_root(CHKARGS) man->meta.title = mandoc_strdup(""); man->meta.msec = mandoc_strdup(""); man->meta.date = man->quick ? mandoc_strdup("") : - mandoc_normdate(man->parse, NULL, n->line, n->pos); + mandoc_normdate(man, NULL, n->line, n->pos); } + + if (man->meta.os_e && + (man->meta.rcsids & (1 << man->meta.os_e)) == 0) + mandoc_msg(MANDOCERR_RCS_MISSING, man->parse, 0, 0, + man->meta.os_e == MANDOC_OS_OPENBSD ? + "(OpenBSD)" : "(NetBSD)"); } static void @@ -193,61 +211,19 @@ post_OP(CHKARGS) static void post_UR(CHKARGS) { - if (n->type == ROFFT_HEAD && n->child == NULL) - mandoc_vmsg(MANDOCERR_UR_NOHEAD, man->parse, - n->line, n->pos, "UR"); + mandoc_msg(MANDOCERR_UR_NOHEAD, man->parse, + n->line, n->pos, roff_name[n->tok]); check_part(man, n); } static void -post_ft(CHKARGS) -{ - char *cp; - int ok; - - if (n->child == NULL) - return; - - ok = 0; - cp = n->child->string; - switch (*cp) { - case '1': - case '2': - case '3': - case '4': - case 'I': - case 'P': - case 'R': - if ('\0' == cp[1]) - ok = 1; - break; - case 'B': - if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2])) - ok = 1; - break; - case 'C': - if ('W' == cp[1] && '\0' == cp[2]) - ok = 1; - break; - default: - break; - } - - if (0 == ok) { - mandoc_vmsg(MANDOCERR_FT_BAD, man->parse, - n->line, n->pos, "ft %s", cp); - *cp = '\0'; - } -} - -static void check_part(CHKARGS) { if (n->type == ROFFT_BODY && n->child == NULL) mandoc_msg(MANDOCERR_BLK_EMPTY, man->parse, - n->line, n->pos, man_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); } static void @@ -263,14 +239,13 @@ check_par(CHKARGS) if (n->child == NULL) mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos, - "%s empty", man_macronames[n->tok]); + "%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", man_macronames[n->tok], - n->child->string, + man->parse, n->line, n->pos, "%s %s%s", + roff_name[n->tok], n->child->string, n->child->next != NULL ? " ..." : ""); break; default: @@ -291,7 +266,7 @@ post_IP(CHKARGS) if (n->parent->head->child == NULL && n->child == NULL) mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos, - "%s empty", man_macronames[n->tok]); + "%s empty", roff_name[n->tok]); break; default: break; @@ -356,8 +331,7 @@ post_TH(CHKARGS) if (n && n->string && '\0' != n->string[0]) { man->meta.date = man->quick ? mandoc_strdup(n->string) : - mandoc_normdate(man->parse, n->string, - n->line, n->pos); + mandoc_normdate(man, n->string, n->line, n->pos); } else { man->meta.date = mandoc_strdup(""); mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, @@ -369,8 +343,14 @@ post_TH(CHKARGS) if (n && (n = n->next)) man->meta.os = mandoc_strdup(n->string); - else if (man->defos != NULL) - man->meta.os = mandoc_strdup(man->defos); + else if (man->os_s != NULL) + man->meta.os = mandoc_strdup(man->os_s); + if (man->meta.os_e == MANDOC_OS_OTHER && man->meta.os != NULL) { + if (strstr(man->meta.os, "OpenBSD") != NULL) + man->meta.os_e = MANDOC_OS_OPENBSD; + else if (strstr(man->meta.os, "NetBSD") != NULL) + man->meta.os_e = MANDOC_OS_NETBSD; + } /* TITLE MSEC DATE OS ->VOL<- */ /* If missing, use the default VOL name for MSEC. */ @@ -469,6 +449,22 @@ post_AT(CHKARGS) } static void +post_in(CHKARGS) +{ + char *s; + + if (n->parent->tok != MAN_TP || + n->parent->type != ROFFT_HEAD || + n->child == NULL || + *n->child->string == '+' || + *n->child->string == '-') + return; + mandoc_asprintf(&s, "+%s", n->child->string); + free(n->child->string); + n->child->string = s; +} + +static void post_vs(CHKARGS) { @@ -478,9 +474,12 @@ post_vs(CHKARGS) 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", man_macronames[n->tok], - man_macronames[n->parent->tok]); + "%s after %s", roff_name[n->tok], + roff_name[n->parent->tok]); /* FALLTHROUGH */ case TOKEN_NONE: /* diff --git a/contrib/mdocml/manconf.h b/contrib/mdocml/manconf.h index 782269e..b4cd316 100644 --- a/contrib/mdocml/manconf.h +++ b/contrib/mdocml/manconf.h @@ -1,6 +1,6 @@ -/* $OpenBSD$ */ +/* $Id: manconf.h,v 1.5 2017/07/01 09:47:30 schwarze Exp $ */ /* - * Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -35,6 +35,7 @@ struct manoutput { int fragment; int mdoc; int synopsisonly; + int noval; }; struct manconf { @@ -44,5 +45,6 @@ struct manconf { void manconf_parse(struct manconf *, const char *, char *, char *); -void manconf_output(struct manoutput *, const char *); +int manconf_output(struct manoutput *, const char *, int); void manconf_free(struct manconf *); +void manpath_base(struct manpaths *); diff --git a/contrib/mdocml/mandoc.1 b/contrib/mdocml/mandoc.1 index 946955a..1db4d32 100644 --- a/contrib/mdocml/mandoc.1 +++ b/contrib/mdocml/mandoc.1 @@ -1,4 +1,4 @@ -.\" $Id: mandoc.1,v 1.171 2017/01/21 02:32:39 schwarze Exp $ +.\" $Id: mandoc.1,v 1.217 2017/07/20 15:26:41 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -15,19 +15,19 @@ .\" 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 21 2017 $ +.Dd $Mdocdate: July 20 2017 $ .Dt MANDOC 1 .Os .Sh NAME .Nm mandoc -.Nd format and display UNIX manuals +.Nd format manual pages .Sh SYNOPSIS .Nm mandoc -.Op Fl acfhkl +.Op Fl ac .Op Fl I Cm os Ns = Ns Ar name .Op Fl K Ar encoding -.Op Fl m Ns Ar format -.Op Fl O Ar option +.Op Fl mdoc | man +.Op Fl O Ar options .Op Fl T Ar output .Op Fl W Ar level .Op Ar @@ -44,9 +44,7 @@ reads .Xr mdoc 7 or .Xr man 7 -text from stdin, implying -.Fl m Ns Cm andoc , -and produces +text from stdin and produces .Fl T Cm locale output. .Pp @@ -67,27 +65,15 @@ to paginate them. This is the default. It can be specified to override .Fl a . -.It Fl f -A synonym for -.Xr whatis 1 . -This overrides any earlier -.Fl k -and -.Fl l -options. -.It Fl h -Display only the SYNOPSIS lines. -Implies -.Fl c . .It Fl I Cm os Ns = Ns Ar name Override the default operating system .Ar name for the .Xr mdoc 7 -.Sq \&Os +.Ic \&Os and for the .Xr man 7 -.Sq \&TH +.Ic \&TH macro. .It Fl K Ar encoding Specify the input encoding. @@ -98,46 +84,53 @@ arguments are .Cm iso-8859-1 , and .Cm utf-8 . -If not specified, autodetection uses the first match: -.Bl -tag -width iso-8859-1 -.It Cm utf-8 -if the first three bytes of the input file -are the UTF-8 byte order mark (BOM, 0xefbbbf) -.It Ar encoding -if the first or second line of the input file matches the +If not specified, autodetection uses the first match in the following +list: +.Bl -enum +.It +If the first three bytes of the input file are the UTF-8 byte order +mark (BOM, 0xefbbbf), input is interpreted as +.Cm utf-8 . +.It +If the first or second line of the input file matches the .Sy emacs mode line format .Pp .D1 .\e" -*- Oo ...; Oc coding: Ar encoding ; No -*- -.It Cm utf-8 -if the first non-ASCII byte in the file introduces a valid UTF-8 sequence -.It Cm iso-8859-1 -otherwise +.Pp +then input is interpreted according to +.Ar encoding . +.It +If the first non-ASCII byte in the file introduces a valid UTF-8 +sequence, input is interpreted as +.Cm utf-8 . +.It +Otherwise, input is interpreted as +.Cm iso-8859-1 . .El -.It Fl k -A synonym for -.Xr apropos 1 . -This overrides any earlier -.Fl f -and -.Fl l -options. -.It Fl l -A synonym for -.Fl a . -Also reverts any earlier -.Fl f -and -.Fl k -options. -.It Fl m Ns Ar format -Input format. -See -.Sx Input Formats -for available formats. -Defaults to -.Fl m Ns Cm andoc . -.It Fl O Ar option +.It Fl mdoc | man +With +.Fl mdoc , +all input files are interpreted as +.Xr mdoc 7 . +With +.Fl man , +all input files are interpreted as +.Xr man 7 . +By default, the input language is automatically detected for each file: +if the the first macro is +.Ic \&Dd +or +.Ic \&Dt , +the +.Xr mdoc 7 +parser is used; otherwise, the +.Xr man 7 +parser is used. +With other arguments, +.Fl m +is silently ignored. +.It Fl O Ar options Comma-separated output options. .It Fl T Ar output Output format. @@ -153,13 +146,33 @@ to be reported on the standard error output and to affect the exit status. The .Ar level can be +.Cm base , +.Cm style , .Cm warning , .Cm error , or -.Cm unsupp ; +.Cm unsupp . +The +.Cm base +level automatically derives the operating system from the contents of the +.Ic \&Os +macro, from the +.Fl Ios +command line option, or from the +.Xr uname 3 +return value. +The levels +.Cm openbsd +and +.Cm netbsd +are variants of +.Cm base +that bypass autodetection and request validation of base system +conventions for a particular operating system. +The level .Cm all is an alias for -.Cm warning . +.Cm base . By default, .Nm is silent. @@ -190,6 +203,9 @@ If multiple files are specified, will halt with the first failed parse. .El .Pp +The options +.Fl fhklw +are also supported and are documented in man(1). In .Fl f and @@ -197,60 +213,20 @@ and mode, .Nm also supports the options -.Fl CMmOSsw +.Fl CMmOSs described in the .Xr apropos 1 manual. -.Ss Input Formats -The -.Nm -utility accepts -.Xr mdoc 7 -and -.Xr man 7 -input with -.Fl m Ns Cm doc -and -.Fl m Ns Cm an , -respectively. -The -.Xr mdoc 7 -format is -.Em strongly -recommended; -.Xr man 7 -should only be used for legacy manuals. -.Pp -A third option, -.Fl m Ns Cm andoc , -which is also the default, determines encoding on-the-fly: if the first -non-comment macro is -.Sq \&Dd -or -.Sq \&Dt , -the -.Xr mdoc 7 -parser is used; otherwise, the -.Xr man 7 -parser is used. -.Pp -If multiple -files are specified with -.Fl m Ns Cm andoc , -each has its file-type determined this way. -If multiple files are -specified and -.Fl m Ns Cm doc -or -.Fl m Ns Cm an -is specified, then this format is used exclusively. +The options +.Fl fkl +are mutually exclusive and override each other. .Ss Output Formats The .Nm utility accepts the following .Fl T arguments, which correspond to output modes: -.Bl -tag -width "-T locale" +.Bl -tag -width "-T markdown" .It Fl T Cm ascii Produce 7-bit ASCII output. See @@ -262,7 +238,9 @@ See .It Fl T Cm lint Parse only: produce no output. Implies -.Fl W Cm warning . +.Fl W Cm all +and redirects parser messages, which usually appear +on standard error output, to standard output. .It Fl T Cm locale Encode output using the current locale. This is the default. @@ -274,6 +252,12 @@ Produce format output. See .Sx Man Output . +.It Fl T Cm markdown +Produce output in +.Sy markdown +format. +See +.Sx Markdown Output . .It Fl T Cm pdf Produce PDF output. See @@ -290,9 +274,6 @@ See Encode output in the UTF\-8 multi-byte format. See .Sx UTF\-8 Output . -.It Fl T Cm xhtml -This is a synonym for -.Fl T Cm html . .El .Pp If multiple input files are specified, these will be processed by the @@ -336,8 +317,7 @@ Increasing this is not recommended; it may result in degraded formatting, for example overfull lines or ugly line breaks. .It Cm width Ns = Ns Ar width The output width is set to -.Ar width , -which will normalise to \(>=58. +.Ar width . .El .Ss HTML Output Output produced by @@ -377,7 +357,7 @@ The string for example, .Ar ../src/%I.html , is used as a template for linked header files (usually via the -.Sq \&In +.Ic \&In macro). Instances of .Sq \&%I @@ -390,7 +370,7 @@ The string for example, .Ar ../html%S/%N.%S.html , is used as a template for linked manuals (usually via the -.Sq \&Xr +.Ic \&Xr macro). Instances of .Sq \&%N @@ -436,13 +416,47 @@ If the input format is .Xr man 7 , the input is copied to the output, expanding any .Xr roff 7 -.Sq so +.Ic so requests. The parser is also run, and as usual, the .Fl W level controls which .Sx DIAGNOSTICS are displayed before copying the input to the output. +.Ss Markdown Output +Translate +.Xr mdoc 7 +input to the +.Sy markdown +format conforming to +.Lk http://daringfireball.net/projects/markdown/syntax.text\ + "John Gruber's 2004 specification" . +The output also almost conforms to the +.Lk http://commonmark.org/ CommonMark +specification. +.Pp +The character set used for the markdown output is ASCII. +Non-ASCII characters are encoded as HTML entities. +Since that is not possible in literal font contexts, because these +are rendered as code spans and code blocks in the markdown output, +non-ASCII characters are transliterated to ASCII approximations in +these contexts. +.Pp +Markdown is a very weak markup language, so all semantic markup is +lost, and even part of the presentational markup may be lost. +Do not use this as an intermediate step in converting to HTML; +instead, use +.Fl T Cm html +directly. +.Pp +The +.Xr man 7 , +.Xr tbl 7 , +and +.Xr eqn 7 +input languages are not supported by +.Fl T Cm markdown +output mode. .Ss PDF Output PDF-1.1 output may be generated by .Fl T Cm pdf . @@ -538,6 +552,8 @@ A closing parenthesis if the node is a closing delimiter. .It A full stop if the node ends a sentence. .It +BROKEN if the node is a block broken by another block. +.It NOSRC if the node is not in the input file, but automatically generated from macros. .It @@ -545,13 +561,32 @@ NOPRT if the node is not supposed to generate output for any output format. .El .El +.Pp +The following +.Fl O +argument is accepted: +.Bl -tag -width Ds +.It Cm noval +Skip validation and show the unvalidated syntax tree. +This can help to find out whether a given behaviour is caused by +the parser or by the validator. +Meta data is not available in this case. +.El .Sh ENVIRONMENT .Bl -tag -width MANPAGER .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER -will be used instead of the standard pagination program, -.Xr more 1 . +is used instead of the standard pagination program, +.Xr more 1 ; +see +.Xr man 1 +for details. +Only used if +.Fl a +or +.Fl l +is specified. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER @@ -559,7 +594,12 @@ is not defined. If neither PAGER nor MANPAGER is defined, .Xr more 1 .Fl s -will be used. +is used. +Only used if +.Fl a +or +.Fl l +is specified. .El .Sh EXIT STATUS The @@ -572,27 +612,36 @@ option: .Pp .Bl -tag -width Ds -compact .It 0 -No warnings or errors occurred, or those that did were ignored because -they were lower than the requested +No base system convention violations, style suggestions, warnings, +or errors occurred, or those that did were ignored because they +were lower than the requested .Ar level . +.It 1 +At least one base system convention violation or style suggestion +occurred, but no warning or error, and +.Fl W Cm base +or +.Fl W Cm style +was specified. .It 2 At least one warning occurred, but no error, and .Fl W Cm warning -was specified. +or a lower +.Ar level +was requested. .It 3 At least one parsing error occurred, but no unsupported feature was encountered, and .Fl W Cm error -or -.Fl W Cm warning -was specified. +or a lower +.Ar level +was requested. .It 4 At least one unsupported feature was encountered, and -.Fl W Cm unsupp , -.Fl W Cm error -or -.Fl W Cm warning -was specified. +.Fl W Cm unsupp +or a lower +.Ar level +was requested. .It 5 Invalid command line arguments were specified. No input files have been read. @@ -607,12 +656,11 @@ to exit at once, possibly in the middle of parsing or formatting a file. Note that selecting .Fl T Cm lint output mode implies -.Fl W Cm warning . +.Fl W Cm all . .Sh EXAMPLES To page manuals to the terminal: .Pp -.Dl $ mandoc \-W all,stop mandoc.1 2\*(Gt&1 | less -.Dl $ mandoc mandoc.1 mdoc.3 mdoc.7 | less +.Dl $ mandoc -l mandoc.1 man.1 apropos.1 makewhatis.8 .Pp To produce HTML manuals with .Pa mandoc.css @@ -641,12 +689,19 @@ parser: Messages displayed by .Nm follow this format: -.Pp -.D1 Nm Ns : Ar file : Ns Ar line : Ns Ar column : level : message : macro args +.Bd -ragged -offset indent +.Nm : +.Ar file : Ns Ar line : Ns Ar column : level : message : macro args +.Pq Ar os +.Ed .Pp 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. +The +.Ar os +operating system specifier is omitted for messages that are relevant +for all operating systems. Fatal messages about invalid command line arguments or operating system errors, for example when memory is exhausted, may also omit the @@ -666,35 +721,41 @@ so using GNU troff instead of .Nm to process the file may be preferable. .It Cm error -An input file contains invalid syntax that cannot be safely interpreted. -By discarding part of the input or inserting missing tokens, -the parser is able to continue, and the error does not prevent -generation of formatted output, but typically, preparing that -output involves information loss, broken document structure -or unintended formatting, no matter whether -.Nm -or GNU troff is used. -In many cases, the output of -.Nm -and GNU troff is identical, but in some, -.Nm -is more resilient than GNU troff with respect to malformed input. -.Pp -Non-existent or unreadable input files are also reported on the -.Cm error -level. -In that case, the parser cannot even be started and no output -is produced from those input files. +Indicates a risk of information loss or severe misformatting, +in most cases caused by serious syntax errors. .It Cm warning -An input file uses obsolete, discouraged or non-portable syntax. -All the same, the meaning of the input is unambiguous and a correct -rendering can be produced. -Documents causing warnings may render poorly when using other -formatting tools instead of -.Nm . +Indicates a risk that the information shown or its formatting +may mismatch the author's intent in minor ways. +Additionally, syntax errors are classified at least as warnings, +even if they do not usually cause misformatting. +.It Cm style +An input file uses dubious or discouraged style. +This is not a complaint about the syntax, and probably neither +formatting nor portability are in danger. +While great care is taken to avoid false positives on the higher +message levels, the +.Cm style +level tries to reduce the probability that issues go unnoticed, +so it may occasionally issue bogus suggestions. +Please use your good judgement to decide whether any particular +.Cm style +suggestion really justifies a change to the input file. +.It Cm base +A convertion used in the base system of a specific operating system +is not adhered to. +These are not markup mistakes, and neither the quality of formatting +nor portability are in danger. +Messages of the +.Cm base +level are printed with the more intuitive +.Cm style +.Ar level +tag. .El .Pp Messages of the +.Cm base , +.Cm style , .Cm warning , .Cm error , and @@ -705,6 +766,199 @@ are hidden unless their level, or a lower level, is requested using a option or .Fl T Cm lint output mode. +.Pp +As indicated below, all +.Cm base +and some +.Cm style +checks are only performed if a specific operating system name occurs +in the arguments of the +.Fl W +command line option, of the +.Ic \&Os +macro, of the +.Fl Ios +command line option, or, if neither are present, in the return value +of the +.Xr uname 3 +function. +.Ss Conventions for base system manuals +.Bl -ohang +.It Sy "Mdocdate found" +.Pq mdoc , Nx +The +.Ic \&Dd +macro uses CVS +.Ic Mdocdate +keyword substitution, which is not supported by the +.Nx +base system. +Consider using the conventional +.Dq "Month dd, yyyy" +format instead. +.It Sy "Mdocdate missing" +.Pq mdoc , Ox +The +.Ic \&Dd +macro does not use CVS +.Ic Mdocdate +keyword substitution, but using it is conventionally expected in the +.Ox +base system. +.It Sy "unknown architecture" +.Pq mdoc , Ox , Nx +The third argument of the +.Ic \&Dt +macro does not match any of the architectures this operating system +is running on. +.It Sy "operating system explicitly specified" +.Pq mdoc , Ox , Nx +The +.Ic \&Os +macro has an argument. +In the base system, it is conventionally left blank. +.It Sy "RCS id missing" +.Pq Ox , Nx +The manual page lacks the comment line with the RCS identifier +generated by CVS +.Ic OpenBSD +or +.Ic NetBSD +keyword substitution as conventionally used in these operating systems. +.It Sy "referenced manual not found" +.Pq mdoc +An +.Ic \&Xr +macro references a manual page that is not found in the base system. +The path to look for base system manuals is configurable at compile +time and defaults to +.Pa /usr/share/man : /usr/X11R6/man . +.El +.Ss Style suggestions +.Bl -ohang +.It Sy "legacy man(7) date format" +.Pq mdoc +The +.Ic \&Dd +macro uses the legacy +.Xr man 7 +date format +.Dq yyyy-dd-mm . +Consider using the conventional +.Xr mdoc 7 +date format +.Dq "Month dd, yyyy" +instead. +.It Sy "lower case character in document title" +.Pq mdoc , man +The title is still used as given in the +.Ic \&Dt +or +.Ic \&TH +macro. +.It Sy "duplicate RCS id" +A single manual page contains two copies of the RCS identifier for +the same operating system. +Consider deleting the later instance and moving the first one up +to the top of the page. +.It Sy "typo in section name" +.Pq mdoc +Fuzzy string matching revealed that the argument of an +.Ic \&Sh +macro is similar, but not identical to a standard section name. +.It Sy "unterminated quoted argument" +.Pq roff +Macro arguments can be enclosed in double quote characters +such that space characters and macro names contained in the quoted +argument need not be escaped. +The closing quote of the last argument of a macro can be omitted. +However, omitting it is not recommended because it makes the code +harder to read. +.It Sy "useless macro" +.Pq mdoc +A +.Ic \&Bt , +.Ic \&Tn , +or +.Ic \&Ud +macro was found. +Simply delete it: it serves no useful purpose. +.It Sy "consider using OS macro" +.Pq mdoc +A string was found in plain text or in a +.Ic \&Bx +macro that could be represented using +.Ic \&Ox , +.Ic \&Nx , +.Ic \&Fx , +or +.Ic \&Dx . +.It Sy "errnos out of order" +.Pq mdoc, Nx +The +.Ic \&Er +items in a +.Ic \&Bl +list are not in alphabetical order. +.It Sy "duplicate errno" +.Pq mdoc, Nx +A +.Ic \&Bl +list contains two consecutive +.Ic \&It +entries describing the same +.Ic \&Er +number. +.It Sy "trailing delimiter" +.Pq mdoc +The last argument of an +.Ic \&Ex , \&Fo , \&Nd , \&Nm , \&Os , \&Sh , \&Ss , \&St , +or +.Ic \&Sx +macro ends with a trailing delimiter. +This is usually bad style and often indicates typos. +Most likely, the delimiter can be removed. +.It Sy "no blank before trailing delimiter" +.Pq mdoc +The last argument of a macro that supports trailing delimiter +arguments is longer than one byte and ends with a trailing delimiter. +Consider inserting a blank such that the delimiter becomes a separate +argument, thus moving it out of the scope of the macro. +.It Sy "fill mode already enabled, skipping" +.Pq man +A +.Ic \&fi +request occurs even though the document is still in fill mode, +or already switched back to fill mode. +It has no effect. +.It Sy "fill mode already disabled, skipping" +.Pq man +An +.Ic \&nf +request occurs even though the document already switched to no-fill mode +and did not switch back to fill mode yet. +It has no effect. +.It Sy "function name without markup" +.Pq mdoc +A word followed by an empty pair of parentheses occurs on a text line. +Consider using an +.Ic \&Fn +or +.Ic \&Xr +macro. +.It Sy "whitespace at end of input line" +.Pq mdoc , man , roff +Whitespace at the end of input lines is almost never semantically +significant \(em but in the odd case where it might be, it is +extremely confusing when reviewing and maintaining documents. +.It Sy "bad comment style" +.Pq roff +Comment lines start with a dot, a backslash, and a double-quote character. +The +.Nm +utility treats the line as a comment line even without the backslash, +but leaving out the backslash might not be portable. +.El .Ss Warnings related to the document prologue .Bl -ohang .It Sy "missing manual title, using UNTITLED" @@ -719,13 +973,6 @@ macro before the first non-prologue macro. There is no .Ic \&TH macro, or it has no arguments. -.It Sy "lower case character in document title" -.Pq mdoc , man -The title is still used as given in the -.Ic \&Dt -or -.Ic \&TH -macro. .It Sy "missing manual section, using \(dq\(dq" .Pq mdoc , man A @@ -761,13 +1008,17 @@ The date given in a or .Ic \&TH macro does not follow the conventional format. +.It Sy "date in the future, using it anyway" +.Pq mdoc , man +The date given in a +.Ic \&Dd +or +.Ic \&TH +macro is more than a day ahead of the current system +.Xr time 3 . .It Sy "missing Os macro, using \(dq\(dq" .Pq mdoc The default or current system is not shown in this case. -.It Sy "duplicate prologue macro" -.Pq mdoc -One of the prologue macros occurs more than once. -The last instance overrides all previous ones. .It Sy "late prologue macro" .Pq mdoc A @@ -775,17 +1026,6 @@ A or .Ic \&Os macro occurs after some non-prologue macro, but still takes effect. -.It Sy "skipping late title macro" -.Pq mdoc -The -.Ic \&Dt -macro appears after the first non-prologue macro. -Traditional formatters cannot handle this because -they write the page header before parsing the document body. -Even though this technical restriction does not apply to -.Nm , -traditional semantics is preserved. -The late macro is discarded including its arguments. .It Sy "prologue macros out of order" .Pq mdoc The prologue macros are not given in the conventional order @@ -857,6 +1097,14 @@ The .Ic \&Nd macro lacks the required argument. The title line of the manual will end after the dash. +.It Sy "description line outside NAME section" +.Pq mdoc +An +.Ic \&Nd +macro appears outside the NAME section. +The arguments are printed anyway and the following text is used for +.Xr apropos 1 , +but none of that behaviour is portable. .It Sy "sections out of conventional order" .Pq mdoc A standard section occurs after another section it usually precedes. @@ -869,6 +1117,24 @@ The same standard section title occurs more than once. .Pq mdoc A standard section header occurs in a section of the manual where it normally isn't useful. +.It Sy "cross reference to self" +.Pq mdoc +An +.Ic \&Xr +macro refers to a name and section matching the section of the present +manual page and a name mentioned in an +.Ic \&Nm +macro in the NAME or SYNOPSIS section, or in an +.Ic \&Fn +or +.Ic \&Fo +macro in the SYNOPSIS. +Consider using +.Ic \&Nm +or +.Ic \&Fn +instead of +.Ic \&Xr . .It Sy "unusual Xr order" .Pq mdoc In the SEE ALSO section, an @@ -955,7 +1221,9 @@ The paragraph macro is moved after the end of the list. .Pq mdoc An input line begins with an .Ic \&Ns -macro. +macro, or the next argument after an +.Ic \&Ns +macro is an isolated closing delimiter. The macro is ignored. .It Sy "blocks badly nested" .Pq mdoc @@ -996,20 +1264,12 @@ list block contains text or macros before the first .Ic \&It macro. The offending children are moved before the beginning of the list. -.It Sy "fill mode already enabled, skipping" -.Pq man -A -.Ic \&fi -request occurs even though the document is still in fill mode, -or already switched back to fill mode. -It has no effect. -.It Sy "fill mode already disabled, skipping" -.Pq man -An -.Ic \&nf -request occurs even though the document already switched to no-fill mode -and did not switch back to fill mode yet. -It has no effect. +.It Sy "first macro on line" +Inside a +.Ic \&Bl Fl column +list, a +.Ic \&Ta +macro occurs as the first macro on a line, which is not portable. .It Sy "line scope broken" .Pq man While parsing the next-line scope of the previous macro, @@ -1063,6 +1323,7 @@ A .Ic \&Bl , .Ic \&D1 , .Ic \&Dl , +.Ic \&MT , .Ic \&RS , or .Ic \&UR @@ -1075,7 +1336,7 @@ or .Ic \&Bl .Fl offset or -.Fl width. +.Fl width . .It Sy "missing display type, using -ragged" .Pq mdoc The @@ -1140,6 +1401,17 @@ list, an .Ic \&It block is empty. An empty list item is shown. +.It Sy "missing argument, using next line" +.Pq mdoc +An +.Ic \&It +macro in a +.Ic \&Bd Fl column +list has no arguments. +While +.Nm +uses the text or macros of the following line, if any, for the cell, +other formatters may misformat the list. .It Sy "missing font type, using \efR" .Pq mdoc A @@ -1198,6 +1470,8 @@ An empty pair of square brackets is shown. .It Sy "missing resource identifier, using \(dq\(dq" .Pq man The +.Ic \&MT +or .Ic \&UR macro is invoked without any argument. An empty pair of angle brackets is shown. @@ -1209,14 +1483,6 @@ An empty box is inserted. .El .Ss "Warnings related to bad macro arguments" .Bl -ohang -.It Sy "unterminated quoted argument" -.Pq roff -Macro arguments can be enclosed in double quote characters -such that space characters and macro names contained in the quoted -argument need not be escaped. -The closing quote of the last argument of a macro can be omitted. -However, omitting it is not recommended because it makes the code -harder to read. .It Sy "duplicate argument" .Pq mdoc A @@ -1297,6 +1563,12 @@ or .Ic \&Fn macro contains an opening or closing parenthesis; that's probably wrong, parentheses are added automatically. +.It Sy "unknown library name" +.Pq mdoc, not on Ox +An +.Ic \&Lb +macro has an unknown name argument and will be rendered as +.Qq library Dq Ar name . .It Sy "invalid content in Rs block" .Pq mdoc An @@ -1352,18 +1624,10 @@ As an implementation dependent choice, tab characters on text lines are passed through to the formatters in any case. Given that the text before the tab character will be filled, it is hard to predict which tab stop position the tab will advance to. -.It Sy "whitespace at end of input line" -.Pq mdoc , man , roff -Whitespace at the end of input lines is almost never semantically -significant \(em but in the odd case where it might be, it is -extremely confusing when reviewing and maintaining documents. -.It Sy "bad comment style" -.Pq roff -Comment lines start with a dot, a backslash, and a double-quote character. -The -.Nm -utility treats the line as a comment line even without the backslash, -but leaving out the backslash might not be portable. +.It Sy "new sentence, new line" +.Pq mdoc +A new sentence starts in the middle of a text line. +Start it on a new input line to help formatters produce correct spacing. .It Sy "invalid escape sequence" .Pq roff An escape sequence has an invalid opening argument delimiter, lacks the @@ -1470,6 +1734,21 @@ and any remaining cells stay empty. .El .Ss "Errors related to roff, mdoc, and man code" .Bl -ohang +.It Sy "duplicate prologue macro" +.Pq mdoc +One of the prologue macros occurs more than once. +The last instance overrides all previous ones. +.It Sy "skipping late title macro" +.Pq mdoc +The +.Ic \&Dt +macro appears after the first non-prologue macro. +Traditional formatters cannot handle this because +they write the page header before parsing the document body. +Even though this technical restriction does not apply to +.Nm , +traditional semantics is preserved. +The late macro is discarded including its arguments. .It Sy "input stack limit exceeded, infinite loop?" .Pq roff Explicit recursion limits are implemented for the following features, @@ -1540,7 +1819,7 @@ An .Xr mdoc 7 block closing macro, a .Xr man 7 -.Ic \&RE +.Ic \&ME , \&RE or .Ic \&UE macro, an @@ -1574,7 +1853,7 @@ At the end of the document, an explicit block, a .Xr man 7 next-line scope or -.Ic \&RS +.Ic \&MT , \&RS or .Ic \&UR block, an equation, table, or @@ -1640,6 +1919,11 @@ whatever mode was active before the block. A .Ic \&Bl macro fails to specify the list type. +.It Sy "argument is not numeric, using 1" +.Pq roff +The argument of a +.Ic \&ce +request is not a number. .It Sy "missing manual name, using \(dq\(dq" .Pq mdoc The first call to @@ -1741,6 +2025,7 @@ A macro or request is invoked with too many arguments: .Bl -dash -offset 2n -width 2n -compact .It .Ic \&Fo , +.Ic \&MT , .Ic \&PD , .Ic \&RS , .Ic \&UR , @@ -1832,6 +2117,19 @@ as if they were a text line. .Xr mdoc 7 , .Xr roff 7 , .Xr tbl 7 +.Sh HISTORY +The +.Nm +utility first appeared in +.Ox 4.8 . +The option +.Fl I +appeared in +.Ox 5.2 , +and +.Fl aCcfhKklMSsw +in +.Ox 5.7 . .Sh AUTHORS .An -nosplit The diff --git a/contrib/mdocml/mandoc.3 b/contrib/mdocml/mandoc.3 index 6f7c3eb..6914a76 100644 --- a/contrib/mdocml/mandoc.3 +++ b/contrib/mdocml/mandoc.3 @@ -1,4 +1,4 @@ -.\" $Id: mandoc.3,v 1.38 2017/01/09 01:37:03 schwarze Exp $ +.\" $Id: mandoc.3,v 1.41 2017/07/04 23:40:01 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 9 2017 $ +.Dd $Mdocdate: July 4 2017 $ .Dt MANDOC 3 .Os .Sh NAME @@ -35,7 +35,7 @@ .Nm mparse_result , .Nm mparse_strerror , .Nm mparse_strlevel , -.Nm mparse_updaterc +.Nm mparse_updaterc .Nd mandoc macro compiler library .Sh SYNOPSIS .In sys/types.h @@ -47,9 +47,10 @@ .Ft struct mparse * .Fo mparse_alloc .Fa "int options" -.Fa "enum mandoclevel wlevel" +.Fa "enum mandocerr mmin" .Fa "mandocmsg mmsg" -.Fa "char *defos" +.Fa "enum mandoc_os oe_e" +.Fa "char *os_s" .Fc .Ft void .Fo (*mandocmsg) @@ -304,12 +305,15 @@ This is for example useful in .Xr makewhatis 8 .Fl Q to quickly build minimal databases. -.It Ar wlevel +.It Ar mmin Can be set to -.Dv MANDOCLEVEL_BADARG , -.Dv MANDOCLEVEL_ERROR , +.Dv MANDOCERR_BASE , +.Dv MANDOCERR_STYLE , +.Dv MANDOCERR_WARNING , +.Dv MANDOCERR_ERROR , +.Dv MANDOCERR_UNSUPP , or -.Dv MANDOCLEVEL_WARNING . +.Dv MANDOCERR_MAX . Messages below the selected level will be suppressed. .It Ar mmsg A callback function to handle errors and warnings. @@ -319,10 +323,19 @@ for an example. If printing of error messages is not desired, .Dv NULL may be passed. -.It Ar defos +.It Ar os_e +Operating system to check base system conventions for. +If +.Dv MANDOC_OS_OTHER , +the system is automatically detected from +.Ic \&Os , +.Fl Ios , +or +.Xr uname 3 . +.It Ar os_s A default string for the .Xr mdoc 7 -.Sq \&Os +.Ic \&Os macro, overriding the .Dv OSNAME preprocessor definition and the results of @@ -650,10 +663,10 @@ TEXT end .Ed .Pp Here, the formatting of the -.Sq \&Ao +.Ic \&Ao block extends from TEXT ao to TEXT ac, while the formatting of the -.Sq \&Bo +.Ic \&Bo block extends from TEXT bo to TEXT bc. It renders as follows in .Fl T Ns Cm ascii @@ -669,11 +682,9 @@ Using badly-nested blocks is .Em strongly discouraged ; for example, the .Fl T Ns Cm html -and -.Fl T Ns Cm xhtml -front-ends to +front-end to .Xr mandoc 1 -are unable to render them in any meaningful way. +is unable to render them in any meaningful way. Furthermore, behaviour when encountering badly-nested blocks is not consistent across troff implementations, especially when using multiple levels of badly-nested blocks. diff --git a/contrib/mdocml/mandoc.c b/contrib/mdocml/mandoc.c index d265463..3e16d2c 100644 --- a/contrib/mdocml/mandoc.c +++ b/contrib/mdocml/mandoc.c @@ -1,7 +1,7 @@ -/* $Id: mandoc.c,v 1.98 2015/11/12 22:44:27 schwarze Exp $ */ +/* $Id: mandoc.c,v 1.103 2017/07/03 13:40:19 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,8 +28,9 @@ #include <string.h> #include <time.h> -#include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc.h" +#include "roff.h" #include "libmandoc.h" static int a2time(time_t *, const char *, const char *); @@ -95,6 +96,8 @@ mandoc_escape(const char **end, const char **start, int *sz) case ',': case '/': return ESCAPE_IGNORE; + case 'p': + return ESCAPE_BREAK; /* * The \z escape is supposed to output the following @@ -175,7 +178,17 @@ mandoc_escape(const char **end, const char **start, int *sz) ++*end; return ESCAPE_ERROR; } - gly = ESCAPE_IGNORE; + switch ((*start)[-1]) { + case 'h': + gly = ESCAPE_HORIZ; + break; + case 'l': + gly = ESCAPE_HLINE; + break; + default: + gly = ESCAPE_IGNORE; + break; + } term = **start; *start = ++*end; break; @@ -508,27 +521,38 @@ fail: } char * -mandoc_normdate(struct mparse *parse, char *in, int ln, int pos) +mandoc_normdate(struct roff_man *man, char *in, int ln, int pos) { + char *cp; time_t t; /* No date specified: use today's date. */ if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) { - mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL); + mandoc_msg(MANDOCERR_DATE_MISSING, man->parse, ln, pos, NULL); return time2a(time(NULL)); } /* Valid mdoc(7) date format. */ if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) || - a2time(&t, "%b %d, %Y", in)) - return time2a(t); + a2time(&t, "%b %d, %Y", in)) { + cp = time2a(t); + if (t > time(NULL) + 86400) + mandoc_msg(MANDOCERR_DATE_FUTURE, man->parse, + ln, pos, cp); + return cp; + } - /* Do not warn about the legacy man(7) format. */ + /* In man(7), do not warn about the legacy format. */ - if ( ! a2time(&t, "%Y-%m-%d", in)) - mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in); + if (a2time(&t, "%Y-%m-%d", in) == 0) + mandoc_msg(MANDOCERR_DATE_BAD, man->parse, ln, pos, 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); /* Use any non-mdoc(7) date verbatim. */ diff --git a/contrib/mdocml/mandoc.css b/contrib/mdocml/mandoc.css index 162b730..2a57a18 100644 --- a/contrib/mdocml/mandoc.css +++ b/contrib/mdocml/mandoc.css @@ -1,4 +1,4 @@ -/* $Id: mandoc.css,v 1.13 2017/01/21 02:29:57 schwarze Exp $ */ +/* $Id: mandoc.css,v 1.22 2017/07/16 18:45:00 schwarze Exp $ */ /* * Standard style sheet for mandoc(1) -Thtml and man.cgi(8). */ @@ -14,6 +14,12 @@ ul, ol, dl { margin-top: 0em; margin-bottom: 0em; } li, dt { margin-top: 1em; } +a.selflink { border-bottom: thin dotted; + color: inherit; + font: inherit; + text-decoration: inherit; } +* { clear: both } + /* Search form and search results. */ fieldset { border: thin solid silver; @@ -74,32 +80,50 @@ li.It-dash:before { ul.Bl-item { list-style-type: none; padding-left: 0em; } li.It-item { } +ul.Bl-compact > li { + margin-top: 0ex; } ol.Bl-enum { padding-left: 2em; } li.It-enum { } +ol.Bl-compact > li { + margin-top: 0ex; } dl.Bl-diag { } dt.It-diag { } -dd.It-diag { } +dd.It-diag { margin-left: 0ex; } b.It-diag { font-style: normal; } dl.Bl-hang { } dt.It-hang { } -dd.It-hang { } +dd.It-hang { margin-left: 10.2ex; } dl.Bl-inset { } dt.It-inset { } -dd.It-inset { } +dd.It-inset { margin-left: 0ex; } dl.Bl-ohang { } dt.It-ohang { } dd.It-ohang { margin-left: 0ex; } -dl.Bl-tag { } -dt.It-tag { } -dd.It-tag { } +dl.Bl-tag { margin-left: 10.2ex; } +dt.It-tag { float: left; + margin-top: 0ex; + margin-left: -10.2ex; + padding-right: 2ex; + vertical-align: top; } +dd.It-tag { clear: right; + width: 100%; + margin-top: 0ex; + margin-left: 0ex; + vertical-align: top; + overflow: auto; } +dl.Bl-compact > dt { + margin-top: 0ex; } table.Bl-column { } tr.It-column { } td.It-column { margin-top: 1em; } +table.Bl-compact > tbody > tr > td { + margin-top: 0ex; } -span.Rs { } +cite.Rs { font-style: normal; + font-weight: normal; } span.RsA { } i.RsB { font-weight: normal; } span.RsC { } @@ -124,7 +148,8 @@ table.Nm { } b.Nm { font-style: normal; } b.Fl { font-style: normal; } b.Cm { font-style: normal; } -i.Ar { font-weight: normal; } +var.Ar { font-style: italic; + font-weight: normal; } span.Op { } b.Ic { font-style: normal; } code.Ev { font-style: normal; @@ -138,11 +163,15 @@ span.Lb { } b.In { font-style: normal; } a.In { } b.Fd { font-style: normal; } -i.Ft { font-weight: normal; } +var.Ft { font-style: italic; + font-weight: normal; } b.Fn { font-style: normal; } -i.Fa { font-weight: normal; } -i.Vt { font-weight: normal; } -i.Va { font-weight: normal; } +var.Fa { font-style: italic; + font-weight: normal; } +var.Vt { font-style: italic; + font-weight: normal; } +var.Va { font-style: italic; + font-weight: normal; } code.Dv { font-style: normal; font-weight: normal; font-family: monospace; } @@ -158,6 +187,7 @@ a.Mt { } b.Cd { font-style: normal; } i.Ad { font-weight: normal; } b.Ms { font-style: normal; } +span.St { } a.Ux { } /* Physical markup. */ diff --git a/contrib/mdocml/mandoc.h b/contrib/mdocml/mandoc.h index 2ea64ea..b234cb5 100644 --- a/contrib/mdocml/mandoc.h +++ b/contrib/mdocml/mandoc.h @@ -1,4 +1,4 @@ -/* $Id: mandoc.h,v 1.213 2017/01/09 01:37:03 schwarze Exp $ */ +/* $Id: mandoc.h,v 1.245 2017/07/08 14:51:04 schwarze Exp $ */ /* * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -28,7 +28,7 @@ */ enum mandoclevel { MANDOCLEVEL_OK = 0, - MANDOCLEVEL_RESERVED, + MANDOCLEVEL_STYLE, /* style suggestions */ MANDOCLEVEL_WARNING, /* warnings: syntax, whitespace, etc. */ MANDOCLEVEL_ERROR, /* input has been thrown away */ MANDOCLEVEL_UNSUPP, /* input needs unimplemented features */ @@ -44,20 +44,46 @@ enum mandoclevel { enum mandocerr { MANDOCERR_OK, + MANDOCERR_BASE, /* ===== start of base system conventions ===== */ + + MANDOCERR_MDOCDATE, /* Mdocdate found: Dd ... */ + MANDOCERR_MDOCDATE_MISSING, /* Mdocdate missing: Dd ... */ + MANDOCERR_ARCH_BAD, /* unknown architecture: Dt ... arch */ + MANDOCERR_OS_ARG, /* operating system explicitly specified: Os ... */ + MANDOCERR_RCS_MISSING, /* RCS id missing */ + MANDOCERR_XR_BAD, /* referenced manual not found: Xr name sec */ + + MANDOCERR_STYLE, /* ===== start of style suggestions ===== */ + + MANDOCERR_DATE_LEGACY, /* legacy man(7) date format: Dd ... */ + MANDOCERR_TITLE_CASE, /* lower case character in document title */ + MANDOCERR_RCS_REP, /* duplicate RCS id: ... */ + MANDOCERR_SEC_TYPO, /* typo in section name: Sh ... */ + MANDOCERR_ARG_QUOTE, /* unterminated quoted argument */ + MANDOCERR_MACRO_USELESS, /* useless macro: macro */ + MANDOCERR_BX, /* consider using OS macro: macro */ + MANDOCERR_ER_ORDER, /* errnos out of order: Er ... */ + MANDOCERR_ER_REP, /* duplicate errno: Er ... */ + MANDOCERR_DELIM, /* trailing delimiter: macro ... */ + MANDOCERR_DELIM_NB, /* no blank before trailing delimiter: macro ... */ + MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */ + MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */ + MANDOCERR_FUNC, /* function name without markup: name() */ + MANDOCERR_SPACE_EOL, /* whitespace at end of input line */ + MANDOCERR_COMMENT_BAD, /* bad comment style */ + MANDOCERR_WARNING, /* ===== start of warnings ===== */ /* related to the prologue */ MANDOCERR_DT_NOTITLE, /* missing manual title, using UNTITLED: line */ MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */ - MANDOCERR_TITLE_CASE, /* lower case character in document title */ MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */ MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */ MANDOCERR_DATE_MISSING, /* missing date, using today's date */ MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */ + MANDOCERR_DATE_FUTURE, /* date in the future, using it anyway: date */ MANDOCERR_OS_MISSING, /* missing Os macro, using "" */ - MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */ MANDOCERR_PROLOG_LATE, /* late prologue macro: macro */ - MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */ MANDOCERR_PROLOG_ORDER, /* prologue macros out of order: macros */ /* related to document structure */ @@ -71,9 +97,11 @@ enum mandocerr { MANDOCERR_NAMESEC_BAD, /* bad NAME section content: macro */ MANDOCERR_NAMESEC_PUNCT, /* missing comma before name: Nm name */ MANDOCERR_ND_EMPTY, /* missing description line, using "" */ + MANDOCERR_ND_LATE, /* description line outside NAME section */ MANDOCERR_SEC_ORDER, /* sections out of conventional order: Sh title */ MANDOCERR_SEC_REP, /* duplicate section title: Sh title */ MANDOCERR_SEC_MSEC, /* unexpected section: Sh title for ... only */ + MANDOCERR_XR_SELF, /* cross reference to self: Xr name sec */ MANDOCERR_XR_ORDER, /* unusual Xr order: ... after ... */ MANDOCERR_XR_PUNCT, /* unusual Xr punctuation: ... after ... */ MANDOCERR_AN_MISSING, /* AUTHORS section without An macro */ @@ -87,9 +115,9 @@ enum mandocerr { MANDOCERR_BLK_NEST, /* blocks badly nested: macro ... */ MANDOCERR_BD_NEST, /* nested displays are not portable: macro ... */ MANDOCERR_BL_MOVE, /* moving content out of list: macro */ - MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */ - MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */ + MANDOCERR_TA_LINE, /* first macro on line: Ta */ MANDOCERR_BLK_LINE, /* line scope broken: macro breaks macro */ + MANDOCERR_BLK_BLANK, /* skipping blank line in line scope */ /* related to missing arguments */ MANDOCERR_REQ_EMPTY, /* skipping empty request: request */ @@ -104,6 +132,7 @@ enum mandocerr { MANDOCERR_FO_NOHEAD, /* missing function name, using "": Fo */ MANDOCERR_IT_NOHEAD, /* empty head in list item: Bl -type It */ MANDOCERR_IT_NOBODY, /* empty list item: Bl -type It */ + MANDOCERR_IT_NOARG, /* missing argument, using next line: Bl -c It */ MANDOCERR_BF_NOFONT, /* missing font type, using \fR: Bf */ MANDOCERR_BF_BADFONT, /* unknown font type, using \fR: Bf font */ MANDOCERR_PF_SKIP, /* nothing follows prefix: Pf arg */ @@ -115,7 +144,6 @@ enum mandocerr { MANDOCERR_EQN_NOBOX, /* missing eqn box, using "": op */ /* related to bad arguments */ - MANDOCERR_ARG_QUOTE, /* unterminated quoted argument */ MANDOCERR_ARG_REP, /* duplicate argument: macro arg */ MANDOCERR_AN_REP, /* skipping duplicate argument: An -arg */ MANDOCERR_BD_REP, /* skipping duplicate display type: Bd -type */ @@ -125,6 +153,7 @@ enum mandocerr { MANDOCERR_AT_BAD, /* unknown AT&T UNIX version: At version */ MANDOCERR_FA_COMMA, /* comma in function argument: arg */ MANDOCERR_FN_PAREN, /* parenthesis in function name: arg */ + 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_FT_BAD, /* unknown font, skipping request: ft font */ @@ -133,8 +162,7 @@ enum mandocerr { /* related to plain text */ MANDOCERR_FI_BLANK, /* blank line in fill mode, using .sp */ MANDOCERR_FI_TAB, /* tab in filled text */ - MANDOCERR_SPACE_EOL, /* whitespace at end of input line */ - MANDOCERR_COMMENT_BAD, /* bad comment style */ + MANDOCERR_EOS, /* new sentence, new line */ MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */ MANDOCERR_STR_UNDEF, /* undefined string, using "": name */ @@ -160,6 +188,8 @@ enum mandocerr { /* related to document structure and macros */ MANDOCERR_FILE, /* cannot open file */ + MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */ + MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */ MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ MANDOCERR_CHAR_BAD, /* skipping bad character: number */ MANDOCERR_MACRO, /* skipping unknown macro: macro */ @@ -176,6 +206,7 @@ enum mandocerr { 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_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 */ @@ -233,9 +264,10 @@ enum tbl_cellt { */ 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 */ - enum tbl_cellt pos; - size_t spacing; int col; /* column number, starting from 0 */ int flags; #define TBL_CELL_TALIGN (1 << 0) /* t, T */ @@ -246,6 +278,7 @@ struct tbl_cell { #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; }; /* @@ -273,9 +306,10 @@ enum tbl_datt { */ struct tbl_dat { struct tbl_cell *layout; /* layout cell */ - int spans; /* how many spans follow */ 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; }; @@ -300,11 +334,9 @@ struct tbl_span { }; enum eqn_boxt { - EQN_ROOT, /* root of parse tree */ EQN_TEXT, /* text (number, variable, whatever) */ EQN_SUBEXPR, /* nested `eqn' subexpression */ EQN_LIST, /* list (braces, etc.) */ - EQN_LISTONE, /* singleton list */ EQN_PILE, /* vertical pile */ EQN_MATRIX /* pile of piles */ }; @@ -370,17 +402,6 @@ struct eqn_box { }; /* - * An equation consists of a tree of expressions starting at a given - * line and position. - */ -struct eqn { - char *name; /* identifier (or NULL) */ - struct eqn_box *root; /* root mathematical expression */ - int ln; /* invocation line */ - int pos; /* invocation position */ -}; - -/* * Parse options. */ #define MPARSE_MDOC 1 /* assume -mdoc */ @@ -390,6 +411,12 @@ struct eqn { #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_IGNORE, /* escape to be ignored */ @@ -402,7 +429,10 @@ enum mandoc_esc { ESCAPE_FONTPREV, /* previous font mode */ ESCAPE_NUMBERED, /* a numbered glyph */ ESCAPE_UNICODE, /* a unicode codepoint */ + ESCAPE_BREAK, /* break the output line */ ESCAPE_NOSPACE, /* suppress space if the last on a line */ + ESCAPE_HORIZ, /* horizontal movement */ + ESCAPE_HLINE, /* horizontal line drawing */ ESCAPE_SKIPCHAR, /* skip the next character */ ESCAPE_OVERSTRIKE /* overstrike all chars in the argument */ }; @@ -422,7 +452,8 @@ 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 mandoclevel, mandocmsg, const char *); +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 *); diff --git a/contrib/mdocml/mandoc_aux.c b/contrib/mdocml/mandoc_aux.c index cc74b7e..db593e4 100644 --- a/contrib/mdocml/mandoc_aux.c +++ b/contrib/mdocml/mandoc_aux.c @@ -1,7 +1,7 @@ -/* $Id: mandoc_aux.c,v 1.9 2015/11/07 14:22:29 schwarze Exp $ */ +/* $Id: mandoc_aux.c,v 1.10 2017/06/12 19:05:47 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -71,7 +71,6 @@ mandoc_malloc(size_t size) void * mandoc_realloc(void *ptr, size_t size) { - ptr = realloc(ptr, size); if (ptr == NULL) err((int)MANDOCLEVEL_SYSERR, NULL); @@ -81,13 +80,21 @@ mandoc_realloc(void *ptr, size_t size) void * mandoc_reallocarray(void *ptr, size_t num, size_t size) { - ptr = reallocarray(ptr, num, size); if (ptr == NULL) err((int)MANDOCLEVEL_SYSERR, NULL); return ptr; } +void * +mandoc_recallocarray(void *ptr, size_t oldnum, size_t num, size_t size) +{ + ptr = recallocarray(ptr, oldnum, num, size); + if (ptr == NULL) + err((int)MANDOCLEVEL_SYSERR, NULL); + return ptr; +} + char * mandoc_strdup(const char *ptr) { diff --git a/contrib/mdocml/mandoc_aux.h b/contrib/mdocml/mandoc_aux.h index 603cc5a..469e331 100644 --- a/contrib/mdocml/mandoc_aux.h +++ b/contrib/mdocml/mandoc_aux.h @@ -1,7 +1,7 @@ -/* $Id: mandoc_aux.h,v 1.5 2016/07/19 13:36:13 schwarze Exp $ */ +/* $Id: mandoc_aux.h,v 1.7 2017/06/12 19:05:47 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,10 +17,11 @@ */ int mandoc_asprintf(char **, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); + __attribute__((__format__ (__printf__, 2, 3))); void *mandoc_calloc(size_t, size_t); void *mandoc_malloc(size_t); void *mandoc_realloc(void *, size_t); void *mandoc_reallocarray(void *, size_t, size_t); +void *mandoc_recallocarray(void *, size_t, size_t, size_t); char *mandoc_strdup(const char *); char *mandoc_strndup(const char *, size_t); diff --git a/contrib/mdocml/mandoc_char.7 b/contrib/mdocml/mandoc_char.7 index d272080..a372b3e 100644 --- a/contrib/mdocml/mandoc_char.7 +++ b/contrib/mdocml/mandoc_char.7 @@ -1,8 +1,8 @@ -.\" $Id: mandoc_char.7,v 1.63 2015/09/02 15:38:35 schwarze Exp $ +.\" $Id: mandoc_char.7,v 1.67 2017/06/14 20:57:07 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 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2011, 2013, 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 @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 2 2015 $ +.Dd $Mdocdate: June 14 2017 $ .Dt MANDOC_CHAR 7 .Os .Sh NAME @@ -62,6 +62,30 @@ blue-eyed lorry-driver .Ed .Pp +If a word on a text input line contains a hyphen, a formatter may decide +to insert an output line break after the hyphen if that helps filling +the current output line, but the whole word would overflow the line. +If it is important that the word is not broken across lines in this +way, a zero-width space +.Pq Sq \e& +can be inserted before or after the hyphen. +While +.Xr mandoc 1 +never breaks the output line after hyphens adjacent to a zero-width +space, after any of the other dash- or hyphen-like characters +represented by escape sequences, or after hyphens inside words in +macro arguments, other software may not respect these rules and may +break the line even in such cases. +.Pp +Some +.Xr roff 7 +implementations contains dictionaries allowing to break the line +at syllable boundaries even inside words that contain no hyphens. +Such automatic hyphenation is not supported by +.Xr mandoc 1 , +which only breaks the line at whitespace, and inside words only +after existing hyphens. +.Pp The mathematical minus sign is used for negative numbers or subtraction. It should be written as .Sq \e(mi : @@ -145,6 +169,8 @@ even on request and macro lines. .Ss Accents In output modes supporting such special output characters, for example .Fl T Cm pdf , +and sometimes less consistently in +.Fl T Cm utf8 , some .Xr roff 7 formatters convert the following ASCII input characters to the @@ -153,6 +179,7 @@ following Unicode special output characters: .It \(ga Ta U+2018 Ta left single quotation mark .It \(aq Ta U+2019 Ta right single quotation mark .It \(ti Ta U+02DC Ta small tilde +.It \(ha Ta U+02C6 Ta modifier letter circumflex .El .Pp In prose, this automatic substitution is often desirable; @@ -163,6 +190,7 @@ escaping to render as follows: .It \e(ga Ta U+0060 Ta grave accent .It \e(aq Ta U+0027 Ta apostrophe .It \e(ti Ta U+007E Ta tilde +.It \e(ha Ta U+005E Ta circumflex accent .El .Ss Periods The period @@ -232,6 +260,7 @@ Lines: .It \e(ba Ta \(ba Ta bar .It \e(br Ta \(br Ta box rule .It \e(ul Ta \(ul Ta underscore +.It \e(ru Ta \(ru Ta underscore (width 0.5m) .It \e(rn Ta \(rn Ta overline .It \e(bb Ta \(bb Ta broken bar .It \e(sl Ta \(sl Ta forward slash @@ -255,6 +284,10 @@ Text markers: .It \e(sh Ta \(sh Ta hash (pound) .It \e(CR Ta \(CR Ta carriage return .It \e(OK Ta \(OK Ta check mark +.It \e(CL Ta \(CL Ta club suit +.It \e(SP Ta \(SP Ta spade suit +.It \e(HE Ta \(HE Ta heart suit +.It \e(DI Ta \(DI Ta diamond suit .El .Pp Legal symbols: @@ -348,6 +381,7 @@ Arrows: .It \e(uA Ta \(uA Ta up double-arrow .It \e(dA Ta \(dA Ta down double-arrow .It \e(vA Ta \(vA Ta up-down double-arrow +.It \e(an Ta \(an Ta horizontal arrow extension .El .Pp Logical: @@ -426,11 +460,20 @@ Mathematical: .It \e(Ah Ta \(Ah Ta aleph .It \e(Im Ta \(Im Ta imaginary .It \e(Re Ta \(Re Ta real +.It \e(wp Ta \(wp Ta Weierstrass p .It \e(pd Ta \(pd Ta partial differential .It \e(-h Ta \(-h Ta Planck constant over 2\(*p -.It \e[12] Ta \[12] Ta one-half -.It \e[14] Ta \[14] Ta one-fourth -.It \e[34] Ta \[34] Ta three-fourths +.It \e[hbar] Ta \[hbar] Ta Planck constant over 2\(*p +.It \e(12 Ta \(12 Ta one-half +.It \e(14 Ta \(14 Ta one-fourth +.It \e(34 Ta \(34 Ta three-fourths +.It \e(18 Ta \(18 Ta one-eighth +.It \e(38 Ta \(38 Ta three-eighths +.It \e(58 Ta \(58 Ta five-eighths +.It \e(78 Ta \(78 Ta seven-eighths +.It \e(S1 Ta \(S1 Ta superscript 1 +.It \e(S2 Ta \(S2 Ta superscript 2 +.It \e(S3 Ta \(S3 Ta superscript 3 .El .Pp Ligatures: @@ -564,6 +607,8 @@ Units: .It \e(fm Ta \(fm Ta minute .It \e(sd Ta \(sd Ta second .It \e(mc Ta \(mc Ta micro +.It \e(Of Ta \(Of Ta Spanish female ordinal +.It \e(Om Ta \(Om Ta Spanish masculine ordinal .El .Pp Greek letters: @@ -724,9 +769,7 @@ the differently between mandoc and groff. .It In -.Fl T Ns Cm html -and -.Fl T Ns Cm xhtml , +.Fl T Ns Cm html , the \e(\(ti=, \e(nb, and \e(nc special characters render differently between mandoc and groff. .It diff --git a/contrib/mdocml/mandoc_escape.3 b/contrib/mdocml/mandoc_escape.3 index fec298b87..90b672c 100644 --- a/contrib/mdocml/mandoc_escape.3 +++ b/contrib/mdocml/mandoc_escape.3 @@ -1,4 +1,4 @@ -.\" $Id: mandoc_escape.3,v 1.3 2015/01/21 20:33:25 schwarze Exp $ +.\" $Id: mandoc_escape.3,v 1.4 2017/07/04 23:40:01 schwarze Exp $ .\" .\" Copyright (c) 2014 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: January 21 2015 $ +.Dd $Mdocdate: July 4 2017 $ .Dt MANDOC_ESCAPE 3 .Os .Sh NAME @@ -122,7 +122,7 @@ library, see the file .Pa roff.c , .It above all externally by the -.Xr mandoc +.Xr mandoc 1 formatting modules, in particular .Fl Tascii and diff --git a/contrib/mdocml/mandoc_headers.3 b/contrib/mdocml/mandoc_headers.3 index b8c204b..4a05d49 100644 --- a/contrib/mdocml/mandoc_headers.3 +++ b/contrib/mdocml/mandoc_headers.3 @@ -1,4 +1,4 @@ -.Dd December 1, 2014 +.Dd $Mdocdate: July 8 2017 $ .Dt MANDOC_HEADERS 3 .Os .Sh NAME @@ -60,9 +60,19 @@ Requires .In sys/types.h for .Vt size_t . +.Pp Provides the utility functions documented in .Xr mandoc_malloc 3 . .It Qq Pa mandoc_ohash.h +Requires +.In stddef.h +for +.Vt ptrdiff_t +and +.In stdint.h +for +.Vt uint32_t . +.Pp Includes .In ohash.h and provides @@ -77,6 +87,7 @@ 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 , @@ -90,7 +101,6 @@ Provides .Vt struct tbl_dat , .Vt struct tbl_span , .Vt struct eqn_box , -.Vt struct eqn , the function prototype typedef .Fn mandocmsg , the function @@ -112,18 +122,44 @@ Uses the type 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 . .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 . +.Pp Provides .Vt enum mdoc_endbody , .Vt enum roff_macroset , .Vt enum roff_next , .Vt enum roff_sec , +.Vt enum roff_tok , .Vt enum roff_type , .Vt struct roff_man , .Vt struct roff_meta , .Vt struct roff_node , -and the function -.Fn deroff . +the constant array +.Va roff_name +and the functions +.Fn deroff , +.Fn roffhash_alloc , +.Fn roffhash_find , +.Fn roffhash_free , +and +.Fn roff_validate . .Pp Uses pointers to the types .Vt struct mdoc_arg @@ -198,9 +234,10 @@ or .El .Ss Parser internals 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 not include any formatter headers. +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 +not include any formatter headers. .Bl -tag -width Ds .It Qq Pa libmandoc.h Requires @@ -227,17 +264,11 @@ and from .Pa roff.c for function prototypes. -Uses the types -.Vt struct tbl_span -and -.Vt struct eqn -from -.Pa mandoc.h -and +Uses the type .Vt struct roff_man from .Pa roff.h -as opaque types for function prototypes. +as an opaque type for function prototypes. .It Qq Pa roff_int.h Requires .Qq Pa roff.h @@ -254,16 +285,11 @@ because the latter two are needed by .Qq Pa roff.c . .Pp Uses the types -.Vt struct eqn -and -.Vt struct tbl_span -from -.Pa mandoc.h , .Vt struct roff_man and .Vt struct roff_node from -.Pa roff.h , +.Pa roff.h and .Vt struct mdoc_arg from @@ -271,6 +297,10 @@ from as opaque types for function prototypes. .It Qq Pa libmdoc.h Requires +.Qq Pa roff.h +for +.Vt enum roff_tok +and .Qq Pa mdoc.h for .Vt enum mdoc_* @@ -303,6 +333,11 @@ When this header is included, the same file should not include or .Pa libroff.h . .It Qq Pa libman.h +Requires +.Qq Pa roff.h +for +.Vt enum roff_tok . +.Pp Provides .Vt struct man_macro and some functions internal to the @@ -326,16 +361,13 @@ or Requires .In sys/types.h for -.Vt size_t , +.Vt size_t +and .Qq Pa mandoc.h for .Vt struct tbl_* and -.Vt struct eqn , -and -.Qq Pa libmandoc.h -for -.Vt enum rofferr . +.Vt struct eqn_box . .Pp Provides .Vt enum tbl_part , @@ -405,6 +437,7 @@ Provides .Vt enum termtype , .Vt struct termp_tbl , .Vt struct termp , +.Fn roff_term_pre , and many terminal formatting functions. .Pp Uses the opaque type @@ -414,11 +447,13 @@ from Uses .Vt struct tbl_span and -.Vt struct eqn +.Vt struct eqn_box from .Pa mandoc.h and .Vt struct roff_meta +and +.Vt struct roff_node from .Pa roff.h as opaque types for function prototypes. @@ -431,10 +466,7 @@ or Requires .In sys/types.h for -.Vt size_t , -.In stdio.h -for -.Dv BUFSIZ , +.Vt size_t and .Qq Pa out.h for @@ -450,8 +482,21 @@ Provides .Vt struct tagq , .Vt struct htmlpair , .Vt struct html , +.Fn roff_html_pre , and many HTML formatting functions. .Pp +Uses +.Vt struct tbl_span +and +.Vt struct eqn_box +from +.Pa mandoc.h +and +.Vt struct roff_node +from +.Pa roff.h +as opaque types for function prototypes. +.Pp When this header is included, the same file should not include .Pa term.h or @@ -489,8 +534,9 @@ Provides and the functions .Fn manconf_parse , .Fn manconf_output , +.Fn manconf_free , and -.Fn manconf_free . +.Fn manpath_base . .It Qq Pa mansearch.h Requires .In sys/types.h @@ -506,8 +552,7 @@ Provides .Vt struct manpage , .Vt struct mansearch , and the functions -.Fn mansearch_setup , -.Fn mansearch , +.Fn mansearch and .Fn mansearch_free . .Pp diff --git a/contrib/mdocml/mandoc_html.3 b/contrib/mdocml/mandoc_html.3 index 80b1fe6..ba25e0a 100644 --- a/contrib/mdocml/mandoc_html.3 +++ b/contrib/mdocml/mandoc_html.3 @@ -1,4 +1,4 @@ -.\" $Id: mandoc_html.3,v 1.3 2017/01/17 15:32:44 schwarze Exp $ +.\" $Id: mandoc_html.3,v 1.10 2017/07/15 17:57:51 schwarze Exp $ .\" .\" Copyright (c) 2014, 2017 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: January 17 2017 $ +.Dd $Mdocdate: July 15 2017 $ .Dt MANDOC_HTML 3 .Os .Sh NAME @@ -48,6 +48,14 @@ .Fa "struct html *h" .Fa "const char *word" .Fc +.Ft char * +.Fo html_make_id +.Fa "const struct roff_node *n" +.Fc +.Ft int +.Fo html_strlen +.Fa "const char *cp" +.Fc .Sh DESCRIPTION The mandoc HTML formatter is not a formal library. However, as it is compiled into more than one program, in particular @@ -137,11 +145,19 @@ Most attributes require one .Va char * argument which becomes the value of the attribute. The arguments have to be given in the same order as the attribute letters. +If an argument is +.Dv NULL , +the respective attribute is not written. .Bl -tag -width 1n -offset indent .It Cm c 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 @@ -175,18 +191,20 @@ Print an arbitrary attribute. This format letter requires two .Vt char * arguments, the attribute name and the value. +The name must not be +.Dv NULL . .It Cm s Print a .Cm style attribute. If present, it must be the last format letter. In contrast to the other format letters, this one does not yet -print the value and does not require an argument. +print the value and does not take an argument. Instead, the rest of the format string consists of pairs of argument type letters and style name letters. .El .Pp -Argument type letters each require on argument as follows: +Argument type letters each require one argument as follows: .Bl -tag -width 1n -offset indent .It Cm h Requires one @@ -201,25 +219,34 @@ argument, used as a style value. Requires one .Vt struct roffsu * argument, used as a length. -.It Cm v -Requires one -.Vt int -argument, interpreted as a vertical length in units of -.Dv SCALE_VS . .It Cm w Requires one .Vt char * argument, interpreted as an .Xr mdoc 7 Ns -style width specifier. +If the argument is +.Dv NULL , +nothing is printed for this pair. +.Pp +The +.Cm w +argument type letter can optionally be followed by one, two, or three +modifier letters. +The modifier +.Cm * +suppresses printing of the pair if the argument matches 6n. +The modifier +.Cm + +increases the width by 20% to make even bold text fit +and adds three units for padding between columns. +The modifier +.Cm \- +makes the width negative by multiplying it with \-1. .El .Pp Style name letters decide what to do with the preceding argument: .Bl -tag -width 1n -offset indent -.It Cm b -Set -.Cm margin-bottom -to the given length. .It Cm h Set .Cm height @@ -232,10 +259,6 @@ to the given length. Set .Cm margin-left to the given length. -.It Cm t -Set -.Cm margin-top -to the given length. .It Cm w Set .Cm width @@ -251,6 +274,8 @@ requires two .Vt char * arguments. The first is the style name, the second its value. +The style name must not be +.Dv NULL . .El .Pp .Fn print_otag @@ -287,8 +312,27 @@ and .Fn print_tagq functions. .Pp +The function +.Fn html_make_id +takes a node containing one or more text children +and returns a newly allocated string containing the concatenation +of the child strings, with blanks replaced by underscores. +If the node +.Fa n +contains any non-text child node, +.Fn html_make_id +returns +.Dv NULL +instead. +The caller is responsible for freeing the returned string. +.Pp +The function +.Fn html_strlen +counts the number of characters in +.Fa cp . +It is used as a crude estimate of the width needed to display a string. +.Pp The functions -.Fn html_strlen , .Fn print_eqn , .Fn print_tbl , and @@ -336,5 +380,6 @@ implementation of common mandoc utility functions .An -nosplit The mandoc HTML formatter was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . -This manual was written by -.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +It is maintained by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org , +who also wrote this manual. diff --git a/contrib/mdocml/mandoc_xr.c b/contrib/mdocml/mandoc_xr.c new file mode 100644 index 0000000..da0a7f0 --- /dev/null +++ b/contrib/mdocml/mandoc_xr.c @@ -0,0 +1,121 @@ +/* $Id: mandoc_xr.c,v 1.3 2017/07/02 21:18:29 schwarze Exp $ */ +/* + * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "mandoc_aux.h" +#include "mandoc_ohash.h" +#include "mandoc_xr.h" + +static struct ohash *xr_hash = NULL; +static struct mandoc_xr *xr_first = NULL; +static struct mandoc_xr *xr_last = NULL; + +static void mandoc_xr_clear(void); + + +static void +mandoc_xr_clear(void) +{ + struct mandoc_xr *xr; + unsigned int slot; + + if (xr_hash == NULL) + return; + for (xr = ohash_first(xr_hash, &slot); xr != NULL; + xr = ohash_next(xr_hash, &slot)) + free(xr); + ohash_delete(xr_hash); +} + +void +mandoc_xr_reset(void) +{ + if (xr_hash == NULL) + xr_hash = mandoc_malloc(sizeof(*xr_hash)); + else + mandoc_xr_clear(); + mandoc_ohash_init(xr_hash, 5, + offsetof(struct mandoc_xr, hashkey)); + xr_first = xr_last = NULL; +} + +int +mandoc_xr_add(const char *sec, const char *name, int line, int pos) +{ + struct mandoc_xr *xr, *oxr; + const char *pend; + size_t ssz, nsz, tsz; + unsigned int slot; + int ret; + uint32_t hv; + + if (xr_hash == NULL) + return 0; + + ssz = strlen(sec) + 1; + nsz = strlen(name) + 1; + tsz = ssz + nsz; + xr = mandoc_malloc(sizeof(*xr) + tsz); + xr->next = NULL; + xr->sec = xr->hashkey; + xr->name = xr->hashkey + ssz; + xr->line = line; + xr->pos = pos; + xr->count = 1; + memcpy(xr->sec, sec, ssz); + memcpy(xr->name, name, nsz); + + pend = xr->hashkey + tsz; + hv = ohash_interval(xr->hashkey, &pend); + slot = ohash_lookup_memory(xr_hash, xr->hashkey, tsz, hv); + if ((oxr = ohash_find(xr_hash, slot)) == NULL) { + ohash_insert(xr_hash, slot, xr); + if (xr_first == NULL) + xr_first = xr; + else + xr_last->next = xr; + xr_last = xr; + return 0; + } + + oxr->count++; + ret = (oxr->line == -1) ^ (xr->line == -1); + if (xr->line == -1) + oxr->line = -1; + free(xr); + return ret; +} + +struct mandoc_xr * +mandoc_xr_get(void) +{ + return xr_first; +} + +void +mandoc_xr_free(void) +{ + mandoc_xr_clear(); + free(xr_hash); + xr_hash = NULL; +} diff --git a/contrib/mdocml/mandoc_xr.h b/contrib/mdocml/mandoc_xr.h new file mode 100644 index 0000000..e0c6af0 --- /dev/null +++ b/contrib/mdocml/mandoc_xr.h @@ -0,0 +1,31 @@ +/* $Id: mandoc_xr.h,v 1.3 2017/07/02 21:18:29 schwarze Exp $ */ +/* + * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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. + */ + +struct mandoc_xr { + struct mandoc_xr *next; + char *sec; + char *name; + int line; /* Or -1 for this page's own names. */ + int pos; + int count; + char hashkey[]; +}; + +void mandoc_xr_reset(void); +int mandoc_xr_add(const char *, const char *, int, int); +struct mandoc_xr *mandoc_xr_get(void); +void mandoc_xr_free(void); diff --git a/contrib/mdocml/mandocd.8 b/contrib/mdocml/mandocd.8 new file mode 100644 index 0000000..2ea18cf --- /dev/null +++ b/contrib/mdocml/mandocd.8 @@ -0,0 +1,198 @@ +.\" $Id: mandocd.8,v 1.2 2017/03/18 19:56:01 schwarze Exp $ +.\" +.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 18 2017 $ +.Dt MANDOCD 8 +.Os +.Sh NAME +.Nm mandocd +.Nd server process to format manual pages in batch mode +.Sh SYNOPSIS +.Nm mandocd +.Op Fl I Cm os Ns = Ns Ar name +.Op Fl T Ar output +.Ar socket_fd +.Sh DESCRIPTION +The +.Nm +utility formats many manual pages without requiring +.Xr fork 2 +and +.Xr exec 3 +overhead in between. +It does not require listing all the manuals to be formatted on the +command line, and it supports writing each formatted manual to its +own file descriptor. +.Pp +This server requires that a connected UNIX domain +.Xr socket 2 +is already present at +.Xr exec 3 +time. +Consequently, it cannot be started from the +.Xr sh 1 +command line because the shell cannot supply such a socket. +Typically, the socket is created by the parent process using +.Xr socketpair 2 +before calling +.Xr fork 2 +and +.Xr exec 3 +on +.Nm . +The parent process will pass the file descriptor number as an argument to +.Xr exec 3 , +formatted as a decimal ASCII-encoded integer. +See +.Xr catman 8 +for a typical implementation of a parent process. +.Pp +.Nm +loops reading one-byte messages with +.Xr recvmsg 2 +from the file descriptor number +.Ar socket_fd . +It ignores the byte read and only uses the out-of-band auxiliary +.Vt struct cmsghdr +control data, typically supplied by the calling process using +.Xr CMSG_FIRSTHDR 3 . +The parent process is expected to pass three file descriptors +with each dummy byte. +The first one is used for +.Xr mdoc 7 +or +.Xr man 7 +input, the second one for formatted output, and the third one +for error output. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl I Cm os Ns = Ns Ar name +Override the default operating system +.Ar name +for the +.Xr mdoc 7 +.Ic \&Os +and for the +.Xr man 7 +.Ic TH +macro. +.It Fl T Ar output +Output format. +The +.Ar output +argument can be +.Cm ascii , +.Cm utf8 , +or +.Cm html ; +see +.Xr mandoc 1 . +In +.Cm html +output mode, the +.Cm fragment +output option is implied. +Other output options are not supported. +.El +.Pp +After exhausting one input file descriptor, all three file descriptors +are closed before reading the next dummy byte and control message. +.Pp +When a zero-byte message is read, when the +.Ar socket_fd +is closed by the parent process, +or when an error occurs, +.Nm +exits. +.Sh EXIT STATUS +.Ex -std +.Pp +A zero-byte message or a closed +.Ar socket_fd +is considered success. +Possible errors include: +.Bl -bullet +.It +missing, invalid, or excessive +.Xr exec 3 +arguments +.It +.Xr recvmsg 2 +failure, for example due to +.Er EMSGSIZE +.It +missing or unexpected control data, in particular a +.Fa cmsg_level +in the +.Vt struct cmsghdr +that differs from +.Dv SOL_SOCKET , +a +.Fa cmsg_type +that differs from +.Dv SCM_RIGHTS , +or a +.Fa cmsg_len +that is not three times the size of an +.Vt int +.It +invalid file descriptors passed in the +.Xr CMSG_DATA 3 +.It +resource exhaustion, in particular +.Xr dup 2 +or +.Xr malloc 3 +failure +.El +.Pp +Except for memory exhaustion and similar system-level failures, +parsing and formatting errors do not cause +.Nm +to return an error exit status. +Even after severe parsing errors, +.Nm +will simply accept and process the next input file descriptor. +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr mandoc 3 , +.Xr catman 8 +.Sh HISTORY +The +.Nm +utility appeared in version 1.14.1 or the +.Sy mandoc +toolkit. +.Sh AUTHORS +.An -nosplit +The concept was designed and implemented by +.An Michael Stapelberg Aq Mt stapelberg@debian.org . +The +.Xr mandoc 3 +glue needed to make it a stand-alone process was added by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +.Sh CAVEATS +If the parsed manual pages contain +.Xr roff 7 +.Pf . Ic so +requests, +.Nm +needs to be started with the current working directory set to the +root of the manual page tree. +Avoid starting it in directories that contain secret files in any +subdirectories, in particular in the user starting it has read +access to these secret files. diff --git a/contrib/mdocml/mandocd.c b/contrib/mdocml/mandocd.c new file mode 100644 index 0000000..9aba9cc --- /dev/null +++ b/contrib/mdocml/mandocd.c @@ -0,0 +1,285 @@ +/* $Id: mandocd.c,v 1.6 2017/06/24 14:38:32 schwarze Exp $ */ +/* + * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> + * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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" + +#if HAVE_CMSG_XPG42 +#define _XPG4_2 +#endif + +#include <sys/types.h> +#include <sys/socket.h> + +#if HAVE_ERR +#include <err.h> +#endif +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mandoc.h" +#include "roff.h" +#include "mdoc.h" +#include "man.h" +#include "main.h" +#include "manconf.h" + +enum outt { + OUTT_ASCII = 0, + OUTT_UTF8, + OUTT_HTML +}; + +static void process(struct mparse *, enum outt, void *); +static int read_fds(int, int *); +static void usage(void) __attribute__((__noreturn__)); + + +#define NUM_FDS 3 +static int +read_fds(int clientfd, int *fds) +{ + struct msghdr msg; + struct iovec iov[1]; + unsigned char dummy[1]; + struct cmsghdr *cmsg; + int *walk; + int cnt; + + /* Union used for alignment. */ + union { + uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))]; + struct cmsghdr align; + } u; + + memset(&msg, '\0', sizeof(msg)); + msg.msg_control = u.controlbuf; + msg.msg_controllen = sizeof(u.controlbuf); + + /* + * Read a dummy byte - sendmsg cannot send an empty message, + * even if we are only interested in the OOB data. + */ + + iov[0].iov_base = dummy; + iov[0].iov_len = sizeof(dummy); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + switch (recvmsg(clientfd, &msg, 0)) { + case -1: + warn("recvmsg"); + return -1; + case 0: + return 0; + default: + break; + } + + if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) { + warnx("CMSG_FIRSTHDR: missing control message"); + return -1; + } + + if (cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS || + cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) { + warnx("CMSG_FIRSTHDR: invalid control message"); + return -1; + } + + walk = (int *)CMSG_DATA(cmsg); + for (cnt = 0; cnt < NUM_FDS; cnt++) + fds[cnt] = *walk++; + + return 1; +} + +int +main(int argc, char *argv[]) +{ + struct manoutput options; + struct mparse *parser; + void *formatter; + const char *defos; + const char *errstr; + int clientfd; + int old_stdin; + int old_stdout; + int old_stderr; + int fds[3]; + int state, opt; + enum outt outtype; + + defos = NULL; + outtype = OUTT_ASCII; + while ((opt = getopt(argc, argv, "I:T:")) != -1) { + switch (opt) { + case 'I': + if (strncmp(optarg, "os=", 3) == 0) + defos = optarg + 3; + else { + warnx("-I %s: Bad argument", optarg); + usage(); + } + break; + case 'T': + if (strcmp(optarg, "ascii") == 0) + outtype = OUTT_ASCII; + else if (strcmp(optarg, "utf8") == 0) + outtype = OUTT_UTF8; + else if (strcmp(optarg, "html") == 0) + outtype = OUTT_HTML; + else { + warnx("-T %s: Bad argument", optarg); + usage(); + } + break; + default: + usage(); + } + } + + if (argc > 0) { + argc -= optind; + argv += optind; + } + if (argc != 1) + usage(); + + errstr = NULL; + clientfd = strtonum(argv[0], 3, INT_MAX, &errstr); + if (errstr) + 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); + + memset(&options, 0, sizeof(options)); + switch (outtype) { + case OUTT_ASCII: + formatter = ascii_alloc(&options); + break; + case OUTT_UTF8: + formatter = utf8_alloc(&options); + break; + case OUTT_HTML: + options.fragment = 1; + formatter = html_alloc(&options); + break; + } + + state = 1; /* work to do */ + fflush(stdout); + fflush(stderr); + if ((old_stdin = dup(STDIN_FILENO)) == -1 || + (old_stdout = dup(STDOUT_FILENO)) == -1 || + (old_stderr = dup(STDERR_FILENO)) == -1) { + warn("dup"); + state = -1; /* error */ + } + + while (state == 1 && (state = read_fds(clientfd, fds)) == 1) { + if (dup2(fds[0], STDIN_FILENO) == -1 || + dup2(fds[1], STDOUT_FILENO) == -1 || + dup2(fds[2], STDERR_FILENO) == -1) { + warn("dup2"); + state = -1; + break; + } + + close(fds[0]); + close(fds[1]); + close(fds[2]); + + process(parser, outtype, formatter); + mparse_reset(parser); + + fflush(stdout); + fflush(stderr); + /* Close file descriptors by restoring the old ones. */ + if (dup2(old_stderr, STDERR_FILENO) == -1 || + dup2(old_stdout, STDOUT_FILENO) == -1 || + dup2(old_stdin, STDIN_FILENO) == -1) { + warn("dup2"); + state = -1; + break; + } + } + + close(clientfd); + switch (outtype) { + case OUTT_ASCII: + case OUTT_UTF8: + ascii_free(formatter); + break; + case OUTT_HTML: + html_free(formatter); + break; + } + mparse_free(parser); + mchars_free(); + return state == -1 ? 1 : 0; +} + +static void +process(struct mparse *parser, enum outt outtype, void *formatter) +{ + struct roff_man *man; + + mparse_readfd(parser, STDIN_FILENO, "<unixfd>"); + mparse_result(parser, &man, NULL); + + if (man == NULL) + return; + + if (man->macroset == MACROSET_MDOC) { + mdoc_validate(man); + switch (outtype) { + case OUTT_ASCII: + case OUTT_UTF8: + terminal_mdoc(formatter, man); + break; + case OUTT_HTML: + html_mdoc(formatter, man); + break; + } + } + if (man->macroset == MACROSET_MAN) { + man_validate(man); + switch (outtype) { + case OUTT_ASCII: + case OUTT_UTF8: + terminal_man(formatter, man); + break; + case OUTT_HTML: + html_man(formatter, man); + break; + } + } +} + +void +usage(void) +{ + fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n"); + exit(1); +} diff --git a/contrib/mdocml/mandocdb.c b/contrib/mdocml/mandocdb.c index 133e736..26117cf 100644 --- a/contrib/mdocml/mandocdb.c +++ b/contrib/mdocml/mandocdb.c @@ -1,4 +1,4 @@ -/* $Id: mandocdb.c,v 1.237 2017/01/11 17:39:53 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.253 2017/07/28 14:48:25 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -162,7 +162,7 @@ static void putmdockey(const struct mpage *, const struct roff_node *, uint64_t, int); static int render_string(char **, size_t *); static void say(const char *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); + __attribute__((__format__ (__printf__, 2, 3))); static int set_basedir(const char *, int); static int treescan(void); static size_t utf8(unsigned int, char [7]); @@ -183,8 +183,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] = { - { NULL, 0, 0 }, /* Ap */ +static const struct mdoc_handler __mdocs[MDOC_MAX - MDOC_Dd] = { { NULL, 0, NODE_NOPRT }, /* Dd */ { NULL, 0, NODE_NOPRT }, /* Dt */ { NULL, 0, NODE_NOPRT }, /* Os */ @@ -200,6 +199,7 @@ static const struct mdoc_handler mdocs[MDOC_MAX] = { { NULL, 0, 0 }, /* It */ { NULL, 0, 0 }, /* Ad */ { NULL, TYPE_An, 0 }, /* An */ + { NULL, 0, 0 }, /* Ap */ { NULL, TYPE_Ar, 0 }, /* Ar */ { NULL, TYPE_Cd, 0 }, /* Cd */ { NULL, TYPE_Cm, 0 }, /* Cm */ @@ -302,12 +302,10 @@ static const struct mdoc_handler mdocs[MDOC_MAX] = { { NULL, 0, 0 }, /* En */ { NULL, TYPE_Dx, NODE_NOSRC }, /* Dx */ { NULL, 0, 0 }, /* %Q */ - { NULL, 0, 0 }, /* br */ - { NULL, 0, 0 }, /* sp */ { NULL, 0, 0 }, /* %U */ { NULL, 0, 0 }, /* Ta */ - { NULL, 0, 0 }, /* ll */ }; +static const struct mdoc_handler *const mdocs = __mdocs - MDOC_Dd; int @@ -422,7 +420,8 @@ mandocdb(int argc, char *argv[]) exitcode = (int)MANDOCLEVEL_OK; mchars_alloc(); - mp = mparse_alloc(mparse_options, MANDOCLEVEL_BADARG, NULL, NULL); + mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL, + MANDOC_OS_OTHER, NULL); mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); @@ -589,7 +588,7 @@ treescan(void) const char *argv[2]; argv[0] = "."; - argv[1] = (char *)NULL; + argv[1] = NULL; f = fts_open((char * const *)argv, FTS_PHYSICAL | FTS_NOCHDIR, fts_compare); @@ -872,6 +871,20 @@ filescan(const char *file) } /* + * In test mode or when the original name is absolute + * but outside our tree, guess the base directory. + */ + + if (op == OP_TEST || (start == buf && *start == '/')) { + if (strncmp(buf, "man/", 4) == 0) + start = buf + 4; + else if ((start = strstr(buf, "/man/")) != NULL) + start += 5; + else + start = buf; + } + + /* * First try to guess our directory structure. * If we find a separator, try to look for man* or cat*. * If we find one of these and what's underneath is a directory, @@ -1139,6 +1152,7 @@ mpages_merge(struct dba *dba, struct mparse *mp) if (mlink->dform != FORM_CAT || mlink->fform != FORM_CAT) { mparse_readfd(mp, fd, mlink->file); close(fd); + fd = -1; mparse_result(mp, &man, &sodest); } @@ -1195,34 +1209,43 @@ mpages_merge(struct dba *dba, struct mparse *mp) mpage->title = mandoc_strdup(man->meta.title); } else if (man != NULL && man->macroset == MACROSET_MAN) { man_validate(man); - mpage->form = FORM_SRC; - mpage->sec = mandoc_strdup(man->meta.msec); - mpage->arch = mandoc_strdup(mlink->arch); - mpage->title = mandoc_strdup(man->meta.title); - } else { + if (*man->meta.msec != '\0' || + *man->meta.title != '\0') { + mpage->form = FORM_SRC; + mpage->sec = mandoc_strdup(man->meta.msec); + mpage->arch = mandoc_strdup(mlink->arch); + mpage->title = mandoc_strdup(man->meta.title); + } else + man = NULL; + } + + assert(mpage->desc == NULL); + if (man == NULL) { mpage->form = FORM_CAT; mpage->sec = mandoc_strdup(mlink->dsec); mpage->arch = mandoc_strdup(mlink->arch); mpage->title = mandoc_strdup(mlink->name); - } - - assert(mpage->desc == NULL); - if (man != NULL && man->macroset == MACROSET_MDOC) + parse_cat(mpage, fd); + } else if (man->macroset == MACROSET_MDOC) parse_mdoc(mpage, &man->meta, man->first); - else if (man != NULL) - parse_man(mpage, &man->meta, man->first); else - parse_cat(mpage, fd); - if (mpage->desc == NULL) - mpage->desc = mandoc_strdup(mpage->mlinks->name); + parse_man(mpage, &man->meta, man->first); + if (mpage->desc == NULL) { + mpage->desc = mandoc_strdup(mlink->name); + if (warnings) + say(mlink->file, "No one-line description, " + "using filename \"%s\"", mlink->name); + } - if (warnings && !use_all) - for (mlink = mpage->mlinks; mlink; - mlink = mlink->next) + for (mlink = mpage->mlinks; + mlink != NULL; + mlink = mlink->next) { + putkey(mpage, mlink->name, NAME_FILE); + if (warnings && !use_all) mlink_check(mpage, mlink); + } dbadd(dba, mpage); - mlink = mpage->mlinks; nextpage: ohash_delete(&strings); @@ -1234,29 +1257,48 @@ static void parse_cat(struct mpage *mpage, int fd) { FILE *stream; - char *line, *p, *title; + struct mlink *mlink; + char *line, *p, *title, *sec; size_t linesz, plen, titlesz; ssize_t len; int offs; - stream = (-1 == fd) ? - fopen(mpage->mlinks->file, "r") : - fdopen(fd, "r"); - if (NULL == stream) { - if (-1 != fd) + mlink = mpage->mlinks; + stream = fd == -1 ? fopen(mlink->file, "r") : fdopen(fd, "r"); + if (stream == NULL) { + if (fd != -1) close(fd); if (warnings) - say(mpage->mlinks->file, "&fopen"); + say(mlink->file, "&fopen"); return; } line = NULL; linesz = 0; - /* Skip to first blank line. */ + /* Parse the section number from the header line. */ - while (getline(&line, &linesz, stream) != -1) + while (getline(&line, &linesz, stream) != -1) { if (*line == '\n') + continue; + if ((sec = strchr(line, '(')) == NULL) + break; + if ((p = strchr(++sec, ')')) == NULL) + break; + free(mpage->sec); + mpage->sec = mandoc_strndup(sec, p - sec); + if (warnings && *mlink->dsec != '\0' && + strcasecmp(mpage->sec, mlink->dsec)) + say(mlink->file, + "Section \"%s\" manual in %s directory", + mpage->sec, mlink->dsec); + break; + } + + /* Skip to first blank line. */ + + while (line == NULL || *line != '\n') + if (getline(&line, &linesz, stream) == -1) break; /* @@ -1302,8 +1344,7 @@ parse_cat(struct mpage *mpage, int fd) if (NULL == title || '\0' == *title) { if (warnings) - say(mpage->mlinks->file, - "Cannot find NAME section"); + say(mlink->file, "Cannot find NAME section"); fclose(stream); free(title); return; @@ -1322,8 +1363,8 @@ parse_cat(struct mpage *mpage, int fd) /* Skip to next word. */ ; } else { if (warnings) - say(mpage->mlinks->file, - "No dash in title line"); + say(mlink->file, "No dash in title line, " + "reusing \"%s\" as one-line description", title); p = title; } @@ -1503,25 +1544,26 @@ parse_mdoc(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { - assert(NULL != n); - for (n = n->child; NULL != n; n = n->next) { - if (n->flags & mdocs[n->tok].taboo) + for (n = n->child; n != NULL; n = n->next) { + if (n->tok == TOKEN_NONE || + n->tok < ROFF_MAX || + n->flags & mdocs[n->tok].taboo) continue; + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); switch (n->type) { case ROFFT_ELEM: case ROFFT_BLOCK: case ROFFT_HEAD: case ROFFT_BODY: case ROFFT_TAIL: - if (NULL != mdocs[n->tok].fp) - if (0 == (*mdocs[n->tok].fp)(mpage, meta, n)) - break; + if (mdocs[n->tok].fp != NULL && + (*mdocs[n->tok].fp)(mpage, meta, n) == 0) + break; if (mdocs[n->tok].mask) putmdockey(mpage, n->child, mdocs[n->tok].mask, mdocs[n->tok].taboo); break; default: - assert(n->type != ROFFT_ROOT); continue; } if (NULL != n->child) @@ -2081,6 +2123,23 @@ dbwrite(struct dba *dba) int status; pid_t child; + /* + * Do not write empty databases, and delete existing ones + * when makewhatis -u causes them to become empty. + */ + + dba_array_start(dba->pages); + if (dba_array_next(dba->pages) == NULL) { + if (unlink(MANDOC_DB) == -1 && errno != ENOENT) + say(MANDOC_DB, "&unlink"); + return; + } + + /* + * Build the database in a temporary file, + * then atomically move it into place. + */ + if (dba_write(MANDOC_DB "~", dba) != -1) { if (rename(MANDOC_DB "~", MANDOC_DB) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; @@ -2090,6 +2149,11 @@ dbwrite(struct dba *dba) return; } + /* + * We lack write permission and cannot replace the database + * file, but let's at least check whether the data changed. + */ + (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn)); if (mkdtemp(tfn) == NULL) { exitcode = (int)MANDOCLEVEL_SYSERR; diff --git a/contrib/mdocml/manpage.c b/contrib/mdocml/manpage.c deleted file mode 100644 index af7d3db..0000000 --- a/contrib/mdocml/manpage.c +++ /dev/null @@ -1,195 +0,0 @@ -/* $Id: manpage.c,v 1.14 2016/07/09 15:24:19 schwarze Exp $ */ -/* - * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include "config.h" - -#include <sys/types.h> - -#include <assert.h> -#include <limits.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "manconf.h" -#include "mansearch.h" - -static void show(const char *, const char *); - -int -main(int argc, char *argv[]) -{ - int ch, term; - size_t i, sz, linesz; - ssize_t len; - struct mansearch search; - struct manpage *res; - char *conf_file, *defpaths, *auxpaths, *line; - char buf[PATH_MAX]; - const char *cmd; - struct manconf conf; - char *progname; - extern char *optarg; - extern int optind; - - term = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO); - - progname = strrchr(argv[0], '/'); - if (progname == NULL) - progname = argv[0]; - else - ++progname; - - auxpaths = defpaths = conf_file = NULL; - memset(&conf, 0, sizeof(conf)); - memset(&search, 0, sizeof(struct mansearch)); - - while (-1 != (ch = getopt(argc, argv, "C:M:m:S:s:"))) - switch (ch) { - case ('C'): - conf_file = optarg; - break; - case ('M'): - defpaths = optarg; - break; - case ('m'): - auxpaths = optarg; - break; - case ('S'): - search.arch = optarg; - break; - case ('s'): - search.sec = optarg; - break; - default: - goto usage; - } - - argc -= optind; - argv += optind; - - if (0 == argc) - goto usage; - - search.outkey = "Nd"; - search.argmode = ARG_EXPR; - - manconf_parse(&conf, conf_file, defpaths, auxpaths); - ch = mansearch(&search, &conf.manpath, argc, argv, &res, &sz); - manconf_free(&conf); - - if (0 == ch) - goto usage; - - if (0 == sz) { - free(res); - return EXIT_FAILURE; - } else if (1 == sz && term) { - i = 1; - goto show; - } else if (NULL == res) - return EXIT_FAILURE; - - for (i = 0; i < sz; i++) { - printf("%6zu %s: %s\n", - i + 1, res[i].names, res[i].output); - free(res[i].names); - free(res[i].output); - } - - if (0 == term) { - for (i = 0; i < sz; i++) - free(res[i].file); - free(res); - return EXIT_SUCCESS; - } - - i = 1; - printf("Enter a choice [1]: "); - fflush(stdout); - - line = NULL; - linesz = 0; - if ((len = getline(&line, &linesz, stdin)) != -1) { - if ('\n' == line[--len] && len > 0) { - line[len] = '\0'; - if ((i = atoi(line)) < 1 || i > sz) - i = 0; - } - } - free(line); - - if (0 == i) { - for (i = 0; i < sz; i++) - free(res[i].file); - free(res); - return EXIT_SUCCESS; - } -show: - cmd = res[i - 1].form ? "mandoc" : "cat"; - strlcpy(buf, res[i - 1].file, PATH_MAX); - for (i = 0; i < sz; i++) - free(res[i].file); - free(res); - - show(cmd, buf); - /* NOTREACHED */ -usage: - fprintf(stderr, "usage: %s [-C conf] " - "[-M paths] " - "[-m paths] " - "[-S arch] " - "[-s section] " - "expr ...\n", - progname); - return EXIT_FAILURE; -} - -static void -show(const char *cmd, const char *file) -{ - int fds[2]; - pid_t pid; - - if (-1 == pipe(fds)) { - perror(NULL); - exit(EXIT_FAILURE); - } - - if (-1 == (pid = fork())) { - perror(NULL); - exit(EXIT_FAILURE); - } else if (pid > 0) { - dup2(fds[0], STDIN_FILENO); - close(fds[1]); - cmd = NULL != getenv("MANPAGER") ? - getenv("MANPAGER") : - (NULL != getenv("PAGER") ? - getenv("PAGER") : "more"); - execlp(cmd, cmd, (char *)NULL); - perror(cmd); - exit(EXIT_FAILURE); - } - - dup2(fds[1], STDOUT_FILENO); - close(fds[0]); - execlp(cmd, cmd, file, (char *)NULL); - perror(cmd); - exit(EXIT_FAILURE); -} diff --git a/contrib/mdocml/manpath.c b/contrib/mdocml/manpath.c index 008c593..54f7a6b 100644 --- a/contrib/mdocml/manpath.c +++ b/contrib/mdocml/manpath.c @@ -1,6 +1,6 @@ -/* $Id: manpath.c,v 1.31 2016/07/19 22:40:33 schwarze Exp $ */ +/* $Id: manpath.c,v 1.35 2017/07/01 09:47:30 schwarze Exp $ */ /* - * Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -91,6 +91,13 @@ manconf_parse(struct manconf *conf, const char *file, manpath_parseline(&conf->manpath, defp, 0); } +void +manpath_base(struct manpaths *dirs) +{ + char path_base[] = MANPATH_BASE; + manpath_parseline(dirs, path_base, 0); +} + /* * Parse a FULL pathname from a colon-separated list of arrays. */ @@ -207,7 +214,7 @@ manconf_file(struct manconf *conf, const char *file) *manpath_default = '\0'; break; case 1: /* output */ - manconf_output(&conf->output, cp); + manconf_output(&conf->output, cp, 1); break; default: break; @@ -221,15 +228,17 @@ out: manpath_parseline(&conf->manpath, manpath_default, 0); } -void -manconf_output(struct manoutput *conf, const char *cp) +int +manconf_output(struct manoutput *conf, const char *cp, int fromfile) { const char *const toks[] = { "includes", "man", "paper", "style", - "indent", "width", "fragment", "mdoc" + "indent", "width", "fragment", "mdoc", "noval" }; - size_t len, tok; + const char *errstr; + char *oldval; + size_t len, tok; for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { len = strlen(toks[tok]); @@ -244,41 +253,81 @@ manconf_output(struct manoutput *conf, const char *cp) } } - if (tok < 6 && *cp == '\0') - return; + if (tok < 6 && *cp == '\0') { + warnx("-O %s=?: Missing argument value", toks[tok]); + return -1; + } + if ((tok == 6 || tok == 7) && *cp != '\0') { + warnx("-O %s: Does not take a value: %s", toks[tok], cp); + return -1; + } switch (tok) { case 0: - if (conf->includes == NULL) - conf->includes = mandoc_strdup(cp); - break; + if (conf->includes != NULL) { + oldval = mandoc_strdup(conf->includes); + break; + } + conf->includes = mandoc_strdup(cp); + return 0; case 1: - if (conf->man == NULL) - conf->man = mandoc_strdup(cp); - break; + if (conf->man != NULL) { + oldval = mandoc_strdup(conf->man); + break; + } + conf->man = mandoc_strdup(cp); + return 0; case 2: - if (conf->paper == NULL) - conf->paper = mandoc_strdup(cp); - break; + if (conf->paper != NULL) { + oldval = mandoc_strdup(conf->paper); + break; + } + conf->paper = mandoc_strdup(cp); + return 0; case 3: - if (conf->style == NULL) - conf->style = mandoc_strdup(cp); - break; + if (conf->style != NULL) { + oldval = mandoc_strdup(conf->style); + break; + } + conf->style = mandoc_strdup(cp); + return 0; case 4: - if (conf->indent == 0) - conf->indent = strtonum(cp, 0, 1000, NULL); - break; + if (conf->indent) { + mandoc_asprintf(&oldval, "%zu", conf->indent); + break; + } + conf->indent = strtonum(cp, 0, 1000, &errstr); + if (errstr == NULL) + return 0; + warnx("-O indent=%s is %s", cp, errstr); + return -1; case 5: - if (conf->width == 0) - conf->width = strtonum(cp, 58, 1000, NULL); - break; + if (conf->width) { + mandoc_asprintf(&oldval, "%zu", conf->width); + break; + } + conf->width = strtonum(cp, 1, 1000, &errstr); + if (errstr == NULL) + return 0; + warnx("-O width=%s is %s", cp, errstr); + return -1; case 6: conf->fragment = 1; - break; + return 0; case 7: conf->mdoc = 1; - break; + return 0; + case 8: + conf->noval = 1; + return 0; default: - break; + if (fromfile) + warnx("-O %s: Bad argument", cp); + return -1; } + if (fromfile == 0) + warnx("-O %s=%s: Option already set to %s", + toks[tok], cp, oldval); + free(oldval); + return -1; } diff --git a/contrib/mdocml/mansearch.3 b/contrib/mdocml/mansearch.3 index 051f9aa..b94815a 100644 --- a/contrib/mdocml/mansearch.3 +++ b/contrib/mdocml/mansearch.3 @@ -1,4 +1,4 @@ -.\" $Id: mansearch.3,v 1.4 2015/03/27 17:37:25 schwarze Exp $ +.\" $Id: mansearch.3,v 1.5 2017/03/30 22:22:05 schwarze Exp $ .\" .\" Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> .\" @@ -14,28 +14,22 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 27 2015 $ +.Dd $Mdocdate: March 30 2017 $ .Dt MANSEARCH 3 .Os .Sh NAME -.Nm mansearch , -.Nm mansearch_setup +.Nm mansearch .Nd search manual page databases .Sh SYNOPSIS .In stdint.h .In manconf.h .In mansearch.h .Ft int -.Fo mansearch_setup -.Fa "int start" -.Fc -.Ft int .Fo mansearch .Fa "const struct mansearch *search" .Fa "const struct manpaths *paths" .Fa "int argc" .Fa "char *argv[]" -.Fa "const char *outkey" .Fa "struct manpage **res" .Fa "size_t *sz" .Fc @@ -44,7 +38,7 @@ The .Fn mansearch function returns information about manuals matching a search query from a .Xr mandoc.db 5 -SQLite3 database. +database. .Pp The query arguments are as follows: .Bl -tag -width Ds @@ -58,18 +52,6 @@ Directories to be searched, defined in Search criteria, usually taken from the command line. .El .Pp -The -.Fa "const char *outkey" -selects which data to return in the -.Va output -field of the -.Fa res -structures. -It takes any of the macro keys defined in -.Pa mansearch_const.c -and described in -.Xr apropos 1 . -.Pp The output arguments are as follows: .Bl -tag -width Ds .It Fa "struct manpage **res" @@ -89,19 +71,6 @@ array itself. Returns the number of result structures contained in .Fa res . .El -.Pp -To speed up searches, the -.Fn mansearch_setup -function can optionally be called with a -.Fa start -argument of 1 before -.Fn mansearch -to set up an SQLite3 pagecache. -If it was called, it has to be called again with a -.Fa start -argument of 0 after the last call to -.Fn mansearch -to release the memory used for the pagecache. .Sh IMPLEMENTATION NOTES For each manual page tree, the search is done in two steps. In the first step, a list of pages matching the search criteria is built. @@ -112,103 +81,26 @@ array. .Pp All function mentioned here are defined in the file .Pa mansearch.c . -No functions except -.Fn mansearch -and -.Fn sql_statement -build any SQL code, and no functions except -.Fn mansearch , -.Fn buildnames , -and -.Fn buildoutput -execute it. .Ss Finding matches -The query is built using the following grammar: -.Bd -literal -offset indent -<query> ::= "SELECT * FROM mpages WHERE" <condition> -<condition> ::= "(" <condition> ")" | - <condition> "OR" <condition> | - <condition> "AND" <condition> | - "desc" <operator> "?" | - "id IN (SELECT pageid FROM" <subquery> ")" -<subquery> ::= "names WHERE name" <operator> "?" | - "keys WHERE key" <operator> "? AND bits & ?" -<operator> ::= "MATCH" | "REGEXP" -.Ed -.Pp -The MATCH and REGEXP operators are implemented by the functions -.Fn sql_match -and -.Fn sql_regexp , -respectively. -This is required because SQLite3 natively neither supports -case-insensitive substring matching nor regular expression matching, -but only string identity, shell globbing, and the weird home-brewed -LIKE operator. -.Pp Command line parsing is done by the function .Fn exprcomp building a singly linked list of .Vt expr structures, using the helper functions -.Fn exprterm +.Fn expr_and and -.Fn exprspec . -The resulting SQL statement is assembled by the function -.Fn sql_statement -and evaluated in the main loop of the -.Fn mansearch -function. +.Fn exprterm . .Ss Assembling the results The names, sections, and architectures of the manuals found are assembled into the .Va names field of the result structure by the function -.Fn buildnames , -using the following query: -.Pp -.Dl "SELECT * FROM mlinks WHERE pageid=? ORDER BY sec, arch, name" -.Pp -If the -.Fa outkey -differs from -.Qq Ic \&Nd , -the requested output data is assembled into the -.Va output -field of the result structure by the function -.Fn buildoutput , -using the following query: -.Pp -.Dl "SELECT * FROM keys WHERE pageid=? AND bits & ?" +.Fn buildnames . .Sh FILES .Bl -tag -width mandoc.db -compact .It Pa mandoc.db The manual page database. .El -.Sh EXAMPLES -The simplest invocation -.Pp -.Dl apropos keyword -.Pp -results in the following SQL query: -.Bd -literal -SELECT * FROM mpages WHERE ( - id IN (SELECT pageid FROM names WHERE name MATCH 'keyword') OR - desc MATCH 'keyword' -); -.Ed -.Pp -A more complicated request like -.Pp -.Dl apropos -s 2 Nm,Xr=getuid -.Pp -results in: -.Bd -literal -SELECT * FROM mpages WHERE ( - id IN (SELECT pageid FROM names WHERE name MATCH 'getuid') OR - id IN (SELECT pageid FROM keys WHERE key MATCH 'getuid' AND bits & 4) -) AND id IN (SELECT pageid FROM keys WHERE key REGEXP '^2$' AND bits & 2); -.Ed .Sh SEE ALSO .Xr apropos 1 , .Xr mandoc.db 5 , @@ -223,6 +115,6 @@ subsystem first appeared in A module to search manual page databases was first written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv in 2011, at first using the Berkeley DB; -he rewrote it for SQLite3 in 2012. -The current version received major changes from -.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +he rewrote it for SQLite3 in 2012, and +.An Ingo Schwarze Aq Mt schwarze@openbsd.org +removed the dependency on SQLite3 in 2016. diff --git a/contrib/mdocml/mansearch.c b/contrib/mdocml/mansearch.c index 6e689bd..c889aec 100644 --- a/contrib/mdocml/mansearch.c +++ b/contrib/mdocml/mansearch.c @@ -1,7 +1,7 @@ /* $OpenBSD: mansearch.c,v 1.50 2016/07/09 15:23:36 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -67,9 +67,9 @@ static struct ohash *manmerge_term(struct expr *, struct ohash *); static struct ohash *manmerge_or(struct expr *, struct ohash *); static struct ohash *manmerge_and(struct expr *, struct ohash *); static char *buildnames(const struct dbm_page *); -static char *buildoutput(size_t, int32_t); -static size_t lstlen(const char *); -static void lstcat(char *, size_t *, const char *); +static char *buildoutput(size_t, struct dbm_page *); +static size_t lstlen(const char *, size_t); +static void lstcat(char *, size_t *, const char *, const char *); static int lstmatch(const char *, const char *); static struct expr *exprcomp(const struct mansearch *, int, char *[], int *); @@ -104,7 +104,8 @@ mansearch(const struct mansearch *search, } cur = maxres = 0; - *res = NULL; + if (res != NULL) + *res = NULL; outkey = KEY_Nd; if (search->outkey != NULL) @@ -155,7 +156,8 @@ mansearch(const struct mansearch *search, chdir_status = 1; if (dbm_open(MANDOC_DB) == -1) { - warn("%s/%s", paths->paths[i], MANDOC_DB); + if (errno != ENOENT) + warn("%s/%s", paths->paths[i], MANDOC_DB); continue; } @@ -172,6 +174,10 @@ mansearch(const struct mansearch *search, lstmatch(search->arch, page->arch) == 0) continue; + if (res == NULL) { + cur = 1; + break; + } if (cur + 1 > maxres) { maxres += 1024; *res = mandoc_reallocarray(*res, @@ -181,9 +187,7 @@ mansearch(const struct mansearch *search, mandoc_asprintf(&mpage->file, "%s/%s", paths->paths[i], page->file + 1); mpage->names = buildnames(page); - mpage->output = (int)outkey == KEY_Nd ? - mandoc_strdup(page->desc) : - buildoutput(outkey, page->addr); + mpage->output = buildoutput(outkey, page); mpage->ipath = i; mpage->bits = rp->bits; mpage->sec = *page->sect - '0'; @@ -205,12 +209,13 @@ mansearch(const struct mansearch *search, if (cur && search->firstmatch) break; } - qsort(*res, cur, sizeof(struct manpage), manpage_compare); + if (res != NULL) + qsort(*res, cur, sizeof(struct manpage), manpage_compare); if (chdir_status && getcwd_status && chdir(buf) == -1) warn("%s", buf); exprfree(e); *sz = cur; - return 1; + return res != NULL || cur; } /* @@ -389,13 +394,29 @@ static int manpage_compare(const void *vp1, const void *vp2) { const struct manpage *mp1, *mp2; + const char *cp1, *cp2; + size_t sz1, sz2; int diff; mp1 = vp1; mp2 = vp2; - return (diff = mp2->bits - mp1->bits) ? diff : - (diff = mp1->sec - mp2->sec) ? diff : - strcasecmp(mp1->names, mp2->names); + if ((diff = mp2->bits - mp1->bits) || + (diff = mp1->sec - mp2->sec)) + return diff; + + /* Fall back to alphabetic ordering of names. */ + sz1 = strcspn(mp1->names, "("); + sz2 = strcspn(mp2->names, "("); + if (sz1 < sz2) + sz1 = sz2; + if ((diff = strncasecmp(mp1->names, mp2->names, sz1))) + return diff; + + /* For identical names and sections, prefer arch-dependent. */ + cp1 = strchr(mp1->names + sz1, '/'); + cp2 = strchr(mp2->names + sz2, '/'); + return cp1 != NULL && cp2 != NULL ? strcasecmp(cp1, cp2) : + cp1 != NULL ? -1 : cp2 != NULL ? 1 : 0; } static char * @@ -404,16 +425,16 @@ buildnames(const struct dbm_page *page) char *buf; size_t i, sz; - sz = lstlen(page->name) + 1 + lstlen(page->sect) + - (page->arch == NULL ? 0 : 1 + lstlen(page->arch)) + 2; + sz = lstlen(page->name, 2) + 1 + lstlen(page->sect, 2) + + (page->arch == NULL ? 0 : 1 + lstlen(page->arch, 2)) + 2; buf = mandoc_malloc(sz); i = 0; - lstcat(buf, &i, page->name); + lstcat(buf, &i, page->name, ", "); buf[i++] = '('; - lstcat(buf, &i, page->sect); + lstcat(buf, &i, page->sect, ", "); if (page->arch != NULL) { buf[i++] = '/'; - lstcat(buf, &i, page->arch); + lstcat(buf, &i, page->arch, ", "); } buf[i++] = ')'; buf[i++] = '\0'; @@ -423,11 +444,11 @@ buildnames(const struct dbm_page *page) /* * Count the buffer space needed to print the NUL-terminated - * list of NUL-terminated strings, when printing two separator + * list of NUL-terminated strings, when printing sep separator * characters between strings. */ static size_t -lstlen(const char *cp) +lstlen(const char *cp, size_t sep) { size_t sz; @@ -435,7 +456,7 @@ lstlen(const char *cp) if (cp[0] == '\0') { if (cp[1] == '\0') break; - sz++; + sz += sep - 1; } else if (cp[0] < ' ') sz--; cp++; @@ -445,17 +466,20 @@ lstlen(const char *cp) /* * Print the NUL-terminated list of NUL-terminated strings - * into the buffer, seperating strings with a comma and a blank. + * into the buffer, seperating strings with sep. */ static void -lstcat(char *buf, size_t *i, const char *cp) +lstcat(char *buf, size_t *i, const char *cp, const char *sep) { + const char *s; + for (;;) { if (cp[0] == '\0') { if (cp[1] == '\0') break; - buf[(*i)++] = ','; - buf[(*i)++] = ' '; + s = sep; + while (*s != '\0') + buf[(*i)++] = *s++; } else if (cp[0] >= ' ') buf[(*i)++] = cp[0]; cp++; @@ -482,17 +506,46 @@ lstmatch(const char *want, const char *have) } /* - * Build a list of values taken by the macro im - * in the manual page with big-endian address addr. + * Build a list of values taken by the macro im in the manual page. */ static char * -buildoutput(size_t im, int32_t addr) +buildoutput(size_t im, struct dbm_page *page) { - const char *oldoutput, *sep; + const char *oldoutput, *sep, *input; char *output, *newoutput, *value; + size_t sz, i; + + switch (im) { + case KEY_Nd: + return mandoc_strdup(page->desc); + case KEY_Nm: + input = page->name; + break; + case KEY_sec: + input = page->sect; + break; + case KEY_arch: + input = page->arch; + if (input == NULL) + input = "all\0"; + break; + default: + input = NULL; + break; + } + + if (input != NULL) { + sz = lstlen(input, 3) + 1; + output = mandoc_malloc(sz); + i = 0; + lstcat(output, &i, input, " # "); + output[i++] = '\0'; + assert(i == sz); + return output; + } output = NULL; - dbm_macro_bypage(im - 2, addr); + dbm_macro_bypage(im - 2, page->addr); while ((value = dbm_macro_next()) != NULL) { if (output == NULL) { oldoutput = ""; @@ -642,6 +695,12 @@ exprterm(const struct mansearch *search, int argc, char *argv[], int *argi) return e; } + if (strcmp("-i", argv[*argi]) == 0 && *argi + 1 < argc) { + cs = 0; + ++*argi; + } else + cs = 1; + e = mandoc_calloc(1, sizeof(*e)); e->type = EXPR_TERM; e->bits = 0; diff --git a/contrib/mdocml/mansearch.h b/contrib/mdocml/mansearch.h index 892c6e1..cc4f364 100644 --- a/contrib/mdocml/mansearch.h +++ b/contrib/mdocml/mansearch.h @@ -1,7 +1,7 @@ -/* $Id: mansearch.h,v 1.27 2016/08/01 12:31:00 schwarze Exp $ */ +/* $Id: mansearch.h,v 1.28 2017/04/17 20:05:08 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013, 2014, 2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013, 2014, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,9 @@ #define MANDOCDB_VERSION 1 #define MACRO_MAX 36 +#define KEY_arch 0 +#define KEY_sec 1 +#define KEY_Nm 38 #define KEY_Nd 39 #define KEY_MAX 40 diff --git a/contrib/mdocml/mdoc.7 b/contrib/mdocml/mdoc.7 index e28db48..5a1863c 100644 --- a/contrib/mdocml/mdoc.7 +++ b/contrib/mdocml/mdoc.7 @@ -1,4 +1,4 @@ -.\" $Id: mdoc.7,v 1.260 2017/01/09 14:10:53 schwarze Exp $ +.\" $Id: mdoc.7,v 1.269 2017/07/20 16:24:53 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2010, 2011, 2013-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 9 2017 $ +.Dd $Mdocdate: July 20 2017 $ .Dt MDOC 7 .Os .Sh NAME @@ -476,10 +476,8 @@ in the alphabetical .It Sx \&Ap Ta apostrophe without surrounding whitespace (no arguments) .It Sx \&Sm Ta switch horizontal spacing mode: Op Cm on | off .It Sx \&Bk , \&Ek Ta keep block: Fl words -.It Sx \&br Ta force output line break in text mode (no arguments) -.It Sx \&sp Ta force vertical space: Op Ar height .El -.Ss Semantic markup for command line utilities: +.Ss Semantic markup for command line utilities .Bl -column "Brq, Bro, Brc" description .It Sx \&Nm Ta start a SYNOPSIS block with the name of a utility .It Sx \&Fl Ta command line options (flags) (>=0 arguments) @@ -490,7 +488,7 @@ in the alphabetical .It Sx \&Ev Ta environmental variable (>0 arguments) .It Sx \&Pa Ta file system path (>=0 arguments) .El -.Ss Semantic markup for function libraries: +.Ss Semantic markup for function libraries .Bl -column "Brq, Bro, Brc" description .It Sx \&Lb Ta function library (one argument) .It Sx \&In Ta include file (one argument) @@ -511,7 +509,7 @@ in the alphabetical .It Sx \&Er Ta error constant (>0 arguments) .It Sx \&Ev Ta environmental variable (>0 arguments) .El -.Ss Various semantic markup: +.Ss Various semantic markup .Bl -column "Brq, Bro, Brc" description .It Sx \&An Ta author name (>0 arguments) .It Sx \&Lk Ta hyperlink: Ar uri Op Ar name @@ -737,7 +735,7 @@ A version of .At . .It Cm III .At III . -.It Cm V[.[1-4]]? +.It Cm V | V.[1-4] A version of .At V . .El @@ -974,10 +972,8 @@ argument. A columnated list. The .Fl width -argument has no effect; instead, each argument specifies the width -of one column, using either the scaling width syntax described in -.Xr roff 7 -or the string length of the argument. +argument has no effect; instead, the string length of each argument +specifies the width of one column. If the first line of the body of a .Fl column list is not an @@ -1854,10 +1850,10 @@ The tab cell delimiter may only be used within the .Sx \&It line itself; on following lines, only the .Sx \&Ta -macro can be used to delimit cells, and +macro can be used to delimit cells, and portability requires that .Sx \&Ta -is only recognised as a macro when called by other macros, -not as the first macro on a line. +is called by other macros: some parsers do not recognize it when +it appears as the first macro on a line. .Pp Note that quoted strings may span tab-delimited cells on an .Sx \&It @@ -2579,11 +2575,6 @@ The second and last Technical Corrigendum. .br This standard is also called X/Open Portability Guide version 7. -.Pp -.It \-p1003.1-2013 -.St -p1003.1-2013 -.br -This is the first Technical Corrigendum. .El .It Other standards .Pp @@ -2738,29 +2729,6 @@ Examples: .Dl \&.Xr mandoc 1 .Dl \&.Xr mandoc 1 \&; .Dl \&.Xr mandoc 1 \&Ns s behaviour -.Ss \&br -Emits a line-break. -This macro should not be used; it is implemented for compatibility with -historical manuals. -.Pp -Consider using -.Sx \&Pp -in the event of natural paragraph breaks. -.Ss \&sp -Emits vertical space. -This macro should not be used; it is implemented for compatibility with -historical manuals. -Its syntax is as follows: -.Pp -.D1 Pf \. Sx \&sp Op Ar height -.Pp -The -.Ar height -argument is a scaling width as described in -.Xr roff 7 . -If unspecified, -.Sx \&sp -asserts a single vertical space. .Sh MACRO SYNTAX The syntax of a macro depends on its classification. In this section, @@ -3045,8 +3013,6 @@ then the macro accepts an arbitrary number of arguments. .It Sx \&Va Ta Yes Ta Yes Ta n .It Sx \&Vt Ta Yes Ta Yes Ta >0 .It Sx \&Xr Ta Yes Ta Yes Ta 2 -.It Sx \&br Ta \&No Ta \&No Ta 0 -.It Sx \&sp Ta \&No Ta \&No Ta 1 .El .Ss Delimiters When a macro argument consists of one single input character @@ -3064,6 +3030,8 @@ For many macros, when the leading arguments are opening delimiters, these delimiters are put before the macro scope, and when the trailing arguments are closing delimiters, these delimiters are put after the macro scope. +Spacing is suppressed after opening delimiters +and before closing delimiters. For example, .Pp .D1 Pf \. \&Aq "( [ word ] ) ." @@ -3120,7 +3088,7 @@ renders as: .D1 Fl a ( b | c \*(Ba d ) e .Pp This applies to both opening and closing delimiters, -and also to the middle delimiter: +and also to the middle delimiter, which does not suppress spacing: .Pp .Bl -tag -width Ds -offset indent -compact .It \&| @@ -3230,7 +3198,7 @@ but produces large indentations. .Xr tbl 7 .Pp The web page -.Lk http://mdocml.bsd.lv/mdoc/ "extended documentation for the mdoc language" +.Lk http://mandoc.bsd.lv/mdoc/ "extended documentation for the mdoc language" provides a few tutorial-style pages for beginners, an extensive style guide for advanced authors, and an alphabetic index helping to choose the best macros for various kinds of content. diff --git a/contrib/mdocml/mdoc.c b/contrib/mdocml/mdoc.c index 0094961..7afcc5d 100644 --- a/contrib/mdocml/mdoc.c +++ b/contrib/mdocml/mdoc.c @@ -1,7 +1,7 @@ -/* $Id: mdoc.c,v 1.258 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: mdoc.c,v 1.267 2017/06/17 13:06:16 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -35,41 +35,6 @@ #include "roff_int.h" #include "libmdoc.h" -const char *const __mdoc_macronames[MDOC_MAX + 1] = { - "text", - "Ap", "Dd", "Dt", "Os", - "Sh", "Ss", "Pp", "D1", - "Dl", "Bd", "Ed", "Bl", - "El", "It", "Ad", "An", - "Ar", "Cd", "Cm", "Dv", - "Er", "Ev", "Ex", "Fa", - "Fd", "Fl", "Fn", "Ft", - "Ic", "In", "Li", "Nd", - "Nm", "Op", "Ot", "Pa", - "Rv", "St", "Va", "Vt", - "Xr", "%A", "%B", "%D", - "%I", "%J", "%N", "%O", - "%P", "%R", "%T", "%V", - "Ac", "Ao", "Aq", "At", - "Bc", "Bf", "Bo", "Bq", - "Bsx", "Bx", "Db", "Dc", - "Do", "Dq", "Ec", "Ef", - "Em", "Eo", "Fx", "Ms", - "No", "Ns", "Nx", "Ox", - "Pc", "Pf", "Po", "Pq", - "Qc", "Ql", "Qo", "Qq", - "Re", "Rs", "Sc", "So", - "Sq", "Sm", "Sx", "Sy", - "Tn", "Ux", "Xc", "Xo", - "Fo", "Fc", "Oo", "Oc", - "Bk", "Ek", "Bt", "Hf", - "Fr", "Ud", "Lb", "Lp", - "Lk", "Mt", "Brq", "Bro", - "Brc", "%C", "Es", "En", - "Dx", "%Q", "br", "sp", - "%U", "Ta", "ll", -}; - const char *const __mdoc_argnames[MDOC_ARG_MAX] = { "split", "nosplit", "ragged", "unfilled", "literal", "file", @@ -80,9 +45,7 @@ const char *const __mdoc_argnames[MDOC_ARG_MAX] = { "width", "compact", "std", "filled", "words", "emphasis", "symbolic", "nested", "centered" - }; - -const char * const *mdoc_macronames = __mdoc_macronames + 1; +}; const char * const *mdoc_argnames = __mdoc_argnames; static int mdoc_ptext(struct roff_man *, int, char *, int); @@ -119,13 +82,12 @@ mdoc_parseln(struct roff_man *mdoc, int ln, char *buf, int offs) void mdoc_macro(MACRO_PROT_ARGS) { - assert(tok > TOKEN_NONE && tok < MDOC_MAX); - + 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, int tok) +mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, enum roff_tok tok) { struct roff_node *p; @@ -135,8 +97,8 @@ mdoc_tail_alloc(struct roff_man *mdoc, int line, int pos, int tok) } struct roff_node * -mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, int tok, - struct roff_node *body, enum mdoc_endbody end) +mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, + enum roff_tok tok, struct roff_node *body) { struct roff_node *p; @@ -145,7 +107,7 @@ mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, int tok, p = roff_node_alloc(mdoc, line, pos, ROFFT_BODY, tok); p->body = body; p->norm = body->norm; - p->end = end; + p->end = ENDBODY_SPACE; roff_node_append(mdoc, p); mdoc->next = ROFF_NEXT_SIBLING; return p; @@ -153,7 +115,7 @@ mdoc_endbody_alloc(struct roff_man *mdoc, int line, int pos, int tok, struct roff_node * mdoc_block_alloc(struct roff_man *mdoc, int line, int pos, - int tok, struct mdoc_arg *args) + enum roff_tok tok, struct mdoc_arg *args) { struct roff_node *p; @@ -180,7 +142,7 @@ mdoc_block_alloc(struct roff_man *mdoc, int line, int pos, void mdoc_elem_alloc(struct roff_man *mdoc, int line, int pos, - int tok, struct mdoc_arg *args) + enum roff_tok tok, struct mdoc_arg *args) { struct roff_node *p; @@ -217,6 +179,7 @@ static int mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) { struct roff_node *n; + const char *cp, *sp; char *c, *ws, *end; n = mdoc->last; @@ -282,16 +245,31 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, line, (int)(ws-buf), NULL); + /* + * Blank lines are allowed in no-fill mode + * and cancel preceding \c, + * but add a single vertical space elsewhere. + */ + if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) { + switch (mdoc->last->type) { + case ROFFT_TEXT: + sp = mdoc->last->string; + cp = end = strchr(sp, '\0') - 2; + if (cp < sp || cp[0] != '\\' || cp[1] != 'c') + break; + while (cp > sp && cp[-1] == '\\') + cp--; + if ((end - cp) % 2) + break; + *end = '\0'; + return 1; + default: + break; + } mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse, line, (int)(c - buf), NULL); - - /* - * Insert a `sp' in the case of a blank line. Technically, - * blank lines aren't allowed, but enough manuals assume this - * behaviour that we want to work around it. - */ - roff_elem_alloc(mdoc, line, offs, MDOC_sp); + roff_elem_alloc(mdoc, line, offs, ROFF_sp); mdoc->last->flags |= NODE_VALID | NODE_ENDED; mdoc->next = ROFF_NEXT_SIBLING; return 1; @@ -312,6 +290,28 @@ mdoc_ptext(struct roff_man *mdoc, int line, char *buf, int offs) if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) mdoc->last->flags |= NODE_EOS; + + for (c = buf + offs; c != NULL; c = strchr(c + 1, '.')) { + if (c - buf < offs + 2) + continue; + if (end - c < 3) + break; + if (c[1] != ' ' || + isalpha((unsigned char)c[-2]) == 0 || + isalpha((unsigned char)c[-1]) == 0 || + (c[-2] == 'n' && c[-1] == 'c') || + (c[-2] == 'v' && c[-1] == 's')) + continue; + c += 2; + if (*c == ' ') + c++; + if (*c == ' ') + c++; + if (isupper((unsigned char)(*c))) + mandoc_msg(MANDOCERR_EOS, mdoc->parse, + line, (int)(c - buf), NULL); + } + return 1; } @@ -324,25 +324,18 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) { struct roff_node *n; const char *cp; - int tok; - int i, sv; - char mac[5]; + size_t sz; + enum roff_tok tok; + int sv; - sv = offs; - - /* - * Copy the first word into a nil-terminated buffer. - * Stop when a space, tab, escape, or eoln is encountered. - */ - - i = 0; - while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) - mac[i++] = buf[offs++]; - - mac[i] = '\0'; - - tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : TOKEN_NONE; + /* Determine the line macro. */ + sv = offs; + tok = TOKEN_NONE; + for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) + 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); @@ -366,7 +359,7 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) /* Jump to the next non-whitespace word. */ - while (buf[offs] && ' ' == buf[offs]) + while (buf[offs] == ' ') offs++; /* diff --git a/contrib/mdocml/mdoc.h b/contrib/mdocml/mdoc.h index ebe4391..1628e0c 100644 --- a/contrib/mdocml/mdoc.h +++ b/contrib/mdocml/mdoc.h @@ -1,4 +1,4 @@ -/* $Id: mdoc.h,v 1.144 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: mdoc.h,v 1.145 2017/04/24 23:06:18 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -16,131 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define MDOC_Ap 0 -#define MDOC_Dd 1 -#define MDOC_Dt 2 -#define MDOC_Os 3 -#define MDOC_Sh 4 -#define MDOC_Ss 5 -#define MDOC_Pp 6 -#define MDOC_D1 7 -#define MDOC_Dl 8 -#define MDOC_Bd 9 -#define MDOC_Ed 10 -#define MDOC_Bl 11 -#define MDOC_El 12 -#define MDOC_It 13 -#define MDOC_Ad 14 -#define MDOC_An 15 -#define MDOC_Ar 16 -#define MDOC_Cd 17 -#define MDOC_Cm 18 -#define MDOC_Dv 19 -#define MDOC_Er 20 -#define MDOC_Ev 21 -#define MDOC_Ex 22 -#define MDOC_Fa 23 -#define MDOC_Fd 24 -#define MDOC_Fl 25 -#define MDOC_Fn 26 -#define MDOC_Ft 27 -#define MDOC_Ic 28 -#define MDOC_In 29 -#define MDOC_Li 30 -#define MDOC_Nd 31 -#define MDOC_Nm 32 -#define MDOC_Op 33 -#define MDOC_Ot 34 -#define MDOC_Pa 35 -#define MDOC_Rv 36 -#define MDOC_St 37 -#define MDOC_Va 38 -#define MDOC_Vt 39 -#define MDOC_Xr 40 -#define MDOC__A 41 -#define MDOC__B 42 -#define MDOC__D 43 -#define MDOC__I 44 -#define MDOC__J 45 -#define MDOC__N 46 -#define MDOC__O 47 -#define MDOC__P 48 -#define MDOC__R 49 -#define MDOC__T 50 -#define MDOC__V 51 -#define MDOC_Ac 52 -#define MDOC_Ao 53 -#define MDOC_Aq 54 -#define MDOC_At 55 -#define MDOC_Bc 56 -#define MDOC_Bf 57 -#define MDOC_Bo 58 -#define MDOC_Bq 59 -#define MDOC_Bsx 60 -#define MDOC_Bx 61 -#define MDOC_Db 62 -#define MDOC_Dc 63 -#define MDOC_Do 64 -#define MDOC_Dq 65 -#define MDOC_Ec 66 -#define MDOC_Ef 67 -#define MDOC_Em 68 -#define MDOC_Eo 69 -#define MDOC_Fx 70 -#define MDOC_Ms 71 -#define MDOC_No 72 -#define MDOC_Ns 73 -#define MDOC_Nx 74 -#define MDOC_Ox 75 -#define MDOC_Pc 76 -#define MDOC_Pf 77 -#define MDOC_Po 78 -#define MDOC_Pq 79 -#define MDOC_Qc 80 -#define MDOC_Ql 81 -#define MDOC_Qo 82 -#define MDOC_Qq 83 -#define MDOC_Re 84 -#define MDOC_Rs 85 -#define MDOC_Sc 86 -#define MDOC_So 87 -#define MDOC_Sq 88 -#define MDOC_Sm 89 -#define MDOC_Sx 90 -#define MDOC_Sy 91 -#define MDOC_Tn 92 -#define MDOC_Ux 93 -#define MDOC_Xc 94 -#define MDOC_Xo 95 -#define MDOC_Fo 96 -#define MDOC_Fc 97 -#define MDOC_Oo 98 -#define MDOC_Oc 99 -#define MDOC_Bk 100 -#define MDOC_Ek 101 -#define MDOC_Bt 102 -#define MDOC_Hf 103 -#define MDOC_Fr 104 -#define MDOC_Ud 105 -#define MDOC_Lb 106 -#define MDOC_Lp 107 -#define MDOC_Lk 108 -#define MDOC_Mt 109 -#define MDOC_Brq 110 -#define MDOC_Bro 111 -#define MDOC_Brc 112 -#define MDOC__C 113 -#define MDOC_Es 114 -#define MDOC_En 115 -#define MDOC_Dx 116 -#define MDOC__Q 117 -#define MDOC_br 118 -#define MDOC_sp 119 -#define MDOC__U 120 -#define MDOC_Ta 121 -#define MDOC_ll 122 -#define MDOC_MAX 123 - enum mdocargt { MDOC_Split, /* -split */ MDOC_Nosplit, /* -nospli */ @@ -274,11 +149,7 @@ union mdoc_data { struct mdoc_rs Rs; }; -/* Names of macros. */ -extern const char *const *mdoc_macronames; - /* Names of macro args. Index is enum mdocargt. */ extern const char *const *mdoc_argnames; - void mdoc_validate(struct roff_man *); diff --git a/contrib/mdocml/mdoc_argv.c b/contrib/mdocml/mdoc_argv.c index b47c7db..db4c63f 100644 --- a/contrib/mdocml/mdoc_argv.c +++ b/contrib/mdocml/mdoc_argv.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_argv.c,v 1.109 2016/08/28 16:15:12 schwarze Exp $ */ +/* $Id: mdoc_argv.c,v 1.115 2017/05/30 16:22:03 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -144,8 +144,7 @@ static const enum mdocargt args_Bl[] = { MDOC_ARG_MAX }; -static const struct mdocarg mdocargs[MDOC_MAX] = { - { ARGSFL_DELIM, NULL }, /* Ap */ +static const struct mdocarg __mdocargs[MDOC_MAX - MDOC_Dd] = { { ARGSFL_NONE, NULL }, /* Dd */ { ARGSFL_NONE, NULL }, /* Dt */ { ARGSFL_NONE, NULL }, /* Os */ @@ -161,6 +160,7 @@ static const struct mdocarg mdocargs[MDOC_MAX] = { { ARGSFL_NONE, NULL }, /* It */ { ARGSFL_DELIM, NULL }, /* Ad */ { ARGSFL_DELIM, args_An }, /* An */ + { ARGSFL_DELIM, NULL }, /* Ap */ { ARGSFL_DELIM, NULL }, /* Ar */ { ARGSFL_DELIM, NULL }, /* Cd */ { ARGSFL_DELIM, NULL }, /* Cm */ @@ -263,12 +263,10 @@ static const struct mdocarg mdocargs[MDOC_MAX] = { { ARGSFL_DELIM, NULL }, /* En */ { ARGSFL_DELIM, NULL }, /* Dx */ { ARGSFL_NONE, NULL }, /* %Q */ - { ARGSFL_NONE, NULL }, /* br */ - { ARGSFL_NONE, NULL }, /* sp */ { ARGSFL_NONE, NULL }, /* %U */ { ARGSFL_NONE, NULL }, /* Ta */ - { ARGSFL_NONE, NULL }, /* ll */ }; +static const struct mdocarg *const mdocargs = __mdocargs - MDOC_Dd; /* @@ -277,7 +275,7 @@ static const struct mdocarg mdocargs[MDOC_MAX] = { * Some flags take no argument, some one, some multiple. */ void -mdoc_argv(struct roff_man *mdoc, int line, int tok, +mdoc_argv(struct roff_man *mdoc, int line, enum roff_tok tok, struct mdoc_arg **reta, int *pos, char *buf) { struct mdoc_argv tmpv; @@ -291,6 +289,7 @@ mdoc_argv(struct roff_man *mdoc, int line, int tok, /* Which flags does this macro support? */ + assert(tok >= MDOC_Dd && tok < MDOC_MAX); argtable = mdocargs[tok].argvs; if (argtable == NULL) return; @@ -415,7 +414,7 @@ argn_free(struct mdoc_arg *p, int iarg) enum margserr mdoc_args(struct roff_man *mdoc, int line, int *pos, - char *buf, int tok, char **v) + char *buf, enum roff_tok tok, char **v) { struct roff_node *n; char *v_local; @@ -424,8 +423,6 @@ mdoc_args(struct roff_man *mdoc, int line, int *pos, if (v == NULL) v = &v_local; fl = tok == TOKEN_NONE ? ARGSFL_NONE : mdocargs[tok].flags; - if (tok != MDOC_It) - return args(mdoc, line, pos, buf, fl, v); /* * We know that we're in an `It', so it's reasonable to expect @@ -434,12 +431,15 @@ mdoc_args(struct roff_man *mdoc, int line, int *pos, * safe fall-back into the default behaviour. */ - for (n = mdoc->last; n; n = n->parent) - if (MDOC_Bl == n->tok) - if (LIST_column == n->norm->Bl.type) { + if (tok == MDOC_It) { + for (n = mdoc->last; n != NULL; n = n->parent) { + if (n->tok != MDOC_Bl) + continue; + if (n->norm->Bl.type == LIST_column) fl = ARGSFL_TABSEP; - break; - } + break; + } + } return args(mdoc, line, pos, buf, fl, v); } @@ -555,14 +555,14 @@ args(struct roff_man *mdoc, int line, int *pos, if ( ! (mdoc->flags & MDOC_PHRASE)) mandoc_msg(MANDOCERR_ARG_QUOTE, mdoc->parse, line, *pos, NULL); - return ARGS_QWORD; + return ARGS_WORD; } mdoc->flags &= ~MDOC_PHRASELIT; buf[(*pos)++] = '\0'; if ('\0' == buf[*pos]) - return ARGS_QWORD; + return ARGS_WORD; while (' ' == buf[*pos]) (*pos)++; @@ -571,7 +571,7 @@ args(struct roff_man *mdoc, int line, int *pos, mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, line, *pos, NULL); - return ARGS_QWORD; + return ARGS_WORD; } p = &buf[*pos]; diff --git a/contrib/mdocml/mdoc_hash.c b/contrib/mdocml/mdoc_hash.c deleted file mode 100644 index cad3c2d..0000000 --- a/contrib/mdocml/mdoc_hash.c +++ /dev/null @@ -1,95 +0,0 @@ -/* $Id: mdoc_hash.c,v 1.27 2016/07/15 18:03:45 schwarze Exp $ */ -/* - * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> - * - * 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 <sys/types.h> - -#include <assert.h> -#include <ctype.h> -#include <limits.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "mandoc.h" -#include "roff.h" -#include "mdoc.h" -#include "libmandoc.h" -#include "libmdoc.h" - -static unsigned char table[27 * 12]; - - -void -mdoc_hash_init(void) -{ - int i, j, major; - const char *p; - - if (*table != '\0') - return; - - memset(table, UCHAR_MAX, sizeof(table)); - - for (i = 0; i < (int)MDOC_MAX; i++) { - p = mdoc_macronames[i]; - - if (isalpha((unsigned char)p[1])) - major = 12 * (tolower((unsigned char)p[1]) - 97); - else - major = 12 * 26; - - for (j = 0; j < 12; j++) - if (UCHAR_MAX == table[major + j]) { - table[major + j] = (unsigned char)i; - break; - } - - assert(j < 12); - } -} - -int -mdoc_hash_find(const char *p) -{ - int major, i, j; - - if (0 == p[0]) - return TOKEN_NONE; - if ( ! isalpha((unsigned char)p[0]) && '%' != p[0]) - return TOKEN_NONE; - - if (isalpha((unsigned char)p[1])) - major = 12 * (tolower((unsigned char)p[1]) - 97); - else if ('1' == p[1]) - major = 12 * 26; - else - return TOKEN_NONE; - - if (p[2] && p[3]) - return TOKEN_NONE; - - for (j = 0; j < 12; j++) { - if (UCHAR_MAX == (i = table[major + j])) - break; - if (0 == strcmp(p, mdoc_macronames[i])) - return i; - } - - return TOKEN_NONE; -} diff --git a/contrib/mdocml/mdoc_html.c b/contrib/mdocml/mdoc_html.c index 3f757d2..0b4b9ad 100644 --- a/contrib/mdocml/mdoc_html.c +++ b/contrib/mdocml/mdoc_html.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_html.c,v 1.260 2017/01/21 02:09:51 schwarze Exp $ */ +/* $Id: mdoc_html.c,v 1.294 2017/07/15 17:57:51 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -27,6 +27,7 @@ #include <unistd.h> #include "mandoc_aux.h" +#include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "out.h" @@ -48,7 +49,7 @@ struct htmlmdoc { void (*post)(MDOC_ARGS); }; -static char *make_id(const struct roff_node *); +static char *cond_id(const struct roff_node *); static void print_mdoc_head(MDOC_ARGS); static void print_mdoc_node(MDOC_ARGS); static void print_mdoc_nodelist(MDOC_ARGS); @@ -108,8 +109,8 @@ static int mdoc_rs_pre(MDOC_ARGS); static int mdoc_sh_pre(MDOC_ARGS); static int mdoc_skip_pre(MDOC_ARGS); static int mdoc_sm_pre(MDOC_ARGS); -static int mdoc_sp_pre(MDOC_ARGS); static int mdoc_ss_pre(MDOC_ARGS); +static int mdoc_st_pre(MDOC_ARGS); static int mdoc_sx_pre(MDOC_ARGS); static int mdoc_sy_pre(MDOC_ARGS); static int mdoc_va_pre(MDOC_ARGS); @@ -117,8 +118,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_ap_pre, NULL}, /* Ap */ +static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = { {NULL, NULL}, /* Dd */ {NULL, NULL}, /* Dt */ {NULL, NULL}, /* Os */ @@ -134,6 +134,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {mdoc_it_pre, NULL}, /* It */ {mdoc_ad_pre, NULL}, /* Ad */ {mdoc_an_pre, NULL}, /* An */ + {mdoc_ap_pre, NULL}, /* Ap */ {mdoc_ar_pre, NULL}, /* Ar */ {mdoc_cd_pre, NULL}, /* Cd */ {mdoc_cm_pre, NULL}, /* Cm */ @@ -155,7 +156,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {mdoc_ft_pre, NULL}, /* Ot */ {mdoc_pa_pre, NULL}, /* Pa */ {mdoc_ex_pre, NULL}, /* Rv */ - {NULL, NULL}, /* St */ + {mdoc_st_pre, NULL}, /* St */ {mdoc_va_pre, NULL}, /* Va */ {mdoc_vt_pre, NULL}, /* Vt */ {mdoc_xr_pre, NULL}, /* Xr */ @@ -173,7 +174,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {NULL, NULL}, /* Ac */ {mdoc_quote_pre, mdoc_quote_post}, /* Ao */ {mdoc_quote_pre, mdoc_quote_post}, /* Aq */ - {NULL, NULL}, /* At */ + {mdoc_xx_pre, NULL}, /* At */ {NULL, NULL}, /* Bc */ {mdoc_bf_pre, NULL}, /* Bf */ {mdoc_quote_pre, mdoc_quote_post}, /* Bo */ @@ -236,12 +237,10 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {mdoc_quote_pre, mdoc_quote_post}, /* En */ {mdoc_xx_pre, NULL}, /* Dx */ {mdoc__x_pre, mdoc__x_post}, /* %Q */ - {mdoc_sp_pre, NULL}, /* br */ - {mdoc_sp_pre, NULL}, /* sp */ {mdoc__x_pre, mdoc__x_post}, /* %U */ {NULL, NULL}, /* Ta */ - {mdoc_skip_pre, NULL}, /* ll */ }; +static const struct htmlmdoc *const mdocs = __mdocs - MDOC_Dd; /* @@ -349,7 +348,7 @@ print_mdoc_node(MDOC_ARGS) return; child = 1; - t = h->tags.head; + t = h->tag; n->flags &= ~NODE_ENDED; switch (n->type) { @@ -361,9 +360,9 @@ print_mdoc_node(MDOC_ARGS) * Make sure that if we're in a literal mode already * (i.e., within a <PRE>) don't print the newline. */ - if (' ' == *n->string && NODE_LINE & n->flags) - if ( ! (HTML_LITERAL & h->flags)) - print_otag(h, TAG_BR, ""); + if (*n->string == ' ' && n->flags & NODE_LINE && + (h->flags & (HTML_LITERAL | HTML_NONEWLINE)) == 0) + print_otag(h, TAG_BR, ""); if (NODE_DELIMC & n->flags) h->flags |= HTML_NOSPACE; print_text(h, n->string); @@ -389,10 +388,17 @@ print_mdoc_node(MDOC_ARGS) */ if (h->tblt != NULL) { print_tblclose(h); - t = h->tags.head; + t = h->tag; } assert(h->tblt == NULL); - if (mdocs[n->tok].pre && (n->end == ENDBODY_NOT || n->child)) + if (n->tok < ROFF_MAX) { + roff_html_pre(h, n); + child = 0; + break; + } + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); + if (mdocs[n->tok].pre != NULL && + (n->end == ENDBODY_NOT || n->child != NULL)) child = (*mdocs[n->tok].pre)(meta, n, h); break; } @@ -411,13 +417,13 @@ print_mdoc_node(MDOC_ARGS) case ROFFT_EQN: break; default: - if ( ! mdocs[n->tok].post || n->flags & NODE_ENDED) + if (n->tok < ROFF_MAX || + mdocs[n->tok].post == NULL || + n->flags & NODE_ENDED) break; (*mdocs[n->tok].post)(meta, n, h); if (n->end != ENDBODY_NOT) n->body->flags |= NODE_ENDED; - if (n->end == ENDBODY_NOSPACE) - h->flags |= HTML_NOSPACE; break; } } @@ -428,7 +434,6 @@ mdoc_root_post(MDOC_ARGS) struct tag *t, *tt; t = print_otag(h, TAG_TABLE, "c", "foot"); - print_otag(h, TAG_TBODY, ""); tt = print_otag(h, TAG_TR, ""); print_otag(h, TAG_TD, "c", "foot-date"); @@ -459,7 +464,6 @@ mdoc_root_pre(MDOC_ARGS) meta->title, meta->msec); t = print_otag(h, TAG_TABLE, "c", "head"); - print_otag(h, TAG_TBODY, ""); tt = print_otag(h, TAG_TR, ""); print_otag(h, TAG_TD, "c", "head-ltitle"); @@ -480,25 +484,19 @@ mdoc_root_pre(MDOC_ARGS) } static char * -make_id(const struct roff_node *n) +cond_id(const struct roff_node *n) { - const struct roff_node *nch; - char *buf, *cp; - - for (nch = n->child; nch != NULL; nch = nch->next) - if (nch->type != ROFFT_TEXT) - return NULL; - - buf = NULL; - deroff(&buf, n); - - /* http://www.w3.org/TR/html5/dom.html#the-id-attribute */ - - for (cp = buf; *cp != '\0'; cp++) - if (*cp == ' ') - *cp = '_'; - - return buf; + if (n->child != NULL && + n->child->type == ROFFT_TEXT && + (n->prev == NULL || + (n->prev->type == ROFFT_TEXT && + strcmp(n->prev->string, "|") == 0)) && + (n->parent->tok == MDOC_It || + (n->parent->tok == MDOC_Xo && + n->parent->parent->prev == NULL && + n->parent->parent->parent->tok == MDOC_It))) + return html_make_id(n); + return NULL; } static int @@ -507,22 +505,20 @@ mdoc_sh_pre(MDOC_ARGS) char *id; switch (n->type) { - case ROFFT_BLOCK: - return 1; + case ROFFT_HEAD: + id = html_make_id(n); + print_otag(h, TAG_H1, "cTi", "Sh", id); + if (id != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + free(id); + break; case ROFFT_BODY: if (n->sec == SEC_AUTHORS) h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT); - return 1; + break; default: break; } - - if ((id = make_id(n)) != NULL) { - print_otag(h, TAG_H1, "ci", "Sh", id); - free(id); - } else - print_otag(h, TAG_H1, "c", "Sh"); - return 1; } @@ -534,21 +530,25 @@ mdoc_ss_pre(MDOC_ARGS) if (n->type != ROFFT_HEAD) return 1; - if ((id = make_id(n)) != NULL) { - print_otag(h, TAG_H2, "ci", "Ss", id); - free(id); - } else - print_otag(h, TAG_H2, "c", "Ss"); - + id = html_make_id(n); + print_otag(h, TAG_H2, "cTi", "Ss", id); + if (id != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + free(id); return 1; } static int mdoc_fl_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Fl"); - print_text(h, "\\-"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_B, "cTi", "Fl", id); + free(id); + print_text(h, "\\-"); if (!(n->child == NULL && (n->next == NULL || n->next->type == ROFFT_TEXT || @@ -561,7 +561,12 @@ mdoc_fl_pre(MDOC_ARGS) static int mdoc_cm_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Cm"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_B, "cTi", "Cm", id); + free(id); return 1; } @@ -574,23 +579,19 @@ mdoc_nd_pre(MDOC_ARGS) /* XXX: this tag in theory can contain block elements. */ print_text(h, "\\(em"); - print_otag(h, TAG_SPAN, "c", "Nd"); + print_otag(h, TAG_SPAN, "cT", "Nd"); return 1; } static int mdoc_nm_pre(MDOC_ARGS) { - int len; - switch (n->type) { case ROFFT_HEAD: print_otag(h, TAG_TD, ""); /* FALLTHROUGH */ case ROFFT_ELEM: - print_otag(h, TAG_B, "c", "Nm"); - if (n->child == NULL && meta->name != NULL) - print_text(h, meta->name); + print_otag(h, TAG_B, "cT", "Nm"); return 1; case ROFFT_BODY: print_otag(h, TAG_TD, ""); @@ -598,20 +599,8 @@ mdoc_nm_pre(MDOC_ARGS) default: break; } - synopsis_pre(h, n); print_otag(h, TAG_TABLE, "c", "Nm"); - - for (len = 0, n = n->head->child; n; n = n->next) - if (n->type == ROFFT_TEXT) - len += html_strlen(n->string); - - if (len == 0 && meta->name != NULL) - len = html_strlen(meta->name); - - print_otag(h, TAG_COL, "shw", len); - print_otag(h, TAG_COL, ""); - print_otag(h, TAG_TBODY, ""); print_otag(h, TAG_TR, ""); return 1; } @@ -623,11 +612,11 @@ mdoc_xr_pre(MDOC_ARGS) return 0; if (h->base_man) - print_otag(h, TAG_A, "chM", "Xr", + print_otag(h, TAG_A, "cThM", "Xr", n->child->string, n->child->next == NULL ? NULL : n->child->next->string); else - print_otag(h, TAG_A, "c", "Xr"); + print_otag(h, TAG_A, "cT", "Xr"); n = n->child; print_text(h, n->string); @@ -656,7 +645,7 @@ mdoc_ns_pre(MDOC_ARGS) static int mdoc_ar_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "c", "Ar"); + print_otag(h, TAG_VAR, "cT", "Ar"); return 1; } @@ -671,11 +660,12 @@ static int mdoc_it_pre(MDOC_ARGS) { const struct roff_node *bl; + struct tag *t; const char *cattr; enum mdoc_list type; bl = n->parent; - while (bl != NULL && bl->tok != MDOC_Bl) + while (bl->tok != MDOC_Bl) bl = bl->parent; type = bl->norm->Bl.type; @@ -725,10 +715,7 @@ mdoc_it_pre(MDOC_ARGS) case ROFFT_HEAD: return 0; case ROFFT_BODY: - if (bl->norm->Bl.comp) - print_otag(h, TAG_LI, "csvt", cattr, 0); - else - print_otag(h, TAG_LI, "c", cattr); + print_otag(h, TAG_LI, "c", cattr); break; default: break; @@ -738,22 +725,45 @@ mdoc_it_pre(MDOC_ARGS) case LIST_hang: case LIST_inset: case LIST_ohang: - case LIST_tag: switch (n->type) { case ROFFT_HEAD: - if (bl->norm->Bl.comp) - print_otag(h, TAG_DT, "csvt", cattr, 0); - else - print_otag(h, TAG_DT, "c", cattr); + print_otag(h, TAG_DT, "c", cattr); if (type == LIST_diag) print_otag(h, TAG_B, "c", cattr); break; case ROFFT_BODY: - if (bl->norm->Bl.width == NULL) + print_otag(h, TAG_DD, "csw*+l", cattr, + bl->norm->Bl.width); + break; + default: + break; + } + break; + case LIST_tag: + switch (n->type) { + case ROFFT_HEAD: + if (h->style != NULL && !bl->norm->Bl.comp && + (n->parent->prev == NULL || + n->parent->prev->body == NULL || + n->parent->prev->body->child != NULL)) { + t = print_otag(h, TAG_DT, "csw*+-l", + cattr, bl->norm->Bl.width); + print_text(h, "\\ "); + print_tagq(h, t); + t = print_otag(h, TAG_DD, "c", cattr); + print_text(h, "\\ "); + print_tagq(h, t); + } + print_otag(h, TAG_DT, "csw*+-l", cattr, + bl->norm->Bl.width); + break; + case ROFFT_BODY: + if (n->child == NULL) { + print_otag(h, TAG_DD, "css?", cattr, + "width", "auto"); + print_text(h, "\\ "); + } else print_otag(h, TAG_DD, "c", cattr); - else - print_otag(h, TAG_DD, "cswl", cattr, - bl->norm->Bl.width); break; default: break; @@ -764,10 +774,7 @@ mdoc_it_pre(MDOC_ARGS) case ROFFT_HEAD: break; case ROFFT_BODY: - if (bl->norm->Bl.comp) - print_otag(h, TAG_TD, "csvt", cattr, 0); - else - print_otag(h, TAG_TD, "c", cattr); + print_otag(h, TAG_TD, "c", cattr); break; default: print_otag(h, TAG_TR, "c", cattr); @@ -782,18 +789,20 @@ mdoc_it_pre(MDOC_ARGS) static int mdoc_bl_pre(MDOC_ARGS) { - const char *cattr; - int i; + char cattr[21]; + struct tag *t; + struct mdoc_bl *bl; + size_t i; enum htmltag elemtype; - if (n->type == ROFFT_BODY) { - if (LIST_column == n->norm->Bl.type) - print_otag(h, TAG_TBODY, ""); + bl = &n->norm->Bl; + + switch (n->type) { + case ROFFT_BODY: return 1; - } - if (n->type == ROFFT_HEAD) { - if (LIST_column != n->norm->Bl.type) + case ROFFT_HEAD: + if (bl->type != LIST_column || bl->ncols == 0) return 0; /* @@ -803,64 +812,67 @@ mdoc_bl_pre(MDOC_ARGS) * screen and we want to preserve that behaviour. */ - for (i = 0; i < (int)n->norm->Bl.ncols - 1; i++) - print_otag(h, TAG_COL, "sww", n->norm->Bl.cols[i]); - print_otag(h, TAG_COL, "swW", n->norm->Bl.cols[i]); - + t = print_otag(h, TAG_COLGROUP, ""); + for (i = 0; i < bl->ncols - 1; i++) + print_otag(h, TAG_COL, "sw+w", bl->cols[i]); + print_otag(h, TAG_COL, "swW", bl->cols[i]); + print_tagq(h, t); return 0; + + default: + break; } - switch (n->norm->Bl.type) { + switch (bl->type) { case LIST_bullet: elemtype = TAG_UL; - cattr = "Bl-bullet"; + (void)strlcpy(cattr, "Bl-bullet", sizeof(cattr)); break; case LIST_dash: case LIST_hyphen: elemtype = TAG_UL; - cattr = "Bl-dash"; + (void)strlcpy(cattr, "Bl-dash", sizeof(cattr)); break; case LIST_item: elemtype = TAG_UL; - cattr = "Bl-item"; + (void)strlcpy(cattr, "Bl-item", sizeof(cattr)); break; case LIST_enum: elemtype = TAG_OL; - cattr = "Bl-enum"; + (void)strlcpy(cattr, "Bl-enum", sizeof(cattr)); break; case LIST_diag: elemtype = TAG_DL; - cattr = "Bl-diag"; + (void)strlcpy(cattr, "Bl-diag", sizeof(cattr)); break; case LIST_hang: elemtype = TAG_DL; - cattr = "Bl-hang"; + (void)strlcpy(cattr, "Bl-hang", sizeof(cattr)); break; case LIST_inset: elemtype = TAG_DL; - cattr = "Bl-inset"; + (void)strlcpy(cattr, "Bl-inset", sizeof(cattr)); break; case LIST_ohang: elemtype = TAG_DL; - cattr = "Bl-ohang"; + (void)strlcpy(cattr, "Bl-ohang", sizeof(cattr)); break; case LIST_tag: - elemtype = TAG_DL; - cattr = "Bl-tag"; - break; + if (bl->offs) + print_otag(h, TAG_DIV, "cswl", "Bl-tag", bl->offs); + print_otag(h, TAG_DL, "csw*+l", bl->comp ? + "Bl-tag Bl-compact" : "Bl-tag", bl->width); + return 1; case LIST_column: elemtype = TAG_TABLE; - cattr = "Bl-column"; + (void)strlcpy(cattr, "Bl-column", sizeof(cattr)); break; default: abort(); } - - if (n->norm->Bl.offs) - print_otag(h, elemtype, "cswl", cattr, n->norm->Bl.offs); - else - print_otag(h, elemtype, "c", cattr); - + if (bl->comp) + (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); + print_otag(h, elemtype, "cswl", cattr, bl->offs); return 1; } @@ -873,9 +885,16 @@ mdoc_ex_pre(MDOC_ARGS) } static int +mdoc_st_pre(MDOC_ARGS) +{ + print_otag(h, TAG_SPAN, "cT", "St"); + return 1; +} + +static int mdoc_em_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "c", "Em"); + print_otag(h, TAG_I, "cT", "Em"); return 1; } @@ -898,12 +917,9 @@ mdoc_sx_pre(MDOC_ARGS) { char *id; - if ((id = make_id(n)) != NULL) { - print_otag(h, TAG_A, "chR", "Sx", id); - free(id); - } else - print_otag(h, TAG_A, "c", "Sx"); - + id = html_make_id(n); + print_otag(h, TAG_A, "cThR", "Sx", id); + free(id); return 1; } @@ -968,9 +984,9 @@ mdoc_bd_pre(MDOC_ARGS) * anyway, so don't sweat it. */ switch (nn->tok) { + case ROFF_br: + case ROFF_sp: case MDOC_Sm: - case MDOC_br: - case MDOC_sp: case MDOC_Bl: case MDOC_D1: case MDOC_Dl: @@ -998,7 +1014,7 @@ mdoc_bd_pre(MDOC_ARGS) static int mdoc_pa_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "c", "Pa"); + print_otag(h, TAG_I, "cT", "Pa"); return 1; } @@ -1029,7 +1045,7 @@ mdoc_an_pre(MDOC_ARGS) if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT)) h->flags |= HTML_SPLIT; - print_otag(h, TAG_SPAN, "c", "An"); + print_otag(h, TAG_SPAN, "cT", "An"); return 1; } @@ -1037,28 +1053,49 @@ static int mdoc_cd_pre(MDOC_ARGS) { synopsis_pre(h, n); - print_otag(h, TAG_B, "c", "Cd"); + print_otag(h, TAG_B, "cT", "Cd"); return 1; } static int mdoc_dv_pre(MDOC_ARGS) { - print_otag(h, TAG_CODE, "c", "Dv"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_CODE, "cTi", "Dv", id); + free(id); return 1; } static int mdoc_ev_pre(MDOC_ARGS) { - print_otag(h, TAG_CODE, "c", "Ev"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_CODE, "cTi", "Ev", id); + free(id); return 1; } static int mdoc_er_pre(MDOC_ARGS) { - print_otag(h, TAG_CODE, "c", "Er"); + char *id; + + id = n->sec == SEC_ERRORS && + (n->parent->tok == MDOC_It || + (n->parent->tok == MDOC_Bq && + n->parent->parent->parent->tok == MDOC_It)) ? + html_make_id(n) : NULL; + + if (id != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_CODE, "cTi", "Er", id); + free(id); return 1; } @@ -1069,12 +1106,12 @@ mdoc_fa_pre(MDOC_ARGS) struct tag *t; if (n->parent->tok != MDOC_Fo) { - print_otag(h, TAG_I, "c", "Fa"); + print_otag(h, TAG_VAR, "cT", "Fa"); return 1; } for (nn = n->child; nn; nn = nn->next) { - t = print_otag(h, TAG_I, "c", "Fa"); + t = print_otag(h, TAG_VAR, "cT", "Fa"); print_text(h, nn->string); print_tagq(h, t); if (nn->next) { @@ -1105,11 +1142,11 @@ mdoc_fd_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); if (strcmp(n->string, "#include")) { - print_otag(h, TAG_B, "c", "Fd"); + print_otag(h, TAG_B, "cT", "Fd"); return 1; } - print_otag(h, TAG_B, "c", "In"); + print_otag(h, TAG_B, "cT", "In"); print_text(h, n->string); if (NULL != (n = n->next)) { @@ -1123,10 +1160,10 @@ mdoc_fd_pre(MDOC_ARGS) cp = strchr(buf, '\0') - 1; if (cp >= buf && (*cp == '>' || *cp == '"')) *cp = '\0'; - t = print_otag(h, TAG_A, "chI", "In", buf); + t = print_otag(h, TAG_A, "cThI", "In", buf); free(buf); } else - t = print_otag(h, TAG_A, "c", "In"); + t = print_otag(h, TAG_A, "cT", "In"); print_text(h, n->string); print_tagq(h, t); @@ -1153,7 +1190,7 @@ mdoc_vt_pre(MDOC_ARGS) } else if (n->type == ROFFT_HEAD) return 0; - print_otag(h, TAG_I, "c", "Vt"); + print_otag(h, TAG_VAR, "cT", "Vt"); return 1; } @@ -1161,7 +1198,7 @@ static int mdoc_ft_pre(MDOC_ARGS) { synopsis_pre(h, n); - print_otag(h, TAG_I, "c", "Ft"); + print_otag(h, TAG_VAR, "cT", "Ft"); return 1; } @@ -1182,7 +1219,7 @@ mdoc_fn_pre(MDOC_ARGS) ep = strchr(sp, ' '); if (NULL != ep) { - t = print_otag(h, TAG_I, "c", "Ft"); + t = print_otag(h, TAG_VAR, "cT", "Ft"); while (ep) { sz = MIN((int)(ep - sp), BUFSIZ - 1); @@ -1195,7 +1232,7 @@ mdoc_fn_pre(MDOC_ARGS) print_tagq(h, t); } - t = print_otag(h, TAG_B, "c", "Fn"); + t = print_otag(h, TAG_B, "cT", "Fn"); if (sp) print_text(h, sp); @@ -1208,10 +1245,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_I, "css?", "Fa", + t = print_otag(h, TAG_VAR, "cTss?", "Fa", "white-space", "nowrap"); else - t = print_otag(h, TAG_I, "c", "Fa"); + t = print_otag(h, TAG_VAR, "cT", "Fa"); print_text(h, n->string); print_tagq(h, t); if (n->next) { @@ -1264,47 +1301,39 @@ mdoc_pp_pre(MDOC_ARGS) } static int -mdoc_sp_pre(MDOC_ARGS) -{ - struct roffsu su; - - SCALE_VS_INIT(&su, 1); - - if (MDOC_sp == n->tok) { - if (NULL != (n = n->child)) { - if ( ! a2roffsu(n->string, &su, SCALE_VS)) - su.scale = 1.0; - else if (su.scale < 0.0) - su.scale = 0.0; - } - } else - su.scale = 0.0; - - print_otag(h, TAG_DIV, "suh", &su); - - /* So the div isn't empty: */ - print_text(h, "\\~"); - - return 0; - -} - -static int mdoc_lk_pre(MDOC_ARGS) { - if (NULL == (n = n->child)) - return 0; - - assert(n->type == ROFFT_TEXT); - - print_otag(h, TAG_A, "ch", "Lk", n->string); + const struct roff_node *link, *descr, *punct; + struct tag *t; - if (NULL == n->next) - print_text(h, n->string); + if ((link = n->child) == NULL) + return 0; - for (n = n->next; n; n = n->next) - print_text(h, n->string); + /* Find beginning of trailing punctuation. */ + punct = n->last; + while (punct != link && punct->flags & NODE_DELIMC) + punct = punct->prev; + punct = punct->next; + + /* Link target and link text. */ + descr = link->next; + if (descr == punct) + descr = link; /* no text */ + t = print_otag(h, TAG_A, "cTh", "Lk", link->string); + do { + if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) + h->flags |= HTML_NOSPACE; + print_text(h, descr->string); + descr = descr->next; + } while (descr != punct); + print_tagq(h, t); + /* Trailing punctuation. */ + while (punct != NULL) { + h->flags |= HTML_NOSPACE; + print_text(h, punct->string); + punct = punct->next; + } return 0; } @@ -1318,7 +1347,7 @@ mdoc_mt_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); mandoc_asprintf(&cp, "mailto:%s", n->string); - t = print_otag(h, TAG_A, "ch", "Mt", cp); + t = print_otag(h, TAG_A, "cTh", "Mt", cp); print_text(h, n->string); print_tagq(h, t); free(cp); @@ -1346,7 +1375,7 @@ mdoc_fo_pre(MDOC_ARGS) return 0; assert(n->child->string); - t = print_otag(h, TAG_B, "c", "Fn"); + t = print_otag(h, TAG_B, "cT", "Fn"); print_text(h, n->child->string); print_tagq(h, t); return 0; @@ -1370,7 +1399,7 @@ mdoc_in_pre(MDOC_ARGS) struct tag *t; synopsis_pre(h, n); - print_otag(h, TAG_B, "c", "In"); + print_otag(h, TAG_B, "cT", "In"); /* * The first argument of the `In' gets special treatment as @@ -1389,9 +1418,9 @@ mdoc_in_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); if (h->base_includes) - t = print_otag(h, TAG_A, "chI", "In", n->string); + t = print_otag(h, TAG_A, "cThI", "In", n->string); else - t = print_otag(h, TAG_A, "c", "In"); + t = print_otag(h, TAG_A, "cT", "In"); print_text(h, n->string); print_tagq(h, t); @@ -1412,14 +1441,19 @@ mdoc_in_pre(MDOC_ARGS) static int mdoc_ic_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Ic"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_B, "cTi", "Ic", id); + free(id); return 1; } static int mdoc_va_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "c", "Va"); + print_otag(h, TAG_VAR, "cT", "Va"); return 1; } @@ -1450,7 +1484,7 @@ mdoc_bf_pre(MDOC_ARGS) else if (FONT_Li == n->norm->Bf.font) cattr = "Li"; else - cattr = "none"; + cattr = "No"; /* * We want this to be inline-formatted, but needs to be div to @@ -1464,7 +1498,12 @@ mdoc_bf_pre(MDOC_ARGS) static int mdoc_ms_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Ms"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_B, "cTi", "Ms", id); + free(id); return 1; } @@ -1493,28 +1532,38 @@ mdoc_rs_pre(MDOC_ARGS) if (n->prev && SEC_SEE_ALSO == n->sec) print_paragraph(h); - print_otag(h, TAG_SPAN, "c", "Rs"); + print_otag(h, TAG_CITE, "cT", "Rs"); return 1; } static int mdoc_no_pre(MDOC_ARGS) { - print_otag(h, TAG_SPAN, "c", "No"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_SPAN, "ci", "No", id); + free(id); return 1; } static int mdoc_li_pre(MDOC_ARGS) { - print_otag(h, TAG_CODE, "c", "Li"); + char *id; + + if ((id = cond_id(n)) != NULL) + print_otag(h, TAG_A, "chR", "selflink", id); + print_otag(h, TAG_CODE, "ci", "Li", id); + free(id); return 1; } static int mdoc_sy_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Sy"); + print_otag(h, TAG_B, "cT", "Sy"); return 1; } @@ -1524,7 +1573,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, "c", "Lb"); + print_otag(h, TAG_SPAN, "cT", "Lb"); return 1; } diff --git a/contrib/mdocml/mdoc_macro.c b/contrib/mdocml/mdoc_macro.c index d4edcea..b463d03 100644 --- a/contrib/mdocml/mdoc_macro.c +++ b/contrib/mdocml/mdoc_macro.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_macro.c,v 1.210 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: mdoc_macro.c,v 1.224 2017/05/30 16:22:03 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -46,19 +46,21 @@ static void phrase_ta(MACRO_PROT_ARGS); static void append_delims(struct roff_man *, int, int *, char *); static void dword(struct roff_man *, int, int, const char *, enum mdelim, int); -static int find_pending(struct roff_man *, int, int, int, - struct roff_node *); +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 parse_rest(struct roff_man *, int, int, int *, char *); -static int rew_alt(int); -static void rew_elem(struct roff_man *, int); +static void break_intermediate(struct roff_node *, + struct roff_node *); +static int parse_rest(struct roff_man *, enum roff_tok, + int, int *, char *); +static enum roff_tok rew_alt(enum roff_tok); +static void rew_elem(struct roff_man *, enum roff_tok); 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] = { - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ap */ +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 */ @@ -74,6 +76,8 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { { blk_full, MDOC_PARSED | MDOC_JOIN }, /* It */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */ { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* An */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | + MDOC_IGNDELIM | MDOC_JOIN }, /* Ap */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */ { in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Cd */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */ @@ -194,14 +198,10 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* En */ { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */ { in_line_eoln, MDOC_JOIN }, /* %Q */ - { in_line_eoln, 0 }, /* br */ - { in_line_eoln, 0 }, /* sp */ { in_line_eoln, 0 }, /* %U */ { phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */ - { in_line_eoln, MDOC_PROLOGUE }, /* ll */ }; - -const struct mdoc_macro * const mdoc_macros = __mdoc_macros; +const struct mdoc_macro *const mdoc_macros = __mdoc_macros - MDOC_Dd; /* @@ -223,7 +223,7 @@ mdoc_endparse(struct roff_man *mdoc) if (n->type == ROFFT_BLOCK && mdoc_macros[n->tok].flags & MDOC_EXPLICIT) mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse, - n->line, n->pos, mdoc_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); /* Rewind to the first. */ @@ -238,20 +238,19 @@ mdoc_endparse(struct roff_man *mdoc) static int lookup(struct roff_man *mdoc, int from, int line, int ppos, const char *p) { - int res; + enum roff_tok res; if (mdoc->flags & MDOC_PHRASEQF) { mdoc->flags &= ~MDOC_PHRASEQF; return TOKEN_NONE; } if (from == TOKEN_NONE || mdoc_macros[from].flags & MDOC_PARSED) { - res = mdoc_hash_find(p); + res = roffhash_find(mdoc->mdocmac, p, 0); if (res != TOKEN_NONE) { if (mdoc_macros[res].flags & MDOC_CALLABLE) return res; - if (res != MDOC_br && res != MDOC_sp && res != MDOC_ll) - mandoc_msg(MANDOCERR_MACRO_CALL, - mdoc->parse, line, ppos, p); + mandoc_msg(MANDOCERR_MACRO_CALL, + mdoc->parse, line, ppos, p); } } return TOKEN_NONE; @@ -322,8 +321,8 @@ rew_pending(struct roff_man *mdoc, const struct roff_node *n) * For a block closing macro, return the corresponding opening one. * Otherwise, return the macro itself. */ -static int -rew_alt(int tok) +static enum roff_tok +rew_alt(enum roff_tok tok) { switch (tok) { case MDOC_Ac: @@ -364,7 +363,7 @@ rew_alt(int tok) } static void -rew_elem(struct roff_man *mdoc, int tok) +rew_elem(struct roff_man *mdoc, enum roff_tok tok) { struct roff_node *n; @@ -376,38 +375,52 @@ rew_elem(struct roff_man *mdoc, int tok) rew_last(mdoc, n); } +static void +break_intermediate(struct roff_node *n, struct roff_node *breaker) +{ + if (n != breaker && + n->type != ROFFT_BLOCK && n->type != ROFFT_HEAD && + (n->type != ROFFT_BODY || n->end != ENDBODY_NOT)) + n = n->parent; + while (n != breaker) { + if ( ! (n->flags & NODE_VALID)) + n->flags |= NODE_BROKEN; + n = n->parent; + } +} + /* * If there is an open sub-block of the target requiring * explicit close-out, postpone closing out the target until * the rew_pending() call closing out the sub-block. */ static int -find_pending(struct roff_man *mdoc, int tok, int line, int ppos, +find_pending(struct roff_man *mdoc, enum roff_tok tok, int line, int ppos, struct roff_node *target) { struct roff_node *n; int irc; + if (target->flags & NODE_VALID) + return 0; + irc = 0; for (n = mdoc->last; n != NULL && n != target; n = n->parent) { - if (n->flags & NODE_ENDED) { - if ( ! (n->flags & NODE_VALID)) - n->flags |= NODE_BROKEN; + if (n->flags & NODE_ENDED) continue; - } if (n->type == ROFFT_BLOCK && mdoc_macros[n->tok].flags & MDOC_EXPLICIT) { irc = 1; - n->flags = NODE_BROKEN; + break_intermediate(mdoc->last, target); if (target->type == ROFFT_HEAD) - target->flags = NODE_ENDED; + target->flags |= NODE_ENDED; else if ( ! (target->flags & NODE_ENDED)) { mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos, - "%s breaks %s", mdoc_macronames[tok], - mdoc_macronames[n->tok]); + "%s breaks %s", roff_name[tok], + roff_name[n->tok]); mdoc_endbody_alloc(mdoc, line, ppos, - tok, target, ENDBODY_NOSPACE); + tok, target); } } } @@ -508,7 +521,8 @@ macro_or_word(MACRO_PROT_ARGS, int parsed) mdoc_macros[tok].flags & MDOC_JOIN); return 0; } else { - if (mdoc_macros[tok].fp == in_line_eoln) + if (tok != TOKEN_NONE && + mdoc_macros[tok].fp == in_line_eoln) rew_elem(mdoc, tok); mdoc_macro(mdoc, ntok, line, ppos, pos, buf); if (tok == TOKEN_NONE) @@ -532,7 +546,7 @@ blk_exp_close(MACRO_PROT_ARGS) int j, lastarg, maxargs, nl, pending; enum margserr ac; - int atok, ntok; + enum roff_tok atok, ntok; char *p; nl = MDOC_NEWLINE & mdoc->flags; @@ -568,22 +582,27 @@ blk_exp_close(MACRO_PROT_ARGS) endbody = itblk = later = NULL; for (n = mdoc->last; n; n = n->parent) { - if (n->flags & NODE_ENDED) { - if ( ! (n->flags & NODE_VALID)) - n->flags |= NODE_BROKEN; + if (n->flags & NODE_ENDED) continue; - } /* - * Mismatching end macros can never break anything, - * SYNOPSIS name blocks can never be broken, + * Mismatching end macros can never break anything * and we only care about the breaking of BLOCKs. */ - if (body == NULL || - n->tok == MDOC_Nm || - n->type != ROFFT_BLOCK) + if (body == NULL || n->type != ROFFT_BLOCK) + continue; + + /* + * SYNOPSIS name blocks can not be broken themselves, + * but they do get broken together with a broken child. + */ + + if (n->tok == MDOC_Nm) { + if (later != NULL) + n->flags |= NODE_BROKEN | NODE_ENDED; continue; + } if (n->tok == MDOC_It) { itblk = n; @@ -591,7 +610,6 @@ blk_exp_close(MACRO_PROT_ARGS) } if (atok == n->tok) { - assert(body); /* * Found the start of our own block. @@ -613,11 +631,10 @@ blk_exp_close(MACRO_PROT_ARGS) mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos, "%s breaks %s", - mdoc_macronames[atok], - mdoc_macronames[later->tok]); + roff_name[atok], roff_name[later->tok]); endbody = mdoc_endbody_alloc(mdoc, line, ppos, - atok, body, ENDBODY_SPACE); + atok, body); if (tok == MDOC_El) itblk->flags |= NODE_ENDED | NODE_BROKEN; @@ -633,15 +650,22 @@ blk_exp_close(MACRO_PROT_ARGS) break; } - /* Explicit blocks close out description lines. */ + /* + * Explicit blocks close out description lines, but + * even those can get broken together with a child. + */ if (n->tok == MDOC_Nd) { - rew_last(mdoc, n); + if (later != NULL) + n->flags |= NODE_BROKEN | NODE_ENDED; + else + rew_last(mdoc, n); continue; } /* Breaking an open sub block. */ + break_intermediate(mdoc->last, body); n->flags |= NODE_BROKEN; if (later == NULL) later = n; @@ -649,14 +673,14 @@ blk_exp_close(MACRO_PROT_ARGS) if (body == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, mdoc->parse, - line, ppos, mdoc_macronames[tok]); + line, ppos, roff_name[tok]); if (maxargs && endbody == NULL) { /* * Stray .Ec without previous .Eo: * Break the output line, keep the arguments. */ - roff_elem_alloc(mdoc, line, ppos, MDOC_br); - rew_elem(mdoc, MDOC_br); + roff_elem_alloc(mdoc, line, ppos, ROFF_br); + rew_elem(mdoc, ROFF_br); } } else if (endbody == NULL) { rew_last(mdoc, body); @@ -668,7 +692,7 @@ blk_exp_close(MACRO_PROT_ARGS) if (buf[*pos] != '\0') mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, line, ppos, - "%s %s", mdoc_macronames[tok], + "%s %s", roff_name[tok], buf + *pos); if (endbody == NULL && n != NULL) rew_pending(mdoc, n); @@ -689,8 +713,7 @@ blk_exp_close(MACRO_PROT_ARGS) if (ac == ARGS_PUNCT || ac == ARGS_EOLN) break; - ntok = ac == ARGS_QWORD ? TOKEN_NONE : - lookup(mdoc, tok, line, lastarg, p); + ntok = lookup(mdoc, tok, line, lastarg, p); if (ntok == TOKEN_NONE) { dword(mdoc, line, lastarg, p, DELIM_MAX, @@ -706,15 +729,14 @@ blk_exp_close(MACRO_PROT_ARGS) } if (n != NULL) { + pending = 0; if (ntok != TOKEN_NONE && n->flags & NODE_BROKEN) { target = n; do target = target->parent; while ( ! (target->flags & NODE_ENDED)); - pending = find_pending(mdoc, ntok, line, ppos, - target); - } else - pending = 0; + pending = find_pending(mdoc, ntok, line, ppos, target); + } if ( ! pending) rew_pending(mdoc, n); } @@ -726,7 +748,7 @@ static void in_line(MACRO_PROT_ARGS) { int la, scope, cnt, firstarg, mayopen, nc, nl; - int ntok; + enum roff_tok ntok; enum margserr ac; enum mdelim d; struct mdoc_arg *arg; @@ -787,7 +809,7 @@ in_line(MACRO_PROT_ARGS) break; } - ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ? + ntok = (tok == MDOC_Fn && !cnt) ? TOKEN_NONE : lookup(mdoc, tok, line, la, p); /* @@ -807,7 +829,7 @@ in_line(MACRO_PROT_ARGS) mdoc_argv_free(arg); mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, line, ppos, - mdoc_macronames[tok]); + roff_name[tok]); } mdoc_macro(mdoc, ntok, line, la, pos, buf); if (nl) @@ -816,14 +838,11 @@ in_line(MACRO_PROT_ARGS) } /* - * Non-quote-enclosed punctuation. Set up our scope, if - * a word; rewind the scope, if a delimiter; then append - * the word. + * Handle punctuation. Set up our scope, if a word; + * rewind the scope, if a delimiter; then append the word. */ - d = ac == ARGS_QWORD ? DELIM_NONE : mdoc_isdelim(p); - - if (DELIM_NONE != d) { + if ((d = mdoc_isdelim(p)) != DELIM_NONE) { /* * If we encounter closing punctuation, no word * has been emitted, no scope is open, and we're @@ -843,11 +862,12 @@ in_line(MACRO_PROT_ARGS) * Close out our scope, if one is open, before * any punctuation. */ - if (scope) + if (scope && tok != MDOC_Lk) { rew_elem(mdoc, tok); - scope = 0; - if (tok == MDOC_Fn) - mayopen = 0; + scope = 0; + if (tok == MDOC_Fn) + mayopen = 0; + } } else if (mayopen && !scope) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); scope = 1; @@ -855,7 +875,7 @@ in_line(MACRO_PROT_ARGS) } dword(mdoc, line, la, p, d, - MDOC_JOIN & mdoc_macros[tok].flags); + mdoc_macros[tok].flags & MDOC_JOIN); /* * If the first argument is a closing delimiter, @@ -877,8 +897,10 @@ in_line(MACRO_PROT_ARGS) } } - if (scope) + if (scope && tok != MDOC_Lk) { rew_elem(mdoc, tok); + scope = 0; + } /* * If no elements have been collected and we're allowed to have @@ -893,11 +915,13 @@ in_line(MACRO_PROT_ARGS) } else { mdoc_argv_free(arg); mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, mdoc_macronames[tok]); + line, ppos, roff_name[tok]); } } if (nl) append_delims(mdoc, line, pos, buf); + if (scope) + rew_elem(mdoc, tok); } static void @@ -916,7 +940,7 @@ blk_full(MACRO_PROT_ARGS) if (buf[*pos] == '\0' && (tok == MDOC_Sh || tok == MDOC_Ss)) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, mdoc_macronames[tok]); + line, ppos, roff_name[tok]); return; } @@ -939,7 +963,7 @@ blk_full(MACRO_PROT_ARGS) mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, "It breaks %s", - mdoc_macronames[blk->tok]); + roff_name[blk->tok]); rew_pending(mdoc, blk); } break; @@ -951,9 +975,8 @@ blk_full(MACRO_PROT_ARGS) case MDOC_Ss: mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, - "%s breaks %s", - mdoc_macronames[tok], - mdoc_macronames[n->tok]); + "%s breaks %s", roff_name[tok], + roff_name[n->tok]); rew_pending(mdoc, n); n = mdoc->last; continue; @@ -979,15 +1002,14 @@ blk_full(MACRO_PROT_ARGS) if (blk != NULL) { mandoc_vmsg(MANDOCERR_BLK_BROKEN, mdoc->parse, line, ppos, - "It breaks %s", - mdoc_macronames[blk->tok]); + "It breaks %s", roff_name[blk->tok]); rew_pending(mdoc, blk); blk = NULL; } /* Close out prior implicit scopes. */ - rew_last(mdoc, n); + rew_pending(mdoc, n); } /* Skip items outside lists. */ @@ -995,8 +1017,8 @@ blk_full(MACRO_PROT_ARGS) if (tok == MDOC_It && (n == NULL || n->tok != MDOC_Bl)) { mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse, line, ppos, "It %s", buf + *pos); - roff_elem_alloc(mdoc, line, ppos, MDOC_br); - rew_elem(mdoc, MDOC_br); + roff_elem_alloc(mdoc, line, ppos, ROFF_br); + rew_elem(mdoc, ROFF_br); return; } } @@ -1073,7 +1095,7 @@ blk_full(MACRO_PROT_ARGS) if (tok == MDOC_Bd || tok == MDOC_Bk) { mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, line, la, "%s ... %s", - mdoc_macronames[tok], buf + la); + roff_name[tok], buf + la); break; } if (tok == MDOC_Rs) { @@ -1091,7 +1113,6 @@ blk_full(MACRO_PROT_ARGS) if (head == NULL && ac != ARGS_PHRASE && - ac != ARGS_QWORD && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; @@ -1188,8 +1209,7 @@ blk_part_imp(MACRO_PROT_ARGS) if (ac == ARGS_EOLN || ac == ARGS_PUNCT) break; - if (body == NULL && ac != ARGS_QWORD && - mdoc_isdelim(p) == DELIM_OPEN) { + if (body == NULL && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } @@ -1245,8 +1265,7 @@ blk_part_exp(MACRO_PROT_ARGS) /* Flush out leading punctuation. */ - if (head == NULL && ac != ARGS_QWORD && - mdoc_isdelim(p) == DELIM_OPEN) { + if (head == NULL && mdoc_isdelim(p) == DELIM_OPEN) { dword(mdoc, line, la, p, DELIM_OPEN, 0); continue; } @@ -1281,7 +1300,7 @@ in_line_argn(MACRO_PROT_ARGS) struct mdoc_arg *arg; char *p; enum margserr ac; - int ntok; + enum roff_tok ntok; int state; /* arg#; -1: not yet open; -2: closed */ int la, maxargs, nl; @@ -1345,7 +1364,7 @@ in_line_argn(MACRO_PROT_ARGS) state = -2; } - ntok = (ac == ARGS_QWORD || (tok == MDOC_Pf && state == 0)) ? + ntok = (tok == MDOC_Pf && state == 0) ? TOKEN_NONE : lookup(mdoc, tok, line, la, p); if (ntok != TOKEN_NONE) { @@ -1357,8 +1376,7 @@ in_line_argn(MACRO_PROT_ARGS) break; } - if (ac == ARGS_QWORD || - mdoc_macros[tok].flags & MDOC_IGNDELIM || + if (mdoc_macros[tok].flags & MDOC_IGNDELIM || mdoc_isdelim(p) == DELIM_NONE) { if (state == -1) { mdoc_elem_alloc(mdoc, line, ppos, tok, arg); @@ -1371,12 +1389,12 @@ in_line_argn(MACRO_PROT_ARGS) } dword(mdoc, line, la, p, DELIM_MAX, - MDOC_JOIN & mdoc_macros[tok].flags); + mdoc_macros[tok].flags & MDOC_JOIN); } if (state == -1) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, mdoc_macronames[tok]); + line, ppos, roff_name[tok]); return; } @@ -1404,9 +1422,9 @@ in_line_eoln(MACRO_PROT_ARGS) } if (buf[*pos] == '\0' && - (tok == MDOC_Fd || mdoc_macronames[tok][0] == '%')) { + (tok == MDOC_Fd || *roff_name[tok] == '%')) { mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, - line, ppos, mdoc_macronames[tok]); + line, ppos, roff_name[tok]); return; } @@ -1423,7 +1441,8 @@ in_line_eoln(MACRO_PROT_ARGS) * or until the next macro, call that macro, and return 1. */ static int -parse_rest(struct roff_man *mdoc, int tok, int line, int *pos, char *buf) +parse_rest(struct roff_man *mdoc, enum roff_tok tok, + int line, int *pos, char *buf) { int la; diff --git a/contrib/mdocml/mdoc_man.c b/contrib/mdocml/mdoc_man.c index 2c19e15..cf552ce 100644 --- a/contrib/mdocml/mdoc_man.c +++ b/contrib/mdocml/mdoc_man.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_man.c,v 1.101 2017/01/11 17:39:53 schwarze Exp $ */ +/* $Id: mdoc_man.c,v 1.122 2017/06/14 22:51:25 schwarze Exp $ */ /* * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -20,6 +20,7 @@ #include <assert.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "mandoc_aux.h" @@ -32,10 +33,13 @@ #define DECL_ARGS const struct roff_meta *meta, struct roff_node *n +typedef int (*int_fp)(DECL_ARGS); +typedef void (*void_fp)(DECL_ARGS); + struct manact { - int (*cond)(DECL_ARGS); /* DON'T run actions */ - int (*pre)(DECL_ARGS); /* pre-node action */ - void (*post)(DECL_ARGS); /* post-node action */ + int_fp cond; /* DON'T run actions */ + int_fp pre; /* pre-node action */ + void_fp post; /* post-node action */ const char *prefix; /* pre-node string constant */ const char *suffix; /* post-node string constant */ }; @@ -44,6 +48,7 @@ static int cond_body(DECL_ARGS); static int cond_head(DECL_ARGS); static void font_push(char); static void font_pop(void); +static int man_strlen(const char *); static void mid_it(void); static void post__t(DECL_ARGS); static void post_aq(DECL_ARGS); @@ -68,7 +73,6 @@ static void post_nm(DECL_ARGS); static void post_percent(DECL_ARGS); static void post_pf(DECL_ARGS); static void post_sect(DECL_ARGS); -static void post_sp(DECL_ARGS); static void post_vt(DECL_ARGS); static int pre__t(DECL_ARGS); static int pre_an(DECL_ARGS); @@ -78,7 +82,7 @@ static int pre_bd(DECL_ARGS); static int pre_bf(DECL_ARGS); static int pre_bk(DECL_ARGS); static int pre_bl(DECL_ARGS); -static int pre_br(DECL_ARGS); +static void pre_br(DECL_ARGS); static int pre_dl(DECL_ARGS); static int pre_en(DECL_ARGS); static int pre_enc(DECL_ARGS); @@ -91,22 +95,24 @@ static int pre_fd(DECL_ARGS); static int pre_fl(DECL_ARGS); static int pre_fn(DECL_ARGS); static int pre_fo(DECL_ARGS); -static int pre_ft(DECL_ARGS); +static void pre_ft(DECL_ARGS); +static int pre_Ft(DECL_ARGS); static int pre_in(DECL_ARGS); static int pre_it(DECL_ARGS); static int pre_lk(DECL_ARGS); static int pre_li(DECL_ARGS); -static int pre_ll(DECL_ARGS); static int pre_nm(DECL_ARGS); static int pre_no(DECL_ARGS); static int pre_ns(DECL_ARGS); +static void pre_onearg(DECL_ARGS); static int pre_pp(DECL_ARGS); static int pre_rs(DECL_ARGS); static int pre_sm(DECL_ARGS); -static int pre_sp(DECL_ARGS); +static void pre_sp(DECL_ARGS); static int pre_sect(DECL_ARGS); static int pre_sy(DECL_ARGS); static void pre_syn(const struct roff_node *); +static void pre_ta(DECL_ARGS); static int pre_vt(DECL_ARGS); static int pre_xr(DECL_ARGS); static void print_word(const char *); @@ -118,8 +124,20 @@ static void print_width(const struct mdoc_bl *, static void print_count(int *); static void print_node(DECL_ARGS); -static const struct manact manacts[MDOC_MAX + 1] = { - { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */ +static const void_fp roff_manacts[ROFF_MAX] = { + pre_br, /* br */ + pre_onearg, /* ce */ + pre_ft, /* ft */ + pre_onearg, /* ll */ + pre_onearg, /* mc */ + pre_onearg, /* po */ + pre_onearg, /* rj */ + pre_sp, /* sp */ + pre_ta, /* ta */ + pre_onearg, /* ti */ +}; + +static const struct manact __manacts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, NULL, NULL, NULL }, /* Dd */ { NULL, NULL, NULL, NULL, NULL }, /* Dt */ { NULL, NULL, NULL, NULL, NULL }, /* Os */ @@ -135,6 +153,7 @@ static const struct manact manacts[MDOC_MAX + 1] = { { NULL, pre_it, post_it, NULL, NULL }, /* It */ { NULL, pre_em, post_font, NULL, NULL }, /* Ad */ { NULL, pre_an, NULL, NULL, NULL }, /* An */ + { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */ { NULL, pre_em, post_font, NULL, NULL }, /* Ar */ { NULL, pre_sy, post_font, NULL, NULL }, /* Cd */ { NULL, pre_sy, post_font, NULL, NULL }, /* Cm */ @@ -146,14 +165,14 @@ static const struct manact manacts[MDOC_MAX + 1] = { { NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */ { NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */ { NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */ - { NULL, pre_ft, post_font, NULL, NULL }, /* Ft */ + { NULL, pre_Ft, post_font, NULL, NULL }, /* Ft */ { NULL, pre_sy, post_font, NULL, NULL }, /* Ic */ { NULL, pre_in, post_in, NULL, NULL }, /* In */ { NULL, pre_li, post_font, NULL, NULL }, /* Li */ { 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_Ft, post_font, NULL, NULL }, /* Ot */ { NULL, pre_em, post_font, NULL, NULL }, /* Pa */ { NULL, pre_ex, NULL, NULL, NULL }, /* Rv */ { NULL, NULL, NULL, NULL, NULL }, /* St */ @@ -179,8 +198,8 @@ static const struct manact manacts[MDOC_MAX + 1] = { { NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */ { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */ { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */ - { NULL, NULL, NULL, NULL, NULL }, /* Bsx */ - { NULL, NULL, NULL, NULL, NULL }, /* Bx */ + { NULL, pre_bk, post_bk, NULL, NULL }, /* Bsx */ + { NULL, pre_bk, post_bk, NULL, NULL }, /* Bx */ { NULL, pre_skip, NULL, NULL, NULL }, /* Db */ { NULL, NULL, NULL, NULL, NULL }, /* Dc */ { cond_body, pre_enc, post_enc, "\\(Lq", "\\(Rq" }, /* Do */ @@ -189,12 +208,12 @@ static const struct manact manacts[MDOC_MAX + 1] = { { NULL, NULL, NULL, NULL, NULL }, /* Ef */ { NULL, pre_em, post_font, NULL, NULL }, /* Em */ { cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */ - { NULL, NULL, NULL, NULL, NULL }, /* Fx */ + { NULL, pre_bk, post_bk, NULL, NULL }, /* Fx */ { NULL, pre_sy, post_font, NULL, NULL }, /* Ms */ { NULL, pre_no, NULL, NULL, NULL }, /* No */ { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */ - { NULL, NULL, NULL, NULL, NULL }, /* Nx */ - { NULL, NULL, NULL, NULL, NULL }, /* Ox */ + { NULL, pre_bk, post_bk, NULL, NULL }, /* Nx */ + { NULL, pre_bk, post_bk, NULL, NULL }, /* Ox */ { NULL, NULL, NULL, NULL, NULL }, /* Pc */ { NULL, NULL, post_pf, NULL, NULL }, /* Pf */ { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */ @@ -235,15 +254,12 @@ static const struct manact manacts[MDOC_MAX + 1] = { { NULL, NULL, post_percent, NULL, NULL }, /* %C */ { NULL, pre_skip, NULL, NULL, NULL }, /* Es */ { cond_body, pre_en, post_en, NULL, NULL }, /* En */ - { NULL, NULL, NULL, NULL, NULL }, /* Dx */ + { NULL, pre_bk, post_bk, NULL, NULL }, /* Dx */ { NULL, NULL, post_percent, NULL, NULL }, /* %Q */ - { NULL, pre_br, NULL, NULL, NULL }, /* br */ - { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */ { NULL, NULL, post_percent, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ - { NULL, pre_ll, post_sp, NULL, NULL }, /* ll */ - { NULL, NULL, NULL, NULL, NULL }, /* ROOT */ }; +static const struct manact *const manacts = __manacts - MDOC_Dd; static int outflags; #define MMAN_spc (1 << 0) /* blank character before next word */ @@ -274,6 +290,49 @@ static struct { } fontqueue; +static int +man_strlen(const char *cp) +{ + size_t rsz; + int skip, sz; + + sz = 0; + skip = 0; + for (;;) { + rsz = strcspn(cp, "\\"); + if (rsz) { + cp += rsz; + if (skip) { + skip = 0; + rsz--; + } + sz += rsz; + } + if ('\0' == *cp) + break; + cp++; + switch (mandoc_escape(&cp, NULL, NULL)) { + case ESCAPE_ERROR: + return sz; + case ESCAPE_UNICODE: + case ESCAPE_NUMBERED: + case ESCAPE_SPECIAL: + case ESCAPE_OVERSTRIKE: + if (skip) + skip = 0; + else + sz++; + break; + case ESCAPE_SKIPCHAR: + skip = 1; + break; + default: + break; + } + } + return sz; +} + static void font_push(char newfont) { @@ -391,7 +450,6 @@ static void print_line(const char *s, int newflags) { - outflags &= ~MMAN_br; outflags |= MMAN_nl; print_word(s); outflags |= newflags; @@ -420,6 +478,7 @@ print_offs(const char *v, int keywords) { char buf[24]; struct roffsu su; + const char *end; int sz; print_line(".RS", MMAN_Bk_susp); @@ -431,8 +490,11 @@ print_offs(const char *v, int keywords) sz = 6; else if (keywords && !strcmp(v, "indent-two")) sz = 12; - else if (a2roffsu(v, &su, SCALE_EN) > 1) { - if (SCALE_EN == su.unit) + else { + end = a2roffsu(v, &su, SCALE_EN); + if (end == NULL || *end != '\0') + sz = man_strlen(v); + else if (SCALE_EN == su.unit) sz = su.scale; else { /* @@ -446,8 +508,7 @@ print_offs(const char *v, int keywords) outflags |= MMAN_nl; return; } - } else - sz = strlen(v); + } /* * We are inside an enclosing list. @@ -469,6 +530,7 @@ print_width(const struct mdoc_bl *bl, const struct roff_node *child) { char buf[24]; struct roffsu su; + const char *end; int numeric, remain, sz, chsz; numeric = 1; @@ -477,21 +539,23 @@ print_width(const struct mdoc_bl *bl, const struct roff_node *child) /* Convert the width into a number (of characters). */ if (bl->width == NULL) sz = (bl->type == LIST_hang) ? 6 : 0; - else if (a2roffsu(bl->width, &su, SCALE_MAX) > 1) { - if (SCALE_EN == su.unit) + else { + end = a2roffsu(bl->width, &su, SCALE_MAX); + if (end == NULL || *end != '\0') + sz = man_strlen(bl->width); + else if (SCALE_EN == su.unit) sz = su.scale; else { sz = 0; numeric = 0; } - } else - sz = strlen(bl->width); + } /* XXX Rough estimation, might have multiple parts. */ if (bl->type == LIST_enum) chsz = (bl->count > 8) + 1; else if (child != NULL && child->type == ROFFT_TEXT) - chsz = strlen(child->string); + chsz = man_strlen(child->string); else chsz = 0; @@ -607,7 +671,11 @@ print_node(DECL_ARGS) outflags &= ~(MMAN_spc | MMAN_spc_force); else if (outflags & MMAN_Sm) outflags |= MMAN_spc; + } else if (n->tok < ROFF_MAX) { + (*roff_manacts[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. @@ -639,9 +707,6 @@ print_node(DECL_ARGS) if (ENDBODY_NOT != n->end) n->body->flags |= NODE_ENDED; - - if (ENDBODY_NOSPACE == n->end) - outflags &= ~(MMAN_spc | MMAN_nl); } static int @@ -718,10 +783,8 @@ static int pre__t(DECL_ARGS) { - if (n->parent && MDOC_Rs == n->parent->tok && - n->parent->norm->Rs.quote_T) { - print_word(""); - putchar('\"'); + if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) { + print_word("\\(lq"); outflags &= ~MMAN_spc; } else font_push('I'); @@ -732,11 +795,9 @@ static void post__t(DECL_ARGS) { - if (n->parent && MDOC_Rs == n->parent->tok && - n->parent->norm->Rs.quote_T) { + if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) { outflags &= ~MMAN_spc; - print_word(""); - putchar('\"'); + print_word("\\(rq"); } else font_pop(); post_percent(meta, n); @@ -931,11 +992,11 @@ post_bf(DECL_ARGS) static int pre_bk(DECL_ARGS) { - switch (n->type) { case ROFFT_BLOCK: return 1; case ROFFT_BODY: + case ROFFT_ELEM: outflags |= MMAN_Bk; return 1; default: @@ -946,9 +1007,18 @@ pre_bk(DECL_ARGS) static void post_bk(DECL_ARGS) { - - if (n->type == ROFFT_BODY) + switch (n->type) { + case ROFFT_ELEM: + while ((n = n->parent) != NULL) + if (n->tok == MDOC_Bk) + return; + /* FALLTHROUGH */ + case ROFFT_BODY: outflags &= ~MMAN_Bk; + break; + default: + break; + } } static int @@ -1018,12 +1088,10 @@ post_bl(DECL_ARGS) } -static int +static void pre_br(DECL_ARGS) { - outflags |= MMAN_br; - return 0; } static int @@ -1268,7 +1336,7 @@ post_fo(DECL_ARGS) } static int -pre_ft(DECL_ARGS) +pre_Ft(DECL_ARGS) { pre_syn(n); @@ -1276,6 +1344,14 @@ pre_ft(DECL_ARGS) return 1; } +static void +pre_ft(DECL_ARGS) +{ + print_line(".ft", 0); + print_word(n->child->string); + outflags |= MMAN_nl; +} + static int pre_in(DECL_ARGS) { @@ -1470,33 +1546,63 @@ post_lb(DECL_ARGS) static int pre_lk(DECL_ARGS) { - const struct roff_node *link, *descr; + const struct roff_node *link, *descr, *punct; + int display; - if (NULL == (link = n->child)) + if ((link = n->child) == NULL) return 0; - if (NULL != (descr = link->next)) { + /* Find beginning of trailing punctuation. */ + punct = n->last; + while (punct != link && punct->flags & NODE_DELIMC) + punct = punct->prev; + punct = punct->next; + + /* Link text. */ + if ((descr = link->next) != NULL && descr != punct) { font_push('I'); - while (NULL != descr) { + while (descr != punct) { print_word(descr->string); descr = descr->next; } - print_word(":"); font_pop(); + print_word(":"); } + /* Link target. */ + display = man_strlen(link->string) >= 26; + if (display) { + print_line(".RS", MMAN_Bk_susp); + print_word("6n"); + outflags |= MMAN_nl; + } font_push('B'); print_word(link->string); font_pop(); + + /* Trailing punctuation. */ + while (punct != NULL) { + print_word(punct->string); + punct = punct->next; + } + if (display) + print_line(".RE", MMAN_nl); return 0; } -static int -pre_ll(DECL_ARGS) +static void +pre_onearg(DECL_ARGS) { - - print_line(".ll", 0); - return 1; + outflags |= MMAN_nl; + print_word("."); + outflags &= ~MMAN_spc; + print_word(roff_name[n->tok]); + if (n->child != NULL) + print_word(n->child->string); + outflags |= MMAN_nl; + if (n->tok == ROFF_ce) + for (n = n->child->next; n != NULL; n = n->next) + print_node(meta, n); } static int @@ -1518,19 +1624,17 @@ pre_nm(DECL_ARGS) } if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) return 1; - name = n->child ? n->child->string : meta->name; + name = n->child == NULL ? NULL : n->child->string; if (NULL == name) return 0; if (n->type == ROFFT_HEAD) { if (NULL == n->parent->prev) outflags |= MMAN_sp; print_block(".HP", 0); - printf(" %zun", strlen(name) + 1); + printf(" %dn", man_strlen(name) + 1); outflags |= MMAN_nl; } font_push('B'); - if (NULL == n->child) - print_word(meta->name); return 1; } @@ -1544,7 +1648,7 @@ post_nm(DECL_ARGS) break; case ROFFT_HEAD: case ROFFT_ELEM: - if (n->child != NULL || meta->name != NULL) + if (n->child != NULL && n->child->string != NULL) font_pop(); break; default: @@ -1622,22 +1726,17 @@ pre_sm(DECL_ARGS) return 0; } -static int +static void pre_sp(DECL_ARGS) { - - if (MMAN_PP & outflags) { + if (outflags & MMAN_PP) { outflags &= ~MMAN_PP; print_line(".PP", 0); - } else + } else { print_line(".sp", 0); - return 1; -} - -static void -post_sp(DECL_ARGS) -{ - + if (n->child != NULL) + print_word(n->child->string); + } outflags |= MMAN_nl; } @@ -1649,6 +1748,15 @@ pre_sy(DECL_ARGS) return 1; } +static void +pre_ta(DECL_ARGS) +{ + print_line(".ta", 0); + for (n = n->child; n != NULL; n = n->next) + print_word(n->string); + outflags |= MMAN_nl; +} + static int pre_vt(DECL_ARGS) { diff --git a/contrib/mdocml/mdoc_markdown.c b/contrib/mdocml/mdoc_markdown.c new file mode 100644 index 0000000..0b0f184 --- /dev/null +++ b/contrib/mdocml/mdoc_markdown.c @@ -0,0 +1,1569 @@ +/* $Id: mdoc_markdown.c,v 1.23 2017/06/14 01:31:26 schwarze Exp $ */ +/* + * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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 <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> + +#include "mandoc_aux.h" +#include "mandoc.h" +#include "roff.h" +#include "mdoc.h" +#include "main.h" + +struct md_act { + int (*cond)(struct roff_node *n); + int (*pre)(struct roff_node *n); + void (*post)(struct roff_node *n); + const char *prefix; /* pre-node string constant */ + const char *suffix; /* post-node string constant */ +}; + +static void md_nodelist(struct roff_node *); +static void md_node(struct roff_node *); +static const char *md_stack(char c); +static void md_preword(void); +static void md_rawword(const char *); +static void md_word(const char *); +static void md_named(const char *); +static void md_char(unsigned char); +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_raw(struct roff_node *); +static int md_pre_word(struct roff_node *); +static int md_pre_skip(struct roff_node *); +static void md_pre_syn(struct roff_node *); +static int md_pre_An(struct roff_node *); +static int md_pre_Ap(struct roff_node *); +static int md_pre_Bd(struct roff_node *); +static int md_pre_Bk(struct roff_node *); +static int md_pre_Bl(struct roff_node *); +static int md_pre_D1(struct roff_node *); +static int md_pre_Dl(struct roff_node *); +static int md_pre_En(struct roff_node *); +static int md_pre_Eo(struct roff_node *); +static int md_pre_Fa(struct roff_node *); +static int md_pre_Fd(struct roff_node *); +static int md_pre_Fn(struct roff_node *); +static int md_pre_Fo(struct roff_node *); +static int md_pre_In(struct roff_node *); +static int md_pre_It(struct roff_node *); +static int md_pre_Lk(struct roff_node *); +static int md_pre_Mt(struct roff_node *); +static int md_pre_Nd(struct roff_node *); +static int md_pre_Nm(struct roff_node *); +static int md_pre_No(struct roff_node *); +static int md_pre_Ns(struct roff_node *); +static int md_pre_Pp(struct roff_node *); +static int md_pre_Rs(struct roff_node *); +static int md_pre_Sh(struct roff_node *); +static int md_pre_Sm(struct roff_node *); +static int md_pre_Vt(struct roff_node *); +static int md_pre_Xr(struct roff_node *); +static int md_pre__T(struct roff_node *); +static int md_pre_br(struct roff_node *); + +static void md_post_raw(struct roff_node *); +static void md_post_word(struct roff_node *); +static void md_post_pc(struct roff_node *); +static void md_post_Bk(struct roff_node *); +static void md_post_Bl(struct roff_node *); +static void md_post_D1(struct roff_node *); +static void md_post_En(struct roff_node *); +static void md_post_Eo(struct roff_node *); +static void md_post_Fa(struct roff_node *); +static void md_post_Fd(struct roff_node *); +static void md_post_Fl(struct roff_node *); +static void md_post_Fn(struct roff_node *); +static void md_post_Fo(struct roff_node *); +static void md_post_In(struct roff_node *); +static void md_post_It(struct roff_node *); +static void md_post_Lb(struct roff_node *); +static void md_post_Nm(struct roff_node *); +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] = { + { NULL, NULL, NULL, NULL, NULL }, /* Dd */ + { NULL, NULL, NULL, NULL, NULL }, /* Dt */ + { NULL, NULL, NULL, NULL, NULL }, /* Os */ + { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */ + { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */ + { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */ + { md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */ + { md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */ + { md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */ + { NULL, NULL, NULL, NULL, NULL }, /* Ed */ + { md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */ + { NULL, NULL, NULL, NULL, NULL }, /* El */ + { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */ + { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */ + { NULL, md_pre_An, NULL, NULL, NULL }, /* An */ + { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */ + { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */ + { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */ + { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */ + { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */ + { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */ + { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */ + { NULL, NULL, NULL, NULL, NULL }, /* Ex */ + { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */ + { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */ + { NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */ + { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */ + { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */ + { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */ + { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */ + { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */ + { 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_raw, md_post_raw, "*", "*" }, /* Pa */ + { NULL, NULL, NULL, NULL, NULL }, /* Rv */ + { NULL, NULL, NULL, NULL, NULL }, /* St */ + { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */ + { NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */ + { NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %A */ + { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %D */ + { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */ + { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %N */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %O */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %P */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %R */ + { NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %V */ + { NULL, NULL, NULL, NULL, NULL }, /* Ac */ + { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */ + { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */ + { NULL, NULL, NULL, NULL, NULL }, /* At */ + { NULL, NULL, NULL, NULL, NULL }, /* Bc */ + { NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */ + { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */ + { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */ + { NULL, NULL, NULL, NULL, NULL }, /* Bsx */ + { NULL, NULL, NULL, NULL, NULL }, /* Bx */ + { NULL, NULL, NULL, NULL, NULL }, /* Db */ + { NULL, NULL, NULL, NULL, NULL }, /* Dc */ + { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */ + { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */ + { NULL, NULL, NULL, NULL, NULL }, /* Ec */ + { NULL, NULL, NULL, NULL, NULL }, /* Ef */ + { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */ + { md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */ + { NULL, NULL, NULL, NULL, NULL }, /* Fx */ + { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */ + { NULL, md_pre_No, NULL, NULL, NULL }, /* No */ + { NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */ + { NULL, NULL, NULL, NULL, NULL }, /* Nx */ + { NULL, NULL, NULL, NULL, NULL }, /* Ox */ + { NULL, NULL, NULL, NULL, NULL }, /* Pc */ + { NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */ + { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */ + { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */ + { NULL, NULL, NULL, NULL, NULL }, /* Qc */ + { md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */ + { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */ + { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */ + { NULL, NULL, NULL, NULL, NULL }, /* Re */ + { md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */ + { NULL, NULL, NULL, NULL, NULL }, /* Sc */ + { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */ + { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */ + { NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */ + { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */ + { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */ + { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */ + { NULL, NULL, NULL, NULL, NULL }, /* Ux */ + { NULL, NULL, NULL, NULL, NULL }, /* Xc */ + { NULL, NULL, NULL, NULL, NULL }, /* Xo */ + { NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */ + { NULL, NULL, NULL, NULL, NULL }, /* Fc */ + { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */ + { NULL, NULL, NULL, NULL, NULL }, /* Oc */ + { NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */ + { NULL, NULL, NULL, NULL, NULL }, /* Ek */ + { NULL, NULL, NULL, NULL, NULL }, /* Bt */ + { NULL, NULL, NULL, NULL, NULL }, /* Hf */ + { 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_Lk, NULL, NULL, NULL }, /* Lk */ + { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */ + { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */ + { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */ + { NULL, NULL, NULL, NULL, NULL }, /* Brc */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %C */ + { NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */ + { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */ + { NULL, NULL, NULL, NULL, NULL }, /* Dx */ + { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */ + { 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 int outflags; +#define MD_spc (1 << 0) /* Blank character before next word. */ +#define MD_spc_force (1 << 1) /* Even before trailing punctuation. */ +#define MD_nonl (1 << 2) /* Prevent linebreak in markdown code. */ +#define MD_nl (1 << 3) /* Break markdown code line. */ +#define MD_br (1 << 4) /* Insert an output line break. */ +#define MD_sp (1 << 5) /* Insert a paragraph break. */ +#define MD_Sm (1 << 6) /* Horizontal spacing mode. */ +#define MD_Bk (1 << 7) /* Word keep mode. */ +#define MD_An_split (1 << 8) /* Author mode is "split". */ +#define MD_An_nosplit (1 << 9) /* Author mode is "nosplit". */ + +static int escflags; /* Escape in generated markdown code: */ +#define ESC_BOL (1 << 0) /* "#*+-" near the beginning of a line. */ +#define ESC_NUM (1 << 1) /* "." after a leading number. */ +#define ESC_HYP (1 << 2) /* "(" immediately after "]". */ +#define ESC_SQU (1 << 4) /* "]" when "[" is open. */ +#define ESC_FON (1 << 5) /* "*" immediately after unrelated "*". */ +#define ESC_EOL (1 << 6) /* " " at the and of a line. */ + +static int code_blocks, quote_blocks, list_blocks; +static int outcount; + +void +markdown_mdoc(void *arg, const struct roff_man *mdoc) +{ + outflags = MD_Sm; + md_word(mdoc->meta.title); + if (mdoc->meta.msec != NULL) { + outflags &= ~MD_spc; + md_word("("); + md_word(mdoc->meta.msec); + md_word(")"); + } + md_word("-"); + md_word(mdoc->meta.vol); + if (mdoc->meta.arch != NULL) { + md_word("("); + md_word(mdoc->meta.arch); + md_word(")"); + } + outflags |= MD_sp; + + md_nodelist(mdoc->first->child); + + outflags |= MD_sp; + md_word(mdoc->meta.os); + md_word("-"); + md_word(mdoc->meta.date); + putchar('\n'); +} + +static void +md_nodelist(struct roff_node *n) +{ + while (n != NULL) { + md_node(n); + n = n->next; + } +} + +static void +md_node(struct roff_node *n) +{ + const struct md_act *act; + int cond, process_children; + + if (n->flags & NODE_NOPRT) + return; + + if (outflags & MD_nonl) + outflags &= ~(MD_nl | MD_sp); + else if (outflags & MD_spc && n->flags & NODE_LINE) + outflags |= MD_nl; + + act = NULL; + cond = 0; + process_children = 1; + n->flags &= ~NODE_ENDED; + + if (n->type == ROFFT_TEXT) { + if (n->flags & NODE_DELIMC) + outflags &= ~(MD_spc | MD_spc_force); + else if (outflags & MD_Sm) + outflags |= MD_spc_force; + md_word(n->string); + if (n->flags & NODE_DELIMO) + outflags &= ~(MD_spc | MD_spc_force); + else if (outflags & MD_Sm) + outflags |= MD_spc; + } else if (n->tok < ROFF_MAX) { + switch (n->tok) { + case ROFF_br: + process_children = md_pre_br(n); + break; + case ROFF_sp: + process_children = md_pre_Pp(n); + break; + default: + process_children = 0; + break; + } + } else { + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); + act = md_acts + n->tok; + cond = act->cond == NULL || (*act->cond)(n); + if (cond && act->pre != NULL && + (n->end == ENDBODY_NOT || n->child != NULL)) + process_children = (*act->pre)(n); + } + + if (process_children && n->child != NULL) + md_nodelist(n->child); + + if (n->flags & NODE_ENDED) + return; + + if (cond && act->post != NULL) + (*act->post)(n); + + if (n->end != ENDBODY_NOT) + n->body->flags |= NODE_ENDED; +} + +static const char * +md_stack(char c) +{ + static char *stack; + static size_t sz; + static size_t cur; + + switch (c) { + case '\0': + break; + case (char)-1: + assert(cur); + stack[--cur] = '\0'; + break; + default: + if (cur + 1 >= sz) { + sz += 8; + stack = mandoc_realloc(stack, sz); + } + stack[cur] = c; + stack[++cur] = '\0'; + break; + } + return stack == NULL ? "" : stack; +} + +/* + * Handle vertical and horizontal spacing. + */ +static void +md_preword(void) +{ + const char *cp; + + /* + * If a list block is nested inside a code block or a blockquote, + * blank lines for paragraph breaks no longer work; instead, + * they terminate the list. Work around this markdown issue + * by using mere line breaks instead. + */ + + if (list_blocks && outflags & MD_sp) { + outflags &= ~MD_sp; + outflags |= MD_br; + } + + /* + * End the old line if requested. + * Escape whitespace at the end of the markdown line + * such that it won't look like an output line break. + */ + + if (outflags & MD_sp) + putchar('\n'); + else if (outflags & MD_br) { + putchar(' '); + putchar(' '); + } else if (outflags & MD_nl && escflags & ESC_EOL) + md_named("zwnj"); + + /* Start a new line if necessary. */ + + if (outflags & (MD_nl | MD_br | MD_sp)) { + putchar('\n'); + for (cp = md_stack('\0'); *cp != '\0'; cp++) { + putchar(*cp); + if (*cp == '>') + putchar(' '); + } + outflags &= ~(MD_nl | MD_br | MD_sp); + escflags = ESC_BOL; + outcount = 0; + + /* Handle horizontal spacing. */ + + } else if (outflags & MD_spc) { + if (outflags & MD_Bk) + fputs(" ", stdout); + else + putchar(' '); + escflags &= ~ESC_FON; + outcount++; + } + + outflags &= ~(MD_spc_force | MD_nonl); + if (outflags & MD_Sm) + outflags |= MD_spc; + else + outflags &= ~MD_spc; +} + +/* + * Print markdown syntax elements. + * Can also be used for constant strings when neither escaping + * nor delimiter handling is required. + */ +static void +md_rawword(const char *s) +{ + md_preword(); + + if (*s == '\0') + return; + + if (escflags & ESC_FON) { + escflags &= ~ESC_FON; + if (*s == '*' && !code_blocks) + fputs("‌", stdout); + } + + while (*s != '\0') { + switch(*s) { + case '*': + if (s[1] == '\0') + escflags |= ESC_FON; + break; + case '[': + escflags |= ESC_SQU; + break; + case ']': + escflags |= ESC_HYP; + escflags &= ~ESC_SQU; + break; + default: + break; + } + md_char(*s++); + } + if (s[-1] == ' ') + escflags |= ESC_EOL; + else + escflags &= ~ESC_EOL; +} + +/* + * Print text and mdoc(7) syntax elements. + */ +static void +md_word(const char *s) +{ + const char *seq, *prevfont, *currfont, *nextfont; + char c; + int bs, sz, uc, breakline; + + /* No spacing before closing delimiters. */ + if (s[0] != '\0' && s[1] == '\0' && + strchr("!),.:;?]", s[0]) != NULL && + (outflags & MD_spc_force) == 0) + outflags &= ~MD_spc; + + md_preword(); + + if (*s == '\0') + return; + + /* No spacing after opening delimiters. */ + if ((s[0] == '(' || s[0] == '[') && s[1] == '\0') + outflags &= ~MD_spc; + + breakline = 0; + prevfont = currfont = ""; + while ((c = *s++) != '\0') { + bs = 0; + switch(c) { + case ASCII_NBRSP: + if (code_blocks) + c = ' '; + else { + md_named("nbsp"); + c = '\0'; + } + break; + case ASCII_HYPH: + bs = escflags & ESC_BOL && !code_blocks; + c = '-'; + break; + case ASCII_BREAK: + continue; + case '#': + case '+': + case '-': + bs = escflags & ESC_BOL && !code_blocks; + break; + case '(': + bs = escflags & ESC_HYP && !code_blocks; + break; + case ')': + bs = escflags & ESC_NUM && !code_blocks; + break; + case '*': + case '[': + case '_': + case '`': + bs = !code_blocks; + break; + case '.': + bs = escflags & ESC_NUM && !code_blocks; + break; + case '<': + if (code_blocks == 0) { + md_named("lt"); + c = '\0'; + } + break; + case '=': + if (escflags & ESC_BOL && !code_blocks) { + md_named("equals"); + c = '\0'; + } + break; + case '>': + if (code_blocks == 0) { + md_named("gt"); + c = '\0'; + } + break; + case '\\': + uc = 0; + nextfont = NULL; + switch (mandoc_escape(&s, &seq, &sz)) { + case ESCAPE_UNICODE: + uc = mchars_num2uc(seq + 1, sz - 1); + break; + case ESCAPE_NUMBERED: + uc = mchars_num2char(seq, sz); + break; + case ESCAPE_SPECIAL: + uc = mchars_spec2cp(seq, sz); + break; + case ESCAPE_FONTBOLD: + nextfont = "**"; + break; + case ESCAPE_FONTITALIC: + nextfont = "*"; + break; + case ESCAPE_FONTBI: + nextfont = "***"; + break; + case ESCAPE_FONT: + case ESCAPE_FONTROMAN: + nextfont = ""; + break; + case ESCAPE_FONTPREV: + nextfont = prevfont; + break; + case ESCAPE_BREAK: + breakline = 1; + break; + case ESCAPE_NOSPACE: + case ESCAPE_SKIPCHAR: + case ESCAPE_OVERSTRIKE: + /* XXX not implemented */ + /* FALLTHROUGH */ + case ESCAPE_ERROR: + default: + break; + } + if (nextfont != NULL && !code_blocks) { + if (*currfont != '\0') { + outflags &= ~MD_spc; + md_rawword(currfont); + } + prevfont = currfont; + currfont = nextfont; + if (*currfont != '\0') { + outflags &= ~MD_spc; + md_rawword(currfont); + } + } + if (uc) { + if ((uc < 0x20 && uc != 0x09) || + (uc > 0x7E && uc < 0xA0)) + uc = 0xFFFD; + if (code_blocks) { + seq = mchars_uc2str(uc); + fputs(seq, stdout); + outcount += strlen(seq); + } else { + printf("&#%d;", uc); + outcount++; + } + escflags &= ~ESC_FON; + } + c = '\0'; + break; + case ']': + bs = escflags & ESC_SQU && !code_blocks; + escflags |= ESC_HYP; + break; + default: + break; + } + if (bs) + putchar('\\'); + md_char(c); + if (breakline && + (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) { + printf(" \n"); + breakline = 0; + while (*s == ' ' || *s == ASCII_NBRSP) + s++; + } + } + if (*currfont != '\0') { + outflags &= ~MD_spc; + md_rawword(currfont); + } else if (s[-2] == ' ') + escflags |= ESC_EOL; + else + escflags &= ~ESC_EOL; +} + +/* + * Print a single HTML named character reference. + */ +static void +md_named(const char *s) +{ + printf("&%s;", s); + escflags &= ~(ESC_FON | ESC_EOL); + outcount++; +} + +/* + * Print a single raw character and maintain certain escape flags. + */ +static void +md_char(unsigned char c) +{ + if (c != '\0') { + putchar(c); + if (c == '*') + escflags |= ESC_FON; + else + escflags &= ~ESC_FON; + outcount++; + } + if (c != ']') + escflags &= ~ESC_HYP; + if (c == ' ' || c == '\t' || c == '>') + return; + if (isdigit(c) == 0) + escflags &= ~ESC_NUM; + else if (escflags & ESC_BOL) + escflags |= ESC_NUM; + escflags &= ~ESC_BOL; +} + +static int +md_cond_head(struct roff_node *n) +{ + return n->type == ROFFT_HEAD; +} + +static int +md_cond_body(struct roff_node *n) +{ + return n->type == ROFFT_BODY; +} + +static int +md_pre_raw(struct roff_node *n) +{ + const char *prefix; + + if ((prefix = md_acts[n->tok].prefix) != NULL) { + md_rawword(prefix); + outflags &= ~MD_spc; + if (*prefix == '`') + code_blocks++; + } + return 1; +} + +static void +md_post_raw(struct roff_node *n) +{ + const char *suffix; + + if ((suffix = md_acts[n->tok].suffix) != NULL) { + outflags &= ~(MD_spc | MD_nl); + md_rawword(suffix); + if (*suffix == '`') + code_blocks--; + } +} + +static int +md_pre_word(struct roff_node *n) +{ + const char *prefix; + + if ((prefix = md_acts[n->tok].prefix) != NULL) { + md_word(prefix); + outflags &= ~MD_spc; + } + return 1; +} + +static void +md_post_word(struct roff_node *n) +{ + const char *suffix; + + if ((suffix = md_acts[n->tok].suffix) != NULL) { + outflags &= ~(MD_spc | MD_nl); + md_word(suffix); + } +} + +static void +md_post_pc(struct roff_node *n) +{ + md_post_raw(n); + if (n->parent->tok != MDOC_Rs) + return; + if (n->next != NULL) { + md_word(","); + if (n->prev != NULL && + n->prev->tok == n->tok && + n->next->tok == n->tok) + md_word("and"); + } else { + md_word("."); + outflags |= MD_nl; + } +} + +static int +md_pre_skip(struct roff_node *n) +{ + return 0; +} + +static void +md_pre_syn(struct roff_node *n) +{ + if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY)) + return; + + if (n->prev->tok == n->tok && + n->tok != MDOC_Ft && + n->tok != MDOC_Fo && + n->tok != MDOC_Fn) { + outflags |= MD_br; + return; + } + + switch (n->prev->tok) { + case MDOC_Fd: + case MDOC_Fn: + case MDOC_Fo: + case MDOC_In: + case MDOC_Vt: + outflags |= MD_sp; + break; + case MDOC_Ft: + if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) { + outflags |= MD_sp; + break; + } + /* FALLTHROUGH */ + default: + outflags |= MD_br; + break; + } +} + +static int +md_pre_An(struct roff_node *n) +{ + switch (n->norm->An.auth) { + case AUTH_split: + outflags &= ~MD_An_nosplit; + outflags |= MD_An_split; + return 0; + case AUTH_nosplit: + outflags &= ~MD_An_split; + outflags |= MD_An_nosplit; + return 0; + default: + if (outflags & MD_An_split) + outflags |= MD_br; + else if (n->sec == SEC_AUTHORS && + ! (outflags & MD_An_nosplit)) + outflags |= MD_An_split; + return 1; + } +} + +static int +md_pre_Ap(struct roff_node *n) +{ + outflags &= ~MD_spc; + md_word("'"); + outflags &= ~MD_spc; + return 0; +} + +static int +md_pre_Bd(struct roff_node *n) +{ + switch (n->norm->Bd.type) { + case DISP_unfilled: + case DISP_literal: + return md_pre_Dl(n); + default: + return md_pre_D1(n); + } +} + +static int +md_pre_Bk(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BLOCK: + return 1; + case ROFFT_BODY: + outflags |= MD_Bk; + return 1; + default: + return 0; + } +} + +static void +md_post_Bk(struct roff_node *n) +{ + if (n->type == ROFFT_BODY) + outflags &= ~MD_Bk; +} + +static int +md_pre_Bl(struct roff_node *n) +{ + n->norm->Bl.count = 0; + if (n->norm->Bl.type == LIST_column) + md_pre_Dl(n); + outflags |= MD_sp; + return 1; +} + +static void +md_post_Bl(struct roff_node *n) +{ + n->norm->Bl.count = 0; + if (n->norm->Bl.type == LIST_column) + md_post_D1(n); + outflags |= MD_sp; +} + +static int +md_pre_D1(struct roff_node *n) +{ + /* + * Markdown blockquote syntax does not work inside code blocks. + * The best we can do is fall back to another nested code block. + */ + if (code_blocks) { + md_stack('\t'); + code_blocks++; + } else { + md_stack('>'); + quote_blocks++; + } + outflags |= MD_sp; + return 1; +} + +static void +md_post_D1(struct roff_node *n) +{ + md_stack((char)-1); + if (code_blocks) + code_blocks--; + else + quote_blocks--; + outflags |= MD_sp; +} + +static int +md_pre_Dl(struct roff_node *n) +{ + /* + * Markdown code block syntax does not work inside blockquotes. + * The best we can do is fall back to another nested blockquote. + */ + if (quote_blocks) { + md_stack('>'); + quote_blocks++; + } else { + md_stack('\t'); + code_blocks++; + } + outflags |= MD_sp; + return 1; +} + +static int +md_pre_En(struct roff_node *n) +{ + if (n->norm->Es == NULL || + n->norm->Es->child == NULL) + return 1; + + md_word(n->norm->Es->child->string); + outflags &= ~MD_spc; + return 1; +} + +static void +md_post_En(struct roff_node *n) +{ + if (n->norm->Es == NULL || + n->norm->Es->child == NULL || + n->norm->Es->child->next == NULL) + return; + + outflags &= ~MD_spc; + md_word(n->norm->Es->child->next->string); +} + +static int +md_pre_Eo(struct roff_node *n) +{ + if (n->end == ENDBODY_NOT && + n->parent->head->child == NULL && + n->child != NULL && + n->child->end != ENDBODY_NOT) + md_preword(); + else if (n->end != ENDBODY_NOT ? n->child != NULL : + n->parent->head->child != NULL && (n->child != NULL || + (n->parent->tail != NULL && n->parent->tail->child != NULL))) + outflags &= ~(MD_spc | MD_nl); + return 1; +} + +static void +md_post_Eo(struct roff_node *n) +{ + if (n->end != ENDBODY_NOT) { + outflags |= MD_spc; + return; + } + + if (n->child == NULL && n->parent->head->child == NULL) + return; + + if (n->parent->tail != NULL && n->parent->tail->child != NULL) + outflags &= ~MD_spc; + else + outflags |= MD_spc; +} + +static int +md_pre_Fa(struct roff_node *n) +{ + int am_Fa; + + am_Fa = n->tok == MDOC_Fa; + + if (am_Fa) + n = n->child; + + while (n != NULL) { + md_rawword("*"); + outflags &= ~MD_spc; + md_node(n); + outflags &= ~MD_spc; + md_rawword("*"); + if ((n = n->next) != NULL) + md_word(","); + } + return 0; +} + +static void +md_post_Fa(struct roff_node *n) +{ + if (n->next != NULL && n->next->tok == MDOC_Fa) + md_word(","); +} + +static int +md_pre_Fd(struct roff_node *n) +{ + md_pre_syn(n); + md_pre_raw(n); + return 1; +} + +static void +md_post_Fd(struct roff_node *n) +{ + md_post_raw(n); + outflags |= MD_br; +} + +static void +md_post_Fl(struct roff_node *n) +{ + md_post_raw(n); + if (n->child == NULL && n->next != NULL && + n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE)) + outflags &= ~MD_spc; +} + +static int +md_pre_Fn(struct roff_node *n) +{ + md_pre_syn(n); + + if ((n = n->child) == NULL) + return 0; + + md_rawword("**"); + outflags &= ~MD_spc; + md_node(n); + outflags &= ~MD_spc; + md_rawword("**"); + outflags &= ~MD_spc; + md_word("("); + + if ((n = n->next) != NULL) + md_pre_Fa(n); + return 0; +} + +static void +md_post_Fn(struct roff_node *n) +{ + md_word(")"); + if (n->flags & NODE_SYNPRETTY) { + md_word(";"); + outflags |= MD_sp; + } +} + +static int +md_pre_Fo(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BLOCK: + md_pre_syn(n); + break; + case ROFFT_HEAD: + if (n->child == NULL) + return 0; + md_pre_raw(n); + break; + case ROFFT_BODY: + outflags &= ~(MD_spc | MD_nl); + md_word("("); + break; + default: + break; + } + return 1; +} + +static void +md_post_Fo(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_HEAD: + if (n->child != NULL) + md_post_raw(n); + break; + case ROFFT_BODY: + md_post_Fn(n); + break; + default: + break; + } +} + +static int +md_pre_In(struct roff_node *n) +{ + if (n->flags & NODE_SYNPRETTY) { + md_pre_syn(n); + md_rawword("**"); + outflags &= ~MD_spc; + md_word("#include <"); + } else { + md_word("<"); + outflags &= ~MD_spc; + md_rawword("*"); + } + outflags &= ~MD_spc; + return 1; +} + +static void +md_post_In(struct roff_node *n) +{ + if (n->flags & NODE_SYNPRETTY) { + outflags &= ~MD_spc; + md_rawword(">**"); + outflags |= MD_nl; + } else { + outflags &= ~MD_spc; + md_rawword("*>"); + } +} + +static int +md_pre_It(struct roff_node *n) +{ + struct roff_node *bln; + + switch (n->type) { + case ROFFT_BLOCK: + return 1; + + case ROFFT_HEAD: + bln = n->parent->parent; + if (bln->norm->Bl.comp == 0 && + bln->norm->Bl.type != LIST_column) + outflags |= MD_sp; + outflags |= MD_nl; + + switch (bln->norm->Bl.type) { + case LIST_item: + outflags |= MD_br; + return 0; + case LIST_inset: + case LIST_diag: + case LIST_ohang: + outflags |= MD_br; + return 1; + case LIST_tag: + case LIST_hang: + outflags |= MD_sp; + return 1; + case LIST_bullet: + md_rawword("*\t"); + break; + case LIST_dash: + case LIST_hyphen: + md_rawword("-\t"); + break; + case LIST_enum: + md_preword(); + if (bln->norm->Bl.count < 99) + bln->norm->Bl.count++; + printf("%d.\t", bln->norm->Bl.count); + escflags &= ~ESC_FON; + break; + case LIST_column: + outflags |= MD_br; + return 0; + default: + return 0; + } + outflags &= ~MD_spc; + outflags |= MD_nonl; + outcount = 0; + md_stack('\t'); + if (code_blocks || quote_blocks) + list_blocks++; + return 0; + + case ROFFT_BODY: + bln = n->parent->parent; + switch (bln->norm->Bl.type) { + case LIST_ohang: + outflags |= MD_br; + break; + case LIST_tag: + case LIST_hang: + md_pre_D1(n); + break; + default: + break; + } + return 1; + + default: + return 0; + } +} + +static void +md_post_It(struct roff_node *n) +{ + struct roff_node *bln; + int i, nc; + + if (n->type != ROFFT_BODY) + return; + + bln = n->parent->parent; + switch (bln->norm->Bl.type) { + case LIST_bullet: + case LIST_dash: + case LIST_hyphen: + case LIST_enum: + md_stack((char)-1); + if (code_blocks || quote_blocks) + list_blocks--; + break; + case LIST_tag: + case LIST_hang: + md_post_D1(n); + break; + + case LIST_column: + if (n->next == NULL) + break; + + /* Calculate the array index of the current column. */ + + i = 0; + while ((n = n->prev) != NULL && n->type != ROFFT_HEAD) + i++; + + /* + * If a width was specified for this column, + * subtract what printed, and + * add the same spacing as in mdoc_term.c. + */ + + nc = bln->norm->Bl.ncols; + i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount + + (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1; + if (i < 1) + i = 1; + while (i-- > 0) + putchar(' '); + + outflags &= ~MD_spc; + escflags &= ~ESC_FON; + outcount = 0; + break; + + default: + break; + } +} + +static void +md_post_Lb(struct roff_node *n) +{ + if (n->sec == SEC_LIBRARY) + outflags |= MD_br; +} + +static void +md_uri(const char *s) +{ + while (*s != '\0') { + if (strchr("%()<>", *s) != NULL) { + printf("%%%2.2hhX", *s); + outcount += 3; + } else { + putchar(*s); + outcount++; + } + s++; + } +} + +static int +md_pre_Lk(struct roff_node *n) +{ + const struct roff_node *link, *descr, *punct; + + if ((link = n->child) == NULL) + return 0; + + /* Find beginning of trailing punctuation. */ + punct = n->last; + while (punct != link && punct->flags & NODE_DELIMC) + punct = punct->prev; + punct = punct->next; + + /* Link text. */ + descr = link->next; + if (descr == punct) + descr = link; /* no text */ + md_rawword("["); + outflags &= ~MD_spc; + do { + md_word(descr->string); + descr = descr->next; + } while (descr != punct); + outflags &= ~MD_spc; + + /* Link target. */ + md_rawword("]("); + md_uri(link->string); + outflags &= ~MD_spc; + md_rawword(")"); + + /* Trailing punctuation. */ + while (punct != NULL) { + md_word(punct->string); + punct = punct->next; + } + return 0; +} + +static int +md_pre_Mt(struct roff_node *n) +{ + const struct roff_node *nch; + + md_rawword("["); + outflags &= ~MD_spc; + for (nch = n->child; nch != NULL; nch = nch->next) + md_word(nch->string); + outflags &= ~MD_spc; + md_rawword("](mailto:"); + for (nch = n->child; nch != NULL; nch = nch->next) { + md_uri(nch->string); + if (nch->next != NULL) { + putchar(' '); + outcount++; + } + } + outflags &= ~MD_spc; + md_rawword(")"); + return 0; +} + +static int +md_pre_Nd(struct roff_node *n) +{ + outflags &= ~MD_nl; + outflags |= MD_spc; + md_word("-"); + return 1; +} + +static int +md_pre_Nm(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BLOCK: + outflags |= MD_Bk; + md_pre_syn(n); + break; + case ROFFT_HEAD: + case ROFFT_ELEM: + md_pre_raw(n); + break; + default: + break; + } + return 1; +} + +static void +md_post_Nm(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BLOCK: + outflags &= ~MD_Bk; + break; + case ROFFT_HEAD: + case ROFFT_ELEM: + md_post_raw(n); + break; + default: + break; + } +} + +static int +md_pre_No(struct roff_node *n) +{ + outflags |= MD_spc_force; + return 1; +} + +static int +md_pre_Ns(struct roff_node *n) +{ + outflags &= ~MD_spc; + return 0; +} + +static void +md_post_Pf(struct roff_node *n) +{ + if (n->next != NULL && (n->next->flags & NODE_LINE) == 0) + outflags &= ~MD_spc; +} + +static int +md_pre_Pp(struct roff_node *n) +{ + outflags |= MD_sp; + return 0; +} + +static int +md_pre_Rs(struct roff_node *n) +{ + if (n->sec == SEC_SEE_ALSO) + outflags |= MD_sp; + return 1; +} + +static int +md_pre_Sh(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BLOCK: + if (n->sec == SEC_AUTHORS) + outflags &= ~(MD_An_split | MD_An_nosplit); + break; + case ROFFT_HEAD: + outflags |= MD_sp; + md_rawword(n->tok == MDOC_Sh ? "#" : "##"); + break; + case ROFFT_BODY: + outflags |= MD_sp; + break; + default: + break; + } + return 1; +} + +static int +md_pre_Sm(struct roff_node *n) +{ + if (n->child == NULL) + outflags ^= MD_Sm; + else if (strcmp("on", n->child->string) == 0) + outflags |= MD_Sm; + else + outflags &= ~MD_Sm; + + if (outflags & MD_Sm) + outflags |= MD_spc; + + return 0; +} + +static int +md_pre_Vt(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BLOCK: + md_pre_syn(n); + return 1; + case ROFFT_BODY: + case ROFFT_ELEM: + md_pre_raw(n); + return 1; + default: + return 0; + } +} + +static void +md_post_Vt(struct roff_node *n) +{ + switch (n->type) { + case ROFFT_BODY: + case ROFFT_ELEM: + md_post_raw(n); + break; + default: + break; + } +} + +static int +md_pre_Xr(struct roff_node *n) +{ + n = n->child; + if (n == NULL) + return 0; + md_node(n); + n = n->next; + if (n == NULL) + return 0; + outflags &= ~MD_spc; + md_word("("); + md_node(n); + md_word(")"); + return 0; +} + +static int +md_pre__T(struct roff_node *n) +{ + if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) + md_word("\""); + else + md_rawword("*"); + outflags &= ~MD_spc; + return 1; +} + +static void +md_post__T(struct roff_node *n) +{ + outflags &= ~MD_spc; + if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) + md_word("\""); + else + md_rawword("*"); + md_post_pc(n); +} + +static int +md_pre_br(struct roff_node *n) +{ + outflags |= MD_br; + return 0; +} diff --git a/contrib/mdocml/mdoc_state.c b/contrib/mdocml/mdoc_state.c index 4e376ef..d9cad18 100644 --- a/contrib/mdocml/mdoc_state.c +++ b/contrib/mdocml/mdoc_state.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_state.c,v 1.4 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: mdoc_state.c,v 1.8 2017/05/05 15:17:32 schwarze Exp $ */ /* - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,6 +16,7 @@ */ #include <sys/types.h> +#include <assert.h> #include <stdlib.h> #include <string.h> @@ -35,8 +36,7 @@ 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] = { - NULL, /* Ap */ +static const state_handler __state_handlers[MDOC_MAX - MDOC_Dd] = { NULL, /* Dd */ NULL, /* Dt */ NULL, /* Os */ @@ -52,6 +52,7 @@ static const state_handler state_handlers[MDOC_MAX] = { NULL, /* It */ NULL, /* Ad */ NULL, /* An */ + NULL, /* Ap */ NULL, /* Ar */ NULL, /* Cd */ NULL, /* Cm */ @@ -154,12 +155,10 @@ static const state_handler state_handlers[MDOC_MAX] = { NULL, /* En */ NULL, /* Dx */ NULL, /* %Q */ - NULL, /* br */ - NULL, /* sp */ NULL, /* %U */ NULL, /* Ta */ - NULL, /* ll */ }; +static const state_handler *const state_handlers = __state_handlers - MDOC_Dd; void @@ -167,9 +166,10 @@ mdoc_state(struct roff_man *mdoc, struct roff_node *n) { state_handler handler; - if (n->tok == TOKEN_NONE) + if (n->tok == TOKEN_NONE || n->tok < ROFF_MAX) return; + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); if ( ! (mdoc_macros[n->tok].flags & MDOC_PROLOGUE)) mdoc->flags |= MDOC_PBODY; diff --git a/contrib/mdocml/mdoc_term.c b/contrib/mdocml/mdoc_term.c index dad54c8..4e420c5 100644 --- a/contrib/mdocml/mdoc_term.c +++ b/contrib/mdocml/mdoc_term.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_term.c,v 1.341 2017/01/11 17:39:53 schwarze Exp $ */ +/* $Id: mdoc_term.c,v 1.364 2017/06/14 17:51:15 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -106,7 +106,6 @@ static int termp_ft_pre(DECL_ARGS); static int termp_in_pre(DECL_ARGS); static int termp_it_pre(DECL_ARGS); static int termp_li_pre(DECL_ARGS); -static int termp_ll_pre(DECL_ARGS); static int termp_lk_pre(DECL_ARGS); static int termp_nd_pre(DECL_ARGS); static int termp_nm_pre(DECL_ARGS); @@ -116,7 +115,7 @@ static int termp_rs_pre(DECL_ARGS); static int termp_sh_pre(DECL_ARGS); static int termp_skip_pre(DECL_ARGS); static int termp_sm_pre(DECL_ARGS); -static int termp_sp_pre(DECL_ARGS); +static int termp_pp_pre(DECL_ARGS); static int termp_ss_pre(DECL_ARGS); static int termp_sy_pre(DECL_ARGS); static int termp_tag_pre(DECL_ARGS); @@ -125,14 +124,13 @@ 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] = { - { termp_ap_pre, NULL }, /* Ap */ +static const struct termact __termacts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL }, /* Dd */ { NULL, NULL }, /* Dt */ { NULL, NULL }, /* Os */ { termp_sh_pre, termp_sh_post }, /* Sh */ { termp_ss_pre, termp_ss_post }, /* Ss */ - { termp_sp_pre, NULL }, /* Pp */ + { termp_pp_pre, NULL }, /* Pp */ { termp_d1_pre, termp_bl_post }, /* D1 */ { termp_d1_pre, termp_bl_post }, /* Dl */ { termp_bd_pre, termp_bd_post }, /* Bd */ @@ -142,6 +140,7 @@ static const struct termact termacts[MDOC_MAX] = { { termp_it_pre, termp_it_post }, /* It */ { termp_under_pre, NULL }, /* Ad */ { termp_an_pre, NULL }, /* An */ + { termp_ap_pre, NULL }, /* Ap */ { termp_under_pre, NULL }, /* Ar */ { termp_cd_pre, NULL }, /* Cd */ { termp_bold_pre, NULL }, /* Cm */ @@ -233,7 +232,7 @@ static const struct termact termacts[MDOC_MAX] = { { termp_under_pre, NULL }, /* Fr */ { NULL, NULL }, /* Ud */ { NULL, termp_lb_post }, /* Lb */ - { termp_sp_pre, NULL }, /* Lp */ + { termp_pp_pre, NULL }, /* Lp */ { termp_lk_pre, NULL }, /* Lk */ { termp_under_pre, NULL }, /* Mt */ { termp_quote_pre, termp_quote_post }, /* Brq */ @@ -244,25 +243,26 @@ static const struct termact termacts[MDOC_MAX] = { { termp_quote_pre, termp_quote_post }, /* En */ { termp_xx_pre, termp_xx_post }, /* Dx */ { NULL, termp____post }, /* %Q */ - { termp_sp_pre, NULL }, /* br */ - { termp_sp_pre, NULL }, /* sp */ { NULL, termp____post }, /* %U */ { NULL, NULL }, /* Ta */ - { termp_ll_pre, NULL }, /* ll */ }; +static const struct termact *const termacts = __termacts - MDOC_Dd; static int fn_prio; + void terminal_mdoc(void *arg, const struct roff_man *mdoc) { struct roff_node *n; struct termp *p; + size_t save_defindent; p = (struct termp *)arg; - p->overstep = 0; - p->rmargin = p->maxrmargin = p->defrmargin; - p->tabwidth = term_len(p, 5); + p->tcol->rmargin = p->maxrmargin = p->defrmargin; + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); n = mdoc->first->child; if (p->synopsisonly) { @@ -278,6 +278,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc) n = n->next; } } else { + save_defindent = p->defindent; if (p->defindent == 0) p->defindent = 5; term_begin(p, print_mdoc_head, print_mdoc_foot, @@ -290,6 +291,7 @@ terminal_mdoc(void *arg, const struct roff_man *mdoc) print_mdoc_nodelist(p, NULL, &mdoc->meta, n); } term_end(p); + p->defindent = save_defindent; } } @@ -314,8 +316,8 @@ print_mdoc_node(DECL_ARGS) return; chld = 1; - offset = p->offset; - rmargin = p->rmargin; + offset = p->tcol->offset; + rmargin = p->tcol->rmargin; n->flags &= ~NODE_ENDED; n->prev_font = p->fonti; @@ -339,7 +341,8 @@ print_mdoc_node(DECL_ARGS) switch (n->type) { case ROFFT_TEXT: - if (' ' == *n->string && NODE_LINE & n->flags) + if (*n->string == ' ' && n->flags & NODE_LINE && + (p->flags & TERMP_NONEWLINE) == 0) term_newln(p); if (NODE_DELIMC & n->flags) p->flags |= TERMP_NOSPACE; @@ -360,7 +363,12 @@ print_mdoc_node(DECL_ARGS) term_tbl(p, n->span); break; default: - if (termacts[n->tok].pre && + if (n->tok < ROFF_MAX) { + roff_term_pre(p, n); + return; + } + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); + if (termacts[n->tok].pre != NULL && (n->end == ENDBODY_NOT || n->child != NULL)) chld = (*termacts[n->tok].pre) (p, &npair, meta, n); @@ -381,7 +389,7 @@ print_mdoc_node(DECL_ARGS) case ROFFT_EQN: break; default: - if ( ! termacts[n->tok].post || NODE_ENDED & n->flags) + if (termacts[n->tok].post == NULL || n->flags & NODE_ENDED) break; (void)(*termacts[n->tok].post)(p, &npair, meta, n); @@ -392,24 +400,15 @@ print_mdoc_node(DECL_ARGS) */ if (ENDBODY_NOT != n->end) n->body->flags |= NODE_ENDED; - - /* - * End of line terminating an implicit block - * while an explicit block is still open. - * Continue the explicit block without spacing. - */ - if (ENDBODY_NOSPACE == n->end) - p->flags |= TERMP_NOSPACE; break; } if (NODE_EOS & n->flags) p->flags |= TERMP_SENTENCE; - if (MDOC_ll != n->tok) { - p->offset = offset; - p->rmargin = rmargin; - } + if (n->type != ROFFT_TEXT) + p->tcol->offset = offset; + p->tcol->rmargin = rmargin; } static void @@ -429,9 +428,9 @@ print_mdoc_foot(struct termp *p, const struct roff_meta *meta) term_vspace(p); - p->offset = 0; + p->tcol->offset = 0; sz = term_strlen(p, meta->date); - p->rmargin = p->maxrmargin > sz ? + p->tcol->rmargin = p->maxrmargin > sz ? (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0; p->trailspace = 1; p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; @@ -439,16 +438,16 @@ print_mdoc_foot(struct termp *p, const struct roff_meta *meta) term_word(p, meta->os); term_flushln(p); - p->offset = p->rmargin; + p->tcol->offset = p->tcol->rmargin; sz = term_strlen(p, meta->os); - p->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0; + p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0; p->flags |= TERMP_NOSPACE; term_word(p, meta->date); term_flushln(p); - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~TERMP_NOBREAK; p->flags |= TERMP_NOSPACE; @@ -456,8 +455,8 @@ print_mdoc_foot(struct termp *p, const struct roff_meta *meta) term_word(p, meta->os); term_flushln(p); - p->offset = 0; - p->rmargin = p->maxrmargin; + p->tcol->offset = 0; + p->tcol->rmargin = p->maxrmargin; p->flags = 0; } @@ -497,8 +496,8 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta) p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; p->trailspace = 1; - p->offset = 0; - p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? + p->tcol->offset = 0; + p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? (p->maxrmargin - vollen + term_len(p, 1)) / 2 : vollen < p->maxrmargin ? p->maxrmargin - vollen : 0; @@ -506,26 +505,26 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta) term_flushln(p); p->flags |= TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->offset + vollen + titlen < p->maxrmargin ? - p->maxrmargin - titlen : p->maxrmargin; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->tcol->offset + vollen + titlen < + p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin; term_word(p, volume); term_flushln(p); p->flags &= ~TERMP_NOBREAK; p->trailspace = 0; - if (p->rmargin + titlen <= p->maxrmargin) { + if (p->tcol->rmargin + titlen <= p->maxrmargin) { p->flags |= TERMP_NOSPACE; - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; term_word(p, title); term_flushln(p); } p->flags &= ~TERMP_NOSPACE; - p->offset = 0; - p->rmargin = p->maxrmargin; + p->tcol->offset = 0; + p->tcol->rmargin = p->maxrmargin; free(title); free(volume); } @@ -534,12 +533,14 @@ static int a2width(const struct termp *p, const char *v) { struct roffsu su; + const char *end; - if (a2roffsu(v, &su, SCALE_MAX) < 2) { + end = a2roffsu(v, &su, SCALE_MAX); + if (end == NULL || *end != '\0') { SCALE_HS_INIT(&su, term_strlen(p, v)); su.scale /= term_strlen(p, "0"); } - return term_hspan(p, &su) / 24; + return term_hen(p, &su); } /* @@ -601,16 +602,9 @@ print_bvspace(struct termp *p, static int -termp_ll_pre(DECL_ARGS) -{ - - term_setwidth(p, n->child != NULL ? n->child->string : NULL); - return 0; -} - -static int termp_it_pre(DECL_ARGS) { + struct roffsu su; char buf[24]; const struct roff_node *bl, *nn; size_t ncols, dcol; @@ -657,8 +651,8 @@ termp_it_pre(DECL_ARGS) if (bl->norm->Bl.offs != NULL) { offset = a2width(p, bl->norm->Bl.offs); - if (offset < 0 && (size_t)(-offset) > p->offset) - offset = -p->offset; + if (offset < 0 && (size_t)(-offset) > p->tcol->offset) + offset = -p->tcol->offset; else if (offset > SHRT_MAX) offset = 0; } @@ -688,9 +682,12 @@ termp_it_pre(DECL_ARGS) for (i = 0, nn = n->prev; nn->prev && i < (int)ncols; - nn = nn->prev, i++) - offset += dcol + a2width(p, - bl->norm->Bl.cols[i]); + nn = nn->prev, i++) { + SCALE_HS_INIT(&su, + term_strlen(p, bl->norm->Bl.cols[i])); + su.scale /= term_strlen(p, "0"); + offset += term_hen(p, &su) + dcol; + } /* * When exceeding the declared number of columns, leave @@ -705,7 +702,9 @@ termp_it_pre(DECL_ARGS) * Use the declared column widths, extended as explained * in the preceding paragraph. */ - width = a2width(p, bl->norm->Bl.cols[i]) + dcol; + SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i])); + su.scale /= term_strlen(p, "0"); + width = term_hen(p, &su) + dcol; break; default: if (NULL == bl->norm->Bl.width) @@ -717,8 +716,8 @@ termp_it_pre(DECL_ARGS) * handling for column for how this changes. */ width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); - if (width < 0 && (size_t)(-width) > p->offset) - width = -p->offset; + if (width < 0 && (size_t)(-width) > p->tcol->offset) + width = -p->tcol->offset; else if (width > SHRT_MAX) width = 0; break; @@ -767,33 +766,15 @@ termp_it_pre(DECL_ARGS) case LIST_bullet: case LIST_dash: case LIST_hyphen: - /* - * Weird special case. - * Some very narrow lists actually hang. - */ - if (width <= (int)term_len(p, 2)) - p->flags |= TERMP_HANG; - if (n->type != ROFFT_HEAD) - break; - p->flags |= TERMP_NOBREAK; - p->trailspace = 1; + if (n->type == ROFFT_HEAD) { + p->flags |= TERMP_NOBREAK | TERMP_HANG; + p->trailspace = 1; + } else if (width <= (int)term_len(p, 2)) + p->flags |= TERMP_NOPAD; break; case LIST_hang: if (n->type != ROFFT_HEAD) break; - - /* - * This is ugly. If `-hang' is specified and the body - * is a `Bl' or `Bd', then we want basically to nullify - * the "overstep" effect in term_flushln() and treat - * this as a `-ohang' list instead. - */ - if (NULL != n->next && - NULL != n->next->child && - (MDOC_Bl == n->next->child->tok || - MDOC_Bd == n->next->child->tok)) - break; - p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; p->trailspace = 1; break; @@ -805,7 +786,7 @@ termp_it_pre(DECL_ARGS) p->trailspace = 2; if (NULL == n->next || NULL == n->next->child) - p->flags |= TERMP_DANGLE; + p->flags |= TERMP_HANG; break; case LIST_column: if (n->type == ROFFT_HEAD) @@ -836,43 +817,31 @@ termp_it_pre(DECL_ARGS) * necessarily lengthened. Everybody gets the offset. */ - p->offset += offset; + p->tcol->offset += offset; switch (type) { - case LIST_hang: - /* - * Same stipulation as above, regarding `-hang'. We - * don't want to recalculate rmargin and offsets when - * using `Bd' or `Bl' within `-hang' overstep lists. - */ - if (n->type == ROFFT_HEAD && - NULL != n->next && - NULL != n->next->child && - (MDOC_Bl == n->next->child->tok || - MDOC_Bd == n->next->child->tok)) - break; - /* FALLTHROUGH */ case LIST_bullet: case LIST_dash: case LIST_enum: case LIST_hyphen: + case LIST_hang: case LIST_tag: if (n->type == ROFFT_HEAD) - p->rmargin = p->offset + width; + p->tcol->rmargin = p->tcol->offset + width; else - p->offset += width; + p->tcol->offset += width; break; case LIST_column: assert(width); - p->rmargin = p->offset + width; + p->tcol->rmargin = p->tcol->offset + width; /* * XXX - this behaviour is not documented: the * right-most column is filled to the right margin. */ if (n->type == ROFFT_HEAD) break; - if (NULL == n->next && p->rmargin < p->maxrmargin) - p->rmargin = p->maxrmargin; + if (n->next == NULL && p->tcol->rmargin < p->maxrmargin) + p->tcol->rmargin = p->maxrmargin; break; default: break; @@ -922,6 +891,7 @@ termp_it_pre(DECL_ARGS) case LIST_column: if (n->type == ROFFT_HEAD) return 0; + p->minbl = 0; break; default: break; @@ -962,8 +932,7 @@ termp_it_post(DECL_ARGS) * has munged them in the meanwhile. */ - p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | - TERMP_DANGLE | TERMP_HANG); + p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG); p->trailspace = 0; } @@ -978,7 +947,7 @@ termp_nm_pre(DECL_ARGS) } if (n->type == ROFFT_BODY) { - if (NULL == n->child) + if (n->child == NULL) return 0; p->flags |= TERMP_NOSPACE; cp = NULL; @@ -987,38 +956,37 @@ termp_nm_pre(DECL_ARGS) if (cp == NULL) cp = meta->name; if (cp == NULL) - p->offset += term_len(p, 6); + p->tcol->offset += term_len(p, 6); else - p->offset += term_len(p, 1) + term_strlen(p, cp); + p->tcol->offset += term_len(p, 1) + + term_strlen(p, cp); return 1; } - if (NULL == n->child && NULL == meta->name) + if (n->child == NULL) return 0; if (n->type == ROFFT_HEAD) synopsis_pre(p, n->parent); if (n->type == ROFFT_HEAD && - NULL != n->next && NULL != n->next->child) { + n->next != NULL && n->next->child != NULL) { p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND; p->trailspace = 1; - p->rmargin = p->offset + term_len(p, 1); - if (NULL == n->child) { - p->rmargin += term_strlen(p, meta->name); - } else if (n->child->type == ROFFT_TEXT) { - p->rmargin += term_strlen(p, n->child->string); - if (n->child->next) + p->tcol->rmargin = p->tcol->offset + term_len(p, 1); + if (n->child == NULL) + p->tcol->rmargin += term_strlen(p, meta->name); + else if (n->child->type == ROFFT_TEXT) { + p->tcol->rmargin += term_strlen(p, n->child->string); + if (n->child->next != NULL) p->flags |= TERMP_HANG; } else { - p->rmargin += term_len(p, 5); + p->tcol->rmargin += term_len(p, 5); p->flags |= TERMP_HANG; } } term_fontpush(p, TERMFONT_BOLD); - if (NULL == n->child) - term_word(p, meta->name); return 1; } @@ -1136,8 +1104,14 @@ static void termp_bl_post(DECL_ARGS) { - if (n->type == ROFFT_BLOCK) - term_newln(p); + if (n->type != ROFFT_BLOCK) + return; + term_newln(p); + if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column) + return; + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); } static int @@ -1279,7 +1253,10 @@ termp_sh_pre(DECL_ARGS) term_fontpush(p, TERMFONT_BOLD); break; case ROFFT_BODY: - p->offset = term_len(p, p->defindent); + p->tcol->offset = term_len(p, p->defindent); + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); switch (n->sec) { case SEC_DESCRIPTION: fn_prio = 0; @@ -1307,7 +1284,7 @@ termp_sh_post(DECL_ARGS) break; case ROFFT_BODY: term_newln(p); - p->offset = 0; + p->tcol->offset = 0; break; default: break; @@ -1329,7 +1306,10 @@ termp_d1_pre(DECL_ARGS) if (n->type != ROFFT_BLOCK) return 1; term_newln(p); - p->offset += term_len(p, p->defindent + 1); + p->tcol->offset += term_len(p, p->defindent + 1); + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); return 1; } @@ -1357,8 +1337,8 @@ termp_fn_pre(DECL_ARGS) return 0; if (pretty) { - rmargin = p->rmargin; - p->rmargin = p->offset + term_len(p, 4); + rmargin = p->tcol->rmargin; + p->tcol->rmargin = p->tcol->offset + term_len(p, 4); p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; } @@ -1373,8 +1353,9 @@ termp_fn_pre(DECL_ARGS) if (pretty) { term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); - p->offset = p->rmargin; - p->rmargin = rmargin; + p->flags |= TERMP_NOPAD; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = rmargin; } p->flags |= TERMP_NOSPACE; @@ -1435,7 +1416,7 @@ termp_fa_pre(DECL_ARGS) static int termp_bd_pre(DECL_ARGS) { - size_t tabwidth, lm, len, rm, rmax; + size_t lm, len; struct roff_node *nn; int offset; @@ -1451,15 +1432,15 @@ termp_bd_pre(DECL_ARGS) ! strcmp(n->norm->Bd.offs, "left")) /* nothing */; else if ( ! strcmp(n->norm->Bd.offs, "indent")) - p->offset += term_len(p, p->defindent + 1); + p->tcol->offset += term_len(p, p->defindent + 1); else if ( ! strcmp(n->norm->Bd.offs, "indent-two")) - p->offset += term_len(p, (p->defindent + 1) * 2); + p->tcol->offset += term_len(p, (p->defindent + 1) * 2); else { offset = a2width(p, n->norm->Bd.offs); - if (offset < 0 && (size_t)(-offset) > p->offset) - p->offset = 0; + if (offset < 0 && (size_t)(-offset) > p->tcol->offset) + p->tcol->offset = 0; else if (offset < SHRT_MAX) - p->offset += offset; + p->tcol->offset += offset; } /* @@ -1470,29 +1451,29 @@ termp_bd_pre(DECL_ARGS) * lines are allowed. */ - if (DISP_literal != n->norm->Bd.type && - DISP_unfilled != n->norm->Bd.type && - DISP_centered != n->norm->Bd.type) + if (n->norm->Bd.type != DISP_literal && + n->norm->Bd.type != DISP_unfilled && + n->norm->Bd.type != DISP_centered) return 1; - tabwidth = p->tabwidth; - if (DISP_literal == n->norm->Bd.type) - p->tabwidth = term_len(p, 8); - - lm = p->offset; - rm = p->rmargin; - rmax = p->maxrmargin; - p->rmargin = p->maxrmargin = TERM_MAXMARGIN; + if (n->norm->Bd.type == DISP_literal) { + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, "8n"); + } - for (nn = n->child; nn; nn = nn->next) { - if (DISP_centered == n->norm->Bd.type) { + 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->offset = len >= rm ? 0 : - lm + len >= rm ? rm - len : - (lm + rm - len) / 2; + 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->offset = lm; + p->tcol->offset = lm; } print_mdoc_node(p, pair, meta, nn); /* @@ -1501,10 +1482,10 @@ termp_bd_pre(DECL_ARGS) * 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_br: - case MDOC_sp: case MDOC_Bl: case MDOC_D1: case MDOC_Dl: @@ -1520,33 +1501,21 @@ termp_bd_pre(DECL_ARGS) term_flushln(p); p->flags |= TERMP_NOSPACE; } - - p->tabwidth = tabwidth; - p->rmargin = rm; - p->maxrmargin = rmax; + p->flags &= ~TERMP_BRNEVER; return 0; } static void termp_bd_post(DECL_ARGS) { - size_t rm, rmax; - if (n->type != ROFFT_BODY) return; - - rm = p->rmargin; - rmax = p->maxrmargin; - if (DISP_literal == n->norm->Bd.type || DISP_unfilled == n->norm->Bd.type) - p->rmargin = p->maxrmargin = TERM_MAXMARGIN; - + p->flags |= TERMP_BRNEVER; p->flags |= TERMP_NOSPACE; term_newln(p); - - p->rmargin = rm; - p->maxrmargin = rmax; + p->flags &= ~TERMP_BRNEVER; } static int @@ -1588,10 +1557,13 @@ termp_ss_pre(DECL_ARGS) break; case ROFFT_HEAD: term_fontpush(p, TERMFONT_BOLD); - p->offset = term_len(p, (p->defindent+1)/2); + p->tcol->offset = term_len(p, (p->defindent+1)/2); break; case ROFFT_BODY: - p->offset = term_len(p, p->defindent); + p->tcol->offset = term_len(p, p->defindent); + term_tab_set(p, NULL); + term_tab_set(p, "T"); + term_tab_set(p, ".5i"); break; default: break; @@ -1651,37 +1623,10 @@ termp_in_post(DECL_ARGS) } static int -termp_sp_pre(DECL_ARGS) +termp_pp_pre(DECL_ARGS) { - struct roffsu su; - int i, len; - - switch (n->tok) { - case MDOC_sp: - if (n->child) { - if ( ! a2roffsu(n->child->string, &su, SCALE_VS)) - su.scale = 1.0; - len = term_vspan(p, &su); - } else - len = 1; - break; - case MDOC_br: - len = 0; - break; - default: - len = 1; - fn_prio = 0; - break; - } - - if (0 == len) - term_newln(p); - else if (len < 0) - p->skipvsp -= len; - else - for (i = 0; i < len; i++) - term_vspace(p); - + fn_prio = 0; + term_vspace(p); return 0; } @@ -1715,6 +1660,8 @@ termp_quote_pre(DECL_ARGS) case MDOC_Bq: term_word(p, "["); break; + case MDOC__T: + /* FALLTHROUGH */ case MDOC_Do: case MDOC_Dq: term_word(p, "\\(Lq"); @@ -1729,7 +1676,6 @@ termp_quote_pre(DECL_ARGS) case MDOC_Pq: term_word(p, "("); break; - case MDOC__T: case MDOC_Qo: case MDOC_Qq: term_word(p, "\""); @@ -1772,6 +1718,8 @@ termp_quote_post(DECL_ARGS) case MDOC_Bq: term_word(p, "]"); break; + case MDOC__T: + /* FALLTHROUGH */ case MDOC_Do: case MDOC_Dq: term_word(p, "\\(Rq"); @@ -1788,7 +1736,6 @@ termp_quote_post(DECL_ARGS) case MDOC_Pq: term_word(p, ")"); break; - case MDOC__T: case MDOC_Qo: case MDOC_Qq: term_word(p, "\""); @@ -1860,8 +1807,8 @@ termp_fo_pre(DECL_ARGS) return 1; } else if (n->type == ROFFT_BODY) { if (pretty) { - rmargin = p->rmargin; - p->rmargin = p->offset + term_len(p, 4); + rmargin = p->tcol->rmargin; + p->tcol->rmargin = p->tcol->offset + term_len(p, 4); p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; } @@ -1872,8 +1819,9 @@ termp_fo_pre(DECL_ARGS) term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); - p->offset = p->rmargin; - p->rmargin = rmargin; + p->flags |= TERMP_NOPAD; + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = rmargin; } return 1; } @@ -1991,26 +1939,50 @@ termp_li_pre(DECL_ARGS) static int termp_lk_pre(DECL_ARGS) { - const struct roff_node *link, *descr; + const struct roff_node *link, *descr, *punct; + int display; - if (NULL == (link = n->child)) + if ((link = n->child) == NULL) return 0; - if (NULL != (descr = link->next)) { + /* Find beginning of trailing punctuation. */ + punct = n->last; + while (punct != link && punct->flags & NODE_DELIMC) + punct = punct->prev; + punct = punct->next; + + /* Link text. */ + if ((descr = link->next) != NULL && descr != punct) { term_fontpush(p, TERMFONT_UNDER); - while (NULL != descr) { + while (descr != punct) { + if (descr->flags & (NODE_DELIMC | NODE_DELIMO)) + p->flags |= TERMP_NOSPACE; term_word(p, descr->string); descr = descr->next; } + term_fontpop(p); p->flags |= TERMP_NOSPACE; term_word(p, ":"); - term_fontpop(p); } + /* Link target. */ + display = term_strlen(p, link->string) >= 26; + if (display) { + term_newln(p); + p->tcol->offset += term_len(p, p->defindent + 1); + } term_fontpush(p, TERMFONT_BOLD); term_word(p, link->string); term_fontpop(p); + /* Trailing punctuation. */ + while (punct != NULL) { + p->flags |= TERMP_NOSPACE; + term_word(p, punct->string); + punct = punct->next; + } + if (display) + term_newln(p); return 0; } diff --git a/contrib/mdocml/mdoc_validate.c b/contrib/mdocml/mdoc_validate.c index 043145a..de36bb8 100644 --- a/contrib/mdocml/mdoc_validate.c +++ b/contrib/mdocml/mdoc_validate.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_validate.c,v 1.317 2017/01/11 17:39:53 schwarze Exp $ */ +/* $Id: mdoc_validate.c,v 1.350 2017/07/20 12:54:02 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -33,6 +33,7 @@ #include "mandoc_aux.h" #include "mandoc.h" +#include "mandoc_xr.h" #include "roff.h" #include "mdoc.h" #include "libmandoc.h" @@ -56,9 +57,11 @@ static void check_text(struct roff_man *, int, int, char *); static void check_argv(struct roff_man *, struct roff_node *, struct mdoc_argv *); static void check_args(struct roff_man *, struct roff_node *); +static void check_toptext(struct roff_man *, int, int, const char *); static int child_an(const struct roff_node *); -static size_t macro2len(int); -static void rewrite_macro2len(char **); +static size_t macro2len(enum roff_tok); +static void rewrite_macro2len(struct roff_man *, char **); +static int similar(const char *, const char *); static void post_an(POST_ARGS); static void post_an_norm(POST_ARGS); @@ -74,6 +77,8 @@ static void post_bx(POST_ARGS); static void post_defaults(POST_ARGS); static void post_display(POST_ARGS); static void post_dd(POST_ARGS); +static void post_delim(POST_ARGS); +static void post_delim_nb(POST_ARGS); static void post_dt(POST_ARGS); static void post_en(POST_ARGS); static void post_es(POST_ARGS); @@ -105,11 +110,12 @@ static void post_sh_authors(POST_ARGS); static void post_sm(POST_ARGS); static void post_st(POST_ARGS); static void post_std(POST_ARGS); +static void post_sx(POST_ARGS); +static void post_useless(POST_ARGS); static void post_xr(POST_ARGS); static void post_xx(POST_ARGS); -static v_post mdoc_valids[MDOC_MAX] = { - NULL, /* Ap */ +static const v_post __mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_dd, /* Dd */ post_dt, /* Dt */ post_os, /* Os */ @@ -123,32 +129,33 @@ static v_post mdoc_valids[MDOC_MAX] = { post_bl, /* Bl */ NULL, /* El */ post_it, /* It */ - NULL, /* Ad */ + post_delim_nb, /* Ad */ post_an, /* An */ + NULL, /* Ap */ post_defaults, /* Ar */ NULL, /* Cd */ - NULL, /* Cm */ - NULL, /* Dv */ - NULL, /* Er */ - NULL, /* Ev */ + post_delim_nb, /* Cm */ + post_delim_nb, /* Dv */ + post_delim_nb, /* Er */ + post_delim_nb, /* Ev */ post_ex, /* Ex */ post_fa, /* Fa */ NULL, /* Fd */ - NULL, /* Fl */ + post_delim_nb, /* Fl */ post_fn, /* Fn */ - NULL, /* Ft */ - NULL, /* Ic */ - NULL, /* In */ + post_delim_nb, /* Ft */ + post_delim_nb, /* Ic */ + post_delim_nb, /* In */ post_defaults, /* Li */ post_nd, /* Nd */ post_nm, /* Nm */ - NULL, /* Op */ + post_delim_nb, /* Op */ post_obsolete, /* Ot */ post_defaults, /* Pa */ post_rv, /* Rv */ post_st, /* St */ - NULL, /* Va */ - NULL, /* Vt */ + post_delim_nb, /* Va */ + post_delim_nb, /* Vt */ post_xr, /* Xr */ NULL, /* %A */ post_hyph, /* %B */ /* FIXME: can be used outside Rs/Re. */ @@ -162,12 +169,12 @@ static v_post mdoc_valids[MDOC_MAX] = { post_hyph, /* %T */ /* FIXME: can be used outside Rs/Re. */ NULL, /* %V */ NULL, /* Ac */ - NULL, /* Ao */ - NULL, /* Aq */ + post_delim_nb, /* Ao */ + post_delim_nb, /* Aq */ post_at, /* At */ NULL, /* Bc */ post_bf, /* Bf */ - NULL, /* Bo */ + post_delim_nb, /* Bo */ NULL, /* Bq */ post_xx, /* Bsx */ post_bx, /* Bx */ @@ -177,66 +184,64 @@ static v_post mdoc_valids[MDOC_MAX] = { NULL, /* Dq */ NULL, /* Ec */ NULL, /* Ef */ - NULL, /* Em */ + post_delim_nb, /* Em */ NULL, /* Eo */ post_xx, /* Fx */ - NULL, /* Ms */ + post_delim_nb, /* Ms */ NULL, /* No */ post_ns, /* Ns */ post_xx, /* Nx */ post_xx, /* Ox */ NULL, /* Pc */ NULL, /* Pf */ - NULL, /* Po */ - NULL, /* Pq */ + post_delim_nb, /* Po */ + post_delim_nb, /* Pq */ NULL, /* Qc */ - NULL, /* Ql */ - NULL, /* Qo */ - NULL, /* Qq */ + post_delim_nb, /* Ql */ + post_delim_nb, /* Qo */ + post_delim_nb, /* Qq */ NULL, /* Re */ post_rs, /* Rs */ NULL, /* Sc */ - NULL, /* So */ - NULL, /* Sq */ + post_delim_nb, /* So */ + post_delim_nb, /* Sq */ post_sm, /* Sm */ - post_hyph, /* Sx */ - NULL, /* Sy */ - NULL, /* Tn */ + post_sx, /* Sx */ + post_delim_nb, /* Sy */ + post_useless, /* Tn */ post_xx, /* Ux */ NULL, /* Xc */ NULL, /* Xo */ post_fo, /* Fo */ NULL, /* Fc */ - NULL, /* Oo */ + post_delim_nb, /* Oo */ NULL, /* Oc */ post_bk, /* Bk */ NULL, /* Ek */ post_eoln, /* Bt */ - NULL, /* Hf */ + post_obsolete, /* Hf */ post_obsolete, /* Fr */ post_eoln, /* Ud */ post_lb, /* Lb */ post_par, /* Lp */ - NULL, /* Lk */ + post_delim_nb, /* Lk */ post_defaults, /* Mt */ - NULL, /* Brq */ - NULL, /* Bro */ + post_delim_nb, /* Brq */ + post_delim_nb, /* Bro */ NULL, /* Brc */ NULL, /* %C */ post_es, /* Es */ post_en, /* En */ post_xx, /* Dx */ NULL, /* %Q */ - post_par, /* br */ - post_par, /* sp */ NULL, /* %U */ NULL, /* Ta */ - NULL, /* ll */ }; +static const v_post *const mdoc_valids = __mdoc_valids - MDOC_Dd; #define RSORD_MAX 14 /* Number of `Rs' blocks. */ -static const int rsord[RSORD_MAX] = { +static const enum roff_tok rsord[RSORD_MAX] = { MDOC__A, MDOC__T, MDOC__B, @@ -284,7 +289,7 @@ void mdoc_node_validate(struct roff_man *mdoc) { struct roff_node *n; - v_post *p; + const v_post *p; n = mdoc->last; mdoc->last = mdoc->last->child; @@ -303,6 +308,11 @@ mdoc_node_validate(struct roff_man *mdoc) if (n->sec != SEC_SYNOPSIS || (n->parent->tok != MDOC_Cd && n->parent->tok != MDOC_Fd)) check_text(mdoc, n->line, n->pos, n->string); + if (n->parent->tok == MDOC_It || + (n->parent->type == ROFFT_BODY && + (n->parent->tok == MDOC_Sh || + n->parent->tok == MDOC_Ss))) + check_toptext(mdoc, n->line, n->pos, n->string); break; case ROFFT_EQN: case ROFFT_TBL: @@ -326,6 +336,20 @@ mdoc_node_validate(struct roff_man *mdoc) /* Call the macro's postprocessor. */ + if (n->tok < ROFF_MAX) { + switch(n->tok) { + case ROFF_br: + case ROFF_sp: + post_par(mdoc); + break; + default: + roff_validate(mdoc); + break; + } + break; + } + + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); p = mdoc_valids + n->tok; if (*p) (*p)(mdoc); @@ -371,6 +395,164 @@ check_text(struct roff_man *mdoc, int ln, int pos, char *p) } static void +check_toptext(struct roff_man *mdoc, int ln, int pos, const char *p) +{ + const char *cp, *cpr; + + if (*p == '\0') + return; + + if ((cp = strstr(p, "OpenBSD")) != NULL) + mandoc_msg(MANDOCERR_BX, mdoc->parse, + ln, pos + (cp - p), "Ox"); + if ((cp = strstr(p, "NetBSD")) != NULL) + mandoc_msg(MANDOCERR_BX, mdoc->parse, + ln, pos + (cp - p), "Nx"); + if ((cp = strstr(p, "FreeBSD")) != NULL) + mandoc_msg(MANDOCERR_BX, mdoc->parse, + ln, pos + (cp - p), "Fx"); + if ((cp = strstr(p, "DragonFly")) != NULL) + mandoc_msg(MANDOCERR_BX, mdoc->parse, + ln, pos + (cp - p), "Dx"); + + cp = p; + while ((cp = strstr(cp + 1, "()")) != NULL) { + for (cpr = cp - 1; cpr >= p; cpr--) + if (*cpr != '_' && !isalnum((unsigned char)*cpr)) + break; + if ((cpr < p || *cpr == ' ') && cpr + 1 < cp) { + cpr++; + mandoc_vmsg(MANDOCERR_FUNC, mdoc->parse, + ln, pos + (cpr - p), + "%.*s()", (int)(cp - cpr), cpr); + } + } +} + +static void +post_delim(POST_ARGS) +{ + const struct roff_node *nch; + const char *lc; + enum mdelim delim; + enum roff_tok tok; + + tok = mdoc->last->tok; + nch = mdoc->last->last; + if (nch == NULL || nch->type != ROFFT_TEXT) + return; + lc = strchr(nch->string, '\0') - 1; + if (lc < nch->string) + return; + delim = mdoc_isdelim(lc); + if (delim == DELIM_NONE || delim == DELIM_OPEN) + return; + if (*lc == ')' && (tok == MDOC_Nd || tok == MDOC_Sh || + tok == MDOC_Ss || tok == MDOC_Fo)) + return; + + mandoc_vmsg(MANDOCERR_DELIM, mdoc->parse, + nch->line, nch->pos + (lc - nch->string), + "%s%s %s", roff_name[tok], + nch == mdoc->last->child ? "" : " ...", nch->string); +} + +static void +post_delim_nb(POST_ARGS) +{ + const struct roff_node *nch; + const char *lc, *cp; + int nw; + enum mdelim delim; + enum roff_tok tok; + + /* + * Find candidates: at least two bytes, + * the last one a closing or middle delimiter. + */ + + tok = mdoc->last->tok; + nch = mdoc->last->last; + if (nch == NULL || nch->type != ROFFT_TEXT) + return; + lc = strchr(nch->string, '\0') - 1; + if (lc <= nch->string) + return; + delim = mdoc_isdelim(lc); + if (delim == DELIM_NONE || delim == DELIM_OPEN) + return; + + /* + * Reduce false positives by allowing various cases. + */ + + /* Escaped delimiters. */ + if (lc > nch->string + 1 && lc[-2] == '\\' && + (lc[-1] == '&' || lc[-1] == 'e')) + return; + + /* Specific byte sequences. */ + switch (*lc) { + case ')': + for (cp = lc; cp >= nch->string; cp--) + if (*cp == '(') + return; + break; + case '.': + if (lc > nch->string + 1 && lc[-2] == '.' && lc[-1] == '.') + return; + if (lc[-1] == '.') + return; + break; + case ';': + if (tok == MDOC_Vt) + return; + break; + case '?': + if (lc[-1] == '?') + return; + break; + case ']': + for (cp = lc; cp >= nch->string; cp--) + if (*cp == '[') + return; + break; + case '|': + if (lc == nch->string + 1 && lc[-1] == '|') + return; + default: + break; + } + + /* Exactly two non-alphanumeric bytes. */ + if (lc == nch->string + 1 && !isalnum((unsigned char)lc[-1])) + return; + + /* At least three alphabetic words with a sentence ending. */ + if (strchr("!.:?", *lc) != NULL && (tok == MDOC_Em || + tok == MDOC_Li || tok == MDOC_Po || tok == MDOC_Pq || + tok == MDOC_Sy)) { + nw = 0; + for (cp = lc - 1; cp >= nch->string; cp--) { + if (*cp == ' ') { + nw++; + if (cp > nch->string && cp[-1] == ',') + cp--; + } else if (isalpha((unsigned int)*cp)) { + if (nw > 1) + return; + } else + break; + } + } + + mandoc_vmsg(MANDOCERR_DELIM_NB, mdoc->parse, + nch->line, nch->pos + (lc - nch->string), + "%s%s %s", roff_name[tok], + nch == mdoc->last->child ? "" : " ...", nch->string); +} + +static void post_bl_norm(POST_ARGS) { struct roff_node *n; @@ -450,7 +632,7 @@ post_bl_norm(POST_ARGS) mdoc->parse, argv->line, argv->pos, "Bl -width %s", argv->value[0]); - rewrite_macro2len(argv->value); + rewrite_macro2len(mdoc, argv->value); n->norm->Bl.width = argv->value[0]; break; case MDOC_Offset: @@ -465,7 +647,7 @@ post_bl_norm(POST_ARGS) mdoc->parse, argv->line, argv->pos, "Bl -offset %s", argv->value[0]); - rewrite_macro2len(argv->value); + rewrite_macro2len(mdoc, argv->value); n->norm->Bl.offs = argv->value[0]; break; default: @@ -518,7 +700,7 @@ post_bl_norm(POST_ARGS) switch (n->norm->Bl.type) { case LIST_tag: - if (NULL == n->norm->Bl.width) + if (n->norm->Bl.width == NULL) mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse, n->line, n->pos, "Bl -tag"); break; @@ -527,19 +709,20 @@ post_bl_norm(POST_ARGS) case LIST_ohang: case LIST_inset: case LIST_item: - if (n->norm->Bl.width) + if (n->norm->Bl.width != NULL) mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse, wa->line, wa->pos, "Bl -%s", mdoc_argnames[mdoclt]); + n->norm->Bl.width = NULL; break; case LIST_bullet: case LIST_dash: case LIST_hyphen: - if (NULL == n->norm->Bl.width) + if (n->norm->Bl.width == NULL) n->norm->Bl.width = "2n"; break; case LIST_enum: - if (NULL == n->norm->Bl.width) + if (n->norm->Bl.width == NULL) n->norm->Bl.width = "3n"; break; default: @@ -592,7 +775,7 @@ post_bd(POST_ARGS) mdoc->parse, argv->line, argv->pos, "Bd -offset %s", argv->value[0]); - rewrite_macro2len(argv->value); + rewrite_macro2len(mdoc, argv->value); n->norm->Bd.offs = argv->value[0]; break; case MDOC_Compact: @@ -659,11 +842,11 @@ post_eoln(POST_ARGS) { struct roff_node *n; + post_useless(mdoc); n = mdoc->last; if (n->child != NULL) - mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, - n->line, n->pos, "%s %s", - mdoc_macronames[n->tok], n->child->string); + mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, n->line, + n->pos, "%s %s", roff_name[n->tok], n->child->string); while (n->child != NULL) roff_node_delete(mdoc, n->child); @@ -745,6 +928,8 @@ post_lb(POST_ARGS) struct roff_node *n; const char *p; + post_delim_nb(mdoc); + n = mdoc->last; assert(n->child->type == ROFFT_TEXT); mdoc->next = ROFF_NEXT_CHILD; @@ -757,6 +942,9 @@ post_lb(POST_ARGS) return; } + mandoc_vmsg(MANDOCERR_LB_BAD, mdoc->parse, n->child->line, + n->child->pos, "Lb %s", n->child->string); + roff_word_alloc(mdoc, n->line, n->pos, "library"); mdoc->last->flags = NODE_NOSRC; roff_word_alloc(mdoc, n->line, n->pos, "\\(Lq"); @@ -811,13 +999,15 @@ post_std(POST_ARGS) { struct roff_node *n; + post_delim(mdoc); + n = mdoc->last; if (n->args && n->args->argc == 1) if (n->args->argv[0].arg == MDOC_Std) return; mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse, - n->line, n->pos, mdoc_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); } static void @@ -852,7 +1042,17 @@ post_obsolete(POST_ARGS) n = mdoc->last; if (n->type == ROFFT_ELEM || n->type == ROFFT_BLOCK) mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse, - n->line, n->pos, mdoc_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); +} + +static void +post_useless(POST_ARGS) +{ + struct roff_node *n; + + n = mdoc->last; + mandoc_msg(MANDOCERR_MACRO_USELESS, mdoc->parse, + n->line, n->pos, roff_name[n->tok]); } /* @@ -937,6 +1137,8 @@ post_fname(POST_ARGS) if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse, n->line, n->pos + pos, n->string); + if (n->sec == SEC_SYNOPSIS && mdoc->meta.msec != NULL) + mandoc_xr_add(mdoc->meta.msec, n->string, -1, -1); } static void @@ -968,7 +1170,8 @@ post_fo(POST_ARGS) "Fo ... %s", n->child->next->string); while (n->child != n->last) roff_node_delete(mdoc, n->last); - } + } else + post_delim(mdoc); post_fname(mdoc); } @@ -992,6 +1195,7 @@ post_fa(POST_ARGS) break; } } + post_delim_nb(mdoc); } static void @@ -1001,6 +1205,11 @@ post_nm(POST_ARGS) n = mdoc->last; + if ((n->sec == SEC_NAME || n->sec == SEC_SYNOPSIS) && + n->child != NULL && n->child->type == ROFFT_TEXT && + mdoc->meta.msec != NULL) + mandoc_xr_add(mdoc->meta.msec, n->child->string, -1, -1); + if (n->last != NULL && (n->last->tok == MDOC_Pp || n->last->tok == MDOC_Lp)) @@ -1013,6 +1222,26 @@ post_nm(POST_ARGS) (mdoc->lastsec == SEC_NAME && n->child == NULL)) mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse, n->line, n->pos, "Nm"); + + switch (n->type) { + case ROFFT_ELEM: + post_delim_nb(mdoc); + break; + case ROFFT_HEAD: + post_delim(mdoc); + break; + default: + return; + } + + if ((n->child != NULL && n->child->type == ROFFT_TEXT) || + mdoc->meta.name == NULL) + return; + + mdoc->next = ROFF_NEXT_CHILD; + roff_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name); + mdoc->last->flags |= NODE_NOSRC; + mdoc->last = n; } static void @@ -1025,9 +1254,15 @@ post_nd(POST_ARGS) if (n->type != ROFFT_BODY) return; + if (n->sec != SEC_NAME) + mandoc_msg(MANDOCERR_ND_LATE, mdoc->parse, + n->line, n->pos, "Nd"); + if (n->child == NULL) mandoc_msg(MANDOCERR_ND_EMPTY, mdoc->parse, n->line, n->pos, "Nd"); + else + post_delim(mdoc); post_hyph(mdoc); } @@ -1046,7 +1281,7 @@ post_display(POST_ARGS) roff_node_delete(mdoc, n); } else if (n->child == NULL) mandoc_msg(MANDOCERR_BLK_EMPTY, mdoc->parse, - n->line, n->pos, mdoc_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); else if (n->tok == MDOC_D1) post_hyph(mdoc); break; @@ -1069,7 +1304,7 @@ post_display(POST_ARGS) if (np->type == ROFFT_BLOCK && np->tok == MDOC_Bd) { mandoc_vmsg(MANDOCERR_BD_NEST, mdoc->parse, n->line, n->pos, - "%s in Bd", mdoc_macronames[n->tok]); + "%s in Bd", roff_name[n->tok]); break; } } @@ -1084,17 +1319,18 @@ post_defaults(POST_ARGS) { struct roff_node *nn; + if (mdoc->last->child != NULL) { + post_delim_nb(mdoc); + return; + } + /* * The `Ar' defaults to "file ..." if no value is provided as an * argument; the `Mt' and `Pa' macros use "~"; the `Li' just * gets an empty string. */ - if (mdoc->last->child != NULL) - return; - nn = mdoc->last; - switch (nn->tok) { case MDOC_Ar: mdoc->next = ROFF_NEXT_CHILD; @@ -1158,6 +1394,8 @@ post_an(POST_ARGS) if (nch == NULL) mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, np->line, np->pos, "An"); + else + post_delim_nb(mdoc); } else if (nch != NULL) mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, nch->line, nch->pos, "An ... %s", nch->string); @@ -1185,6 +1423,9 @@ post_xx(POST_ARGS) { struct roff_node *n; const char *os; + char *v; + + post_delim_nb(mdoc); n = mdoc->last; switch (n->tok) { @@ -1199,6 +1440,20 @@ post_xx(POST_ARGS) break; case MDOC_Nx: os = "NetBSD"; + if (n->child == NULL) + break; + v = n->child->string; + if ((v[0] != '0' && v[0] != '1') || v[1] != '.' || + v[2] < '0' || v[2] > '9' || + v[3] < 'a' || v[3] > 'z' || v[4] != '\0') + break; + n->child->flags |= NODE_NOPRT; + mdoc->next = ROFF_NEXT_CHILD; + roff_word_alloc(mdoc, n->child->line, n->child->pos, v); + v = mdoc->last->string; + v[3] = toupper((unsigned char)v[3]); + mdoc->last->flags |= NODE_NOSRC; + mdoc->last = n; break; case MDOC_Ox: os = "OpenBSD"; @@ -1255,25 +1510,40 @@ post_it(POST_ARGS) /* FALLTHROUGH */ case LIST_item: if ((nch = nit->head->child) != NULL) - mandoc_vmsg(MANDOCERR_ARG_SKIP, - mdoc->parse, nit->line, nit->pos, - "It %s", nch->string == NULL ? - mdoc_macronames[nch->tok] : nch->string); + mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, + nit->line, nit->pos, "It %s", + nch->string == NULL ? roff_name[nch->tok] : + nch->string); break; case LIST_column: cols = (int)nbl->norm->Bl.ncols; assert(nit->head->child == NULL); - i = 0; - for (nch = nit->child; nch != NULL; nch = nch->next) - if (nch->type == ROFFT_BODY) - i++; + if (nit->head->next->child == NULL && + nit->head->next->next == NULL) { + mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse, + nit->line, nit->pos, "It"); + roff_node_delete(mdoc, nit); + break; + } + i = 0; + for (nch = nit->child; nch != NULL; nch = nch->next) { + if (nch->type != ROFFT_BODY) + continue; + if (i++ && nch->flags & NODE_LINE) + mandoc_msg(MANDOCERR_TA_LINE, mdoc->parse, + nch->line, nch->pos, "Ta"); + } if (i < cols || i > cols + 1) mandoc_vmsg(MANDOCERR_BL_COL, mdoc->parse, nit->line, nit->pos, "%d columns, %d cells", cols, i); + else if (nit->head->next->child != NULL && + nit->head->next->child->line > nit->line) + mandoc_msg(MANDOCERR_IT_NOARG, mdoc->parse, + nit->line, nit->pos, "Bl -column It"); break; default: abort(); @@ -1296,7 +1566,7 @@ post_bl_block(POST_ARGS) switch (nc->tok) { case MDOC_Pp: case MDOC_Lp: - case MDOC_br: + case ROFF_br: break; default: nc = NULL; @@ -1305,14 +1575,13 @@ post_bl_block(POST_ARGS) if (ni->next == NULL) { mandoc_msg(MANDOCERR_PAR_MOVE, mdoc->parse, nc->line, nc->pos, - mdoc_macronames[nc->tok]); + roff_name[nc->tok]); mdoc_node_relink(mdoc, nc); } else if (n->norm->Bl.comp == 0 && n->norm->Bl.type != LIST_column) { mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, nc->line, nc->pos, - "%s before It", - mdoc_macronames[nc->tok]); + "%s before It", roff_name[nc->tok]); roff_node_delete(mdoc, nc); } else break; @@ -1325,17 +1594,17 @@ post_bl_block(POST_ARGS) * If the argument of -offset or -width is a macro, * replace it with the associated default width. */ -void -rewrite_macro2len(char **arg) +static void +rewrite_macro2len(struct roff_man *mdoc, char **arg) { size_t width; - int tok; + enum roff_tok tok; if (*arg == NULL) return; else if ( ! strcmp(*arg, "Ds")) width = 6; - else if ((tok = mdoc_hash_find(*arg)) == TOKEN_NONE) + else if ((tok = roffhash_find(mdoc->mdocmac, *arg, 0)) == TOKEN_NONE) return; else width = macro2len(tok); @@ -1413,6 +1682,8 @@ post_bl(POST_ARGS) struct roff_node *nparent, *nprev; /* of the Bl block */ struct roff_node *nblock, *nbody; /* of the Bl */ struct roff_node *nchild, *nnext; /* of the Bl body */ + const char *prev_Er; + int order; nbody = mdoc->last; switch (nbody->type) { @@ -1476,8 +1747,7 @@ post_bl(POST_ARGS) } mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse, - nchild->line, nchild->pos, - mdoc_macronames[nchild->tok]); + nchild->line, nchild->pos, roff_name[nchild->tok]); /* * Move the node out of the Bl block. @@ -1514,6 +1784,35 @@ post_bl(POST_ARGS) nchild = nnext; } + + if (mdoc->meta.os_e != MANDOC_OS_NETBSD) + return; + + prev_Er = NULL; + for (nchild = nbody->child; nchild != NULL; nchild = nchild->next) { + if (nchild->tok != MDOC_It) + continue; + if ((nnext = nchild->head->child) == NULL) + continue; + if (nnext->type == ROFFT_BLOCK) + nnext = nnext->body->child; + if (nnext == NULL || nnext->tok != MDOC_Er) + continue; + nnext = nnext->child; + if (prev_Er != NULL) { + order = strcmp(prev_Er, nnext->string); + if (order > 0) + mandoc_vmsg(MANDOCERR_ER_ORDER, + mdoc->parse, nnext->line, nnext->pos, + "Er %s %s (NetBSD)", + prev_Er, nnext->string); + else if (order == 0) + mandoc_vmsg(MANDOCERR_ER_REP, + mdoc->parse, nnext->line, nnext->pos, + "Er %s (NetBSD)", prev_Er); + } + prev_Er = nnext->string; + } } static void @@ -1555,7 +1854,7 @@ post_sm(POST_ARGS) mandoc_vmsg(MANDOCERR_SM_BAD, mdoc->parse, nch->line, nch->pos, - "%s %s", mdoc_macronames[mdoc->last->tok], nch->string); + "%s %s", roff_name[mdoc->last->tok], nch->string); mdoc_node_relink(mdoc, nch); return; } @@ -1563,14 +1862,35 @@ post_sm(POST_ARGS) static void post_root(POST_ARGS) { + const char *openbsd_arch[] = { + "alpha", "amd64", "arm64", "armv7", "hppa", "i386", + "landisk", "loongson", "luna88k", "macppc", "mips64", + "octeon", "sgi", "socppc", "sparc64", NULL + }; + const char *netbsd_arch[] = { + "acorn26", "acorn32", "algor", "alpha", "amiga", + "arc", "atari", + "bebox", "cats", "cesfic", "cobalt", "dreamcast", + "emips", "evbarm", "evbmips", "evbppc", "evbsh3", "evbsh5", + "hp300", "hpcarm", "hpcmips", "hpcsh", "hppa", + "i386", "ibmnws", "luna68k", + "mac68k", "macppc", "mipsco", "mmeye", "mvme68k", "mvmeppc", + "netwinder", "news68k", "newsmips", "next68k", + "pc532", "playstation2", "pmax", "pmppc", "prep", + "sandpoint", "sbmips", "sgimips", "shark", + "sparc", "sparc64", "sun2", "sun3", + "vax", "walnut", "x68k", "x86", "x86_64", "xen", NULL + }; + const char **arches[] = { NULL, netbsd_arch, openbsd_arch }; + struct roff_node *n; + const char **arch; /* Add missing prologue data. */ if (mdoc->meta.date == NULL) - mdoc->meta.date = mdoc->quick ? - mandoc_strdup("") : - mandoc_normdate(mdoc->parse, NULL, 0, 0); + mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : + mandoc_normdate(mdoc, NULL, 0, 0); if (mdoc->meta.title == NULL) { mandoc_msg(MANDOCERR_DT_NOTITLE, @@ -1585,6 +1905,27 @@ post_root(POST_ARGS) mandoc_msg(MANDOCERR_OS_MISSING, mdoc->parse, 0, 0, NULL); mdoc->meta.os = mandoc_strdup(""); + } else if (mdoc->meta.os_e && + (mdoc->meta.rcsids & (1 << mdoc->meta.os_e)) == 0) + mandoc_msg(MANDOCERR_RCS_MISSING, mdoc->parse, 0, 0, + mdoc->meta.os_e == MANDOC_OS_OPENBSD ? + "(OpenBSD)" : "(NetBSD)"); + + if (mdoc->meta.arch != NULL && + (arch = arches[mdoc->meta.os_e]) != NULL) { + while (*arch != NULL && strcmp(*arch, mdoc->meta.arch)) + arch++; + if (*arch == NULL) { + n = mdoc->first->child; + while (n->tok != MDOC_Dt) + n = n->next; + n = n->child->next->next; + mandoc_vmsg(MANDOCERR_ARCH_BAD, + mdoc->parse, n->line, n->pos, + "Dt ... %s %s", mdoc->meta.arch, + mdoc->meta.os_e == MANDOC_OS_OPENBSD ? + "(OpenBSD)" : "(NetBSD)"); + } } /* Check that we begin with a proper `Sh'. */ @@ -1598,7 +1939,7 @@ post_root(POST_ARGS) mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL); else if (n->tok != MDOC_Sh) mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse, - n->line, n->pos, mdoc_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); } static void @@ -1632,9 +1973,8 @@ post_rs(POST_ARGS) break; if (i == RSORD_MAX) { - mandoc_msg(MANDOCERR_RS_BAD, - mdoc->parse, nch->line, nch->pos, - mdoc_macronames[nch->tok]); + mandoc_msg(MANDOCERR_RS_BAD, mdoc->parse, + nch->line, nch->pos, roff_name[nch->tok]); i = -1; } else if (nch->tok == MDOC__J || nch->tok == MDOC__B) np->norm->Rs.quote_T++; @@ -1718,10 +2058,20 @@ post_hyph(POST_ARGS) static void post_ns(POST_ARGS) { + struct roff_node *n; - if (mdoc->last->flags & NODE_LINE) + n = mdoc->last; + if (n->flags & NODE_LINE || + (n->next != NULL && n->next->flags & NODE_DELIMC)) mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse, - mdoc->last->line, mdoc->last->pos, NULL); + n->line, n->pos, NULL); +} + +static void +post_sx(POST_ARGS) +{ + post_delim(mdoc); + post_hyph(mdoc); } static void @@ -1787,7 +2137,7 @@ post_sh_name(POST_ARGS) /* FALLTHROUGH */ default: mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse, - n->line, n->pos, mdoc_macronames[n->tok]); + n->line, n->pos, roff_name[n->tok]); continue; } break; @@ -1857,7 +2207,7 @@ post_sh_see_also(POST_ARGS) if (isalpha((const unsigned char)*name)) return; lastpunct = n->string; - if (n->next == NULL) + if (n->next == NULL || n->next->tok == MDOC_Rs) mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse, n->line, n->pos, "%s after %s(%s)", lastpunct, lastname, lastsec); @@ -1884,11 +2234,54 @@ post_sh_authors(POST_ARGS) mdoc->last->line, mdoc->last->pos, NULL); } +/* + * Return an upper bound for the string distance (allowing + * transpositions). Not a full Levenshtein implementation + * because Levenshtein is quadratic in the string length + * and this function is called for every standard name, + * so the check for each custom name would be cubic. + * The following crude heuristics is linear, resulting + * in quadratic behaviour for checking one custom name, + * which does not cause measurable slowdown. + */ +static int +similar(const char *s1, const char *s2) +{ + const int maxdist = 3; + int dist = 0; + + while (s1[0] != '\0' && s2[0] != '\0') { + if (s1[0] == s2[0]) { + s1++; + s2++; + continue; + } + if (++dist > maxdist) + return INT_MAX; + if (s1[1] == s2[1]) { /* replacement */ + s1++; + s2++; + } else if (s1[0] == s2[1] && s1[1] == s2[0]) { + s1 += 2; /* transposition */ + s2 += 2; + } else if (s1[0] == s2[1]) /* insertion */ + s2++; + else if (s1[1] == s2[0]) /* deletion */ + s1++; + else + return INT_MAX; + } + dist += strlen(s1) + strlen(s2); + return dist > maxdist ? INT_MAX : dist; +} + static void post_sh_head(POST_ARGS) { struct roff_node *nch; const char *goodsec; + const char *const *testsec; + int dist, mindist; enum roff_sec sec; /* @@ -1908,7 +2301,7 @@ post_sh_head(POST_ARGS) sec != SEC_CUSTOM ? secnames[sec] : (nch = mdoc->last->child) == NULL ? "" : nch->type == ROFFT_TEXT ? nch->string : - mdoc_macronames[nch->tok]); + roff_name[nch->tok]); /* The SYNOPSIS gets special attention in other areas. */ @@ -1926,8 +2319,25 @@ post_sh_head(POST_ARGS) /* We don't care about custom sections after this. */ - if (sec == SEC_CUSTOM) + if (sec == SEC_CUSTOM) { + if ((nch = mdoc->last->child) == NULL || + nch->type != ROFFT_TEXT || nch->next != NULL) + return; + goodsec = NULL; + mindist = INT_MAX; + for (testsec = secnames + 1; *testsec != NULL; testsec++) { + dist = similar(nch->string, *testsec); + if (dist < mindist) { + goodsec = *testsec; + mindist = dist; + } + } + if (goodsec != NULL) + mandoc_vmsg(MANDOCERR_SEC_TYPO, mdoc->parse, + nch->line, nch->pos, "Sh %s instead of %s", + nch->string, goodsec); return; + } /* * Check whether our non-custom section is being repeated or is @@ -1993,9 +2403,15 @@ post_xr(POST_ARGS) if (nch->next == NULL) { mandoc_vmsg(MANDOCERR_XR_NOSEC, mdoc->parse, n->line, n->pos, "Xr %s", nch->string); - return; + } else { + assert(nch->next == n->last); + if(mandoc_xr_add(nch->next->string, nch->string, + nch->line, nch->pos)) + mandoc_vmsg(MANDOCERR_XR_SELF, mdoc->parse, + nch->line, nch->pos, "Xr %s %s", + nch->string, nch->next->string); } - assert(nch->next == n->last); + post_delim_nb(mdoc); } static void @@ -2004,7 +2420,11 @@ post_ignpar(POST_ARGS) struct roff_node *np; switch (mdoc->last->type) { + case ROFFT_BLOCK: + post_prevpar(mdoc); + return; case ROFFT_HEAD: + post_delim(mdoc); post_hyph(mdoc); return; case ROFFT_BODY: @@ -2017,8 +2437,8 @@ post_ignpar(POST_ARGS) if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, np->line, np->pos, - "%s after %s", mdoc_macronames[np->tok], - mdoc_macronames[mdoc->last->tok]); + "%s after %s", roff_name[np->tok], + roff_name[mdoc->last->tok]); roff_node_delete(mdoc, np); } @@ -2026,8 +2446,8 @@ post_ignpar(POST_ARGS) if (np->tok == MDOC_Pp || np->tok == MDOC_Lp) { mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, np->line, np->pos, "%s at the end of %s", - mdoc_macronames[np->tok], - mdoc_macronames[mdoc->last->tok]); + roff_name[np->tok], + roff_name[mdoc->last->tok]); roff_node_delete(mdoc, np); } } @@ -2050,7 +2470,7 @@ post_prevpar(POST_ARGS) if (n->prev->tok != MDOC_Pp && n->prev->tok != MDOC_Lp && - n->prev->tok != MDOC_br) + n->prev->tok != ROFF_br) return; if (n->tok == MDOC_Bl && n->norm->Bl.comp) return; @@ -2060,9 +2480,8 @@ post_prevpar(POST_ARGS) return; mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, - n->prev->line, n->prev->pos, - "%s before %s", mdoc_macronames[n->prev->tok], - mdoc_macronames[n->tok]); + n->prev->line, n->prev->pos, "%s before %s", + roff_name[n->prev->tok], roff_name[n->tok]); roff_node_delete(mdoc, n->prev); } @@ -2072,10 +2491,10 @@ post_par(POST_ARGS) struct roff_node *np; np = mdoc->last; - if (np->tok != MDOC_br && np->tok != MDOC_sp) + if (np->tok != ROFF_br && np->tok != ROFF_sp) post_prevpar(mdoc); - if (np->tok == MDOC_sp) { + if (np->tok == ROFF_sp) { if (np->child != NULL && np->child->next != NULL) mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse, np->child->next->line, np->child->next->pos, @@ -2083,21 +2502,20 @@ post_par(POST_ARGS) } else if (np->child != NULL) mandoc_vmsg(MANDOCERR_ARG_SKIP, mdoc->parse, np->line, np->pos, "%s %s", - mdoc_macronames[np->tok], np->child->string); + roff_name[np->tok], np->child->string); if ((np = mdoc->last->prev) == NULL) { np = mdoc->last->parent; if (np->tok != MDOC_Sh && np->tok != MDOC_Ss) return; } else if (np->tok != MDOC_Pp && np->tok != MDOC_Lp && - (mdoc->last->tok != MDOC_br || - (np->tok != MDOC_sp && np->tok != MDOC_br))) + (mdoc->last->tok != ROFF_br || + (np->tok != ROFF_sp && np->tok != ROFF_br))) return; mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse, - mdoc->last->line, mdoc->last->pos, - "%s after %s", mdoc_macronames[mdoc->last->tok], - mdoc_macronames[np->tok]); + mdoc->last->line, mdoc->last->pos, "%s after %s", + roff_name[mdoc->last->tok], roff_name[np->tok]); roff_node_delete(mdoc, mdoc->last); } @@ -2126,7 +2544,7 @@ post_dd(POST_ARGS) if (n->child == NULL || n->child->string[0] == '\0') { mdoc->meta.date = mdoc->quick ? mandoc_strdup("") : - mandoc_normdate(mdoc->parse, NULL, n->line, n->pos); + mandoc_normdate(mdoc, NULL, n->line, n->pos); return; } @@ -2135,7 +2553,7 @@ post_dd(POST_ARGS) if (mdoc->quick) mdoc->meta.date = datestr; else { - mdoc->meta.date = mandoc_normdate(mdoc->parse, + mdoc->meta.date = mandoc_normdate(mdoc, datestr, n->line, n->pos); free(datestr); } @@ -2241,11 +2659,21 @@ static void post_bx(POST_ARGS) { struct roff_node *n, *nch; + const char *macro; + + post_delim_nb(mdoc); n = mdoc->last; nch = n->child; if (nch != NULL) { + macro = !strcmp(nch->string, "Open") ? "Ox" : + !strcmp(nch->string, "Net") ? "Nx" : + !strcmp(nch->string, "Free") ? "Fx" : + !strcmp(nch->string, "DragonFly") ? "Dx" : NULL; + if (macro != NULL) + mandoc_msg(MANDOCERR_BX, mdoc->parse, + n->line, n->pos, macro); mdoc->last = nch; nch = nch->next; mdoc->next = ROFF_NEXT_SIBLING; @@ -2299,6 +2727,8 @@ post_os(POST_ARGS) mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse, n->line, n->pos, "Os"); + post_delim(mdoc); + /* * Set the operating system by way of the `Os' macro. * The order of precedence is: @@ -2312,11 +2742,11 @@ post_os(POST_ARGS) mdoc->meta.os = NULL; deroff(&mdoc->meta.os, n); if (mdoc->meta.os) - return; + goto out; - if (mdoc->defos) { - mdoc->meta.os = mandoc_strdup(mdoc->defos); - return; + if (mdoc->os_s != NULL) { + mdoc->meta.os = mandoc_strdup(mdoc->os_s); + goto out; } #ifdef OSNAME @@ -2333,6 +2763,44 @@ post_os(POST_ARGS) } mdoc->meta.os = mandoc_strdup(defbuf); #endif /*!OSNAME*/ + +out: + if (mdoc->meta.os_e == MANDOC_OS_OTHER) { + if (strstr(mdoc->meta.os, "OpenBSD") != NULL) + mdoc->meta.os_e = MANDOC_OS_OPENBSD; + else if (strstr(mdoc->meta.os, "NetBSD") != NULL) + mdoc->meta.os_e = MANDOC_OS_NETBSD; + } + + /* + * This is the earliest point where we can check + * Mdocdate conventions because we don't know + * the operating system earlier. + */ + + if (n->child != NULL) + mandoc_vmsg(MANDOCERR_OS_ARG, mdoc->parse, + n->child->line, n->child->pos, + "Os %s (%s)", n->child->string, + mdoc->meta.os_e == MANDOC_OS_OPENBSD ? + "OpenBSD" : "NetBSD"); + + while (n->tok != MDOC_Dd) + if ((n = n->prev) == NULL) + return; + if ((n = n->child) == NULL) + return; + if (strncmp(n->string, "$" "Mdocdate", 9)) { + if (mdoc->meta.os_e == MANDOC_OS_OPENBSD) + mandoc_vmsg(MANDOCERR_MDOCDATE_MISSING, + mdoc->parse, n->line, n->pos, + "Dd %s (OpenBSD)", n->string); + } else { + if (mdoc->meta.os_e == MANDOC_OS_NETBSD) + mandoc_vmsg(MANDOCERR_MDOCDATE, + mdoc->parse, n->line, n->pos, + "Dd %s (NetBSD)", n->string); + } } enum roff_sec @@ -2348,7 +2816,7 @@ mdoc_a2sec(const char *p) } static size_t -macro2len(int macro) +macro2len(enum roff_tok macro) { switch (macro) { diff --git a/contrib/mdocml/msec.in b/contrib/mdocml/msec.in index 86d0dd8..d5b319f 100644 --- a/contrib/mdocml/msec.in +++ b/contrib/mdocml/msec.in @@ -32,9 +32,3 @@ LINE("6", "FreeBSD Games Manual") LINE("7", "FreeBSD Miscellaneous Information Manual") LINE("8", "FreeBSD System Manager\'s Manual") LINE("9", "FreeBSD Kernel Developer\'s Manual") -LINE("X11", "X11 Developer\'s Manual") -LINE("X11R6", "X11 Developer\'s Manual") -LINE("unass", "Unassociated") -LINE("local", "Local") -LINE("draft", "Draft") -LINE("paper", "Paper") diff --git a/contrib/mdocml/out.c b/contrib/mdocml/out.c index aff3558..b2b6437 100644 --- a/contrib/mdocml/out.c +++ b/contrib/mdocml/out.c @@ -1,7 +1,7 @@ -/* $Id: out.c,v 1.62 2015/10/12 00:08:16 schwarze Exp $ */ +/* $Id: out.c,v 1.70 2017/06/27 18:25:02 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,6 +20,7 @@ #include <sys/types.h> #include <assert.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -29,9 +30,10 @@ #include "out.h" static void tblcalc_data(struct rofftbl *, struct roffcol *, - const struct tbl_opts *, const struct tbl_dat *); + const struct tbl_opts *, const struct tbl_dat *, + size_t); static void tblcalc_literal(struct rofftbl *, struct roffcol *, - const struct tbl_dat *); + const struct tbl_dat *, size_t); static void tblcalc_number(struct rofftbl *, struct roffcol *, const struct tbl_opts *, const struct tbl_dat *); @@ -40,10 +42,10 @@ static void tblcalc_number(struct rofftbl *, struct roffcol *, * Parse the *src string and store a scaling unit into *dst. * If the string doesn't specify the unit, use the default. * If no default is specified, fail. - * Return 2 on complete success, 1 when a conversion was done, - * but there was trailing garbage, and 0 on total failure. + * Return a pointer to the byte after the last byte used, + * or NULL on total failure. */ -int +const char * a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) { char *endptr; @@ -51,7 +53,7 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) dst->unit = def == SCALE_MAX ? SCALE_BU : def; dst->scale = strtod(src, &endptr); if (endptr == src) - return 0; + return NULL; switch (*endptr++) { case 'c': @@ -84,17 +86,14 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) case 'v': dst->unit = SCALE_VS; break; - case '\0': - endptr--; - /* FALLTHROUGH */ default: + endptr--; if (SCALE_MAX == def) - return 0; + return NULL; dst->unit = def; break; } - - return *endptr == '\0' ? 2 : 1; + return endptr; } /* @@ -105,8 +104,9 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) */ void tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, - size_t totalwidth) + size_t offset, size_t rmargin) { + struct roffsu su; const struct tbl_opts *opts; const struct tbl_dat *dp; struct roffcol *col; @@ -141,13 +141,29 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, if (1 < spans) continue; icol = dp->layout->col; - if (maxcol < icol) - maxcol = icol; + while (maxcol < icol) + tbl->cols[++maxcol].spacing = SIZE_MAX; col = tbl->cols + icol; col->flags |= dp->layout->flags; if (dp->layout->flags & TBL_CELL_WIGN) continue; - tblcalc_data(tbl, col, opts, dp); + if (dp->layout->wstr != NULL && + dp->layout->width == 0 && + a2roffsu(dp->layout->wstr, &su, SCALE_EN) + != NULL) + dp->layout->width = + (*tbl->sulen)(&su, tbl->arg); + if (col->width < dp->layout->width) + col->width = dp->layout->width; + if (dp->layout->spacing != SIZE_MAX && + (col->spacing == SIZE_MAX || + col->spacing < dp->layout->spacing)) + col->spacing = dp->layout->spacing; + tblcalc_data(tbl, col, opts, dp, + dp->block == 0 ? 0 : + dp->layout->width ? dp->layout->width : + rmargin ? (rmargin + sp->opts->cols / 2) + / (sp->opts->cols + 1) : 0); } } @@ -161,6 +177,8 @@ 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->flags & TBL_CELL_EQUAL) { necol++; if (ewidth < col->width) @@ -184,7 +202,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, continue; if (col->width == ewidth) continue; - if (nxcol && totalwidth) + if (nxcol && rmargin) xwidth += ewidth - col->width; col->width = ewidth; } @@ -196,10 +214,13 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, * Distribute the available width evenly. */ - if (nxcol && totalwidth) { - xwidth = totalwidth - xwidth - 3*maxcol - + if (nxcol && rmargin) { + xwidth += 3*maxcol + (opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ? 2 : !!opts->lvert + !!opts->rvert); + if (rmargin <= offset + xwidth) + return; + xwidth = rmargin - offset - xwidth; /* * Emulate a bug in GNU tbl width calculation that @@ -232,7 +253,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp, static void tblcalc_data(struct rofftbl *tbl, struct roffcol *col, - const struct tbl_opts *opts, const struct tbl_dat *dp) + const struct tbl_opts *opts, const struct tbl_dat *dp, size_t mw) { size_t sz; @@ -249,7 +270,7 @@ tblcalc_data(struct rofftbl *tbl, struct roffcol *col, case TBL_CELL_CENTRE: case TBL_CELL_LEFT: case TBL_CELL_RIGHT: - tblcalc_literal(tbl, col, dp); + tblcalc_literal(tbl, col, dp, mw); break; case TBL_CELL_NUMBER: tblcalc_number(tbl, col, opts, dp); @@ -263,16 +284,35 @@ tblcalc_data(struct rofftbl *tbl, struct roffcol *col, static void tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, - const struct tbl_dat *dp) + const struct tbl_dat *dp, size_t mw) { - size_t sz; - const char *str; - - str = dp->string ? dp->string : ""; - sz = (*tbl->slen)(str, tbl->arg); - - if (col->width < sz) - col->width = sz; + const char *str; /* Beginning of the first line. */ + const char *beg; /* Beginning of the current line. */ + char *end; /* End of the current line. */ + size_t lsz; /* Length of the current line. */ + size_t wsz; /* Length of the current word. */ + + if (dp->string == NULL || *dp->string == '\0') + return; + str = mw ? mandoc_strdup(dp->string) : dp->string; + lsz = 0; + for (beg = str; beg != NULL && *beg != '\0'; beg = end) { + end = mw ? strchr(beg, ' ') : NULL; + if (end != NULL) { + *end++ = '\0'; + while (*end == ' ') + end++; + } + wsz = (*tbl->slen)(beg, tbl->arg); + if (mw && lsz && lsz + 1 + wsz <= mw) + lsz += 1 + wsz; + else + lsz = wsz; + if (col->width < lsz) + col->width = lsz; + } + if (mw) + free((void *)str); } static void diff --git a/contrib/mdocml/out.h b/contrib/mdocml/out.h index 2c1cf3f..f6aceb9 100644 --- a/contrib/mdocml/out.h +++ b/contrib/mdocml/out.h @@ -1,6 +1,7 @@ -/* $Id: out.h,v 1.27 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: out.h,v 1.31 2017/06/27 18:25:02 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -32,6 +33,7 @@ enum roffscale { struct roffcol { size_t width; /* width of cell */ size_t decimal; /* decimal position in cell */ + size_t spacing; /* spacing after the column */ int flags; /* layout flags, see tbl_cell */ }; @@ -40,14 +42,16 @@ struct roffsu { double scale; }; +typedef size_t (*tbl_sulen)(const struct roffsu *, void *); typedef size_t (*tbl_strlen)(const char *, void *); typedef size_t (*tbl_len)(size_t, void *); struct rofftbl { + tbl_sulen sulen; /* calculate scaling unit length */ tbl_strlen slen; /* calculate string length */ tbl_len len; /* produce width of empty space */ struct roffcol *cols; /* master column specifiers */ - void *arg; /* passed to slen and len */ + void *arg; /* passed to sulen, slen, and len */ }; #define SCALE_VS_INIT(p, v) \ @@ -63,6 +67,6 @@ struct rofftbl { struct tbl_span; -int a2roffsu(const char *, struct roffsu *, enum roffscale); +const char *a2roffsu(const char *, struct roffsu *, enum roffscale); void tblcalc(struct rofftbl *tbl, - const struct tbl_span *, size_t); + const struct tbl_span *, size_t, size_t); diff --git a/contrib/mdocml/preconv.c b/contrib/mdocml/preconv.c index 1fc137a..08f8df8 100644 --- a/contrib/mdocml/preconv.c +++ b/contrib/mdocml/preconv.c @@ -1,4 +1,4 @@ -/* $Id: preconv.c,v 1.15 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: preconv.c,v 1.16 2017/02/18 13:43:52 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> @@ -26,14 +26,14 @@ #include "libmandoc.h" int -preconv_encode(struct buf *ib, size_t *ii, struct buf *ob, size_t *oi, +preconv_encode(const struct buf *ib, size_t *ii, struct buf *ob, size_t *oi, int *filenc) { - unsigned char *cu; - int nby; - unsigned int accum; + const unsigned char *cu; + int nby; + unsigned int accum; - cu = (unsigned char *)ib->buf + *ii; + cu = (const unsigned char *)ib->buf + *ii; assert(*cu & 0x80); if ( ! (*filenc & MPARSE_UTF8)) @@ -90,7 +90,7 @@ preconv_encode(struct buf *ib, size_t *ii, struct buf *ob, size_t *oi, assert(accum < 0xd800 || accum > 0xdfff); *oi += snprintf(ob->buf + *oi, 11, "\\[u%.4X]", accum); - *ii = (char *)cu - ib->buf; + *ii = (const char *)cu - ib->buf; *filenc &= ~MPARSE_LATIN1; return 1; diff --git a/contrib/mdocml/read.c b/contrib/mdocml/read.c index d20a609..1af1b28 100644 --- a/contrib/mdocml/read.c +++ b/contrib/mdocml/read.c @@ -1,4 +1,4 @@ -/* $Id: read.c,v 1.157 2017/01/09 01:37:03 schwarze Exp $ */ +/* $Id: read.c,v 1.192 2017/07/20 14:36:36 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -24,13 +24,9 @@ #include <assert.h> #include <ctype.h> -#if HAVE_ERR -#include <err.h> -#endif #include <errno.h> #include <fcntl.h> #include <stdarg.h> -#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -43,21 +39,20 @@ #include "mdoc.h" #include "man.h" #include "libmandoc.h" -#include "roff_int.h" #define REPARSE_LIMIT 1000 struct mparse { - struct roff_man *man; /* man parser */ struct roff *roff; /* roff parser (!NULL) */ + struct roff_man *man; /* man parser */ char *sodest; /* filename pointed to by .so */ const char *file; /* filename of current input file */ struct buf *primary; /* buffer currently being parsed */ struct buf *secondary; /* preprocessed copy of input */ - const char *defos; /* default operating system */ + const char *os_s; /* default operating system */ mandocmsg mmsg; /* warning/error message handler */ enum mandoclevel file_status; /* status of current parse */ - enum mandoclevel wlevel; /* ignore messages below this */ + enum mandocerr mmin; /* ignore messages below this */ int options; /* parser options */ int gzip; /* current input file is gzipped */ int filenc; /* encoding of the current file */ @@ -67,7 +62,7 @@ struct mparse { static void choose_parser(struct mparse *); static void resize_buf(struct buf *, size_t); -static void mparse_buf_r(struct mparse *, struct buf, size_t, int); +static int mparse_buf_r(struct mparse *, struct buf, size_t, int); static int read_whole_file(struct mparse *, const char *, int, struct buf *, int *); static void mparse_end(struct mparse *); @@ -76,7 +71,7 @@ static void mparse_parse_buffer(struct mparse *, struct buf, static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = { MANDOCERR_OK, - MANDOCERR_WARNING, + MANDOCERR_OK, MANDOCERR_WARNING, MANDOCERR_ERROR, MANDOCERR_UNSUPP, @@ -87,20 +82,46 @@ static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = { static const char * const mandocerrs[MANDOCERR_MAX] = { "ok", + "base system convention", + + "Mdocdate found", + "Mdocdate missing", + "unknown architecture", + "operating system explicitly specified", + "RCS id missing", + "referenced manual not found", + + "generic style suggestion", + + "legacy man(7) date format", + "lower case character in document title", + "duplicate RCS id", + "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", + "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 \"\"", - "lower case character in document title", "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 \"\"", - "duplicate prologue macro", "late prologue macro", - "skipping late title macro", "prologue macros out of order", /* related to document structure */ @@ -114,9 +135,11 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "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", @@ -130,9 +153,9 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "blocks badly nested", "nested displays are not portable", "moving content out of list", - "fill mode already enabled, skipping", - "fill mode already disabled, skipping", + "first macro on line", "line scope broken", + "skipping blank line in line scope", /* related to missing macro arguments */ "skipping empty request", @@ -147,6 +170,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "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", @@ -158,7 +182,6 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "missing eqn box, using \"\"", /* related to bad macro arguments */ - "unterminated quoted argument", "duplicate argument", "skipping duplicate argument", "skipping duplicate display type", @@ -168,6 +191,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "unknown AT&T UNIX version", "comma in function argument", "parenthesis in function name", + "unknown library name", "invalid content in Rs block", "invalid Boolean argument", "unknown font, skipping request", @@ -176,8 +200,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { /* related to plain text */ "blank line in fill mode, using .sp", "tab in filled text", - "whitespace at end of input line", - "bad comment style", + "new sentence, new line", "invalid escape sequence", "undefined string, using \"\"", @@ -203,6 +226,8 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { /* 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", @@ -219,6 +244,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "NOT IMPLEMENTED: Bd -file", "skipping display without arguments", "missing list type, using -item", + "argument is not numeric, using 1", "missing manual name, using \"\"", "uname(3) system call failed, using UNKNOWN", "unknown standard specifier", @@ -240,7 +266,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { static const char * const mandoclevels[MANDOCLEVEL_MAX] = { "SUCCESS", - "RESERVED", + "STYLE", "WARNING", "ERROR", "UNSUPP", @@ -292,14 +318,15 @@ choose_parser(struct mparse *curp) } if (format == MPARSE_MDOC) { - mdoc_hash_init(); curp->man->macroset = MACROSET_MDOC; - curp->man->first->tok = TOKEN_NONE; + if (curp->man->mdocmac == NULL) + curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); } else { - man_hash_init(); curp->man->macroset = MACROSET_MAN; - curp->man->first->tok = TOKEN_NONE; + if (curp->man->manmac == NULL) + curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); } + curp->man->first->tok = TOKEN_NONE; } /* @@ -309,15 +336,13 @@ choose_parser(struct mparse *curp) * macros, inline equations, and input line traps) * and indirectly (for .so file inclusion). */ -static void +static int mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) { - const struct tbl_span *span; struct buf ln; const char *save_file; char *cp; size_t pos; /* byte number in the ln buffer */ - size_t j; /* auxiliary byte number in the blk buffer */ enum rofferr rr; int of; int lnn; /* line number in the real file */ @@ -399,79 +424,14 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) continue; } - /* Trailing backslash = a plain char. */ - - if (blk.buf[i] != '\\' || i + 1 == blk.sz) { - ln.buf[pos++] = blk.buf[i++]; - continue; - } - - /* - * Found escape and at least one other character. - * When it's a newline character, skip it. - * When there is a carriage return in between, - * skip that one as well. - */ - - if ('\r' == blk.buf[i + 1] && i + 2 < blk.sz && - '\n' == blk.buf[i + 2]) - ++i; - if ('\n' == blk.buf[i + 1]) { - i += 2; - ++lnn; - continue; - } - - if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) { - j = i; - i += 2; - /* Comment, skip to end of line */ - for (; i < blk.sz; ++i) { - if (blk.buf[i] != '\n') - continue; - if (blk.buf[i - 1] == ' ' || - blk.buf[i - 1] == '\t') - mandoc_msg( - MANDOCERR_SPACE_EOL, - curp, curp->line, - pos + i-1 - j, NULL); - ++i; - ++lnn; - break; - } - - /* Backout trailing whitespaces */ - for (; pos > 0; --pos) { - if (ln.buf[pos - 1] != ' ') - break; - if (pos > 2 && ln.buf[pos - 2] == '\\') - break; - } - break; - } - - /* Catch escaped bogus characters. */ - - c = (unsigned char) blk.buf[i+1]; - - if ( ! (isascii(c) && - (isgraph(c) || isblank(c)))) { - mandoc_vmsg(MANDOCERR_CHAR_BAD, curp, - curp->line, pos, "0x%x", c); - i += 2; - ln.buf[pos++] = '?'; - continue; - } - - /* Some other escape sequence, copy & cont. */ - - ln.buf[pos++] = blk.buf[i++]; ln.buf[pos++] = blk.buf[i++]; } - if (pos >= ln.sz) + if (pos + 1 >= ln.sz) resize_buf(&ln, 256); + if (i == blk.sz || blk.buf[i] == '\0') + ln.buf[pos++] = '\n'; ln.buf[pos] = '\0'; /* @@ -510,13 +470,16 @@ rerun: switch (rr) { case ROFF_REPARSE: - if (REPARSE_LIMIT >= ++curp->reparse_count) - mparse_buf_r(curp, ln, of, 0); - else + if (++curp->reparse_count > REPARSE_LIMIT) mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, pos, NULL); - pos = 0; - continue; + else if (mparse_buf_r(curp, ln, of, 0) == 1 || + start == 1) { + pos = 0; + continue; + } + free(ln.buf); + return 0; case ROFF_APPEND: pos = strlen(ln.buf); continue; @@ -530,7 +493,7 @@ rerun: (i >= blk.sz || blk.buf[i] == '\0')) { curp->sodest = mandoc_strdup(ln.buf + of); free(ln.buf); - return; + return 1; } /* * We remove `so' clauses from our lookaside @@ -566,21 +529,7 @@ rerun: if (curp->man->macroset == MACROSET_NONE) choose_parser(curp); - /* - * Lastly, push down into the parsers themselves. - * If libroff returns ROFF_TBL, then add it to the - * currently open parse. Since we only get here if - * there does exist data (see tbl_data.c), we're - * guaranteed that something's been allocated. - * Do the same for ROFF_EQN. - */ - - if (rr == ROFF_TBL) - while ((span = roff_span(curp->roff)) != NULL) - roff_addtbl(curp->man, span); - else if (rr == ROFF_EQN) - roff_addeqn(curp->man, roff_eqn(curp->roff)); - else if ((curp->man->macroset == MACROSET_MDOC ? + if ((curp->man->macroset == MACROSET_MDOC ? mdoc_parseln(curp->man, curp->line, ln.buf, of) : man_parseln(curp->man, curp->line, ln.buf, of)) == 2) break; @@ -596,20 +545,23 @@ rerun: } free(ln.buf); + return 1; } static int read_whole_file(struct mparse *curp, const char *file, int fd, struct buf *fb, int *with_mmap) { + struct stat st; gzFile gz; size_t off; ssize_t ssz; - struct stat st; - - if (fstat(fd, &st) == -1) - err((int)MANDOCLEVEL_SYSERR, "%s", file); + if (fstat(fd, &st) == -1) { + mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + "fstat: %s", strerror(errno)); + return 0; + } /* * If we're a regular file, try just reading in the whole entry @@ -631,8 +583,11 @@ read_whole_file(struct mparse *curp, const char *file, int fd, } if (curp->gzip) { - if ((gz = gzdopen(fd, "rb")) == NULL) - err((int)MANDOCLEVEL_SYSERR, "%s", file); + if ((gz = gzdopen(fd, "rb")) == NULL) { + mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + "gzdopen: %s", strerror(errno)); + return 0; + } } else gz = NULL; @@ -661,8 +616,11 @@ read_whole_file(struct mparse *curp, const char *file, int fd, fb->sz = off; return 1; } - if (ssz == -1) - err((int)MANDOCLEVEL_SYSERR, "%s", file); + if (ssz == -1) { + mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + "read: %s", strerror(errno)); + break; + } off += (size_t)ssz; } @@ -798,29 +756,32 @@ mparse_open(struct mparse *curp, const char *file) } struct mparse * -mparse_alloc(int options, enum mandoclevel wlevel, mandocmsg mmsg, - const char *defos) +mparse_alloc(int options, enum mandocerr mmin, mandocmsg mmsg, + enum mandoc_os os_e, const char *os_s) { struct mparse *curp; curp = mandoc_calloc(1, sizeof(struct mparse)); curp->options = options; - curp->wlevel = wlevel; + curp->mmin = mmin; curp->mmsg = mmsg; - curp->defos = defos; + curp->os_s = os_s; curp->roff = roff_alloc(curp, options); - curp->man = roff_man_alloc( curp->roff, curp, curp->defos, + curp->man = roff_man_alloc(curp->roff, curp, curp->os_s, curp->options & MPARSE_QUICK ? 1 : 0); if (curp->options & MPARSE_MDOC) { - mdoc_hash_init(); curp->man->macroset = MACROSET_MDOC; + if (curp->man->mdocmac == NULL) + curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); } else if (curp->options & MPARSE_MAN) { - man_hash_init(); curp->man->macroset = MACROSET_MAN; + if (curp->man->manmac == NULL) + curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); } curp->man->first->tok = TOKEN_NONE; + curp->man->meta.os_e = os_e; return curp; } @@ -829,22 +790,25 @@ mparse_reset(struct mparse *curp) { roff_reset(curp->roff); roff_man_reset(curp->man); + + free(curp->sodest); + curp->sodest = NULL; + if (curp->secondary) curp->secondary->sz = 0; curp->file_status = MANDOCLEVEL_OK; - - free(curp->sodest); - curp->sodest = NULL; + curp->gzip = 0; } void mparse_free(struct mparse *curp) { + roffhash_free(curp->man->mdocmac); + roffhash_free(curp->man->manmac); roff_man_free(curp->man); - if (curp->roff) - roff_free(curp->roff); + roff_free(curp->roff); if (curp->secondary) free(curp->secondary->buf); @@ -893,13 +857,13 @@ mandoc_msg(enum mandocerr er, struct mparse *m, { enum mandoclevel level; + if (er < m->mmin && er != MANDOCERR_FILE) + return; + level = MANDOCLEVEL_UNSUPP; while (er < mandoclimits[level]) level--; - if (level < m->wlevel && er != MANDOCERR_FILE) - return; - if (m->mmsg) (*m->mmsg)(er, level, m->file, ln, col, msg); diff --git a/contrib/mdocml/roff.7 b/contrib/mdocml/roff.7 index 114f63e..74780e9 100644 --- a/contrib/mdocml/roff.7 +++ b/contrib/mdocml/roff.7 @@ -1,7 +1,7 @@ -.\" $Id: roff.7,v 1.75 2015/09/24 18:41:22 schwarze Exp $ +.\" $Id: roff.7,v 1.94 2017/07/05 12:25:17 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2010, 2011, 2013-2015 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010,2011,2013-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 @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 24 2015 $ +.Dd $Mdocdate: July 5 2017 $ .Dt ROFF 7 .Os .Sh NAME @@ -180,7 +180,7 @@ single-character two-character .Sq \e*(XX , and N-character -.Sq \e*[N] . +.Sq \e* Ns Bq N . .Pp Examples: .Bl -tag -width Ds -offset indent -compact @@ -397,219 +397,218 @@ The parser recognises the following requests. For requests marked as "ignored" or "unsupported", any arguments are ignored, and the number of arguments is not checked. -.Ss \&ab +.Bl -tag -width Ds +.It Ic \&ab Op Ar message Abort processing. Currently unsupported. -.Ss \&ad -Set line adjustment mode. -It takes one argument to select normal, left, right, -or center adjustment for subsequent text. +.It Ic \&ad Op Cm b | c | l | n | r +Set line adjustment mode for subsequent text. Currently ignored. -.Ss \&af +.It Ic \&af Ar registername format Assign an output format to a number register. Currently ignored. -.Ss \&aln +.It Ic \&aln Ar newname oldname Create an alias for a number register. Currently unsupported. -.Ss \&als +.It Ic \&als Ar newname oldname Create an alias for a request, string, macro, or diversion. -Currently unsupported. -.Ss \&am +.It Ic \&am Ar macroname Op Ar endmacro Append to a macro definition. The syntax of this request is the same as that of -.Sx \&de . -.Ss \&am1 +.Ic \&de . +.It Ic \&am1 Ar macroname Op Ar endmacro Append to a macro definition, switching roff compatibility mode off during macro execution (groff extension). The syntax of this request is the same as that of -.Sx \&de1 . +.Ic \&de1 . Since .Xr mandoc 1 does not implement .Nm compatibility mode at all, it handles this request as an alias for -.Sx \&am . -.Ss \&ami +.Ic \&am . +.It Ic \&ami Ar macrostring Op Ar endstring Append to a macro definition, specifying the macro name indirectly (groff extension). The syntax of this request is the same as that of -.Sx \&dei . -.Ss \&ami1 +.Ic \&dei . +.It Ic \&ami1 Ar macrostring Op Ar endstring Append to a macro definition, specifying the macro name indirectly and switching roff compatibility mode off during macro execution (groff extension). The syntax of this request is the same as that of -.Sx \&dei1 . +.Ic \&dei1 . Since .Xr mandoc 1 does not implement .Nm compatibility mode at all, it handles this request as an alias for -.Sx \&ami . -.Ss \&as +.Ic \&ami . +.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 . If a user-defined string with the specified name does not yet exist, it is set to the empty string before appending. -.Ss \&as1 +.It Ic \&as1 Ar stringname Op Ar string Append to a user-defined string, switching roff compatibility mode off during macro execution (groff extension). The syntax of this request is the same as that of -.Sx \&ds1 . +.Ic \&ds1 . Since .Xr mandoc 1 does not implement .Nm compatibility mode at all, it handles this request as an alias for -.Sx \&as . -.Ss \&asciify +.Ic \&as . +.It Ic \&asciify Ar divname Fully unformat a diversion. Currently unsupported. -.Ss \&backtrace +.It Ic \&backtrace Print a backtrace of the input stack. This is a groff extension and currently ignored. -.Ss \&bd +.It Ic \&bd Ar font Oo Ar curfont Oc Op Ar offset Artificially embolden by repeated printing with small shifts. Currently ignored. -.Ss \&bleedat +.It Ic \&bleedat Ar left top width height Set the BleedBox page parameter for PDF generation. This is a Heirloom extension and currently ignored. -.Ss \&blm +.It Ic \&blm Ar macroname Set a blank line trap. Currently unsupported. -.Ss \&box +.It Ic \&box Ar divname Begin a diversion without including a partially filled line. Currently unsupported. -.Ss \&boxa +.It Ic \&boxa Ar divname Add to a diversion without including a partially filled line. Currently unsupported. -.Ss \&bp -Begin new page. +.It Ic \&bp Oo Cm + Ns | Ns Cm - Oc Ns Ar pagenumber +Begin a new page. Currently ignored. -.Ss \&BP +.It Ic \&BP Ar source height width position offset flags label Define a frame and place a picture in it. This is a Heirloom extension and currently unsupported. -.Ss \&br +.It Ic \&br Break the output line. -See -.Xr man 7 -and -.Xr mdoc 7 . -.Ss \&break +.It Ic \&break Break out of a -.Sx \&while +.Ic \&while loop. Currently unsupported. -.Ss \&breakchar +.It Ic \&breakchar Ar char ... Optional line break characters. This is a Heirloom extension and currently ignored. -.Ss \&brnl -Break output line after next N input lines. +.It Ic \&brnl Ar N +Break output line after the next +.Ar N +input lines. This is a Heirloom extension and currently ignored. -.Ss \&brp +.It Ic \&brp Break and spread output line. Currently, this is implemented as an alias for -.Sx \&br . -.Ss \&brpnl -Break and spread output line after next N input lines. +.Ic \&br . +.It Ic \&brpnl Ar N +Break and spread output line after the next +.Ar N +input lines. This is a Heirloom extension and currently ignored. -.Ss \&c2 +.It Ic \&c2 Op Ar char Change the no-break control character. Currently unsupported. -.Ss \&cc +.It Ic \&cc Op Ar char Change the control character. -Its syntax is as follows: -.Bd -literal -offset indent -.Pf . Cm \&cc Op Ar c -.Ed -.Pp If -.Ar c +.Ar char is not specified, the control character is reset to .Sq \&. . Trailing characters are ignored. -.Ss \&ce -Center some lines. -It takes one integer argument, specifying how many lines to center. -Currently ignored. -.Ss \&cf +.It Ic \&ce Op Ar N +Center the next +.Ar N +input lines without filling. +.Ar N +defaults to 1. +An argument of 0 or less ends centering. +Currently, high level macros abort centering. +.It Ic \&cf Ar filename Output the contents of a file. Ignored because insecure. -.Ss \&cflags +.It Ic \&cflags Ar flags char ... Set character flags. This is a groff extension and currently ignored. -.Ss \&ch +.It Ic \&ch Ar macroname Op Ar dist Change a trap location. Currently ignored. -.Ss \&char +.It Ic \&char Ar glyphname Op Ar string Define a new glyph. Currently unsupported. -.Ss \&chop +.It Ic \&chop Ar stringname Remove the last character from a macro, string, or diversion. Currently unsupported. -.Ss \&class +.It Ic \&class Ar classname char ... Define a character class. This is a groff extension and currently ignored. -.Ss \&close +.It Ic \&close Ar streamname Close an open file. Ignored because insecure. -.Ss \&CL +.It Ic \&CL Ar color text Print text in color. This is a Heirloom extension and currently unsupported. -.Ss \&color +.It Ic \&color Op Cm 1 | 0 Activate or deactivate colors. This is a groff extension and currently ignored. -.Ss \&composite +.It Ic \&composite Ar from to Define a name component for composite glyph names. This is a groff extension and currently unsupported. -.Ss \&continue +.It Ic \&continue Immediately start the next iteration of a -.Sx \&while +.Ic \&while loop. Currently unsupported. -.Ss \&cp +.It Ic \&cp Op Cm 1 | 0 Switch .Nm compatibility mode on or off. Currently ignored. -.Ss \&cropat +.It Ic \&cropat Ar left top width height Set the CropBox page parameter for PDF generation. This is a Heirloom extension and currently ignored. -.Ss \&cs +.It Ic \&cs Ar font Op Ar width Op Ar emsize Constant character spacing mode. Currently ignored. -.Ss \&cu -Underline including whitespace. +.It Ic \&cu Op Ar N +Underline next +.Ar N +input lines including whitespace. Currently ignored. -.Ss \&da +.It Ic \&da Ar divname Append to a diversion. Currently unsupported. -.Ss \&dch +.It Ic \&dch Ar macroname Op Ar dist Change a trap location in the current diversion. This is a Heirloom extension and currently unsupported. -.Ss \&de +.It Ic \&de Ar macroname Op Ar endmacro Define a .Nm macro. Its syntax can be either .Bd -literal -offset indent -.Pf . Cm \&de Ar name -.Ar macro definition +.Pf . Ic \&de Ar macroname +.Ar definition \&.. .Ed .Pp or .Bd -literal -offset indent -.Pf . Cm \&de Ar name Ar end -.Ar macro definition -.Pf . Ar end +.Pf . Ic \&de Ar macroname Ar endmacro +.Ar definition +.Pf . Ar endmacro .Ed .Pp Both forms define or redefine the macro -.Ar name +.Ar macroname to represent the -.Ar macro definition , +.Ar definition , which may consist of one or more input lines, including the newline characters terminating each line, optionally containing calls to .Nm @@ -622,13 +621,13 @@ or macros, whichever applies to the document in question. .Pp Specifying a custom -.Ar end +.Ar endmacro macro works in the same way as for -.Sx \&ig ; +.Ic \&ig ; namely, the call to -.Sq Pf . Ar end +.Sq Pf . Ar endmacro first ends the -.Ar macro definition , +.Ar definition , and after that, it is also evaluated as a .Nm request or @@ -637,7 +636,7 @@ macro, but not as a high-level macro. .Pp The macro can be invoked later using the syntax .Pp -.D1 Pf . Ar name Op Ar argument Op Ar argument ... +.D1 Pf . Ar macroname Op Ar argument Op Ar argument ... .Pp Regarding argument parsing, see .Sx MACRO SYNTAX @@ -645,7 +644,7 @@ above. .Pp The line invoking the macro will be replaced in the input stream by the -.Ar macro definition , +.Ar definition , replacing all occurrences of .No \e\e$ Ns Ar N , where @@ -670,15 +669,15 @@ joined together with single blank characters. .Pp Since macros and user-defined strings share a common string table, defining a macro -.Ar name +.Ar macroname clobbers the user-defined string -.Ar name , +.Ar macroname , and the -.Ar macro definition +.Ar definition can also be printed using the .Sq \e* string interpolation syntax described below -.Sx ds , +.Ic ds , but this is rarely useful because every macro definition contains at least one explicit newline character. .Pp @@ -689,7 +688,7 @@ to a large, but finite number, and .Xr mandoc 1 also limits the length of the expanded input line. Do not rely on the exact values of these limits. -.Ss \&de1 +.It Ic \&de1 Ar macroname Op Ar endmacro Define a .Nm macro that will be executed with @@ -701,24 +700,20 @@ Since does not implement .Nm compatibility mode at all, it handles this request as an alias for -.Sx \&de . -.Ss \&defcolor +.Ic \&de . +.It Ic \&defcolor Ar newname scheme component ... Define a color name. This is a groff extension and currently ignored. -.Ss \&dei +.It Ic \&dei Ar macrostring Op Ar endstring Define a .Nm macro, specifying the macro name indirectly (groff extension). The syntax of this request is the same as that of -.Sx \&de . -The request -.Pp -.D1 Pf . Cm \&dei Ar name Op Ar end -.Pp -has the same effect as: +.Ic \&de . +The effect is the same as: .Pp -.D1 Pf . Cm \&de No \e* Ns Bo Ar name Bc Op \e* Ns Bq Ar end -.Ss \&dei1 +.D1 Pf . Cm \&de No \e* Ns Bo Ar macrostring Bc Op \e* Ns Bq Ar endstring +.It Ic \&dei1 Ar macrostring Op Ar endstring Define a .Nm macro that will be executed with @@ -730,29 +725,23 @@ Since does not implement .Nm compatibility mode at all, it handles this request as an alias for -.Sx \&dei . -.Ss \&device -This request only makes sense with the groff-specific intermediate -output format and is unsupported. -.Ss \&devicem -This request only makes sense with the groff-specific intermediate -output format and is unsupported. -.Ss \&di +.Ic \&dei . +.It Ic \&device Ar string ... +.It Ic \&devicem Ar stringname +These two requests only make sense with the groff-specific intermediate +output format and are unsupported. +.It Ic \&di Ar divname Begin a diversion. Currently unsupported. -.Ss \&do +.It Ic \&do Ar command Op Ar argument ... Execute .Nm request or macro line with compatibility mode disabled. Currently unsupported. -.Ss \&ds +.It Ic \&ds Ar stringname Op Oo \(dq Oc Ns Ar string Define a user-defined string. -Its syntax is as follows: -.Pp -.D1 Pf . Cm \&ds Ar name Oo \(dq Oc Ns Ar string -.Pp The -.Ar name +.Ar stringname and .Ar string arguments are space-separated. @@ -767,11 +756,11 @@ including whitespace and double-quote characters, even trailing ones. The .Ar string can be interpolated into subsequent text by using -.No \e* Ns Bq Ar name +.No \e* Ns Bq Ar stringname for a -.Ar name +.Ar stringname of arbitrary length, or \e*(NN or \e*N if the length of -.Ar name +.Ar stringname is two or one characters, respectively. Interpolation can be prevented by escaping the leading backslash; that is, an asterisk preceded by an even number of backslashes @@ -779,11 +768,11 @@ does not trigger string interpolation. .Pp Since user-defined strings and macros share a common string table, defining a string -.Ar name +.Ar stringname clobbers the macro -.Ar name , +.Ar stringname , and the -.Ar name +.Ar stringname used for defining a string can also be invoked as a macro, in which case the following input line will be appended to the .Ar string , @@ -798,12 +787,12 @@ H SYNOPSIS .Ed .Pp invokes the -.Cm SH +.Ic SH macro when used in a .Xr man 7 document. Such abuse is of course strongly discouraged. -.Ss \&ds1 +.It Ic \&ds1 Ar stringname Op Oo \(dq Oc Ns Ar string Define a user-defined string that will be expanded with .Nm compatibility mode switched off during string expansion. @@ -813,125 +802,122 @@ Since does not implement .Nm compatibility mode at all, it handles this request as an alias for -.Sx \&ds . -.Ss \&dwh +.Ic \&ds . +.It Ic \&dwh Ar dist macroname Set a location trap in the current diversion. This is a Heirloom extension and currently unsupported. -.Ss \&dt +.It Ic \&dt Op Ar dist macroname Set a trap within a diversion. Currently unsupported. -.Ss \&ec -Change the escape character. -Currently unsupported. -.Ss \&ecs +.It Ic \&ec Op Ar char +Enable the escape mechanism and change the escape character. +The +.Ar char +argument defaults to the backslash +.Pq Sq \e . +.It Ic \&ecr Restore the escape character. Currently unsupported. -.Ss \&ecr +.It Ic \&ecs Save the escape character. Currently unsupported. -.Ss \&el +.It Ic \&el Ar body The -.Qq else +.Dq else half of an if/else conditional. Pops a result off the stack of conditional evaluations pushed by -.Sx \&ie +.Ic \&ie and uses it as its conditional. If no stack entries are present (e.g., due to no prior -.Sx \&ie +.Ic \&ie calls) then false is assumed. The syntax of this request is similar to -.Sx \&if +.Ic \&if except that the conditional is missing. -.Ss \&em +.It Ic \&em Ar macroname Set a trap at the end of input. Currently unsupported. -.Ss \&EN +.It Ic \&EN End an equation block. See -.Sx \&EQ . -.Ss \&eo +.Ic \&EQ . +.It Ic \&eo Disable the escape mechanism completely. -Currently unsupported. -.Ss \&EP +.It Ic \&EP End a picture started by -.Sx \&BP . +.Ic \&BP . This is a Heirloom extension and currently unsupported. -.Ss \&EQ +.It Ic \&EQ Begin an equation block. See .Xr eqn 7 for a description of the equation language. -.Ss \&errprint +.It Ic \&errprint Ar message Print a string like an error message. This is a Heirloom extension and currently ignored. -.Ss \&ev +.It Ic \&ev Op Ar envname Switch to another environment. Currently unsupported. -.Ss \&evc +.It Ic \&evc Op Ar envname Copy an environment into the current environment. Currently unsupported. -.Ss \&ex +.It Ic \&ex Abort processing and exit. Currently unsupported. -.Ss \&fallback +.It Ic \&fallback Ar curfont font ... Select the fallback sequence for a font. This is a Heirloom extension and currently ignored. -.Ss \&fam +.It Ic \&fam Op Ar familyname Change the font family. -Takes one argument specifying the font family to be selected. -It is a groff extension and currently ignored. -.Ss \&fc +This is a groff extension and currently ignored. +.It Ic \&fc Op Ar delimchar Op Ar padchar Define a delimiting and a padding character for fields. Currently unsupported. -.Ss \&fchar +.It Ic \&fchar Ar glyphname Op Ar string Define a fallback glyph. Currently unsupported. -.Ss \&fcolor +.It Ic \&fcolor Ar colorname Set the fill color for \eD objects. This is a groff extension and currently ignored. -.Ss \&fdeferlig +.It Ic \&fdeferlig Ar font string ... Defer ligature building. This is a Heirloom extension and currently ignored. -.Ss \&feature +.It Ic \&feature Cm + Ns | Ns Cm - Ns Ar name Enable or disable an OpenType feature. This is a Heirloom extension and currently ignored. -.Ss \&fi +.It Ic \&fi Switch to fill mode. See .Xr man 7 . Ignored in .Xr mdoc 7 . -.Ss \&fkern +.It Ic \&fkern Ar font minkern Control the use of kerning tables for a font. This is a Heirloom extension and currently ignored. -.Ss \&fl +.It Ic \&fl Flush output. Currently ignored. -.Ss \&flig +.It Ic \&flig Ar font string char ... Define ligatures. This is a Heirloom extension and currently ignored. -.Ss \&fp +.It Ic \&fp Ar position font Op Ar filename Assign font position. Currently ignored. -.Ss \&fps +.It Ic \&fps Ar mapname ... Mount a font with a special character map. This is a Heirloom extension and currently ignored. -.Ss \&fschar +.It Ic \&fschar Ar font glyphname Op Ar string Define a font-specific fallback glyph. This is a groff extension and currently unsupported. -.Ss \&fspacewidth +.It Ic \&fspacewidth Ar font Op Ar afmunits Set a font-specific width for the space character. This is a Heirloom extension and currently ignored. -.Ss \&fspecial +.It Ic \&fspecial Ar curfont Op Ar font ... Conditionally define a special font. This is a groff extension and currently ignored. -.Ss \&ft +.It Ic \&ft Op Ar font Change the font. -Its syntax is as follows: -.Pp -.D1 Pf . Cm \&ft Op Ar font -.Pp The following .Ar font arguments are supported: @@ -950,91 +936,88 @@ switches to normal font switches back to the previous font .El .Pp -This request takes effect only locally, may be overridden by macros -and escape sequences, and is only supported in -.Xr man 7 -for now. -.Ss \&ftr +This request takes effect only locally and may be overridden +by macros and escape sequences. +.It Ic \&ftr Ar newname Op Ar oldname Translate font name. This is a groff extension and currently ignored. -.Ss \&fzoom +.It Ic \&fzoom Ar font Op Ar permille Zoom font size. Currently ignored. -.Ss \&gcolor +.It Ic \&gcolor Op Ar colorname Set glyph color. This is a groff extension and currently ignored. -.Ss \&hc +.It Ic \&hc Op Ar char Set the hyphenation character. Currently ignored. -.Ss \&hcode +.It Ic \&hcode Ar char code ... Set hyphenation codes of characters. Currently ignored. -.Ss \&hidechar +.It Ic \&hidechar Ar font char ... Hide characters in a font. This is a Heirloom extension and currently ignored. -.Ss \&hla +.It Ic \&hla Ar language Set hyphenation language. This is a groff extension and currently ignored. -.Ss \&hlm +.It Ic \&hlm Op Ar number Set maximum number of consecutive hyphenated lines. Currently ignored. -.Ss \&hpf +.It Ic \&hpf Ar filename Load hyphenation pattern file. This is a groff extension and currently ignored. -.Ss \&hpfa +.It Ic \&hpfa Ar filename Load hyphenation pattern file, appending to the current patterns. This is a groff extension and currently ignored. -.Ss \&hpfcode +.It Ic \&hpfcode Ar code code ... Define mapping values for character codes in hyphenation patterns. This is a groff extension and currently ignored. -.Ss \&hw +.It Ic \&hw Ar word ... Specify hyphenation points in words. Currently ignored. -.Ss \&hy +.It Ic \&hy Op Ar mode Set automatic hyphenation mode. Currently ignored. -.Ss \&hylang +.It Ic \&hylang Ar language Set hyphenation language. This is a Heirloom extension and currently ignored. -.Ss \&hylen +.It Ic \&hylen Ar nchar Minimum word length for hyphenation. This is a Heirloom extension and currently ignored. -.Ss \&hym +.It Ic \&hym Op Ar length Set hyphenation margin. This is a groff extension and currently ignored. -.Ss \&hypp +.It Ic \&hypp Ar penalty ... Define hyphenation penalties. This is a Heirloom extension and currently ignored. -.Ss \&hys +.It Ic \&hys Op Ar length Set hyphenation space. This is a groff extension and currently ignored. -.Ss \&ie +.It Ic \&ie Ar condition body The -.Qq if +.Dq if half of an if/else conditional. The result of the conditional is pushed into a stack used by subsequent invocations of -.Sx \&el , +.Ic \&el , which may be separated by any intervening input (or not exist at all). Its syntax is equivalent to -.Sx \&if . -.Ss \&if -Begins a conditional. -This request has the following syntax: -.Bd -literal -offset indent -\&.if COND BODY +.Ic \&if . +.It Ic \&if Ar condition body +Begin a conditional. +This request can also be written as follows: +.Bd -unfilled -offset indent +.Pf . Ic \&if Ar condition No \e{ Ns Ar body +.Ar body ... Ns \e} .Ed -.Bd -literal -offset indent -\&.if COND \e{BODY -BODY...\e} -.Ed -.Bd -literal -offset indent -\&.if COND \e{\e -BODY... -\&.\e} +.Bd -unfilled -offset indent +.Pf . Ic \&if Ar condition No \e{\e +.Ar body ... +.Pf . No \e} .Ed .Pp -COND is a conditional statement. +The +.Ar condition +is a boolean expression. Currently, .Xr mandoc 1 supports the following subset of roff conditionals: @@ -1042,21 +1025,25 @@ supports the following subset of roff conditionals: .It If .Sq \&! -is prefixed to COND, the condition is logically inverted. +is prefixed to +.Ar condition , +it is logically inverted. .It -If the first character of COND is +If the first character of +.Ar condition +is .Sq n .Pq nroff mode or .Sq o .Pq odd page , -COND evaluates to true. +it evaluates to true. .It -If the first character of COND is +If the first character of +.Ar condition +is .Sq c .Pq character available , -.Sq d -.Pq string defined , .Sq e .Pq even page , .Sq t @@ -1064,26 +1051,44 @@ If the first character of COND is or .Sq v .Pq vroff mode , -COND evaluates to false. +it evaluates to false. .It -If the first character of COND is +If the first character of +.Ar condition +is +.Sq d , +it evaluates to true if the rest of +.Ar condition +is the name of an existing user defined macro or string; +otherwise, it evaluates to false. +.It +If the first character of +.Ar condition +is .Sq r , -it evaluates to true if the rest of COND is the name of an existing -number register; otherwise, it evaluates to false. +it evaluates to true if the rest of +.Ar condition +is the name of an existing number register; +otherwise, it evaluates to false. .It -If COND starts with a parenthesis or with an optionally signed +If the +.Ar condition +starts with a parenthesis or with an optionally signed integer number, it is evaluated according to the rules of .Sx Numerical expressions explained below. It evaluates to true if the result is positive, or to false if the result is zero or negative. .It -Otherwise, the first character of COND is regarded as a delimiter -and COND evaluates to true if the string extending from its first -to its second occurrence is equal to the string extending from its -second to its third occurrence. +Otherwise, the first character of +.Ar condition +is regarded as a delimiter and it evaluates to true if the string +extending from its first to its second occurrence is equal to the +string extending from its second to its third occurrence. .It -If COND cannot be parsed, it evaluates to false. +If +.Ar condition +cannot be parsed, it evaluates to false. .El .Pp If a conditional is false, its children are not processed, but are @@ -1104,28 +1109,33 @@ conditional. Sub-conditionals, in this case, obviously inherit the truth value of the parent. .Pp -If the BODY section is begun by an escaped brace +If the +.Ar body +section is begun by an escaped brace .Sq \e{ , scope continues until the end of the input line containing the matching closing-brace escape sequence .Sq \e} . -If the BODY is not enclosed in braces, scope continues until -the end of the line. -If the COND is followed by a BODY on the same line, whether after a -brace or not, then requests and macros +If the +.Ar body +is not enclosed in braces, scope continues until the end of the line. +If the +.Ar condition +is followed by a +.Ar body +on the same line, whether after a brace or not, then requests and macros .Em must begin with a control character. It is generally more intuitive, in this case, to write -.Bd -literal -offset indent -\&.if COND \e{\e -\&.foo -bar -\&.\e} +.Bd -unfilled -offset indent +.Pf . Ic \&if Ar condition No \e{\e +.Pf . Ar request +.Pf . No \e} .Ed .Pp than having the request or macro follow as .Pp -.D1 \&.if COND \e{ .foo +.D1 Pf . Ic \&if Ar condition Pf \e{. Ar request .Pp The scope of a conditional is always parsed, but only executed if the conditional evaluates to true. @@ -1144,7 +1154,7 @@ will result in being considered an argument of the .Sq \&Fl macro. -.Ss \&ig +.It Ic \&ig Op Ar endmacro Ignore input. Its syntax can be either .Bd -literal -offset indent @@ -1155,31 +1165,31 @@ Its syntax can be either .Pp or .Bd -literal -offset indent -.Pf . Cm \&ig Ar end +.Pf . Cm \&ig Ar endmacro .Ar ignored text -.Pf . Ar end +.Pf . Ar endmacro .Ed .Pp In the first case, input is ignored until a .Sq \&.. request is encountered on its own line. In the second case, input is ignored until the specified -.Sq Pf . Ar end -macro is encountered. +.Sq Pf . Ar endmacro +is encountered. Do not use the escape character .Sq \e anywhere in the definition of -.Ar end ; +.Ar endmacro ; it would cause very strange behaviour. .Pp When the -.Ar end -macro is a roff request or a roff macro, like in +.Ar endmacro +is a roff request or a roff macro, like in .Pp .D1 \&.ig if .Pp the subsequent invocation of -.Sx \&if +.Ic \&if will first terminate the .Ar ignored text , then be invoked as usual. @@ -1188,21 +1198,17 @@ Otherwise, it only terminates the and arguments following it or the .Sq \&.. request are discarded. -.Ss \&in +.It Ic \&in Op Oo Cm + Ns | Ns Cm - Oc Ns Ar width Change indentation. See .Xr man 7 . Ignored in .Xr mdoc 7 . -.Ss \&index +.It Ic \&index Ar register stringname substring Find a substring in a string. This is a Heirloom extension and currently unsupported. -.Ss \&it +.It Ic \&it Ar expression macro Set an input line trap. -Its syntax is as follows: -.Pp -.D1 Pf . Cm it Ar expression macro -.Pp The named .Ar macro will be invoked after processing the number of input text lines @@ -1213,63 +1219,59 @@ While evaluating the the unit suffixes described below .Sx Scaling Widths are ignored. -.Ss \&itc +.It Ic \&it Ar expression macro Set an input line trap, not counting lines ending with \ec. Currently unsupported. -.Ss \&IX +.It Ic \&IX Ar class keystring To support the generation of a table of contents, .Xr pod2man 1 emits this user-defined macro, usually without defining it. To avoid reporting large numbers of spurious errors, .Xr mandoc 1 ignores it. -.Ss \&kern +.It Ic \&kern Op Cm 1 | 0 Switch kerning on or off. Currently ignored. -.Ss \&kernafter +.It Ic \&kernafter Ar font char ... afmunits ... Increase kerning after some characters. This is a Heirloom extension and currently ignored. -.Ss \&kernbefore +.It Ic \&kernbefore Ar font char ... afmunits ... Increase kerning before some characters. This is a Heirloom extension and currently ignored. -.Ss \&kernpair +.It Ic \&kernpair Ar font char ... font char ... afmunits Add a kerning pair to the kerning table. This is a Heirloom extension and currently ignored. -.Ss \&lc +.It Ic \&lc Op Ar glyph Define a leader repetition character. Currently unsupported. -.Ss \&lc_ctype +.It Ic \&lc_ctype Ar localename Set the .Dv LC_CTYPE locale. This is a Heirloom extension and currently unsupported. -.Ss \&lds +.It Ic \&lds Ar macroname string Define a local string. This is a Heirloom extension and currently unsupported. -.Ss \&length -Count the number of input characters in a user-defined string. +.It Ic \&length Ar register string +Count the number of input characters in a string. Currently unsupported. -.Ss \&letadj +.It Ic \&letadj Ar lspmin lshmin letss lspmax lshmax Dynamic letter spacing and reshaping. This is a Heirloom extension and currently ignored. -.Ss \&lf +.It Ic \&lf Ar lineno Op Ar filename Change the line number for error messages. Ignored because insecure. -.Ss \&lg +.It Ic \&lg Op Cm 1 | 0 Switch the ligature mechanism on or off. Currently ignored. -.Ss \&lhang +.It Ic \&lhang Ar font char ... afmunits Hang characters at left margin. This is a Heirloom extension and currently ignored. -.Ss \&linetabs +.It Ic \&linetabs Op Cm 1 | 0 Enable or disable line-tabs mode. This is a groff extension and currently unsupported. -.Ss \&ll +.It Ic \&ll Op Oo Cm + Ns | Ns Cm - Oc Ns Ar width Change the output line length. -Its syntax is as follows: -.Pp -.D1 Pf . Cm \&ll Op Oo +|- Oc Ns Ar width -.Pp If the .Ar width argument is omitted, the line length is reset to its previous value. @@ -1281,77 +1283,75 @@ among others because it overrides the .Xr mandoc 1 .Fl O Cm width command line option. -.Ss \&lnr +.It Ic \&lnr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar value Op Ar increment Set local number register. This is a Heirloom extension and currently unsupported. -.Ss \&lnrf +.It Ic \&lnrf Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar value Op Ar increment Set local floating-point register. This is a Heirloom extension and currently unsupported. -.Ss \&lpfx +.It Ic \&lpfx Ar string Set a line prefix. This is a Heirloom extension and currently unsupported. -.Ss \&ls +.It Ic \&ls Op Ar factor Set line spacing. It takes one integer argument specifying the vertical distance of subsequent output text lines measured in v units. Currently ignored. -.Ss \&lsm +.It Ic \&lsm Ar macroname Set a leading spaces trap. This is a groff extension and currently unsupported. -.Ss \< +.It Ic \< Op Oo Cm + Ns | Ns Cm - Oc Ns Ar width Set title line length. Currently ignored. -.Ss \&mc +.It Ic \&mc Ar glyph Op Ar dist Print margin character in the right margin. -Currently ignored. -.Ss \&mediasize +The +.Ar dist +is currently ignored; instead, 1n is used. +.It Ic \&mediasize Ar media Set the device media size. This is a Heirloom extension and currently ignored. -.Ss \&minss +.It Ic \&minss Ar width Set minimum word space. This is a Heirloom extension and currently ignored. -.Ss \&mk +.It Ic \&mk Op Ar register Mark vertical position. Currently ignored. -.Ss \&mso -Load a macro file. +.It Ic \&mso Ar filename +Load a macro file using the search path. Ignored because insecure. -.Ss \&na +.It Ic \&na Disable adjusting without changing the adjustment mode. Currently ignored. -.Ss \&ne +.It Ic \&ne Op Ar height Declare the need for the specified minimum vertical space before the next trap or the bottom of the page. Currently ignored. -.Ss \&nf +.It Ic \&nf Switch to no-fill mode. See .Xr man 7 . Ignored by .Xr mdoc 7 . -.Ss \&nh +.It Ic \&nh Turn off automatic hyphenation mode. Currently ignored. -.Ss \&nhychar +.It Ic \&nhychar Ar char ... Define hyphenation-inhibiting characters. This is a Heirloom extension and currently ignored. -.Ss \&nm +.It Ic \&nm Op Ar start Op Ar inc Op Ar space Op Ar indent Print line numbers. Currently unsupported. -.Ss \&nn +.It Ic \&nn Op Ar number Temporarily turn off line numbering. Currently unsupported. -.Ss \&nop +.It Ic \&nop Ar body Execute the rest of the input line as a request or macro line. Currently unsupported. -.Ss \&nr +.It Ic \&nr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression Define or change a register. A register is an arbitrary string value that defines some sort of state, which influences parsing and/or formatting. -Its syntax is as follows: -.Pp -.D1 Pf \. Cm \&nr Ar name Oo +|- Oc Ns Ar expression -.Pp For the syntax of .Ar expression , see @@ -1360,8 +1360,8 @@ below. If it is prefixed by a sign, the register will be incremented or decremented instead of assigned to. .Pp -The following register -.Ar name +The following +.Ar register is handled specially: .Bl -tag -width Ds .It Cm nS @@ -1378,155 +1378,165 @@ section itself. Note that starting a new .Xr mdoc 7 section with the -.Cm \&Sh +.Ic \&Sh macro will reset this register. .El -.Ss \&nrf +.It Xo +.Ic \&nrf Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression +.Op Ar increment +.Xc Define or change a floating-point register. This is a Heirloom extension and currently unsupported. -.Ss \&nroff +.It Ic \&nroff Force nroff mode. This is a groff extension and currently ignored. -.Ss \&ns +.It Ic \&ns Turn on no-space mode. Currently ignored. -.Ss \&nx +.It Ic \&nx Op Ar filename Abort processing of the current input file and process another one. Ignored because insecure. -.Ss \&open +.It Ic \&open Ar stream file Open a file for writing. Ignored because insecure. -.Ss \&opena +.It Ic \&opena Ar stream file Open a file for appending. Ignored because insecure. -.Ss \&os +.It Ic \&os Output saved vertical space. Currently ignored. -.Ss \&output +.It Ic \&output Ar string Output directly to intermediate output. Not supported. -.Ss \&padj +.It Ic \&padj Op Cm 1 | 0 Globally control paragraph-at-once adjustment. This is a Heirloom extension and currently ignored. -.Ss \&papersize +.It Ic \&papersize Ar media Set the paper size. This is a Heirloom extension and currently ignored. -.Ss \&pc +.It Ic \&pc Op Ar char Change the page number character. Currently ignored. -.Ss \&pev +.It Ic \&pev Print environments. This is a groff extension and currently ignored. -.Ss \&pi +.It Ic \&pi Ar command Pipe output to a shell command. Ignored because insecure. -.Ss \&PI +.It Ic \&PI Low-level request used by -.Sx \&BP . +.Ic \&BP . This is a Heirloom extension and currently unsupported. -.Ss \&pl +.It Ic \&pl Op Oo Cm + Ns | Ns Cm - Oc Ns Ar height Change page length. -Takes one height argument. Currently ignored. -.Ss \&pm -Print names and sizes of macros, strings, and diversions. +.It Ic \&pm +Print names and sizes of macros, strings, and diversions +to standard error output. Currently ignored. -.Ss \&pn -Change page number of the next page. +.It Ic \&pn Oo Cm + Ns | Ns Cm - Oc Ns Ar number +Change the page number of the next page. Currently ignored. -.Ss \&pnr -Print all number registers. +.It Ic \&pnr +Print all number registers on standard error output. Currently ignored. -.Ss \&po -Set horizontal page offset. -Currently ignored. -.Ss \&ps +.It Ic \&po Op Oo Cm + Ns | Ns Cm - Oc Ns Ar offset +Set a horizontal page offset. +If no argument is specified, the page offset is reverted to its +previous value. +If a sign is specified, the new page offset is calculated relative +to the current one; otherwise, it is absolute. +The argument follows the syntax of +.Sx Scaling Widths +and the default scaling unit is +.Cm m . +.It Ic \&ps Op Oo Cm + Ns | Ns Cm - Oc Ns size Change point size. -Takes one numerical argument. Currently ignored. -.Ss \&psbb +.It Ic \&psbb Ar filename Retrieve the bounding box of a PostScript file. Currently unsupported. -.Ss \&pshape +.It Ic \&pshape Ar indent length ... Set a special shape for the current paragraph. This is a Heirloom extension and currently unsupported. -.Ss \&pso +.It Ic \&pso Ar command Include output of a shell command. Ignored because insecure. -.Ss \&ptr -Print the names and positions of all traps. +.It Ic \&ptr +Print the names and positions of all traps on standard error output. This is a groff extension and currently ignored. -.Ss \&pvs +.It Ic \&pvs Op Oo Cm + Ns | Ns Cm - Oc Ns Ar height Change post-vertical spacing. This is a groff extension and currently ignored. -.Ss \&rchar +.It Ic \&rchar Ar glyph ... Remove glyph definitions. Currently unsupported. -.Ss \&rd +.It Ic \&rd Op Ar prompt Op Ar agument ... Read from standard input. Currently ignored. -.Ss \&recursionlimit +.It Ic \&recursionlimit Ar maxrec maxtail Set the maximum stack depth for recursive macros. This is a Heirloom extension and currently ignored. -.Ss \&return +.It Ic \&return Op Ar twice Exit a macro and return to the caller. Currently unsupported. -.Ss \&rfschar +.It Ic \&rfschar Ar font glyph ... Remove font-specific fallback glyph definitions. Currently unsupported. -.Ss \&rhang +.It Ic \&rhang Ar font char ... afmunits Hang characters at right margin. This is a Heirloom extension and currently ignored. -.Ss \&rj -Justify unfilled text to the right margin. -Currently ignored. -.Ss \&rm +.It Ic \&rj Op Ar N +Justify the next +.Ar N +input lines to the right margin without filling. +.Ar N +defaults to 1. +An argument of 0 or less ends right adjustment. +.It Ic \&rm Ar macroname Remove a request, macro or string. -Its syntax is as follows: -.Pp -.D1 Pf \. Cm \&rm Ar name -.Ss \&rn +.It Ic \&rn Ar oldname newname Rename a request, macro, diversion, or string. -Currently unsupported. -.Ss \&rnn +In +.Xr mandoc 1 , +user-defined macros, +.Xr mdoc 7 +and +.Xr man 7 +macros, and user-defined strings can be renamed, but renaming of +predefined strings and of +.Nm +requests is not supported, and diversions are not implemented at all. +.It Ic \&rnn Ar oldname newname Rename a number register. Currently unsupported. -.Ss \&rr +.It Ic \&rr Ar register Remove a register. -Its syntax is as follows: -.Pp -.D1 Pf \. Cm \&rr Ar name -.Ss \&rs +.It Ic \&rs End no-space mode. Currently ignored. -.Ss \&rt +.It Ic \&rt Op Ar dist Return to marked vertical position. Currently ignored. -.Ss \&schar +.It Ic \&schar Ar glyph Op Ar string Define global fallback glyph. This is a groff extension and currently unsupported. -.Ss \&sentchar +.It Ic \&sentchar Ar char ... Define sentence-ending characters. This is a Heirloom extension and currently ignored. -.Ss \&shc +.It Ic \&shc Op Ar glyph Change the soft hyphen character. Currently ignored. -.Ss \&shift +.It Ic \&shift Op Ar number Shift macro arguments. Currently unsupported. -.Ss \&sizes +.It Ic \&sizes Ar size ... Define permissible point sizes. This is a groff extension and currently ignored. -.Ss \&so +.It Ic \&so Ar filename Include a source file. -Its syntax is as follows: -.Pp -.D1 Pf \. Cm \&so Ar file -.Pp -The -.Ar file -will be read and its contents processed as input in place of the -.Sq \&.so +The file is read and its contents processed as input in place of the +.Ic \&so request line. To avoid inadvertent inclusion of unrelated files, .Xr mandoc 1 @@ -1545,168 +1555,184 @@ Typical usage looks like: .Dl \&.so man3/Xcursor.3 .Pp As the whole concept is rather fragile, the use of -.Sx \&so +.Ic \&so is discouraged. Use .Xr ln 1 instead. -.Ss \&spacewidth +.It Ic \&sp Op Ar height +Break the output line and emit vertical space. +The argument follows the syntax of +.Sx Scaling Widths +and defaults to one blank line +.Pq Li 1v . +.It Ic \&spacewidth Op Cm 1 | 0 Set the space width from the font metrics file. This is a Heirloom extension and currently ignored. -.Ss \&special +.It Ic \&special Op Ar font ... Define a special font. This is a groff extension and currently ignored. -.Ss \&spreadwarn +.It Ic \&spreadwarn Op Ar width Warn about wide spacing between words. Currently ignored. -.Ss \&ss +.It Ic \&ss Ar wordspace Op Ar sentencespace Set space character size. Currently ignored. -.Ss \&sty +.It Ic \&sty Ar position style Associate style with a font position. This is a groff extension and currently ignored. -.Ss \&substring +.It Ic \&substring Ar stringname startpos Op Ar endpos Replace a user-defined string with a substring. Currently unsupported. -.Ss \&sv +.It Ic \&sv Op Ar height Save vertical space. Currently ignored. -.Ss \&sy +.It Ic \&sy Ar command Execute shell command. Ignored because insecure. -.Ss \&T& +.It Ic \&T& Re-start a table layout, retaining the options of the prior table invocation. See .Sx \&TS . -.Ss \&ta +.It Ic \&ta Op Ar width ... Op Cm T Ar width ... Set tab stops. -Takes an arbitrary number of arguments. -Currently unsupported. -.Ss \&tc +Each +.Ar width +argument follows the syntax of +.Sx Scaling Widths . +If prefixed by a plus sign, it is relative to the previous tab stop. +The arguments after the +.Cm T +marker are used repeatedly as often as needed; for each reuse, +they are taken relative to the last previously established tab stop. +When +.Ic \&ta +is called without arguments, all tab stops are cleared. +.It Ic \&tc Op Ar glyph Change tab repetition character. Currently unsupported. -.Ss \&TE +.It Ic \&TE End a table context. See .Sx \&TS . -.Ss \&ti -Temporary indent. -Currently unsupported. -.Ss \&tkf +.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 . +If a sign is specified, the temporary indentation is calculated +relative to the current indentation; otherwise, it is absolute. +The argument follows the syntax of +.Sx Scaling Widths +and the default scaling unit is +.Cm m . +.It Ic \&tkf Ar font minps width1 maxps width2 Enable track kerning for a font. Currently ignored. -.Ss \&tl +.It Ic \&tl No \& Ap Ar left Ap Ar center Ap Ar right Ap Print a title line. Currently unsupported. -.Ss \&tm +.It Ic \&tm Ar string Print to standard error output. Currently ignored. -.Ss \&tm1 +.It Ic \&tm1 Ar string Print to standard error output, allowing leading blanks. This is a groff extension and currently ignored. -.Ss \&tmc +.It Ic \&tmc Ar string Print to standard error output without a trailing newline. This is a groff extension and currently ignored. -.Ss \&tr +.It Ic \&tr Ar glyph glyph ... Output character translation. -Its syntax is as follows: -.Pp -.D1 Pf \. Cm \&tr Ar [ab]+ -.Pp -Pairs of -.Ar ab -characters are replaced -.Ar ( a -for -.Ar b ) . -Replacement (or origin) characters may also be character escapes; thus, +The first glyph in each pair is replaced by the second one. +Character escapes can be used; for example, .Pp .Dl tr \e(xx\e(yy .Pp replaces all invocations of \e(xx with \e(yy. -.Ss \&track +.It Ic \&track Ar font minps width1 maxps width2 Static letter space tracking. This is a Heirloom extension and currently ignored. -.Ss \&transchar +.It Ic \&transchar Ar char ... Define transparent characters for sentence-ending. This is a Heirloom extension and currently ignored. -.Ss \&trf +.It Ic \&trf Ar filename Output the contents of a file, disallowing invalid characters. This is a groff extension and ignored because insecure. -.Ss \&trimat +.It Ic \&trimat Ar left top width height Set the TrimBox page parameter for PDF generation. This is a Heirloom extension and currently ignored. -.Ss \&trin +.It Ic \&trin Ar glyph glyph ... Output character translation, ignored by -.Cm \&asciify . +.Ic \&asciify . Currently unsupported. -.Ss \&trnt +.It Ic \&trnt Ar glyph glyph ... Output character translation, ignored by \e!. Currently unsupported. -.Ss \&troff +.It Ic \&troff Force troff mode. This is a groff extension and currently ignored. -.Ss \&TS +.It Ic \&TS Begin a table, which formats input in aligned rows and columns. See .Xr tbl 7 for a description of the tbl language. -.Ss \&uf +.It Ic \&uf Ar font Globally set the underline font. Currently ignored. -.Ss \&ul -Underline. +.It Ic \&ul Op Ar N +Underline next +.Ar N +input lines. Currently ignored. -.Ss \&unformat +.It Ic \&unformat Ar divname Unformat spaces and tabs in a diversion. Currently unsupported. -.Ss \&unwatch +.It Ic \&unwatch Ar macroname Disable notification for string or macro. This is a Heirloom extension and currently ignored. -.Ss \&unwatchn +.It Ic \&unwatchn Ar register Disable notification for register. This is a Heirloom extension and currently ignored. -.Ss \&vpt +.It Ic \&vpt Op Cm 1 | 0 Enable or disable vertical position traps. This is a groff extension and currently ignored. -.Ss \&vs +.It Ic \&vs Op Oo Cm + Ns | Ns Cm - Oc Ns Ar height Change vertical spacing. Currently ignored. -.Ss \&warn +.It Ic \&warn Ar flags Set warning level. Currently ignored. -.Ss \&warnscale +.It Ic \&warnscale Ar si Set the scaling indicator used in warnings. This is a groff extension and currently ignored. -.Ss \&watch +.It Ic \&watch Ar macroname Notify on change of string or macro. This is a Heirloom extension and currently ignored. -.Ss \&watchlength +.It Ic \&watchlength Ar maxlength On change, report the contents of macros and strings up to the specified length. This is a Heirloom extension and currently ignored. -.Ss \&watchn +.It Ic \&watchn Ar register Notify on change of register. This is a Heirloom extension and currently ignored. -.Ss \&wh +.It Ic \&wh Ar dist Op Ar macroname Set a page location trap. Currently unsupported. -.Ss \&while +.It Ic \&while Ar condition body Repeated execution while a condition is true. Currently unsupported. -.Ss \&write +.It Ic \&write Oo \(dq Oc Ns Ar string Write to an open file. Ignored because insecure. -.Ss \&writec +.It Ic \&writec Oo \(dq Oc Ns Ar string Write to an open file without appending a newline. Ignored because insecure. -.Ss \&writem +.It Ic \&writem Ar macroname Write macro or string to an open file. Ignored because insecure. -.Ss \&xflag +.It Ic \&xflag Ar level Set the extension level. This is a Heirloom extension and currently ignored. +.El .Ss Numerical expressions The .Sx \&nr , @@ -1772,7 +1798,7 @@ logical and (corresponds to C .Ic && ) .It Ic \&: logical or (corresponds to C -.Ic \&|| ) +.Ic || ) .It Ic <? minimum (not available in C) .It Ic >? @@ -1780,7 +1806,7 @@ maximum (not available in C) .El .Pp There is no concept of precedence; evaluation proceeds from left to right, -except when subexpressions are enclosed in parantheses. +except when subexpressions are enclosed in parentheses. Inside parentheses, whitespace is ignored. .Sh ESCAPE SEQUENCE REFERENCE The @@ -1828,7 +1854,7 @@ instead. .Sx Special Characters with two-letter names, see .Xr mandoc_char 7 . -.Ss \e*[ Ns Ar name ] +.Ss \e* Ns Bq Ar name Interpolate the string with the .Ar name ; see @@ -1848,7 +1874,7 @@ Special character .Ss \e/ Right italic correction (groff extension); ignored by .Xr mandoc 1 . -.Ss \e[ Ns Ar name ] +.Ss \e Ns Bq Ar name .Sx Special Characters with names of arbitrary length, see .Xr mandoc_char 7 . @@ -1904,14 +1930,14 @@ Move down by half a line; ignored by .Xr mandoc 1 . .Ss \ee Backslash special character. -.Ss \eF[ Ns Ar name ] +.Ss \eF Ns Bq Ar name Switch font family (groff extension); ignored by .Xr mandoc 1 . For short names, there are variants .No \eF Ns Ar c and .No \eF( Ns Ar cc . -.Ss \ef[ Ns Ar name ] +.Ss \ef Ns Bq Ar name Switch to the font .Ar name , see @@ -1920,7 +1946,7 @@ For short names, there are variants .No \ef Ns Ar c and .No \ef( Ns Ar cc . -.Ss \eg[ Ns Ar name ] +.Ss \eg Ns Bq Ar name Interpolate the format of a number register; ignored by .Xr mandoc 1 . For short names, there are variants @@ -1930,10 +1956,14 @@ and .Ss \eH\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq Set the height of the current font; ignored by .Xr mandoc 1 . -.Ss \eh\(aq Ns Ar number Ns \(aq -Horizontal motion; ignored by -.Xr mandoc 1 . -.Ss \ek[ Ns Ar name ] +.Ss \eh\(aq Ns Oo Cm \&| Oc Ns Ar width Ns \(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 Mark horizontal input place in register; ignored by .Xr mandoc 1 . For short names, there are variants @@ -1943,17 +1973,19 @@ and .Ss \eL\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq Vertical line drawing function; ignored by .Xr mandoc 1 . -.Ss \el\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq -Horizontal line drawing function; ignored by -.Xr mandoc 1 . -.Ss \eM[ Ns Ar name ] +.Ss \el\(aq Ns Ar width Ns Oo Ar c Oc Ns \(aq +Draw a horizontal line of +.Ar width +using the glyph +.Ar c . +.Ss \eM Ns Bq Ar name Set fill (background) color (groff extension); ignored by .Xr mandoc 1 . For short names, there are variants .No \eM Ns Ar c and .No \eM( Ns Ar cc . -.Ss \em[ Ns Ar name ] +.Ss \em Ns Bq Ar name Set glyph drawing color (groff extension); ignored by .Xr mandoc 1 . For short names, there are variants @@ -1964,7 +1996,7 @@ and Character .Ar number on the current font. -.Ss \en[ Ns Ar name ] +.Ss \en Ns Bq Ar name Interpolate the number register .Ar name . For short names, there are variants @@ -1977,6 +2009,8 @@ Overstrike, writing all the characters contained in the to the same output position. In terminal and HTML output modes, only the last one of the characters is visible. +.Ss \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 Set number register; ignored by .Xr mandoc 1 . @@ -1989,9 +2023,9 @@ Change point size; ignored by 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 [ Oo +|- Oc Ns Ar number ] , +.No \es Ns Bq Oo +|- Oc Ns Ar number , and -.No \es Ns Oo +|- Oc Ns [ Ar number Ns ] +.No \es Ns Oo +|- Oc Ns Bq Ar number are also parsed and ignored. .Ss \et Horizontal tab; ignored by @@ -1999,7 +2033,7 @@ Horizontal tab; ignored by .Ss \eu Move up by half a line; ignored by .Xr mandoc 1 . -.Ss \eV[ Ns Ar name ] +.Ss \eV Ns Bq Ar name Interpolate an environment variable; ignored by .Xr mandoc 1 . For short names, there are variants @@ -2026,7 +2060,7 @@ as device control function; ignored in nroff mode and by .Ss \ex\(aq Ns Ar number Ns \(aq Extra line space function; ignored by .Xr mandoc 1 . -.Ss \eY[ Ns Ar name ] +.Ss \eY Ns Bq Ar name Output a string as a device control function; ignored in nroff mode and by .Xr mandoc 1 . For short names, there are variants diff --git a/contrib/mdocml/roff.c b/contrib/mdocml/roff.c index 966ed9d..e2e498d 100644 --- a/contrib/mdocml/roff.c +++ b/contrib/mdocml/roff.c @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.288 2017/01/12 18:02:20 schwarze Exp $ */ +/* $Id: roff.c,v 1.324 2017/07/14 17:16:16 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -22,12 +22,15 @@ #include <assert.h> #include <ctype.h> #include <limits.h> +#include <stddef.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "mandoc.h" #include "mandoc_aux.h" +#include "mandoc_ohash.h" #include "roff.h" #include "libmandoc.h" #include "roff_int.h" @@ -36,254 +39,15 @@ /* Maximum number of string expansions per line, to break infinite loops. */ #define EXPAND_LIMIT 1000 -/* --- data types --------------------------------------------------------- */ +/* Types of definitions of macros and strings. */ +#define ROFFDEF_USER (1 << 1) /* User-defined. */ +#define ROFFDEF_PRE (1 << 2) /* Predefined. */ +#define ROFFDEF_REN (1 << 3) /* Renamed standard macro. */ +#define ROFFDEF_STD (1 << 4) /* mdoc(7) or man(7) macro. */ +#define ROFFDEF_ANY (ROFFDEF_USER | ROFFDEF_PRE | \ + ROFFDEF_REN | ROFFDEF_STD) -enum rofft { - ROFF_ab, - ROFF_ad, - ROFF_af, - ROFF_aln, - ROFF_als, - ROFF_am, - ROFF_am1, - ROFF_ami, - ROFF_ami1, - ROFF_as, - ROFF_as1, - ROFF_asciify, - ROFF_backtrace, - ROFF_bd, - ROFF_bleedat, - ROFF_blm, - ROFF_box, - ROFF_boxa, - ROFF_bp, - ROFF_BP, - /* MAN_br, MDOC_br */ - ROFF_break, - ROFF_breakchar, - ROFF_brnl, - ROFF_brp, - ROFF_brpnl, - ROFF_c2, - ROFF_cc, - ROFF_ce, - ROFF_cf, - ROFF_cflags, - ROFF_ch, - ROFF_char, - ROFF_chop, - ROFF_class, - ROFF_close, - ROFF_CL, - ROFF_color, - ROFF_composite, - ROFF_continue, - ROFF_cp, - ROFF_cropat, - ROFF_cs, - ROFF_cu, - ROFF_da, - ROFF_dch, - ROFF_Dd, - ROFF_de, - ROFF_de1, - ROFF_defcolor, - ROFF_dei, - ROFF_dei1, - ROFF_device, - ROFF_devicem, - ROFF_di, - ROFF_do, - ROFF_ds, - ROFF_ds1, - ROFF_dwh, - ROFF_dt, - ROFF_ec, - ROFF_ecr, - ROFF_ecs, - ROFF_el, - ROFF_em, - ROFF_EN, - ROFF_eo, - ROFF_EP, - ROFF_EQ, - ROFF_errprint, - ROFF_ev, - ROFF_evc, - ROFF_ex, - ROFF_fallback, - ROFF_fam, - ROFF_fc, - ROFF_fchar, - ROFF_fcolor, - ROFF_fdeferlig, - ROFF_feature, - /* MAN_fi; ignored in mdoc(7) */ - ROFF_fkern, - ROFF_fl, - ROFF_flig, - ROFF_fp, - ROFF_fps, - ROFF_fschar, - ROFF_fspacewidth, - ROFF_fspecial, - /* MAN_ft; ignored in mdoc(7) */ - ROFF_ftr, - ROFF_fzoom, - ROFF_gcolor, - ROFF_hc, - ROFF_hcode, - ROFF_hidechar, - ROFF_hla, - ROFF_hlm, - ROFF_hpf, - ROFF_hpfa, - ROFF_hpfcode, - ROFF_hw, - ROFF_hy, - ROFF_hylang, - ROFF_hylen, - ROFF_hym, - ROFF_hypp, - ROFF_hys, - ROFF_ie, - ROFF_if, - ROFF_ig, - /* MAN_in; ignored in mdoc(7) */ - ROFF_index, - ROFF_it, - ROFF_itc, - ROFF_IX, - ROFF_kern, - ROFF_kernafter, - ROFF_kernbefore, - ROFF_kernpair, - ROFF_lc, - ROFF_lc_ctype, - ROFF_lds, - ROFF_length, - ROFF_letadj, - ROFF_lf, - ROFF_lg, - ROFF_lhang, - ROFF_linetabs, - /* MAN_ll, MDOC_ll */ - ROFF_lnr, - ROFF_lnrf, - ROFF_lpfx, - ROFF_ls, - ROFF_lsm, - ROFF_lt, - ROFF_mc, - ROFF_mediasize, - ROFF_minss, - ROFF_mk, - ROFF_mso, - ROFF_na, - ROFF_ne, - /* MAN_nf; ignored in mdoc(7) */ - ROFF_nh, - ROFF_nhychar, - ROFF_nm, - ROFF_nn, - ROFF_nop, - ROFF_nr, - ROFF_nrf, - ROFF_nroff, - ROFF_ns, - ROFF_nx, - ROFF_open, - ROFF_opena, - ROFF_os, - ROFF_output, - ROFF_padj, - ROFF_papersize, - ROFF_pc, - ROFF_pev, - ROFF_pi, - ROFF_PI, - ROFF_pl, - ROFF_pm, - ROFF_pn, - ROFF_pnr, - ROFF_po, - ROFF_ps, - ROFF_psbb, - ROFF_pshape, - ROFF_pso, - ROFF_ptr, - ROFF_pvs, - ROFF_rchar, - ROFF_rd, - ROFF_recursionlimit, - ROFF_return, - ROFF_rfschar, - ROFF_rhang, - ROFF_rj, - ROFF_rm, - ROFF_rn, - ROFF_rnn, - ROFF_rr, - ROFF_rs, - ROFF_rt, - ROFF_schar, - ROFF_sentchar, - ROFF_shc, - ROFF_shift, - ROFF_sizes, - ROFF_so, - /* MAN_sp, MDOC_sp */ - ROFF_spacewidth, - ROFF_special, - ROFF_spreadwarn, - ROFF_ss, - ROFF_sty, - ROFF_substring, - ROFF_sv, - ROFF_sy, - ROFF_T_, - ROFF_ta, - ROFF_tc, - ROFF_TE, - ROFF_TH, - ROFF_ti, - ROFF_tkf, - ROFF_tl, - ROFF_tm, - ROFF_tm1, - ROFF_tmc, - ROFF_tr, - ROFF_track, - ROFF_transchar, - ROFF_trf, - ROFF_trimat, - ROFF_trin, - ROFF_trnt, - ROFF_troff, - ROFF_TS, - ROFF_uf, - ROFF_ul, - ROFF_unformat, - ROFF_unwatch, - ROFF_unwatchn, - ROFF_vpt, - ROFF_vs, - ROFF_warn, - ROFF_warnscale, - ROFF_watch, - ROFF_watchlength, - ROFF_watchn, - ROFF_wh, - ROFF_while, - ROFF_write, - ROFF_writec, - ROFF_writem, - ROFF_xflag, - ROFF_cblock, - ROFF_USERDEF, - ROFF_MAX -}; +/* --- data types --------------------------------------------------------- */ /* * An incredibly-simple string buffer. @@ -311,21 +75,31 @@ struct roffreg { struct roffreg *next; }; +/* + * Association of request and macro names with token IDs. + */ +struct roffreq { + enum roff_tok tok; + char name[]; +}; + struct roff { struct mparse *parse; /* parse point */ + struct roff_man *man; /* mdoc or man parser */ struct roffnode *last; /* leaf of stack */ int *rstack; /* stack of inverted `ie' values */ + struct ohash *reqtab; /* request lookup table */ struct roffreg *regtab; /* number registers */ struct roffkv *strtab; /* user-defined strings & macros */ + struct roffkv *rentab; /* renamed strings & macros */ struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ struct roffstr *xtab; /* single-byte trans table (`tr') */ const char *current_string; /* value of last called user macro */ struct tbl_node *first_tbl; /* first table parsed */ struct tbl_node *last_tbl; /* last table parsed */ struct tbl_node *tbl; /* current table being parsed */ - struct eqn_node *last_eqn; /* last equation parsed */ - struct eqn_node *first_eqn; /* first equation parsed */ - struct eqn_node *eqn; /* current equation being parsed */ + struct eqn_node *last_eqn; /* equation parser */ + struct eqn_node *eqn; /* active equation parser */ int eqn_inline; /* current equation is inline */ int options; /* parse options */ int rstacksz; /* current size limit of rstack */ @@ -333,10 +107,11 @@ struct roff { int format; /* current file in mdoc or man format */ int argc; /* number of args of the last macro */ char control; /* control character */ + char escape; /* escape character */ }; struct roffnode { - enum rofft tok; /* type of node */ + enum roff_tok tok; /* type of node */ struct roffnode *parent; /* up one in stack */ int line; /* parse line */ int col; /* parse col */ @@ -347,7 +122,7 @@ struct roffnode { }; #define ROFF_ARGS struct roff *r, /* parse ctx */ \ - enum rofft tok, /* tok of macro */ \ + enum roff_tok tok, /* tok of macro */ \ struct buf *buf, /* input buffer */ \ int ln, /* parse line */ \ int ppos, /* original pos in buffer */ \ @@ -357,13 +132,11 @@ struct roffnode { typedef enum rofferr (*roffproc)(ROFF_ARGS); struct roffmac { - const char *name; /* macro name */ roffproc proc; /* process new macro */ roffproc text; /* process as child text of macro */ roffproc sub; /* process as child of macro */ int flags; #define ROFFMAC_STRUCT (1 << 0) /* always interpret */ - struct roffmac *next; }; struct predef { @@ -376,16 +149,16 @@ struct predef { /* --- function prototypes ------------------------------------------------ */ -static enum rofft roffhash_find(const char *, size_t); -static void roffhash_init(void); static void roffnode_cleanscope(struct roff *); static void roffnode_pop(struct roff *); -static void roffnode_push(struct roff *, enum rofft, +static void roffnode_push(struct roff *, enum roff_tok, const char *, int, int); +static void roff_addtbl(struct roff_man *, struct tbl_node *); +static enum rofferr roff_als(ROFF_ARGS); static enum rofferr roff_block(ROFF_ARGS); static enum rofferr roff_block_text(ROFF_ARGS); static enum rofferr roff_block_sub(ROFF_ARGS); -static enum rofferr roff_brp(ROFF_ARGS); +static enum rofferr roff_br(ROFF_ARGS); static enum rofferr roff_cblock(ROFF_ARGS); static enum rofferr roff_cc(ROFF_ARGS); static void roff_ccond(struct roff *, int, int); @@ -393,6 +166,8 @@ static enum rofferr roff_cond(ROFF_ARGS); static enum rofferr roff_cond_text(ROFF_ARGS); static enum rofferr roff_cond_sub(ROFF_ARGS); static enum rofferr roff_ds(ROFF_ARGS); +static enum rofferr roff_ec(ROFF_ARGS); +static enum rofferr roff_eo(ROFF_ARGS); static enum rofferr roff_eqndelim(struct roff *, struct buf *, int); static int roff_evalcond(struct roff *r, int, char *, int *); static int roff_evalnum(struct roff *, int, @@ -411,7 +186,7 @@ static int roff_getregn(const struct roff *, static int roff_getregro(const struct roff *, const char *name); static const char *roff_getstrn(const struct roff *, - const char *, size_t); + const char *, size_t, int *); static int roff_hasregn(const struct roff *, const char *, size_t); static enum rofferr roff_insec(ROFF_ARGS); @@ -419,12 +194,17 @@ static enum rofferr roff_it(ROFF_ARGS); static enum rofferr roff_line_ignore(ROFF_ARGS); static void roff_man_alloc1(struct roff_man *); static void roff_man_free1(struct roff_man *); +static enum rofferr roff_manyarg(ROFF_ARGS); static enum rofferr roff_nr(ROFF_ARGS); -static enum rofft roff_parse(struct roff *, char *, int *, +static enum rofferr roff_onearg(ROFF_ARGS); +static enum roff_tok roff_parse(struct roff *, char *, int *, int, int); -static enum rofferr roff_parsetext(struct buf *, int, int *); +static enum rofferr roff_parsetext(struct roff *, struct buf *, + int, int *); +static enum rofferr roff_renamed(ROFF_ARGS); static enum rofferr roff_res(struct roff *, struct buf *, int, int); static enum rofferr roff_rm(ROFF_ARGS); +static enum rofferr roff_rn(ROFF_ARGS); static enum rofferr roff_rr(ROFF_ARGS); static void roff_setstr(struct roff *, const char *, const char *, int); @@ -433,7 +213,6 @@ static void roff_setstrn(struct roffkv **, const char *, static enum rofferr roff_so(ROFF_ARGS); static enum rofferr roff_tr(ROFF_ARGS); static enum rofferr roff_Dd(ROFF_ARGS); -static enum rofferr roff_TH(ROFF_ARGS); static enum rofferr roff_TE(ROFF_ARGS); static enum rofferr roff_TS(ROFF_ARGS); static enum rofferr roff_EQ(ROFF_ARGS); @@ -444,287 +223,360 @@ static enum rofferr roff_userdef(ROFF_ARGS); /* --- constant data ------------------------------------------------------ */ -/* See roffhash_find() */ - -#define ASCII_HI 126 -#define ASCII_LO 33 -#define HASHWIDTH (ASCII_HI - ASCII_LO + 1) - #define ROFFNUM_SCALE (1 << 0) /* Honour scaling in roff_getnum(). */ #define ROFFNUM_WHITE (1 << 1) /* Skip whitespace in roff_evalnum(). */ -static struct roffmac *hash[HASHWIDTH]; - -static struct roffmac roffs[ROFF_MAX] = { - { "ab", roff_unsupp, NULL, NULL, 0, NULL }, - { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, - { "af", roff_line_ignore, NULL, NULL, 0, NULL }, - { "aln", roff_unsupp, NULL, NULL, 0, NULL }, - { "als", roff_unsupp, NULL, NULL, 0, NULL }, - { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "ami1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "as", roff_ds, NULL, NULL, 0, NULL }, - { "as1", roff_ds, NULL, NULL, 0, NULL }, - { "asciify", roff_unsupp, NULL, NULL, 0, NULL }, - { "backtrace", roff_line_ignore, NULL, NULL, 0, NULL }, - { "bd", roff_line_ignore, NULL, NULL, 0, NULL }, - { "bleedat", roff_line_ignore, NULL, NULL, 0, NULL }, - { "blm", roff_unsupp, NULL, NULL, 0, NULL }, - { "box", roff_unsupp, NULL, NULL, 0, NULL }, - { "boxa", roff_unsupp, NULL, NULL, 0, NULL }, - { "bp", roff_line_ignore, NULL, NULL, 0, NULL }, - { "BP", roff_unsupp, NULL, NULL, 0, NULL }, - { "break", roff_unsupp, NULL, NULL, 0, NULL }, - { "breakchar", roff_line_ignore, NULL, NULL, 0, NULL }, - { "brnl", roff_line_ignore, NULL, NULL, 0, NULL }, - { "brp", roff_brp, NULL, NULL, 0, NULL }, - { "brpnl", roff_line_ignore, NULL, NULL, 0, NULL }, - { "c2", roff_unsupp, NULL, NULL, 0, NULL }, - { "cc", roff_cc, NULL, NULL, 0, NULL }, - { "ce", roff_line_ignore, NULL, NULL, 0, NULL }, - { "cf", roff_insec, NULL, NULL, 0, NULL }, - { "cflags", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ch", roff_line_ignore, NULL, NULL, 0, NULL }, - { "char", roff_unsupp, NULL, NULL, 0, NULL }, - { "chop", roff_unsupp, NULL, NULL, 0, NULL }, - { "class", roff_line_ignore, NULL, NULL, 0, NULL }, - { "close", roff_insec, NULL, NULL, 0, NULL }, - { "CL", roff_unsupp, NULL, NULL, 0, NULL }, - { "color", roff_line_ignore, NULL, NULL, 0, NULL }, - { "composite", roff_unsupp, NULL, NULL, 0, NULL }, - { "continue", roff_unsupp, NULL, NULL, 0, NULL }, - { "cp", roff_line_ignore, NULL, NULL, 0, NULL }, - { "cropat", roff_line_ignore, NULL, NULL, 0, NULL }, - { "cs", roff_line_ignore, NULL, NULL, 0, NULL }, - { "cu", roff_line_ignore, NULL, NULL, 0, NULL }, - { "da", roff_unsupp, NULL, NULL, 0, NULL }, - { "dch", roff_unsupp, NULL, NULL, 0, NULL }, - { "Dd", roff_Dd, NULL, NULL, 0, NULL }, - { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "defcolor", roff_line_ignore, NULL, NULL, 0, NULL }, - { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "dei1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "device", roff_unsupp, NULL, NULL, 0, NULL }, - { "devicem", roff_unsupp, NULL, NULL, 0, NULL }, - { "di", roff_unsupp, NULL, NULL, 0, NULL }, - { "do", roff_unsupp, NULL, NULL, 0, NULL }, - { "ds", roff_ds, NULL, NULL, 0, NULL }, - { "ds1", roff_ds, NULL, NULL, 0, NULL }, - { "dwh", roff_unsupp, NULL, NULL, 0, NULL }, - { "dt", roff_unsupp, NULL, NULL, 0, NULL }, - { "ec", roff_unsupp, NULL, NULL, 0, NULL }, - { "ecr", roff_unsupp, NULL, NULL, 0, NULL }, - { "ecs", roff_unsupp, NULL, NULL, 0, NULL }, - { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, - { "em", roff_unsupp, NULL, NULL, 0, NULL }, - { "EN", roff_EN, NULL, NULL, 0, NULL }, - { "eo", roff_unsupp, NULL, NULL, 0, NULL }, - { "EP", roff_unsupp, NULL, NULL, 0, NULL }, - { "EQ", roff_EQ, NULL, NULL, 0, NULL }, - { "errprint", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ev", roff_unsupp, NULL, NULL, 0, NULL }, - { "evc", roff_unsupp, NULL, NULL, 0, NULL }, - { "ex", roff_unsupp, NULL, NULL, 0, NULL }, - { "fallback", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fam", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fc", roff_unsupp, NULL, NULL, 0, NULL }, - { "fchar", roff_unsupp, NULL, NULL, 0, NULL }, - { "fcolor", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fdeferlig", roff_line_ignore, NULL, NULL, 0, NULL }, - { "feature", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fkern", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fl", roff_line_ignore, NULL, NULL, 0, NULL }, - { "flig", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fp", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fps", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fschar", roff_unsupp, NULL, NULL, 0, NULL }, - { "fspacewidth", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fspecial", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ftr", roff_line_ignore, NULL, NULL, 0, NULL }, - { "fzoom", roff_line_ignore, NULL, NULL, 0, NULL }, - { "gcolor", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hc", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hcode", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hidechar", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hla", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hlm", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hpf", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hpfa", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hpfcode", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hw", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hylang", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hylen", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hym", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hypp", roff_line_ignore, NULL, NULL, 0, NULL }, - { "hys", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, - { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, - { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "index", roff_unsupp, NULL, NULL, 0, NULL }, - { "it", roff_it, NULL, NULL, 0, NULL }, - { "itc", roff_unsupp, NULL, NULL, 0, NULL }, - { "IX", roff_line_ignore, NULL, NULL, 0, NULL }, - { "kern", roff_line_ignore, NULL, NULL, 0, NULL }, - { "kernafter", roff_line_ignore, NULL, NULL, 0, NULL }, - { "kernbefore", roff_line_ignore, NULL, NULL, 0, NULL }, - { "kernpair", roff_line_ignore, NULL, NULL, 0, NULL }, - { "lc", roff_unsupp, NULL, NULL, 0, NULL }, - { "lc_ctype", roff_unsupp, NULL, NULL, 0, NULL }, - { "lds", roff_unsupp, NULL, NULL, 0, NULL }, - { "length", roff_unsupp, NULL, NULL, 0, NULL }, - { "letadj", roff_line_ignore, NULL, NULL, 0, NULL }, - { "lf", roff_insec, NULL, NULL, 0, NULL }, - { "lg", roff_line_ignore, NULL, NULL, 0, NULL }, - { "lhang", roff_line_ignore, NULL, NULL, 0, NULL }, - { "linetabs", roff_unsupp, NULL, NULL, 0, NULL }, - { "lnr", roff_unsupp, NULL, NULL, 0, NULL }, - { "lnrf", roff_unsupp, NULL, NULL, 0, NULL }, - { "lpfx", roff_unsupp, NULL, NULL, 0, NULL }, - { "ls", roff_line_ignore, NULL, NULL, 0, NULL }, - { "lsm", roff_unsupp, NULL, NULL, 0, NULL }, - { "lt", roff_line_ignore, NULL, NULL, 0, NULL }, - { "mc", roff_line_ignore, NULL, NULL, 0, NULL }, - { "mediasize", roff_line_ignore, NULL, NULL, 0, NULL }, - { "minss", roff_line_ignore, NULL, NULL, 0, NULL }, - { "mk", roff_line_ignore, NULL, NULL, 0, NULL }, - { "mso", roff_insec, NULL, NULL, 0, NULL }, - { "na", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, - { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, - { "nhychar", roff_line_ignore, NULL, NULL, 0, NULL }, - { "nm", roff_unsupp, NULL, NULL, 0, NULL }, - { "nn", roff_unsupp, NULL, NULL, 0, NULL }, - { "nop", roff_unsupp, NULL, NULL, 0, NULL }, - { "nr", roff_nr, NULL, NULL, 0, NULL }, - { "nrf", roff_unsupp, NULL, NULL, 0, NULL }, - { "nroff", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ns", roff_line_ignore, NULL, NULL, 0, NULL }, - { "nx", roff_insec, NULL, NULL, 0, NULL }, - { "open", roff_insec, NULL, NULL, 0, NULL }, - { "opena", roff_insec, NULL, NULL, 0, NULL }, - { "os", roff_line_ignore, NULL, NULL, 0, NULL }, - { "output", roff_unsupp, NULL, NULL, 0, NULL }, - { "padj", roff_line_ignore, NULL, NULL, 0, NULL }, - { "papersize", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pc", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pev", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pi", roff_insec, NULL, NULL, 0, NULL }, - { "PI", roff_unsupp, NULL, NULL, 0, NULL }, - { "pl", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pm", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pn", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pnr", roff_line_ignore, NULL, NULL, 0, NULL }, - { "po", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ps", roff_line_ignore, NULL, NULL, 0, NULL }, - { "psbb", roff_unsupp, NULL, NULL, 0, NULL }, - { "pshape", roff_unsupp, NULL, NULL, 0, NULL }, - { "pso", roff_insec, NULL, NULL, 0, NULL }, - { "ptr", roff_line_ignore, NULL, NULL, 0, NULL }, - { "pvs", roff_line_ignore, NULL, NULL, 0, NULL }, - { "rchar", roff_unsupp, NULL, NULL, 0, NULL }, - { "rd", roff_line_ignore, NULL, NULL, 0, NULL }, - { "recursionlimit", roff_line_ignore, NULL, NULL, 0, NULL }, - { "return", roff_unsupp, NULL, NULL, 0, NULL }, - { "rfschar", roff_unsupp, NULL, NULL, 0, NULL }, - { "rhang", roff_line_ignore, NULL, NULL, 0, NULL }, - { "rj", roff_line_ignore, NULL, NULL, 0, NULL }, - { "rm", roff_rm, NULL, NULL, 0, NULL }, - { "rn", roff_unsupp, NULL, NULL, 0, NULL }, - { "rnn", roff_unsupp, NULL, NULL, 0, NULL }, - { "rr", roff_rr, NULL, NULL, 0, NULL }, - { "rs", roff_line_ignore, NULL, NULL, 0, NULL }, - { "rt", roff_line_ignore, NULL, NULL, 0, NULL }, - { "schar", roff_unsupp, NULL, NULL, 0, NULL }, - { "sentchar", roff_line_ignore, NULL, NULL, 0, NULL }, - { "shc", roff_line_ignore, NULL, NULL, 0, NULL }, - { "shift", roff_unsupp, NULL, NULL, 0, NULL }, - { "sizes", roff_line_ignore, NULL, NULL, 0, NULL }, - { "so", roff_so, NULL, NULL, 0, NULL }, - { "spacewidth", roff_line_ignore, NULL, NULL, 0, NULL }, - { "special", roff_line_ignore, NULL, NULL, 0, NULL }, - { "spreadwarn", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ss", roff_line_ignore, NULL, NULL, 0, NULL }, - { "sty", roff_line_ignore, NULL, NULL, 0, NULL }, - { "substring", roff_unsupp, NULL, NULL, 0, NULL }, - { "sv", roff_line_ignore, NULL, NULL, 0, NULL }, - { "sy", roff_insec, NULL, NULL, 0, NULL }, - { "T&", roff_T_, NULL, NULL, 0, NULL }, - { "ta", roff_unsupp, NULL, NULL, 0, NULL }, - { "tc", roff_unsupp, NULL, NULL, 0, NULL }, - { "TE", roff_TE, NULL, NULL, 0, NULL }, - { "TH", roff_TH, NULL, NULL, 0, NULL }, - { "ti", roff_unsupp, NULL, NULL, 0, NULL }, - { "tkf", roff_line_ignore, NULL, NULL, 0, NULL }, - { "tl", roff_unsupp, NULL, NULL, 0, NULL }, - { "tm", roff_line_ignore, NULL, NULL, 0, NULL }, - { "tm1", roff_line_ignore, NULL, NULL, 0, NULL }, - { "tmc", roff_line_ignore, NULL, NULL, 0, NULL }, - { "tr", roff_tr, NULL, NULL, 0, NULL }, - { "track", roff_line_ignore, NULL, NULL, 0, NULL }, - { "transchar", roff_line_ignore, NULL, NULL, 0, NULL }, - { "trf", roff_insec, NULL, NULL, 0, NULL }, - { "trimat", roff_line_ignore, NULL, NULL, 0, NULL }, - { "trin", roff_unsupp, NULL, NULL, 0, NULL }, - { "trnt", roff_unsupp, NULL, NULL, 0, NULL }, - { "troff", roff_line_ignore, NULL, NULL, 0, NULL }, - { "TS", roff_TS, NULL, NULL, 0, NULL }, - { "uf", roff_line_ignore, NULL, NULL, 0, NULL }, - { "ul", roff_line_ignore, NULL, NULL, 0, NULL }, - { "unformat", roff_unsupp, NULL, NULL, 0, NULL }, - { "unwatch", roff_line_ignore, NULL, NULL, 0, NULL }, - { "unwatchn", roff_line_ignore, NULL, NULL, 0, NULL }, - { "vpt", roff_line_ignore, NULL, NULL, 0, NULL }, - { "vs", roff_line_ignore, NULL, NULL, 0, NULL }, - { "warn", roff_line_ignore, NULL, NULL, 0, NULL }, - { "warnscale", roff_line_ignore, NULL, NULL, 0, NULL }, - { "watch", roff_line_ignore, NULL, NULL, 0, NULL }, - { "watchlength", roff_line_ignore, NULL, NULL, 0, NULL }, - { "watchn", roff_line_ignore, NULL, NULL, 0, NULL }, - { "wh", roff_unsupp, NULL, NULL, 0, NULL }, - { "while", roff_unsupp, NULL, NULL, 0, NULL }, - { "write", roff_insec, NULL, NULL, 0, NULL }, - { "writec", roff_insec, NULL, NULL, 0, NULL }, - { "writem", roff_insec, NULL, NULL, 0, NULL }, - { "xflag", roff_line_ignore, NULL, NULL, 0, NULL }, - { ".", roff_cblock, NULL, NULL, 0, NULL }, - { NULL, roff_userdef, NULL, NULL, 0, NULL }, +const char *__roff_name[MAN_MAX + 1] = { + "br", "ce", "ft", "ll", + "mc", "po", "rj", "sp", + "ta", "ti", NULL, + "ab", "ad", "af", "aln", + "als", "am", "am1", "ami", + "ami1", "as", "as1", "asciify", + "backtrace", "bd", "bleedat", "blm", + "box", "boxa", "bp", "BP", + "break", "breakchar", "brnl", "brp", + "brpnl", "c2", "cc", + "cf", "cflags", "ch", "char", + "chop", "class", "close", "CL", + "color", "composite", "continue", "cp", + "cropat", "cs", "cu", "da", + "dch", "Dd", "de", "de1", + "defcolor", "dei", "dei1", "device", + "devicem", "di", "do", "ds", + "ds1", "dwh", "dt", "ec", + "ecr", "ecs", "el", "em", + "EN", "eo", "EP", "EQ", + "errprint", "ev", "evc", "ex", + "fallback", "fam", "fc", "fchar", + "fcolor", "fdeferlig", "feature", "fkern", + "fl", "flig", "fp", "fps", + "fschar", "fspacewidth", "fspecial", "ftr", + "fzoom", "gcolor", "hc", "hcode", + "hidechar", "hla", "hlm", "hpf", + "hpfa", "hpfcode", "hw", "hy", + "hylang", "hylen", "hym", "hypp", + "hys", "ie", "if", "ig", + "index", "it", "itc", "IX", + "kern", "kernafter", "kernbefore", "kernpair", + "lc", "lc_ctype", "lds", "length", + "letadj", "lf", "lg", "lhang", + "linetabs", "lnr", "lnrf", "lpfx", + "ls", "lsm", "lt", + "mediasize", "minss", "mk", "mso", + "na", "ne", "nh", "nhychar", + "nm", "nn", "nop", "nr", + "nrf", "nroff", "ns", "nx", + "open", "opena", "os", "output", + "padj", "papersize", "pc", "pev", + "pi", "PI", "pl", "pm", + "pn", "pnr", "ps", + "psbb", "pshape", "pso", "ptr", + "pvs", "rchar", "rd", "recursionlimit", + "return", "rfschar", "rhang", + "rm", "rn", "rnn", "rr", + "rs", "rt", "schar", "sentchar", + "shc", "shift", "sizes", "so", + "spacewidth", "special", "spreadwarn", "ss", + "sty", "substring", "sv", "sy", + "T&", "tc", "TE", + "TH", "tkf", "tl", + "tm", "tm1", "tmc", "tr", + "track", "transchar", "trf", "trimat", + "trin", "trnt", "troff", "TS", + "uf", "ul", "unformat", "unwatch", + "unwatchn", "vpt", "vs", "warn", + "warnscale", "watch", "watchlength", "watchn", + "wh", "while", "write", "writec", + "writem", "xflag", ".", NULL, + NULL, "text", + "Dd", "Dt", "Os", "Sh", + "Ss", "Pp", "D1", "Dl", + "Bd", "Ed", "Bl", "El", + "It", "Ad", "An", "Ap", + "Ar", "Cd", "Cm", "Dv", + "Er", "Ev", "Ex", "Fa", + "Fd", "Fl", "Fn", "Ft", + "Ic", "In", "Li", "Nd", + "Nm", "Op", "Ot", "Pa", + "Rv", "St", "Va", "Vt", + "Xr", "%A", "%B", "%D", + "%I", "%J", "%N", "%O", + "%P", "%R", "%T", "%V", + "Ac", "Ao", "Aq", "At", + "Bc", "Bf", "Bo", "Bq", + "Bsx", "Bx", "Db", "Dc", + "Do", "Dq", "Ec", "Ef", + "Em", "Eo", "Fx", "Ms", + "No", "Ns", "Nx", "Ox", + "Pc", "Pf", "Po", "Pq", + "Qc", "Ql", "Qo", "Qq", + "Re", "Rs", "Sc", "So", + "Sq", "Sm", "Sx", "Sy", + "Tn", "Ux", "Xc", "Xo", + "Fo", "Fc", "Oo", "Oc", + "Bk", "Ek", "Bt", "Hf", + "Fr", "Ud", "Lb", "Lp", + "Lk", "Mt", "Brq", "Bro", + "Brc", "%C", "Es", "En", + "Dx", "%Q", "%U", "Ta", + NULL, + "TH", "SH", "SS", "TP", + "LP", "PP", "P", "IP", + "HP", "SM", "SB", "BI", + "IB", "BR", "RB", "R", + "B", "I", "IR", "RI", + "nf", "fi", + "RE", "RS", "DT", "UC", + "PD", "AT", "in", + "OP", "EX", "EE", "UR", + "UE", "MT", "ME", NULL }; - -/* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */ -const char *const __mdoc_reserved[] = { - "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", - "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", - "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx", - "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq", - "Dt", "Dv", "Dx", "D1", - "Ec", "Ed", "Ef", "Ek", "El", "Em", - "En", "Eo", "Er", "Es", "Ev", "Ex", - "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", - "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", - "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", - "Oc", "Oo", "Op", "Os", "Ot", "Ox", - "Pa", "Pc", "Pf", "Po", "Pp", "Pq", - "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv", - "Sc", "Sh", "Sm", "So", "Sq", - "Ss", "St", "Sx", "Sy", - "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr", - "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", - "%P", "%Q", "%R", "%T", "%U", "%V", - NULL -}; - -/* not currently implemented: BT DE DS ME MT PT SY TQ YS */ -const char *const __man_reserved[] = { - "AT", "B", "BI", "BR", "DT", - "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR", - "LP", "OP", "P", "PD", "PP", - "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", - "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR", - NULL +const char *const *roff_name = __roff_name; + +static struct roffmac roffs[TOKEN_NONE] = { + { roff_br, NULL, NULL, 0 }, /* br */ + { roff_onearg, NULL, NULL, 0 }, /* ce */ + { roff_onearg, NULL, NULL, 0 }, /* ft */ + { roff_onearg, NULL, NULL, 0 }, /* ll */ + { roff_onearg, NULL, NULL, 0 }, /* mc */ + { roff_onearg, NULL, NULL, 0 }, /* po */ + { roff_onearg, NULL, NULL, 0 }, /* rj */ + { roff_onearg, NULL, NULL, 0 }, /* sp */ + { roff_manyarg, NULL, NULL, 0 }, /* ta */ + { roff_onearg, NULL, NULL, 0 }, /* ti */ + { NULL, NULL, NULL, 0 }, /* ROFF_MAX */ + { roff_unsupp, NULL, NULL, 0 }, /* ab */ + { roff_line_ignore, NULL, NULL, 0 }, /* ad */ + { roff_line_ignore, NULL, NULL, 0 }, /* af */ + { roff_unsupp, NULL, NULL, 0 }, /* aln */ + { roff_als, NULL, NULL, 0 }, /* als */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* am */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* am1 */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* ami1 */ + { roff_ds, NULL, NULL, 0 }, /* as */ + { roff_ds, NULL, NULL, 0 }, /* as1 */ + { roff_unsupp, NULL, NULL, 0 }, /* asciify */ + { roff_line_ignore, NULL, NULL, 0 }, /* backtrace */ + { roff_line_ignore, NULL, NULL, 0 }, /* bd */ + { roff_line_ignore, NULL, NULL, 0 }, /* bleedat */ + { roff_unsupp, NULL, NULL, 0 }, /* blm */ + { roff_unsupp, NULL, NULL, 0 }, /* box */ + { roff_unsupp, NULL, NULL, 0 }, /* boxa */ + { roff_line_ignore, NULL, NULL, 0 }, /* bp */ + { roff_unsupp, NULL, NULL, 0 }, /* BP */ + { roff_unsupp, NULL, NULL, 0 }, /* break */ + { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ + { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ + { roff_br, NULL, NULL, 0 }, /* brp */ + { roff_line_ignore, NULL, NULL, 0 }, /* brpnl */ + { roff_unsupp, NULL, NULL, 0 }, /* c2 */ + { roff_cc, NULL, NULL, 0 }, /* cc */ + { roff_insec, NULL, NULL, 0 }, /* cf */ + { roff_line_ignore, NULL, NULL, 0 }, /* cflags */ + { roff_line_ignore, NULL, NULL, 0 }, /* ch */ + { roff_unsupp, NULL, NULL, 0 }, /* char */ + { roff_unsupp, NULL, NULL, 0 }, /* chop */ + { roff_line_ignore, NULL, NULL, 0 }, /* class */ + { roff_insec, NULL, NULL, 0 }, /* close */ + { roff_unsupp, NULL, NULL, 0 }, /* CL */ + { roff_line_ignore, NULL, NULL, 0 }, /* color */ + { roff_unsupp, NULL, NULL, 0 }, /* composite */ + { roff_unsupp, NULL, NULL, 0 }, /* continue */ + { roff_line_ignore, NULL, NULL, 0 }, /* cp */ + { roff_line_ignore, NULL, NULL, 0 }, /* cropat */ + { roff_line_ignore, NULL, NULL, 0 }, /* cs */ + { roff_line_ignore, NULL, NULL, 0 }, /* cu */ + { roff_unsupp, NULL, NULL, 0 }, /* da */ + { roff_unsupp, NULL, NULL, 0 }, /* dch */ + { roff_Dd, NULL, NULL, 0 }, /* Dd */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* de */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* de1 */ + { roff_line_ignore, NULL, NULL, 0 }, /* defcolor */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* dei1 */ + { roff_unsupp, NULL, NULL, 0 }, /* device */ + { roff_unsupp, NULL, NULL, 0 }, /* devicem */ + { roff_unsupp, NULL, NULL, 0 }, /* di */ + { roff_unsupp, NULL, NULL, 0 }, /* do */ + { roff_ds, NULL, NULL, 0 }, /* ds */ + { roff_ds, NULL, NULL, 0 }, /* ds1 */ + { roff_unsupp, NULL, NULL, 0 }, /* dwh */ + { roff_unsupp, NULL, NULL, 0 }, /* dt */ + { roff_ec, NULL, NULL, 0 }, /* ec */ + { roff_unsupp, NULL, NULL, 0 }, /* ecr */ + { roff_unsupp, NULL, NULL, 0 }, /* ecs */ + { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* el */ + { roff_unsupp, NULL, NULL, 0 }, /* em */ + { roff_EN, NULL, NULL, 0 }, /* EN */ + { roff_eo, NULL, NULL, 0 }, /* eo */ + { roff_unsupp, NULL, NULL, 0 }, /* EP */ + { roff_EQ, NULL, NULL, 0 }, /* EQ */ + { roff_line_ignore, NULL, NULL, 0 }, /* errprint */ + { roff_unsupp, NULL, NULL, 0 }, /* ev */ + { roff_unsupp, NULL, NULL, 0 }, /* evc */ + { roff_unsupp, NULL, NULL, 0 }, /* ex */ + { roff_line_ignore, NULL, NULL, 0 }, /* fallback */ + { roff_line_ignore, NULL, NULL, 0 }, /* fam */ + { roff_unsupp, NULL, NULL, 0 }, /* fc */ + { roff_unsupp, NULL, NULL, 0 }, /* fchar */ + { roff_line_ignore, NULL, NULL, 0 }, /* fcolor */ + { roff_line_ignore, NULL, NULL, 0 }, /* fdeferlig */ + { roff_line_ignore, NULL, NULL, 0 }, /* feature */ + { roff_line_ignore, NULL, NULL, 0 }, /* fkern */ + { roff_line_ignore, NULL, NULL, 0 }, /* fl */ + { roff_line_ignore, NULL, NULL, 0 }, /* flig */ + { roff_line_ignore, NULL, NULL, 0 }, /* fp */ + { roff_line_ignore, NULL, NULL, 0 }, /* fps */ + { roff_unsupp, NULL, NULL, 0 }, /* fschar */ + { roff_line_ignore, NULL, NULL, 0 }, /* fspacewidth */ + { roff_line_ignore, NULL, NULL, 0 }, /* fspecial */ + { roff_line_ignore, NULL, NULL, 0 }, /* ftr */ + { roff_line_ignore, NULL, NULL, 0 }, /* fzoom */ + { roff_line_ignore, NULL, NULL, 0 }, /* gcolor */ + { roff_line_ignore, NULL, NULL, 0 }, /* hc */ + { roff_line_ignore, NULL, NULL, 0 }, /* hcode */ + { roff_line_ignore, NULL, NULL, 0 }, /* hidechar */ + { roff_line_ignore, NULL, NULL, 0 }, /* hla */ + { roff_line_ignore, NULL, NULL, 0 }, /* hlm */ + { roff_line_ignore, NULL, NULL, 0 }, /* hpf */ + { roff_line_ignore, NULL, NULL, 0 }, /* hpfa */ + { roff_line_ignore, NULL, NULL, 0 }, /* hpfcode */ + { roff_line_ignore, NULL, NULL, 0 }, /* hw */ + { roff_line_ignore, NULL, NULL, 0 }, /* hy */ + { roff_line_ignore, NULL, NULL, 0 }, /* hylang */ + { roff_line_ignore, NULL, NULL, 0 }, /* hylen */ + { roff_line_ignore, NULL, NULL, 0 }, /* hym */ + { roff_line_ignore, NULL, NULL, 0 }, /* hypp */ + { roff_line_ignore, NULL, NULL, 0 }, /* hys */ + { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* ie */ + { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /* if */ + { roff_block, roff_block_text, roff_block_sub, 0 }, /* ig */ + { roff_unsupp, NULL, NULL, 0 }, /* index */ + { roff_it, NULL, NULL, 0 }, /* it */ + { roff_unsupp, NULL, NULL, 0 }, /* itc */ + { roff_line_ignore, NULL, NULL, 0 }, /* IX */ + { roff_line_ignore, NULL, NULL, 0 }, /* kern */ + { roff_line_ignore, NULL, NULL, 0 }, /* kernafter */ + { roff_line_ignore, NULL, NULL, 0 }, /* kernbefore */ + { roff_line_ignore, NULL, NULL, 0 }, /* kernpair */ + { roff_unsupp, NULL, NULL, 0 }, /* lc */ + { roff_unsupp, NULL, NULL, 0 }, /* lc_ctype */ + { roff_unsupp, NULL, NULL, 0 }, /* lds */ + { roff_unsupp, NULL, NULL, 0 }, /* length */ + { roff_line_ignore, NULL, NULL, 0 }, /* letadj */ + { roff_insec, NULL, NULL, 0 }, /* lf */ + { roff_line_ignore, NULL, NULL, 0 }, /* lg */ + { roff_line_ignore, NULL, NULL, 0 }, /* lhang */ + { roff_unsupp, NULL, NULL, 0 }, /* linetabs */ + { roff_unsupp, NULL, NULL, 0 }, /* lnr */ + { roff_unsupp, NULL, NULL, 0 }, /* lnrf */ + { roff_unsupp, NULL, NULL, 0 }, /* lpfx */ + { roff_line_ignore, NULL, NULL, 0 }, /* ls */ + { roff_unsupp, NULL, NULL, 0 }, /* lsm */ + { roff_line_ignore, NULL, NULL, 0 }, /* lt */ + { roff_line_ignore, NULL, NULL, 0 }, /* mediasize */ + { roff_line_ignore, NULL, NULL, 0 }, /* minss */ + { roff_line_ignore, NULL, NULL, 0 }, /* mk */ + { roff_insec, NULL, NULL, 0 }, /* mso */ + { roff_line_ignore, NULL, NULL, 0 }, /* na */ + { roff_line_ignore, NULL, NULL, 0 }, /* ne */ + { roff_line_ignore, NULL, NULL, 0 }, /* nh */ + { roff_line_ignore, NULL, NULL, 0 }, /* nhychar */ + { roff_unsupp, NULL, NULL, 0 }, /* nm */ + { roff_unsupp, NULL, NULL, 0 }, /* nn */ + { roff_unsupp, NULL, NULL, 0 }, /* nop */ + { roff_nr, NULL, NULL, 0 }, /* nr */ + { roff_unsupp, NULL, NULL, 0 }, /* nrf */ + { roff_line_ignore, NULL, NULL, 0 }, /* nroff */ + { roff_line_ignore, NULL, NULL, 0 }, /* ns */ + { roff_insec, NULL, NULL, 0 }, /* nx */ + { roff_insec, NULL, NULL, 0 }, /* open */ + { roff_insec, NULL, NULL, 0 }, /* opena */ + { roff_line_ignore, NULL, NULL, 0 }, /* os */ + { roff_unsupp, NULL, NULL, 0 }, /* output */ + { roff_line_ignore, NULL, NULL, 0 }, /* padj */ + { roff_line_ignore, NULL, NULL, 0 }, /* papersize */ + { roff_line_ignore, NULL, NULL, 0 }, /* pc */ + { roff_line_ignore, NULL, NULL, 0 }, /* pev */ + { roff_insec, NULL, NULL, 0 }, /* pi */ + { roff_unsupp, NULL, NULL, 0 }, /* PI */ + { roff_line_ignore, NULL, NULL, 0 }, /* pl */ + { roff_line_ignore, NULL, NULL, 0 }, /* pm */ + { roff_line_ignore, NULL, NULL, 0 }, /* pn */ + { roff_line_ignore, NULL, NULL, 0 }, /* pnr */ + { roff_line_ignore, NULL, NULL, 0 }, /* ps */ + { roff_unsupp, NULL, NULL, 0 }, /* psbb */ + { roff_unsupp, NULL, NULL, 0 }, /* pshape */ + { roff_insec, NULL, NULL, 0 }, /* pso */ + { roff_line_ignore, NULL, NULL, 0 }, /* ptr */ + { roff_line_ignore, NULL, NULL, 0 }, /* pvs */ + { roff_unsupp, NULL, NULL, 0 }, /* rchar */ + { roff_line_ignore, NULL, NULL, 0 }, /* rd */ + { roff_line_ignore, NULL, NULL, 0 }, /* recursionlimit */ + { roff_unsupp, NULL, NULL, 0 }, /* return */ + { roff_unsupp, NULL, NULL, 0 }, /* rfschar */ + { roff_line_ignore, NULL, NULL, 0 }, /* rhang */ + { roff_rm, NULL, NULL, 0 }, /* rm */ + { roff_rn, NULL, NULL, 0 }, /* rn */ + { roff_unsupp, NULL, NULL, 0 }, /* rnn */ + { roff_rr, NULL, NULL, 0 }, /* rr */ + { roff_line_ignore, NULL, NULL, 0 }, /* rs */ + { roff_line_ignore, NULL, NULL, 0 }, /* rt */ + { roff_unsupp, NULL, NULL, 0 }, /* schar */ + { roff_line_ignore, NULL, NULL, 0 }, /* sentchar */ + { roff_line_ignore, NULL, NULL, 0 }, /* shc */ + { roff_unsupp, NULL, NULL, 0 }, /* shift */ + { roff_line_ignore, NULL, NULL, 0 }, /* sizes */ + { roff_so, NULL, NULL, 0 }, /* so */ + { roff_line_ignore, NULL, NULL, 0 }, /* spacewidth */ + { roff_line_ignore, NULL, NULL, 0 }, /* special */ + { roff_line_ignore, NULL, NULL, 0 }, /* spreadwarn */ + { roff_line_ignore, NULL, NULL, 0 }, /* ss */ + { roff_line_ignore, NULL, NULL, 0 }, /* sty */ + { roff_unsupp, NULL, NULL, 0 }, /* substring */ + { roff_line_ignore, NULL, NULL, 0 }, /* sv */ + { roff_insec, NULL, NULL, 0 }, /* sy */ + { roff_T_, NULL, NULL, 0 }, /* T& */ + { roff_unsupp, NULL, NULL, 0 }, /* tc */ + { roff_TE, NULL, NULL, 0 }, /* TE */ + { roff_Dd, NULL, NULL, 0 }, /* TH */ + { roff_line_ignore, NULL, NULL, 0 }, /* tkf */ + { roff_unsupp, NULL, NULL, 0 }, /* tl */ + { roff_line_ignore, NULL, NULL, 0 }, /* tm */ + { roff_line_ignore, NULL, NULL, 0 }, /* tm1 */ + { roff_line_ignore, NULL, NULL, 0 }, /* tmc */ + { roff_tr, NULL, NULL, 0 }, /* tr */ + { roff_line_ignore, NULL, NULL, 0 }, /* track */ + { roff_line_ignore, NULL, NULL, 0 }, /* transchar */ + { roff_insec, NULL, NULL, 0 }, /* trf */ + { roff_line_ignore, NULL, NULL, 0 }, /* trimat */ + { roff_unsupp, NULL, NULL, 0 }, /* trin */ + { roff_unsupp, NULL, NULL, 0 }, /* trnt */ + { roff_line_ignore, NULL, NULL, 0 }, /* troff */ + { roff_TS, NULL, NULL, 0 }, /* TS */ + { roff_line_ignore, NULL, NULL, 0 }, /* uf */ + { roff_line_ignore, NULL, NULL, 0 }, /* ul */ + { roff_unsupp, NULL, NULL, 0 }, /* unformat */ + { roff_line_ignore, NULL, NULL, 0 }, /* unwatch */ + { roff_line_ignore, NULL, NULL, 0 }, /* unwatchn */ + { roff_line_ignore, NULL, NULL, 0 }, /* vpt */ + { roff_line_ignore, NULL, NULL, 0 }, /* vs */ + { roff_line_ignore, NULL, NULL, 0 }, /* warn */ + { roff_line_ignore, NULL, NULL, 0 }, /* warnscale */ + { roff_line_ignore, NULL, NULL, 0 }, /* watch */ + { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */ + { roff_line_ignore, NULL, NULL, 0 }, /* watchn */ + { roff_unsupp, NULL, NULL, 0 }, /* wh */ + { roff_unsupp, NULL, NULL, 0 }, /* while */ + { roff_insec, NULL, NULL, 0 }, /* write */ + { roff_insec, NULL, NULL, 0 }, /* writec */ + { roff_insec, NULL, NULL, 0 }, /* writem */ + { roff_line_ignore, NULL, NULL, 0 }, /* xflag */ + { roff_cblock, NULL, NULL, 0 }, /* . */ + { roff_renamed, NULL, NULL, 0 }, + { roff_userdef, NULL, NULL, 0 } }; /* Array of injected predefined strings. */ @@ -733,65 +585,66 @@ static const struct predef predefs[PREDEFS_MAX] = { #include "predefs.in" }; -/* See roffhash_find() */ -#define ROFF_HASH(p) (p[0] - ASCII_LO) - +static int roffce_lines; /* number of input lines to center */ +static struct roff_node *roffce_node; /* active request */ static int roffit_lines; /* number of lines to delay */ static char *roffit_macro; /* nil-terminated macro line */ /* --- request table ------------------------------------------------------ */ -static void -roffhash_init(void) +struct ohash * +roffhash_alloc(enum roff_tok mintok, enum roff_tok maxtok) { - struct roffmac *n; - int buc, i; - - for (i = 0; i < (int)ROFF_USERDEF; i++) { - assert(roffs[i].name[0] >= ASCII_LO); - assert(roffs[i].name[0] <= ASCII_HI); + struct ohash *htab; + struct roffreq *req; + enum roff_tok tok; + size_t sz; + unsigned int slot; - buc = ROFF_HASH(roffs[i].name); + htab = mandoc_malloc(sizeof(*htab)); + mandoc_ohash_init(htab, 8, offsetof(struct roffreq, name)); - if (NULL != (n = hash[buc])) { - for ( ; n->next; n = n->next) - /* Do nothing. */ ; - n->next = &roffs[i]; - } else - hash[buc] = &roffs[i]; + for (tok = mintok; tok < maxtok; tok++) { + if (roff_name[tok] == NULL) + continue; + sz = strlen(roff_name[tok]); + req = mandoc_malloc(sizeof(*req) + sz + 1); + req->tok = tok; + memcpy(req->name, roff_name[tok], sz + 1); + slot = ohash_qlookup(htab, req->name); + ohash_insert(htab, slot, req); } + return htab; } -/* - * Look up a roff token by its name. Returns ROFF_MAX if no macro by - * the nil-terminated string name could be found. - */ -static enum rofft -roffhash_find(const char *p, size_t s) +void +roffhash_free(struct ohash *htab) { - int buc; - struct roffmac *n; + struct roffreq *req; + unsigned int slot; - /* - * libroff has an extremely simple hashtable, for the time - * being, which simply keys on the first character, which must - * be printable, then walks a chain. It works well enough until - * optimised. - */ - - if (p[0] < ASCII_LO || p[0] > ASCII_HI) - return ROFF_MAX; - - buc = ROFF_HASH(p); + if (htab == NULL) + return; + for (req = ohash_first(htab, &slot); req != NULL; + req = ohash_next(htab, &slot)) + free(req); + ohash_delete(htab); + free(htab); +} - if (NULL == (n = hash[buc])) - return ROFF_MAX; - for ( ; n; n = n->next) - if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s]) - return (enum rofft)(n - roffs); +enum roff_tok +roffhash_find(struct ohash *htab, const char *name, size_t sz) +{ + struct roffreq *req; + const char *end; - return ROFF_MAX; + if (sz) { + end = name + sz; + req = ohash_find(htab, ohash_qlookupi(htab, name, &end)); + } else + req = ohash_find(htab, ohash_qlookup(htab, name)); + return req == NULL ? TOKEN_NONE : req->tok; } /* --- stack of request blocks -------------------------------------------- */ @@ -819,7 +672,7 @@ roffnode_pop(struct roff *r) * removed with roffnode_pop(). */ static void -roffnode_push(struct roff *r, enum rofft tok, const char *name, +roffnode_push(struct roff *r, enum roff_tok tok, const char *name, int line, int col) { struct roffnode *p; @@ -842,7 +695,6 @@ static void roff_free1(struct roff *r) { struct tbl_node *tbl; - struct eqn_node *e; int i; while (NULL != (tbl = r->first_tbl)) { @@ -851,11 +703,9 @@ roff_free1(struct roff *r) } r->first_tbl = r->last_tbl = r->tbl = NULL; - while (NULL != (e = r->first_eqn)) { - r->first_eqn = e->next; - eqn_free(e); - } - r->first_eqn = r->last_eqn = r->eqn = NULL; + if (r->last_eqn != NULL) + eqn_free(r->last_eqn); + r->last_eqn = r->eqn = NULL; while (r->last) roffnode_pop(r); @@ -869,8 +719,9 @@ roff_free1(struct roff *r) r->regtab = NULL; roff_freestr(r->strtab); + roff_freestr(r->rentab); roff_freestr(r->xmbtab); - r->strtab = r->xmbtab = NULL; + r->strtab = r->rentab = r->xmbtab = NULL; if (r->xtab) for (i = 0; i < 128; i++) @@ -882,17 +733,21 @@ roff_free1(struct roff *r) void roff_reset(struct roff *r) { - roff_free1(r); r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); - r->control = 0; + r->control = '\0'; + r->escape = '\\'; + roffce_lines = 0; + roffce_node = NULL; + roffit_lines = 0; + roffit_macro = NULL; } void roff_free(struct roff *r) { - roff_free1(r); + roffhash_free(r->reqtab); free(r); } @@ -903,12 +758,11 @@ roff_alloc(struct mparse *parse, int options) r = mandoc_calloc(1, sizeof(struct roff)); r->parse = parse; + r->reqtab = roffhash_alloc(0, ROFF_USERDEF); r->options = options; r->format = options & (MPARSE_MDOC | MPARSE_MAN); r->rstackpos = -1; - - roffhash_init(); - + r->escape = '\\'; return r; } @@ -962,16 +816,17 @@ roff_man_free(struct roff_man *man) struct roff_man * roff_man_alloc(struct roff *roff, struct mparse *parse, - const char *defos, int quick) + const char *os_s, int quick) { struct roff_man *man; man = mandoc_calloc(1, sizeof(*man)); man->parse = parse; man->roff = roff; - man->defos = defos; + man->os_s = os_s; man->quick = quick; roff_man_alloc1(man); + roff->man = man; return man; } @@ -1125,31 +980,21 @@ roff_body_alloc(struct roff_man *man, int line, int pos, int tok) return n; } -void -roff_addeqn(struct roff_man *man, const struct eqn *eqn) -{ - struct roff_node *n; - - n = roff_node_alloc(man, eqn->ln, eqn->pos, ROFFT_EQN, TOKEN_NONE); - n->eqn = eqn; - if (eqn->ln > man->last->line) - n->flags |= NODE_LINE; - roff_node_append(man, n); - man->next = ROFF_NEXT_SIBLING; -} - -void -roff_addtbl(struct roff_man *man, const struct tbl_span *tbl) +static void +roff_addtbl(struct roff_man *man, struct tbl_node *tbl) { struct roff_node *n; + const struct tbl_span *span; if (man->macroset == MACROSET_MAN) - man_breakscope(man, TOKEN_NONE); - n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE); - n->span = tbl; - roff_node_append(man, n); - n->flags |= NODE_VALID | NODE_ENDED; - man->next = ROFF_NEXT_SIBLING; + man_breakscope(man, ROFF_TS); + while ((span = tbl_span(tbl)) != NULL) { + n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE); + n->span = span; + roff_node_append(man, n); + n->flags |= NODE_VALID | NODE_ENDED; + man->next = ROFF_NEXT_SIBLING; + } } void @@ -1197,6 +1042,8 @@ roff_node_free(struct roff_node *n) mdoc_argv_free(n->args); if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM) free(n->norm); + if (n->eqn != NULL) + eqn_box_free(n->eqn); free(n->string); free(n); } @@ -1226,15 +1073,22 @@ deroff(char **dest, const struct roff_node *n) /* Skip leading whitespace. */ for (cp = n->string; *cp != '\0'; cp++) { - if (cp[0] == '\\' && strchr(" %&0^|~", cp[1]) != NULL) + if (cp[0] == '\\' && cp[1] != '\0' && + strchr(" %&0^|~", cp[1]) != NULL) cp++; else if ( ! isspace((unsigned char)*cp)) break; } + /* Skip trailing backslash. */ + + sz = strlen(cp); + if (sz > 0 && cp[sz - 1] == '\\') + sz--; + /* Skip trailing whitespace. */ - for (sz = strlen(cp); sz; sz--) + for (; sz; sz--) if ( ! isspace((unsigned char)cp[sz-1])) break; @@ -1277,27 +1131,104 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) int expand_count; /* to avoid infinite loops */ int npos; /* position in numeric expression */ int arg_complete; /* argument not interrupted by eol */ + int done; /* no more input available */ + int deftype; /* type of definition to paste */ + int rcsid; /* kind of RCS id seen */ char term; /* character terminating the escape */ - expand_count = 0; + /* Search forward for comments. */ + + done = 0; start = buf->buf + pos; - stesc = strchr(start, '\0') - 1; - while (stesc-- > start) { + for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) { + if (stesc[0] != r->escape || stesc[1] == '\0') + continue; + stesc++; + if (*stesc != '"' && *stesc != '#') + continue; + + /* Comment found, look for RCS id. */ + + rcsid = 0; + if ((cp = strstr(stesc, "$" "OpenBSD")) != NULL) { + rcsid = 1 << MANDOC_OS_OPENBSD; + cp += 8; + } else if ((cp = strstr(stesc, "$" "NetBSD")) != NULL) { + rcsid = 1 << MANDOC_OS_NETBSD; + cp += 7; + } + if (cp != NULL && + isalnum((unsigned char)*cp) == 0 && + strchr(cp, '$') != NULL) { + if (r->man->meta.rcsids & rcsid) + mandoc_msg(MANDOCERR_RCS_REP, r->parse, + ln, stesc + 1 - buf->buf, stesc + 1); + r->man->meta.rcsids |= rcsid; + } + + /* Handle trailing whitespace. */ + + cp = strchr(stesc--, '\0') - 1; + if (*cp == '\n') { + done = 1; + cp--; + } + if (*cp == ' ' || *cp == '\t') + mandoc_msg(MANDOCERR_SPACE_EOL, r->parse, + ln, cp - buf->buf, NULL); + while (stesc > start && stesc[-1] == ' ') + stesc--; + *stesc = '\0'; + break; + } + if (stesc == start) + return ROFF_CONT; + stesc--; + + /* Notice the end of the input. */ + + if (*stesc == '\n') { + *stesc-- = '\0'; + done = 1; + } + + expand_count = 0; + while (stesc >= start) { /* Search backwards for the next backslash. */ - if (*stesc != '\\') + if (*stesc != r->escape) { + if (*stesc == '\\') { + *stesc = '\0'; + buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s", + buf->buf, stesc + 1) + 1; + start = nbuf + pos; + stesc = nbuf + (stesc - buf->buf); + free(buf->buf); + buf->buf = nbuf; + } + stesc--; continue; + } /* If it is escaped, skip it. */ for (cp = stesc - 1; cp >= start; cp--) - if (*cp != '\\') + if (*cp != r->escape) break; if ((stesc - cp) % 2 == 0) { - stesc = (char *)cp; + while (stesc > cp) + *stesc-- = '\\'; continue; + } else if (stesc[1] != '\0') { + *stesc = '\\'; + } else { + *stesc-- = '\0'; + if (done) + continue; + else + return ROFF_APPEND; } /* Decide whether to expand or to check only. */ @@ -1323,6 +1254,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) mandoc_vmsg(MANDOCERR_ESC_BAD, r->parse, ln, (int)(stesc - buf->buf), "%.*s", (int)(cp - stesc), stesc); + stesc--; continue; } @@ -1400,8 +1332,10 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) switch (stesc[1]) { case '*': - if (arg_complete) - res = roff_getstrn(r, stnam, naml); + if (arg_complete) { + deftype = ROFFDEF_USER | ROFFDEF_PRE; + res = roff_getstrn(r, stnam, naml, &deftype); + } break; case 'B': npos = 0; @@ -1456,7 +1390,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int pos) * Process text streams. */ static enum rofferr -roff_parsetext(struct buf *buf, int pos, int *offs) +roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) { size_t sz; const char *start; @@ -1478,6 +1412,16 @@ roff_parsetext(struct buf *buf, int pos, int *offs) } else if (roffit_lines > 1) --roffit_lines; + if (roffce_node != NULL && buf->buf[pos] != '\0') { + if (roffce_lines < 1) { + r->man->last = roffce_node; + r->man->next = ROFF_NEXT_SIBLING; + roffce_lines = 0; + roffce_node = NULL; + } else + roffce_lines--; + } + /* Convert all breakable hyphens into ASCII_HYPH. */ start = p = buf->buf + pos; @@ -1514,7 +1458,7 @@ roff_parsetext(struct buf *buf, int pos, int *offs) enum rofferr roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) { - enum rofft t; + enum roff_tok t; enum rofferr e; int pos; /* parse point */ int spos; /* saved parse point for messages */ @@ -1537,7 +1481,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) /* Expand some escape sequences. */ e = roff_res(r, buf, ln, pos); - if (e == ROFF_IGN) + if (e == ROFF_IGN || e == ROFF_APPEND) return e; assert(e == ROFF_CONT); @@ -1553,18 +1497,22 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) if (r->last != NULL && ! ctl) { t = r->last->tok; - assert(roffs[t].text); e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); - assert(e == ROFF_IGN || e == ROFF_CONT); - if (e != ROFF_CONT) + if (e == ROFF_IGN) return e; + assert(e == ROFF_CONT); + } + if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) { + eqn_read(r->eqn, buf->buf + ppos); + return ROFF_IGN; + } + if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) { + tbl_read(r->tbl, ln, buf->buf, ppos); + roff_addtbl(r->man, r->tbl); + return ROFF_IGN; } - if (r->eqn != NULL) - return eqn_read(&r->eqn, ln, buf->buf, ppos, offs); - if (r->tbl != NULL && ( ! ctl || buf->buf[pos] == '\0')) - return tbl_read(r->tbl, ln, buf->buf, ppos); if ( ! ctl) - return roff_parsetext(buf, pos, offs); + return roff_parsetext(r, buf, pos, offs); /* Skip empty request lines. */ @@ -1583,7 +1531,6 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) if (r->last) { t = r->last->tok; - assert(roffs[t].sub); return (*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs); } @@ -1594,16 +1541,30 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) /* Tables ignore most macros. */ - if (r->tbl != NULL && (t == ROFF_MAX || t == ROFF_TS)) { + if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS || + t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) { mandoc_msg(MANDOCERR_TBLMACRO, r->parse, ln, pos, buf->buf + spos); - if (t == ROFF_TS) + if (t != TOKEN_NONE) return ROFF_IGN; while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') pos++; - while (buf->buf[pos] != '\0' && buf->buf[pos] == ' ') + while (buf->buf[pos] == ' ') pos++; - return tbl_read(r->tbl, ln, buf->buf, pos); + tbl_read(r->tbl, ln, buf->buf, pos); + roff_addtbl(r->man, r->tbl); + return ROFF_IGN; + } + + /* For now, let high level macros abort .ce mode. */ + + if (ctl && roffce_node != NULL && + (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ || + t == ROFF_TH || t == ROFF_TS)) { + r->man->last = roffce_node; + r->man->next = ROFF_NEXT_SIBLING; + roffce_lines = 0; + roffce_node = NULL; } /* @@ -1611,34 +1572,34 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) * Let the standard macro set parsers handle it. */ - if (t == ROFF_MAX) + if (t == TOKEN_NONE) return ROFF_CONT; /* Execute a roff request or a user defined macro. */ - assert(roffs[t].proc); - return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); + return (*roffs[t].proc)(r, t, buf, ln, spos, pos, offs); } void roff_endparse(struct roff *r) { - - if (r->last) + if (r->last != NULL) mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, r->last->line, r->last->col, - roffs[r->last->tok].name); + roff_name[r->last->tok]); - if (r->eqn) { + if (r->eqn != NULL) { mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, - r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ"); - eqn_end(&r->eqn); + r->eqn->node->line, r->eqn->node->pos, "EQ"); + eqn_parse(r->eqn); + r->eqn = NULL; } - if (r->tbl) { + if (r->tbl != NULL) { mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, r->tbl->line, r->tbl->pos, "TS"); - tbl_end(&r->tbl); + tbl_end(r->tbl); + r->tbl = NULL; } } @@ -1646,28 +1607,38 @@ roff_endparse(struct roff *r) * Parse a roff node's type from the input buffer. This must be in the * form of ".foo xxx" in the usual way. */ -static enum rofft +static enum roff_tok roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) { char *cp; const char *mac; size_t maclen; - enum rofft t; + int deftype; + enum roff_tok t; cp = buf + *pos; if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp) - return ROFF_MAX; + return TOKEN_NONE; mac = cp; maclen = roff_getname(r, &cp, ln, ppos); - t = (r->current_string = roff_getstrn(r, mac, maclen)) - ? ROFF_USERDEF : roffhash_find(mac, maclen); - - if (ROFF_MAX != t) + deftype = ROFFDEF_USER | ROFFDEF_REN; + r->current_string = roff_getstrn(r, mac, maclen, &deftype); + switch (deftype) { + case ROFFDEF_USER: + t = ROFF_USERDEF; + break; + case ROFFDEF_REN: + t = ROFF_RENAMED; + break; + default: + t = roffhash_find(r->reqtab, mac, maclen); + break; + } + if (t != TOKEN_NONE) *pos = cp - buf; - return t; } @@ -1759,9 +1730,10 @@ roff_ccond(struct roff *r, int ln, int ppos) static enum rofferr roff_block(ROFF_ARGS) { - const char *name; - char *iname, *cp; - size_t namesz; + const char *name, *value; + char *call, *cp, *iname, *rname; + size_t csz, namesz, rsz; + int deftype; /* Ignore groff compatibility mode for now. */ @@ -1789,7 +1761,9 @@ roff_block(ROFF_ARGS) /* Resolve the macro name argument if it is indirect. */ if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { - if ((name = roff_getstrn(r, iname, namesz)) == NULL) { + deftype = ROFFDEF_USER; + name = roff_getstrn(r, iname, namesz, &deftype); + if (name == NULL) { mandoc_vmsg(MANDOCERR_STR_UNDEF, r->parse, ln, (int)(iname - buf->buf), "%.*s", (int)namesz, iname); @@ -1801,7 +1775,7 @@ roff_block(ROFF_ARGS) if (namesz == 0 && tok != ROFF_ig) { mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, - ln, ppos, roffs[tok].name); + ln, ppos, roff_name[tok]); return ROFF_IGN; } @@ -1813,8 +1787,37 @@ roff_block(ROFF_ARGS) * appended from roff_block_text() in multiline mode. */ - if (tok == ROFF_de || tok == ROFF_dei) + if (tok == ROFF_de || tok == ROFF_dei) { roff_setstrn(&r->strtab, name, namesz, "", 0, 0); + roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); + } else if (tok == ROFF_am || tok == ROFF_ami) { + deftype = ROFFDEF_ANY; + value = roff_getstrn(r, iname, namesz, &deftype); + switch (deftype) { /* Before appending, ... */ + case ROFFDEF_PRE: /* copy predefined to user-defined. */ + roff_setstrn(&r->strtab, name, namesz, + value, strlen(value), 0); + break; + case ROFFDEF_REN: /* call original standard macro. */ + csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n", + (int)strlen(value), value); + roff_setstrn(&r->strtab, name, namesz, call, csz, 0); + roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); + free(call); + break; + case ROFFDEF_STD: /* rename and call standard macro. */ + rsz = mandoc_asprintf(&rname, "__%s_renamed", name); + roff_setstrn(&r->rentab, rname, rsz, name, namesz, 0); + csz = mandoc_asprintf(&call, ".%.*s \\$* \\\"\n", + (int)rsz, rname); + roff_setstrn(&r->strtab, name, namesz, call, csz, 0); + free(call); + free(rname); + break; + default: + break; + } + } if (*cp == '\0') return ROFF_IGN; @@ -1827,7 +1830,9 @@ roff_block(ROFF_ARGS) /* Resolve the end marker if it is indirect. */ if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) { - if ((name = roff_getstrn(r, iname, namesz)) == NULL) { + deftype = ROFFDEF_USER; + name = roff_getstrn(r, iname, namesz, &deftype); + if (name == NULL) { mandoc_vmsg(MANDOCERR_STR_UNDEF, r->parse, ln, (int)(iname - buf->buf), "%.*s", (int)namesz, iname); @@ -1842,7 +1847,7 @@ roff_block(ROFF_ARGS) if (*cp != '\0') mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, - ln, pos, ".%s ... %s", roffs[tok].name, cp); + ln, pos, ".%s ... %s", roff_name[tok], cp); return ROFF_IGN; } @@ -1850,7 +1855,7 @@ roff_block(ROFF_ARGS) static enum rofferr roff_block_sub(ROFF_ARGS) { - enum rofft t; + enum roff_tok t; int i, j; /* @@ -1879,7 +1884,7 @@ roff_block_sub(ROFF_ARGS) pos = i; if (roff_parse(r, buf->buf, &pos, ln, ppos) != - ROFF_MAX) + TOKEN_NONE) return ROFF_RERUN; return ROFF_IGN; } @@ -1898,7 +1903,6 @@ roff_block_sub(ROFF_ARGS) return ROFF_IGN; } - assert(roffs[t].proc); return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); } @@ -1915,24 +1919,12 @@ roff_block_text(ROFF_ARGS) static enum rofferr roff_cond_sub(ROFF_ARGS) { - enum rofft t; + enum roff_tok t; char *ep; int rr; rr = r->last->rule; roffnode_cleanscope(r); - t = roff_parse(r, buf->buf, &pos, ln, ppos); - - /* - * Fully handle known macros when they are structurally - * required or when the conditional evaluated to true. - */ - - if ((t != ROFF_MAX) && - (rr || roffs[t].flags & ROFFMAC_STRUCT)) { - assert(roffs[t].proc); - return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); - } /* * If `\}' occurs on a macro line without a preceding macro, @@ -1946,14 +1938,29 @@ roff_cond_sub(ROFF_ARGS) /* Always check for the closing delimiter `\}'. */ while ((ep = strchr(ep, '\\')) != NULL) { - if (*(++ep) == '}') { - *ep = '&'; - roff_ccond(r, ln, ep - buf->buf - 1); - } - if (*ep != '\0') + switch (ep[1]) { + case '}': + memmove(ep, ep + 2, strlen(ep + 2) + 1); + roff_ccond(r, ln, ep - buf->buf); + break; + case '\0': ++ep; + break; + default: + ep += 2; + break; + } } - return rr ? ROFF_CONT : ROFF_IGN; + + /* + * Fully handle known macros when they are structurally + * required or when the conditional evaluated to true. + */ + + t = roff_parse(r, buf->buf, &pos, ln, ppos); + return t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) + ? (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : rr + ? ROFF_CONT : ROFF_IGN; } static enum rofferr @@ -2103,7 +2110,7 @@ roff_evalcond(struct roff *r, int ln, char *v, int *pos) { char *cp, *name; size_t sz; - int number, savepos, wanttrue; + int deftype, number, savepos, istrue, wanttrue; if ('!' == v[*pos]) { wanttrue = 0; @@ -2119,17 +2126,29 @@ roff_evalcond(struct roff *r, int ln, char *v, int *pos) (*pos)++; return wanttrue; case 'c': - case 'd': case 'e': case 't': case 'v': (*pos)++; return !wanttrue; + case 'd': case 'r': - cp = name = v + ++*pos; - sz = roff_getname(r, &cp, ln, *pos); + cp = v + *pos + 1; + while (*cp == ' ') + cp++; + name = cp; + sz = roff_getname(r, &cp, ln, cp - v); + if (sz == 0) + istrue = 0; + else if (v[*pos] == 'r') + istrue = roff_hasregn(r, name, sz); + else { + deftype = ROFFDEF_ANY; + roff_getstrn(r, name, sz, &deftype); + istrue = !!deftype; + } *pos = cp - v; - return (sz && roff_hasregn(r, name, sz)) == wanttrue; + return istrue == wanttrue; default: break; } @@ -2155,7 +2174,7 @@ roff_insec(ROFF_ARGS) { mandoc_msg(MANDOCERR_REQ_INSEC, r->parse, - ln, ppos, roffs[tok].name); + ln, ppos, roff_name[tok]); return ROFF_IGN; } @@ -2164,7 +2183,7 @@ roff_unsupp(ROFF_ARGS) { mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse, - ln, ppos, roffs[tok].name); + ln, ppos, roff_name[tok]); return ROFF_IGN; } @@ -2237,7 +2256,7 @@ roff_cond(ROFF_ARGS) if (buf->buf[pos] == '\0') mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, - ln, ppos, roffs[tok].name); + ln, ppos, roff_name[tok]); r->last->endspan = 1; @@ -2281,6 +2300,7 @@ roff_ds(ROFF_ARGS) /* The rest is the value. */ roff_setstrn(&r->strtab, name, namesz, string, strlen(string), ROFF_as == tok); + roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); return ROFF_IGN; } @@ -2692,6 +2712,7 @@ roff_rm(ROFF_ARGS) name = cp; namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf)); roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); + roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); if (name[namesz] == '\\') break; } @@ -2730,46 +2751,49 @@ roff_it(ROFF_ARGS) static enum rofferr roff_Dd(ROFF_ARGS) { - const char *const *cp; - - if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0) - for (cp = __mdoc_reserved; *cp; cp++) - roff_setstr(r, *cp, NULL, 0); - - if (r->format == 0) - r->format = MPARSE_MDOC; - - return ROFF_CONT; -} - -static enum rofferr -roff_TH(ROFF_ARGS) -{ - const char *const *cp; - - if ((r->options & MPARSE_QUICK) == 0) - for (cp = __man_reserved; *cp; cp++) - roff_setstr(r, *cp, NULL, 0); - - if (r->format == 0) - r->format = MPARSE_MAN; + int mask; + enum roff_tok t, te; + switch (tok) { + case ROFF_Dd: + tok = MDOC_Dd; + te = MDOC_MAX; + if (r->format == 0) + r->format = MPARSE_MDOC; + mask = MPARSE_MDOC | MPARSE_QUICK; + break; + case ROFF_TH: + tok = MAN_TH; + te = MAN_MAX; + if (r->format == 0) + r->format = MPARSE_MAN; + mask = MPARSE_QUICK; + break; + default: + abort(); + } + if ((r->options & mask) == 0) + for (t = tok; t < te; t++) + roff_setstr(r, roff_name[t], NULL, 0); return ROFF_CONT; } static enum rofferr roff_TE(ROFF_ARGS) { - - if (NULL == r->tbl) + if (r->tbl == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "TE"); - else if ( ! tbl_end(&r->tbl)) { + return ROFF_IGN; + } + if (tbl_end(r->tbl) == 0) { + r->tbl = NULL; free(buf->buf); buf->buf = mandoc_strdup(".sp"); buf->sz = 4; return ROFF_REPARSE; } + r->tbl = NULL; return ROFF_IGN; } @@ -2781,7 +2805,7 @@ roff_T_(ROFF_ARGS) mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "T&"); else - tbl_restart(ppos, ln, r->tbl); + tbl_restart(ln, ppos, r->tbl); return ROFF_IGN; } @@ -2855,20 +2879,25 @@ roff_eqndelim(struct roff *r, struct buf *buf, int pos) static enum rofferr roff_EQ(ROFF_ARGS) { - struct eqn_node *e; - - assert(r->eqn == NULL); - e = eqn_alloc(ppos, ln, r->parse); + struct roff_node *n; - if (r->last_eqn) { - r->last_eqn->next = e; - e->delim = r->last_eqn->delim; - e->odelim = r->last_eqn->odelim; - e->cdelim = r->last_eqn->cdelim; - } else - r->first_eqn = r->last_eqn = e; + if (r->man->macroset == MACROSET_MAN) + man_breakscope(r->man, ROFF_EQ); + n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE); + if (ln > r->man->last->line) + n->flags |= NODE_LINE; + n->eqn = mandoc_calloc(1, sizeof(*n->eqn)); + n->eqn->expectargs = UINT_MAX; + roff_node_append(r->man, n); + r->man->next = ROFF_NEXT_SIBLING; - r->eqn = r->last_eqn = e; + assert(r->eqn == NULL); + if (r->last_eqn == NULL) + r->last_eqn = eqn_alloc(r->parse); + else + eqn_reset(r->last_eqn); + r->eqn = r->last_eqn; + r->eqn->node = n; if (buf->buf[pos] != '\0') mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, @@ -2880,39 +2909,156 @@ roff_EQ(ROFF_ARGS) static enum rofferr roff_EN(ROFF_ARGS) { - - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); + if (r->eqn != NULL) { + eqn_parse(r->eqn); + r->eqn = NULL; + } else + mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); + if (buf->buf[pos] != '\0') + mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + "EN %s", buf->buf + pos); return ROFF_IGN; } static enum rofferr roff_TS(ROFF_ARGS) { - struct tbl_node *tbl; - - if (r->tbl) { + if (r->tbl != NULL) { mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, ln, ppos, "TS breaks TS"); - tbl_end(&r->tbl); + tbl_end(r->tbl); } - - tbl = tbl_alloc(ppos, ln, r->parse); - + r->tbl = tbl_alloc(ppos, ln, r->parse); if (r->last_tbl) - r->last_tbl->next = tbl; + r->last_tbl->next = r->tbl; else - r->first_tbl = r->last_tbl = tbl; + r->first_tbl = r->tbl; + r->last_tbl = r->tbl; + return ROFF_IGN; +} + +static enum rofferr +roff_onearg(ROFF_ARGS) +{ + struct roff_node *n; + char *cp; + int npos; + + if (r->man->flags & (MAN_BLINE | MAN_ELINE) && + (tok == ROFF_ce || tok == ROFF_rj || tok == ROFF_sp || + tok == ROFF_ti)) + man_breakscope(r->man, tok); + + if (roffce_node != NULL && (tok == ROFF_ce || tok == ROFF_rj)) { + r->man->last = roffce_node; + r->man->next = ROFF_NEXT_SIBLING; + } - r->tbl = r->last_tbl = tbl; + roff_elem_alloc(r->man, ln, ppos, tok); + n = r->man->last; + + cp = buf->buf + pos; + if (*cp != '\0') { + while (*cp != '\0' && *cp != ' ') + cp++; + while (*cp == ' ') + *cp++ = '\0'; + if (*cp != '\0') + mandoc_vmsg(MANDOCERR_ARG_EXCESS, + r->parse, ln, cp - buf->buf, + "%s ... %s", roff_name[tok], cp); + roff_word_alloc(r->man, ln, pos, buf->buf + pos); + } + + if (tok == ROFF_ce || tok == ROFF_rj) { + if (r->man->last->type == ROFFT_ELEM) { + roff_word_alloc(r->man, ln, pos, "1"); + r->man->last->flags |= NODE_NOSRC; + } + npos = 0; + if (roff_evalnum(r, ln, r->man->last->string, &npos, + &roffce_lines, 0) == 0) { + mandoc_vmsg(MANDOCERR_CE_NONUM, + r->parse, ln, pos, "ce %s", buf->buf + pos); + roffce_lines = 1; + } + if (roffce_lines < 1) { + r->man->last = r->man->last->parent; + roffce_node = NULL; + roffce_lines = 0; + } else + roffce_node = r->man->last->parent; + } else { + n->flags |= NODE_VALID | NODE_ENDED; + r->man->last = n; + } + n->flags |= NODE_LINE; + r->man->next = ROFF_NEXT_SIBLING; return ROFF_IGN; } static enum rofferr -roff_brp(ROFF_ARGS) +roff_manyarg(ROFF_ARGS) { + struct roff_node *n; + char *sp, *ep; - buf->buf[pos - 1] = '\0'; - return ROFF_CONT; + roff_elem_alloc(r->man, ln, ppos, tok); + n = r->man->last; + + for (sp = ep = buf->buf + pos; *sp != '\0'; sp = ep) { + while (*ep != '\0' && *ep != ' ') + ep++; + while (*ep == ' ') + *ep++ = '\0'; + roff_word_alloc(r->man, ln, sp - buf->buf, sp); + } + + n->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; + r->man->last = n; + r->man->next = ROFF_NEXT_SIBLING; + return ROFF_IGN; +} + +static enum rofferr +roff_als(ROFF_ARGS) +{ + char *oldn, *newn, *end, *value; + size_t oldsz, newsz, valsz; + + newn = oldn = buf->buf + pos; + if (*newn == '\0') + return ROFF_IGN; + + newsz = roff_getname(r, &oldn, ln, pos); + if (newn[newsz] == '\\' || *oldn == '\0') + return ROFF_IGN; + + end = oldn; + oldsz = roff_getname(r, &end, ln, oldn - buf->buf); + if (oldsz == 0) + return ROFF_IGN; + + valsz = mandoc_asprintf(&value, ".%.*s \\$*\\\"\n", + (int)oldsz, oldn); + roff_setstrn(&r->strtab, newn, newsz, value, valsz, 0); + roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); + free(value); + return ROFF_IGN; +} + +static enum rofferr +roff_br(ROFF_ARGS) +{ + if (r->man->flags & (MAN_BLINE | MAN_ELINE)) + man_breakscope(r->man, ROFF_br); + roff_elem_alloc(r->man, ln, ppos, ROFF_br); + if (buf->buf[pos] != '\0') + mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + "%s %s", roff_name[tok], buf->buf + pos); + r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; + r->man->next = ROFF_NEXT_SIBLING; + return ROFF_IGN; } static enum rofferr @@ -2923,7 +3069,7 @@ roff_cc(ROFF_ARGS) p = buf->buf + pos; if (*p == '\0' || (r->control = *p++) == '.') - r->control = 0; + r->control = '\0'; if (*p != '\0') mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, @@ -2933,6 +3079,33 @@ roff_cc(ROFF_ARGS) } static enum rofferr +roff_ec(ROFF_ARGS) +{ + const char *p; + + p = buf->buf + pos; + if (*p == '\0') + r->escape = '\\'; + else { + r->escape = *p; + if (*++p != '\0') + mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, + ln, p - buf->buf, "ec ... %s", p); + } + return ROFF_IGN; +} + +static enum rofferr +roff_eo(ROFF_ARGS) +{ + r->escape = '\0'; + if (buf->buf[pos] != '\0') + mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, + ln, pos, "eo %s", buf->buf + pos); + return ROFF_IGN; +} + +static enum rofferr roff_tr(ROFF_ARGS) { const char *p, *first, *second; @@ -2995,6 +3168,56 @@ roff_tr(ROFF_ARGS) } static enum rofferr +roff_rn(ROFF_ARGS) +{ + const char *value; + char *oldn, *newn, *end; + size_t oldsz, newsz; + int deftype; + + oldn = newn = buf->buf + pos; + if (*oldn == '\0') + return ROFF_IGN; + + oldsz = roff_getname(r, &newn, ln, pos); + if (oldn[oldsz] == '\\' || *newn == '\0') + return ROFF_IGN; + + end = newn; + newsz = roff_getname(r, &end, ln, newn - buf->buf); + if (newsz == 0) + return ROFF_IGN; + + deftype = ROFFDEF_ANY; + value = roff_getstrn(r, oldn, oldsz, &deftype); + switch (deftype) { + case ROFFDEF_USER: + roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0); + roff_setstrn(&r->strtab, oldn, oldsz, NULL, 0, 0); + roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); + break; + case ROFFDEF_PRE: + roff_setstrn(&r->strtab, newn, newsz, value, strlen(value), 0); + roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); + break; + case ROFFDEF_REN: + roff_setstrn(&r->rentab, newn, newsz, value, strlen(value), 0); + roff_setstrn(&r->rentab, oldn, oldsz, NULL, 0, 0); + roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); + break; + case ROFFDEF_STD: + roff_setstrn(&r->rentab, newn, newsz, oldn, oldsz, 0); + roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); + break; + default: + roff_setstrn(&r->strtab, newn, newsz, NULL, 0, 0); + roff_setstrn(&r->rentab, newn, newsz, NULL, 0, 0); + break; + } + return ROFF_IGN; +} + +static enum rofferr roff_so(ROFF_ARGS) { char *name, *cp; @@ -3029,9 +3252,9 @@ roff_so(ROFF_ARGS) static enum rofferr roff_userdef(ROFF_ARGS) { - const char *arg[9], *ap; + const char *arg[16], *ap; char *cp, *n1, *n2; - int i, ib, ie; + int expand_count, i, ib, ie; size_t asz, rsz; /* @@ -3041,7 +3264,7 @@ roff_userdef(ROFF_ARGS) r->argc = 0; cp = buf->buf + pos; - for (i = 0; i < 9; i++) { + for (i = 0; i < 16; i++) { if (*cp == '\0') arg[i] = ""; else { @@ -3055,8 +3278,9 @@ roff_userdef(ROFF_ARGS) */ buf->sz = strlen(r->current_string) + 1; - n1 = cp = mandoc_malloc(buf->sz); + n1 = n2 = cp = mandoc_malloc(buf->sz); memcpy(n1, r->current_string, buf->sz); + expand_count = 0; while (*cp != '\0') { /* Scan ahead for the next argument invocation. */ @@ -3076,6 +3300,20 @@ roff_userdef(ROFF_ARGS) cp -= 2; /* + * Prevent infinite recursion. + */ + + if (cp >= n2) + expand_count = 1; + else if (++expand_count > EXPAND_LIMIT) { + mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, + ln, (int)(cp - n1), NULL); + free(buf->buf); + buf->buf = n1; + return ROFF_IGN; + } + + /* * Determine the size of the expanded argument, * taking escaping of quotes into account. */ @@ -3153,6 +3391,22 @@ roff_userdef(ROFF_ARGS) ROFF_REPARSE : ROFF_APPEND; } +/* + * Calling a high-level macro that was renamed with .rn. + * r->current_string has already been set up by roff_parse(). + */ +static enum rofferr +roff_renamed(ROFF_ARGS) +{ + char *nbuf; + + buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string, + buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1; + free(buf->buf); + buf->buf = nbuf; + return ROFF_CONT; +} + static size_t roff_getname(struct roff *r, char **cpp, int ln, int pos) { @@ -3202,9 +3456,12 @@ static void roff_setstr(struct roff *r, const char *name, const char *string, int append) { + size_t namesz; - roff_setstrn(&r->strtab, name, strlen(name), string, + namesz = strlen(name); + roff_setstrn(&r->strtab, name, namesz, string, string ? strlen(string) : 0, append); + roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); } static void @@ -3280,21 +3537,63 @@ roff_setstrn(struct roffkv **r, const char *name, size_t namesz, } static const char * -roff_getstrn(const struct roff *r, const char *name, size_t len) -{ - const struct roffkv *n; - int i; - - for (n = r->strtab; n; n = n->next) - if (0 == strncmp(name, n->key.p, len) && - '\0' == n->key.p[(int)len]) - return n->val.p; - - for (i = 0; i < PREDEFS_MAX; i++) - if (0 == strncmp(name, predefs[i].name, len) && - '\0' == predefs[i].name[(int)len]) - return predefs[i].str; - +roff_getstrn(const struct roff *r, const char *name, size_t len, + int *deftype) +{ + const struct roffkv *n; + int i; + enum roff_tok tok; + + if (*deftype & ROFFDEF_USER) { + for (n = r->strtab; n != NULL; n = n->next) { + if (strncmp(name, n->key.p, len) == 0 && + n->key.p[len] == '\0' && + n->val.p != NULL) { + *deftype = ROFFDEF_USER; + return n->val.p; + } + } + } + if (*deftype & ROFFDEF_PRE) { + for (i = 0; i < PREDEFS_MAX; i++) { + if (strncmp(name, predefs[i].name, len) == 0 && + predefs[i].name[len] == '\0') { + *deftype = ROFFDEF_PRE; + return predefs[i].str; + } + } + } + if (*deftype & ROFFDEF_REN) { + for (n = r->rentab; n != NULL; n = n->next) { + if (strncmp(name, n->key.p, len) == 0 && + n->key.p[len] == '\0' && + n->val.p != NULL) { + *deftype = ROFFDEF_REN; + return n->val.p; + } + } + } + if (*deftype & ROFFDEF_STD) { + if (r->man->macroset != MACROSET_MAN) { + for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) { + if (strncmp(name, roff_name[tok], len) == 0 && + roff_name[tok][len] == '\0') { + *deftype = ROFFDEF_STD; + return NULL; + } + } + } + if (r->man->macroset != MACROSET_MDOC) { + for (tok = MAN_TH; tok < MAN_MAX; tok++) { + if (strncmp(name, roff_name[tok], len) == 0 && + roff_name[tok][len] == '\0') { + *deftype = ROFFDEF_STD; + return NULL; + } + } + } + } + *deftype = 0; return NULL; } @@ -3313,20 +3612,6 @@ roff_freestr(struct roffkv *r) /* --- accessors and utility functions ------------------------------------ */ -const struct tbl_span * -roff_span(const struct roff *r) -{ - - return r->tbl ? tbl_span(r->tbl) : NULL; -} - -const struct eqn * -roff_eqn(const struct roff *r) -{ - - return r->last_eqn ? &r->last_eqn->eqn : NULL; -} - /* * Duplicate an input string, making the appropriate character * conversations (as stipulated by `tr') along the way. @@ -3358,7 +3643,8 @@ roff_strdup(const struct roff *r, const char *p) ssz = 0; while ('\0' != *p) { - if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { + assert((unsigned int)*p < 128); + if ('\\' != *p && r->xtab && r->xtab[(unsigned int)*p].p) { sz = r->xtab[(int)*p].sz; res = mandoc_realloc(res, ssz + sz + 1); memcpy(res + ssz, r->xtab[(int)*p].p, sz); @@ -3443,9 +3729,9 @@ roff_getcontrol(const struct roff *r, const char *cp, int *ppos) pos = *ppos; - if (0 != r->control && cp[pos] == r->control) + if (r->control != '\0' && cp[pos] == r->control) pos++; - else if (0 != r->control) + else if (r->control != '\0') return 0; else if ('\\' == cp[pos] && '.' == cp[pos + 1]) pos += 2; diff --git a/contrib/mdocml/roff.h b/contrib/mdocml/roff.h index 3039d4e..8b28d59 100644 --- a/contrib/mdocml/roff.h +++ b/contrib/mdocml/roff.h @@ -1,4 +1,4 @@ -/* $Id: roff.h,v 1.39 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: roff.h,v 1.58 2017/07/08 14:51:05 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +struct ohash; struct mdoc_arg; union mdoc_data; @@ -64,6 +65,413 @@ enum roff_type { ROFFT_EQN }; +enum roff_tok { + ROFF_br = 0, + ROFF_ce, + ROFF_ft, + ROFF_ll, + ROFF_mc, + ROFF_po, + ROFF_rj, + ROFF_sp, + ROFF_ta, + ROFF_ti, + ROFF_MAX, + ROFF_ab, + ROFF_ad, + ROFF_af, + ROFF_aln, + ROFF_als, + ROFF_am, + ROFF_am1, + ROFF_ami, + ROFF_ami1, + ROFF_as, + ROFF_as1, + ROFF_asciify, + ROFF_backtrace, + ROFF_bd, + ROFF_bleedat, + ROFF_blm, + ROFF_box, + ROFF_boxa, + ROFF_bp, + ROFF_BP, + ROFF_break, + ROFF_breakchar, + ROFF_brnl, + ROFF_brp, + ROFF_brpnl, + ROFF_c2, + ROFF_cc, + ROFF_cf, + ROFF_cflags, + ROFF_ch, + ROFF_char, + ROFF_chop, + ROFF_class, + ROFF_close, + ROFF_CL, + ROFF_color, + ROFF_composite, + ROFF_continue, + ROFF_cp, + ROFF_cropat, + ROFF_cs, + ROFF_cu, + ROFF_da, + ROFF_dch, + ROFF_Dd, + ROFF_de, + ROFF_de1, + ROFF_defcolor, + ROFF_dei, + ROFF_dei1, + ROFF_device, + ROFF_devicem, + ROFF_di, + ROFF_do, + ROFF_ds, + ROFF_ds1, + ROFF_dwh, + ROFF_dt, + ROFF_ec, + ROFF_ecr, + ROFF_ecs, + ROFF_el, + ROFF_em, + ROFF_EN, + ROFF_eo, + ROFF_EP, + ROFF_EQ, + ROFF_errprint, + ROFF_ev, + ROFF_evc, + ROFF_ex, + ROFF_fallback, + ROFF_fam, + ROFF_fc, + ROFF_fchar, + ROFF_fcolor, + ROFF_fdeferlig, + ROFF_feature, + /* MAN_fi; ignored in mdoc(7) */ + ROFF_fkern, + ROFF_fl, + ROFF_flig, + ROFF_fp, + ROFF_fps, + ROFF_fschar, + ROFF_fspacewidth, + ROFF_fspecial, + ROFF_ftr, + ROFF_fzoom, + ROFF_gcolor, + ROFF_hc, + ROFF_hcode, + ROFF_hidechar, + ROFF_hla, + ROFF_hlm, + ROFF_hpf, + ROFF_hpfa, + ROFF_hpfcode, + ROFF_hw, + ROFF_hy, + ROFF_hylang, + ROFF_hylen, + ROFF_hym, + ROFF_hypp, + ROFF_hys, + ROFF_ie, + ROFF_if, + ROFF_ig, + /* MAN_in; ignored in mdoc(7) */ + ROFF_index, + ROFF_it, + ROFF_itc, + ROFF_IX, + ROFF_kern, + ROFF_kernafter, + ROFF_kernbefore, + ROFF_kernpair, + ROFF_lc, + ROFF_lc_ctype, + ROFF_lds, + ROFF_length, + ROFF_letadj, + ROFF_lf, + ROFF_lg, + ROFF_lhang, + ROFF_linetabs, + ROFF_lnr, + ROFF_lnrf, + ROFF_lpfx, + ROFF_ls, + ROFF_lsm, + ROFF_lt, + ROFF_mediasize, + ROFF_minss, + ROFF_mk, + ROFF_mso, + ROFF_na, + ROFF_ne, + /* MAN_nf; ignored in mdoc(7) */ + ROFF_nh, + ROFF_nhychar, + ROFF_nm, + ROFF_nn, + ROFF_nop, + ROFF_nr, + ROFF_nrf, + ROFF_nroff, + ROFF_ns, + ROFF_nx, + ROFF_open, + ROFF_opena, + ROFF_os, + ROFF_output, + ROFF_padj, + ROFF_papersize, + ROFF_pc, + ROFF_pev, + ROFF_pi, + ROFF_PI, + ROFF_pl, + ROFF_pm, + ROFF_pn, + ROFF_pnr, + ROFF_ps, + ROFF_psbb, + ROFF_pshape, + ROFF_pso, + ROFF_ptr, + ROFF_pvs, + ROFF_rchar, + ROFF_rd, + ROFF_recursionlimit, + ROFF_return, + ROFF_rfschar, + ROFF_rhang, + ROFF_rm, + ROFF_rn, + ROFF_rnn, + ROFF_rr, + ROFF_rs, + ROFF_rt, + ROFF_schar, + ROFF_sentchar, + ROFF_shc, + ROFF_shift, + ROFF_sizes, + ROFF_so, + ROFF_spacewidth, + ROFF_special, + ROFF_spreadwarn, + ROFF_ss, + ROFF_sty, + ROFF_substring, + ROFF_sv, + ROFF_sy, + ROFF_T_, + ROFF_tc, + ROFF_TE, + ROFF_TH, + ROFF_tkf, + ROFF_tl, + ROFF_tm, + ROFF_tm1, + ROFF_tmc, + ROFF_tr, + ROFF_track, + ROFF_transchar, + ROFF_trf, + ROFF_trimat, + ROFF_trin, + ROFF_trnt, + ROFF_troff, + ROFF_TS, + ROFF_uf, + ROFF_ul, + ROFF_unformat, + ROFF_unwatch, + ROFF_unwatchn, + ROFF_vpt, + ROFF_vs, + ROFF_warn, + ROFF_warnscale, + ROFF_watch, + ROFF_watchlength, + ROFF_watchn, + ROFF_wh, + ROFF_while, + ROFF_write, + ROFF_writec, + ROFF_writem, + ROFF_xflag, + ROFF_cblock, + ROFF_RENAMED, + ROFF_USERDEF, + TOKEN_NONE, + MDOC_Dd, + MDOC_Dt, + MDOC_Os, + MDOC_Sh, + MDOC_Ss, + MDOC_Pp, + MDOC_D1, + MDOC_Dl, + MDOC_Bd, + MDOC_Ed, + MDOC_Bl, + MDOC_El, + MDOC_It, + MDOC_Ad, + MDOC_An, + MDOC_Ap, + MDOC_Ar, + MDOC_Cd, + MDOC_Cm, + MDOC_Dv, + MDOC_Er, + MDOC_Ev, + MDOC_Ex, + MDOC_Fa, + MDOC_Fd, + MDOC_Fl, + MDOC_Fn, + MDOC_Ft, + MDOC_Ic, + MDOC_In, + MDOC_Li, + MDOC_Nd, + MDOC_Nm, + MDOC_Op, + MDOC_Ot, + MDOC_Pa, + MDOC_Rv, + MDOC_St, + MDOC_Va, + MDOC_Vt, + MDOC_Xr, + MDOC__A, + MDOC__B, + MDOC__D, + MDOC__I, + MDOC__J, + MDOC__N, + MDOC__O, + MDOC__P, + MDOC__R, + MDOC__T, + MDOC__V, + MDOC_Ac, + MDOC_Ao, + MDOC_Aq, + MDOC_At, + MDOC_Bc, + MDOC_Bf, + MDOC_Bo, + MDOC_Bq, + MDOC_Bsx, + MDOC_Bx, + MDOC_Db, + MDOC_Dc, + MDOC_Do, + MDOC_Dq, + MDOC_Ec, + MDOC_Ef, + MDOC_Em, + MDOC_Eo, + MDOC_Fx, + MDOC_Ms, + MDOC_No, + MDOC_Ns, + MDOC_Nx, + MDOC_Ox, + MDOC_Pc, + MDOC_Pf, + MDOC_Po, + MDOC_Pq, + MDOC_Qc, + MDOC_Ql, + MDOC_Qo, + MDOC_Qq, + MDOC_Re, + MDOC_Rs, + MDOC_Sc, + MDOC_So, + MDOC_Sq, + MDOC_Sm, + MDOC_Sx, + MDOC_Sy, + MDOC_Tn, + MDOC_Ux, + MDOC_Xc, + MDOC_Xo, + MDOC_Fo, + MDOC_Fc, + MDOC_Oo, + MDOC_Oc, + MDOC_Bk, + MDOC_Ek, + MDOC_Bt, + MDOC_Hf, + MDOC_Fr, + MDOC_Ud, + MDOC_Lb, + MDOC_Lp, + MDOC_Lk, + MDOC_Mt, + MDOC_Brq, + MDOC_Bro, + MDOC_Brc, + MDOC__C, + MDOC_Es, + MDOC_En, + MDOC_Dx, + MDOC__Q, + MDOC__U, + MDOC_Ta, + MDOC_MAX, + MAN_TH, + MAN_SH, + MAN_SS, + MAN_TP, + MAN_LP, + MAN_PP, + MAN_P, + MAN_IP, + MAN_HP, + MAN_SM, + MAN_SB, + MAN_BI, + MAN_IB, + MAN_BR, + MAN_RB, + MAN_R, + MAN_B, + MAN_I, + MAN_IR, + MAN_RI, + MAN_nf, + MAN_fi, + MAN_RE, + MAN_RS, + MAN_DT, + MAN_UC, + MAN_PD, + MAN_AT, + MAN_in, + MAN_OP, + MAN_EX, + MAN_EE, + MAN_UR, + MAN_UE, + MAN_MT, + MAN_ME, + MAN_MAX +}; + enum roff_next { ROFF_NEXT_SIBLING = 0, ROFF_NEXT_CHILD @@ -75,8 +483,7 @@ enum roff_next { */ enum mdoc_endbody { ENDBODY_NOT = 0, - ENDBODY_SPACE, /* Is broken: append a space. */ - ENDBODY_NOSPACE /* Is broken: don't append a space. */ + ENDBODY_SPACE /* Is broken: append a space. */ }; struct roff_node { @@ -92,11 +499,9 @@ struct roff_node { union mdoc_data *norm; /* Normalized arguments. */ char *string; /* TEXT */ const struct tbl_span *span; /* TBL */ - const struct eqn *eqn; /* EQN */ + struct eqn_box *eqn; /* EQN */ int line; /* Input file line number. */ int pos; /* Input file column number. */ - int tok; /* Request or macro ID. */ -#define TOKEN_NONE (-1) /* No request or macro. */ int flags; #define NODE_VALID (1 << 0) /* Has been validated. */ #define NODE_ENDED (1 << 1) /* Gone past body end mark. */ @@ -110,6 +515,7 @@ struct roff_node { #define NODE_NOPRT (1 << 9) /* Shall not print anything. */ int prev_font; /* Before entering this node. */ int aux; /* Decoded node data, type-dependent. */ + enum roff_tok tok; /* Request or macro ID. */ enum roff_type type; /* AST node type. */ enum roff_sec sec; /* Current named section. */ enum mdoc_endbody end; /* BODY */ @@ -124,13 +530,17 @@ struct roff_meta { char *name; /* Leading manual name. */ char *date; /* Normalized date. */ 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. */ - const char *defos; /* Default operating system. */ + 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. */ @@ -159,5 +569,11 @@ struct roff_man { enum roff_next next; /* Where to put the next node. */ }; +extern const char *const *roff_name; + void deroff(char **, const struct roff_node *); +struct ohash *roffhash_alloc(enum roff_tok, enum roff_tok); +enum roff_tok roffhash_find(struct ohash *, const char *, size_t); +void roffhash_free(struct ohash *); +void roff_validate(struct roff_man *); diff --git a/contrib/mdocml/roff_html.c b/contrib/mdocml/roff_html.c new file mode 100644 index 0000000..53ae6d7 --- /dev/null +++ b/contrib/mdocml/roff_html.c @@ -0,0 +1,96 @@ +/* $Id: roff_html.c,v 1.11 2017/06/24 14:38:33 schwarze Exp $ */ +/* + * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2014, 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> + +#include <assert.h> +#include <stddef.h> + +#include "mandoc.h" +#include "roff.h" +#include "out.h" +#include "html.h" + +#define ROFF_HTML_ARGS struct html *h, const struct roff_node *n + +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_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 */ + NULL, /* ll */ + NULL, /* mc */ + NULL, /* po */ + roff_html_pre_ce, /* rj */ + roff_html_pre_sp, /* sp */ + NULL, /* ta */ + NULL, /* ti */ +}; + + +void +roff_html_pre(struct html *h, const struct roff_node *n) +{ + assert(n->tok < ROFF_MAX); + if (roff_html_pre_acts[n->tok] != NULL) + (*roff_html_pre_acts[n->tok])(h, 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); +} + +static void +roff_html_pre_ce(ROFF_HTML_ARGS) +{ + for (n = n->child->next; n != NULL; n = n->next) { + if (n->type == ROFFT_TEXT) { + if (n->flags & NODE_LINE) + roff_html_pre_br(h, n); + print_text(h, n->string); + } else + roff_html_pre(h, n); + } + roff_html_pre_br(h, n); +} + +static void +roff_html_pre_sp(ROFF_HTML_ARGS) +{ + struct roffsu su; + + SCALE_VS_INIT(&su, 1); + if ((n = n->child) != NULL) { + if (a2roffsu(n->string, &su, SCALE_VS) == NULL) + su.scale = 1.0; + else if (su.scale < 0.0) + su.scale = 0.0; + } + print_otag(h, TAG_DIV, "suh", &su); + print_text(h, "\\~"); /* So the div isn't empty. */ +} diff --git a/contrib/mdocml/roff_int.h b/contrib/mdocml/roff_int.h index 5567b75..48996dc 100644 --- a/contrib/mdocml/roff_int.h +++ b/contrib/mdocml/roff_int.h @@ -1,4 +1,4 @@ -/* $Id: roff_int.h,v 1.7 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: roff_int.h,v 1.9 2017/07/08 17:52:50 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -25,8 +25,6 @@ void roff_elem_alloc(struct roff_man *, int, int, int); 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_addeqn(struct roff_man *, const struct eqn *); -void roff_addtbl(struct roff_man *, const struct tbl_span *); void roff_node_unlink(struct roff_man *, struct roff_node *); void roff_node_free(struct roff_node *); void roff_node_delete(struct roff_man *, struct roff_node *); diff --git a/contrib/mdocml/roff_term.c b/contrib/mdocml/roff_term.c new file mode 100644 index 0000000..b5ec764 --- /dev/null +++ b/contrib/mdocml/roff_term.c @@ -0,0 +1,248 @@ +/* $Id: roff_term.c,v 1.14 2017/06/24 14:38:33 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> + +#include <assert.h> +#include <stddef.h> + +#include "mandoc.h" +#include "roff.h" +#include "out.h" +#include "term.h" + +#define ROFF_TERM_ARGS struct termp *p, const struct roff_node *n + +typedef void (*roff_term_pre_fp)(ROFF_TERM_ARGS); + +static void roff_term_pre_br(ROFF_TERM_ARGS); +static void roff_term_pre_ce(ROFF_TERM_ARGS); +static void roff_term_pre_ft(ROFF_TERM_ARGS); +static void roff_term_pre_ll(ROFF_TERM_ARGS); +static void roff_term_pre_mc(ROFF_TERM_ARGS); +static void roff_term_pre_po(ROFF_TERM_ARGS); +static void roff_term_pre_sp(ROFF_TERM_ARGS); +static void roff_term_pre_ta(ROFF_TERM_ARGS); +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_ft, /* ft */ + roff_term_pre_ll, /* ll */ + roff_term_pre_mc, /* mc */ + roff_term_pre_po, /* po */ + roff_term_pre_ce, /* rj */ + roff_term_pre_sp, /* sp */ + roff_term_pre_ta, /* ta */ + roff_term_pre_ti, /* ti */ +}; + + +void +roff_term_pre(struct termp *p, const struct roff_node *n) +{ + assert(n->tok < ROFF_MAX); + (*roff_term_pre_acts[n->tok])(p, n); +} + +static void +roff_term_pre_br(ROFF_TERM_ARGS) +{ + term_newln(p); + if (p->flags & TERMP_BRIND) { + p->tcol->offset = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin; + p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); + } +} + +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; + 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); + else + roff_term_pre(p, nc1); + nc1 = nc1->next; + } + p->flags |= TERMP_NOSPACE; + term_flushln(p); + } + p->tcol->offset = lm; +} + +static void +roff_term_pre_ft(ROFF_TERM_ARGS) +{ + switch (*n->child->string) { + case '4': + case '3': + case 'B': + term_fontrepl(p, TERMFONT_BOLD); + break; + case '2': + case 'I': + term_fontrepl(p, TERMFONT_UNDER); + break; + case 'P': + term_fontlast(p); + break; + case '1': + case 'C': + case 'R': + term_fontrepl(p, TERMFONT_NONE); + break; + default: + break; + } +} + +static void +roff_term_pre_ll(ROFF_TERM_ARGS) +{ + term_setwidth(p, n->child != NULL ? n->child->string : NULL); +} + +static void +roff_term_pre_mc(ROFF_TERM_ARGS) +{ + if (p->col) { + p->flags |= TERMP_NOBREAK; + term_flushln(p); + p->flags &= ~(TERMP_NOBREAK | TERMP_NOSPACE); + } + if (n->child != NULL) { + p->mc = n->child->string; + p->flags |= TERMP_NEWMC; + } else + p->flags |= TERMP_ENDMC; +} + +static void +roff_term_pre_po(ROFF_TERM_ARGS) +{ + struct roffsu su; + static int po, polast; + int ponew; + + if (n->child != NULL && + a2roffsu(n->child->string, &su, SCALE_EM) != NULL) { + ponew = term_hen(p, &su); + if (*n->child->string == '+' || + *n->child->string == '-') + ponew += po; + } else + ponew = polast; + polast = po; + po = ponew; + + ponew = po - polast + (int)p->tcol->offset; + p->tcol->offset = ponew > 0 ? ponew : 0; +} + +static void +roff_term_pre_sp(ROFF_TERM_ARGS) +{ + struct roffsu su; + int len; + + if (n->child != NULL) { + if (a2roffsu(n->child->string, &su, SCALE_VS) == NULL) + su.scale = 1.0; + len = term_vspan(p, &su); + } else + len = 1; + + if (len < 0) + p->skipvsp -= len; + else + while (len--) + term_vspace(p); + + roff_term_pre_br(p, n); +} + +static void +roff_term_pre_ta(ROFF_TERM_ARGS) +{ + term_tab_set(p, NULL); + for (n = n->child; n != NULL; n = n->next) + term_tab_set(p, n->string); +} + +static void +roff_term_pre_ti(ROFF_TERM_ARGS) +{ + struct roffsu su; + const char *cp; + int len, sign; + + roff_term_pre_br(p, n); + + if (n->child == NULL) + return; + cp = n->child->string; + if (*cp == '+') { + sign = 1; + cp++; + } else if (*cp == '-') { + sign = -1; + cp++; + } else + sign = 0; + + if (a2roffsu(cp, &su, SCALE_EM) == NULL) + return; + len = term_hen(p, &su); + + if (sign == 0) { + p->ti = len - p->tcol->offset; + p->tcol->offset = len; + } else if (sign == 1) { + p->ti = len; + p->tcol->offset += len; + } else if ((size_t)len < p->tcol->offset) { + p->ti = -len; + p->tcol->offset -= len; + } else { + p->ti = -p->tcol->offset; + p->tcol->offset = 0; + } +} diff --git a/contrib/mdocml/roff_validate.c b/contrib/mdocml/roff_validate.c new file mode 100644 index 0000000..801e931 --- /dev/null +++ b/contrib/mdocml/roff_validate.c @@ -0,0 +1,97 @@ +/* $Id: roff_validate.c,v 1.9 2017/06/14 22:51:25 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> + +#include <assert.h> +#include <stddef.h> + +#include "mandoc.h" +#include "roff.h" +#include "libmandoc.h" +#include "roff_int.h" + +#define ROFF_VALID_ARGS struct roff_man *man, struct roff_node *n + +typedef void (*roff_valid_fp)(ROFF_VALID_ARGS); + +static void roff_valid_ft(ROFF_VALID_ARGS); + +static const roff_valid_fp roff_valids[ROFF_MAX] = { + NULL, /* br */ + NULL, /* ce */ + roff_valid_ft, /* ft */ + NULL, /* ll */ + NULL, /* mc */ + NULL, /* po */ + NULL, /* rj */ + NULL, /* sp */ + NULL, /* ta */ + NULL, /* ti */ +}; + + +void +roff_validate(struct roff_man *man) +{ + struct roff_node *n; + + n = man->last; + assert(n->tok < ROFF_MAX); + if (roff_valids[n->tok] != NULL) + (*roff_valids[n->tok])(man, n); +} + +static void +roff_valid_ft(ROFF_VALID_ARGS) +{ + char *cp; + + if (n->child == NULL) { + man->next = ROFF_NEXT_CHILD; + roff_word_alloc(man, n->line, n->pos, "P"); + man->last = n; + return; + } + + cp = n->child->string; + switch (*cp) { + case '1': + case '2': + case '3': + case '4': + case 'I': + case 'P': + case 'R': + if (cp[1] == '\0') + return; + break; + case 'B': + if (cp[1] == '\0' || (cp[1] == 'I' && cp[2] == '\0')) + return; + break; + case 'C': + if (cp[1] == 'W' && cp[2] == '\0') + return; + break; + default: + break; + } + + mandoc_vmsg(MANDOCERR_FT_BAD, man->parse, + n->line, n->pos, "ft %s", cp); + roff_node_delete(man, n); +} diff --git a/contrib/mdocml/soelim.1 b/contrib/mdocml/soelim.1 index 20f15ec..6b42750 100644 --- a/contrib/mdocml/soelim.1 +++ b/contrib/mdocml/soelim.1 @@ -1,4 +1,4 @@ -.\" $Id: soelim.1,v 1.3 2015/05/20 22:59:12 schwarze Exp $ +.\" $Id: soelim.1,v 1.5 2017/07/04 23:40:01 schwarze Exp $ .\" .\" Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org> .\" All rights reserved. @@ -24,7 +24,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd $Mdocdate: May 20 2015 $ +.Dd $Mdocdate: July 4 2017 $ .Dt SOELIM 1 .Os .Sh NAME @@ -53,15 +53,15 @@ Recognise when not followed by a space character. .It Fl r Compatibility with GNU groff's -.Xr soelim 1 +.Nm soelim (does nothing). .It Fl t Compatibility with GNU groff's -.Xr soelim 1 +.Nm soelim (does nothing). .It Fl v Compatibility with GNU groff's -.Xr soelim 1 +.Nm soelim (does nothing). .It Fl I Ar dir This option specify directories where @@ -69,8 +69,8 @@ This option specify directories where searches for files (both those on the command line and those named in .Dq .so directive.) -This options may be specified multiple times. The directories will be searched -in the order specified. +This options may be specified multiple times. +The directories will be searched in the order specified. .El .Pp The files are always searched first in the current directory. diff --git a/contrib/mdocml/st.c b/contrib/mdocml/st.c index 02868f0..d166566 100644 --- a/contrib/mdocml/st.c +++ b/contrib/mdocml/st.c @@ -1,4 +1,4 @@ -/* $Id: st.c,v 1.13 2015/10/06 18:32:20 schwarze Exp $ */ +/* $Id: st.c,v 1.14 2017/06/24 14:38:33 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -20,6 +20,7 @@ #include <string.h> +#include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "libmdoc.h" diff --git a/contrib/mdocml/st.in b/contrib/mdocml/st.in index e70680f..6087c76 100644 --- a/contrib/mdocml/st.in +++ b/contrib/mdocml/st.in @@ -1,4 +1,4 @@ -/* $Id: st.in,v 1.28 2015/02/17 20:37:17 schwarze Exp $ */ +/* $Id: st.in,v 1.29 2017/06/24 13:49:29 schwarze Exp $ */ /* * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -34,7 +34,6 @@ LINE("-p1003.1-96", "ISO/IEC 9945-1:1996 (\\(LqPOSIX.1\\(Rq)") LINE("-p1003.1-2001", "IEEE Std 1003.1-2001 (\\(LqPOSIX.1\\(Rq)") LINE("-p1003.1-2004", "IEEE Std 1003.1-2004 (\\(LqPOSIX.1\\(Rq)") LINE("-p1003.1-2008", "IEEE Std 1003.1-2008 (\\(LqPOSIX.1\\(Rq)") -LINE("-p1003.1-2013", "IEEE Std 1003.1-2008/Cor 1-2013 (\\(LqPOSIX.1\\(Rq)") LINE("-p1003.1", "IEEE Std 1003.1 (\\(LqPOSIX.1\\(Rq)") LINE("-p1003.1b", "IEEE Std 1003.1b (\\(LqPOSIX.1b\\(Rq)") LINE("-p1003.1b-93", "IEEE Std 1003.1b-1993 (\\(LqPOSIX.1b\\(Rq)") diff --git a/contrib/mdocml/tag.c b/contrib/mdocml/tag.c index 0fbd2e1..21ac6b3 100644 --- a/contrib/mdocml/tag.c +++ b/contrib/mdocml/tag.c @@ -1,4 +1,4 @@ -/* $Id: tag.c,v 1.17 2017/01/09 17:49:58 schwarze Exp $ */ +/* $Id: tag.c,v 1.18 2017/02/17 14:31:52 schwarze Exp $ */ /* * Copyright (c) 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -38,7 +38,7 @@ struct tag_entry { char s[]; }; -static void tag_signal(int) __attribute__((noreturn)); +static void tag_signal(int) __attribute__((__noreturn__)); static struct ohash tag_data; static struct tag_files tag_files; diff --git a/contrib/mdocml/tbl.7 b/contrib/mdocml/tbl.7 index c8fa8e4..e2f8453 100644 --- a/contrib/mdocml/tbl.7 +++ b/contrib/mdocml/tbl.7 @@ -1,7 +1,7 @@ -.\" $Id: tbl.7,v 1.26 2015/01/29 00:33:57 schwarze Exp $ +.\" $Id: tbl.7,v 1.28 2017/06/28 00:59:57 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 29 2015 $ +.Dd $Mdocdate: June 28 2017 $ .Dt TBL 7 .Os .Sh NAME @@ -24,128 +24,43 @@ .Sh DESCRIPTION The .Nm tbl -language is a table-formatting language. +language formats tables. It is used within .Xr mdoc 7 and .Xr man 7 -.Ux -manual pages. +pages. This manual describes the subset of the .Nm language accepted by the .Xr mandoc 1 utility. .Pp -Tables within -.Xr mdoc 7 -or -.Xr man 7 -are enclosed by the -.Sq TS -and -.Sq TE -macro tags, whose precise syntax is documented in -.Xr roff 7 . -Tables consist of a series of options on a single line, followed by the -table layout, followed by data. -.Pp -For example, the following creates a boxed table with digits centered in -the cells. -.Bd -literal -offset indent -\&.TS -tab(:) box; -c5 c5 c5. -1:2:3 -4:5:6 -\&.TE -.Ed -.Pp -When formatted, the following output is produced: -.Bd -filled -offset indent -compact -.TS -tab(:) box; -c5 c5 c5. -1:2:3 -4:5:6 -.TE -.Ed -.Sh TABLE STRUCTURE -Tables are enclosed by the -.Sq TS -and -.Sq TE +Each table is started with a .Xr roff 7 -macros. -A table consists of an optional single line of table -.Sx Options -terminated by a semicolon, followed by one or more lines of +.Ic \&TS +macro, consist of at most one line of +.Sx Options , +one or more .Sx Layout -specifications terminated by a period, then -.Sx Data . +lines, one or more +.Sx Data +lines, and ends with a +.Ic \&TE +macro. All input must be 7-bit ASCII. -Example: -.Bd -literal -offset indent -\&.TS -box tab(:); -c | c -| c | c. -1:2 -3:4 -\&.TE -.Ed -.Pp -Table data is -.Em pre-processed , -that is, data rows are parsed then inserted into the underlying stream -of input data. -This allows data rows to be interspersed by arbitrary -.Xr roff 7 , -.Xr mdoc 7 , -and -.Xr man 7 -macros such as -.Bd -literal -offset indent -\&.TS -tab(:); -c c c. -1:2:3 -\&.Ao -3:2:1 -\&.Ac -\&.TE -.Ed -.Pp -in the case of -.Xr mdoc 7 -or -.Bd -literal -offset indent -\&.TS -tab(:); -c c c. -\&.ds ab 2 -1:\e*(ab:3 -\&.I -3:2:1 -\&.TE -.Ed -.Pp -in the case of -.Xr man 7 . .Ss Options -The first line of a table may contain options separated by spaces, tabs, -or commas and terminated by a semicolon. -If the first line does not have a terminating semicolon, it is assumed -that no options are specified and instead a +If the first input line of a table ends with a semicolon, it contains +case-insensitive options separated by spaces, tabs, or commas. +Otherwise, it is interpreted as the first .Sx Layout -is processed. -Some options require arguments enclosed by parentheses. -The following case-insensitive options are available: +line. +.Pp +The following options are available. +Some of them require arguments enclosed in parentheses: .Bl -tag -width Ds .It Cm allbox Draw a single-line box around each table cell. -Currently treated as a synonym for -.Cm box . .It Cm box Draw a single-line box around the table. For GNU compatibility, this may also be invoked with @@ -185,73 +100,77 @@ Suppress warnings about tables exceeding the current line length. This is a GNU extension and currently ignored. .It Cm tab Use the single-character argument as a delimiter between data cells. -By default, the tab character is used. +By default, the horizontal tabulator character is used. .El .Ss Layout -The table layout follows +The table layout follows an .Sx Options -or a -.Sq \&T& -macro invocation. -Layout specifies how data rows are displayed on output. -Each layout line corresponds to a line of data; the last layout line -applies to all remaining data lines. -Layout lines may also be separated by a comma. -Each layout cell consists of one of the following case-insensitive keys: +line or a +.Xr roff 7 +.Ic \&TS +or +.Ic \&T& +macro. +Each layout line specifies how one line of +.Sx Data +is formatted. +The last layout line ends with a full stop. +It also applies to all remaining data lines. +Multiple layout lines can be joined by commas on a single physical +input line. +.Pp +Each layout line consists of one or more layout cell specifications, +optionally separated by whitespace. +The following case-insensitive key characters start a new cell +specification: .Bl -tag -width 2n .It Cm c -Center a literal string within its column. +Center the string in this cell. .It Cm r -Right-justify a literal string within its column. +Right-justify the string in this cell. .It Cm l -Left-justify a literal string within its column. +Left-justify the string in this cell. .It Cm n Justify a number around its last decimal point. -If the decimal point is not found on the number, it's assumed to trail -the number. +If no decimal point is found in the number, +it is assumed to trail the number. .It Cm s Horizontally span columns from the last -.No non- Ns Cm s -data cell. -It is an error if spanning columns follow a -.Cm \- +.Pf non- Cm s +layout cell. +It is an error if a column span follows a +.Cm _ or -.Cm \(ba -cell, or come first. -This option is not supported by -.Xr mandoc 1 . +.Cm = +cell, or comes first on a layout line. +The combined cell as a whole consumes only one cell +of the corresponding data line. .It Cm a -Left-justify a literal string and pad with one space. +Left-justify a string and pad with one space. .It Cm ^ Vertically span rows from the last -.No non- Ns Cm ^ -data cell. -It is an error to invoke a vertical span on the first layout row. -Unlike a horizontal spanner, you must specify an empty cell (if it not -empty, the data is discarded) in the corresponding data cell. -.It Cm \- -Replace the data cell (its contents will be lost) with a single -horizontal line. -This may also be invoked with -.Cm _ . +.Pf non- Cm ^ +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 +and discards the content. +.It Cm _ +Draw a single horizontal line in this cell. +This consumes a data cell and discards the content. +It may also be invoked with +.Cm \- . .It Cm = -Replace the data cell (its contents will be lost) with a double -horizontal line. -.It Cm \(ba -Emit a vertical bar instead of data. -.It Cm \(ba\(ba -Emit a double-vertical bar instead of data. +Draw a double horizontal line in this cell. +This consumes a data cell and discards the content. .El .Pp -Keys may be followed by a set of modifiers. -A modifier is either a modifier key or a natural number for specifying -the minimum width of a column. -The following case-insensitive modifier keys are available: +Each cell key may be followed by zero or more of the following +case-insensitive modifiers: .Bl -tag -width 2n .It Cm b -Use a bold font for the contents of this column. +Use a bold font for the contents of this cell. .It Cm d -Move cell content down to the last cell of a vertical span. +Move content down to the last row of this vertical span. Currently ignored. .It Cm e Make this column wider to match the maximum width @@ -259,12 +178,12 @@ of any other column also having the .Cm e modifier. .It Cm f -The next character selects the font to use for this column. +The next character selects the font to use for this cell. See the .Xr roff 7 manual for supported one-character font names. .It Cm i -Use an italic font for the contents of this column. +Use an italic font for the contents of this cell. .It Cm m Specify a cell start macro. This is a GNU extension and currently unsupported. @@ -277,15 +196,14 @@ Set the vertical line spacing to the following unsigned argument, or change it by the following signed argument. Currently ignored. .It Cm t -Do not vertically center cell content in the vertical span, -leave it at the top. +Do not vertically center content in this vertical span, +leave it in the top row. Currently ignored. .It Cm u -Move cell content up by half a table line. +Move cell content up by half a table row. Currently ignored. .It Cm w -Specify minimum column width. -Currently ignored. +Specify a minimum column width. .It Cm x After determining the width of all other columns, distribute the rest of the line length among all columns having the @@ -293,40 +211,185 @@ rest of the line length among all columns having the modifier. .It Cm z Do not use this cell for determining the width of this column. +.It Cm \&| +Draw a single vertical line to the right of this cell. +.It Cm || +Draw a double vertical line to the right of this cell. .El .Pp -For example, the following layout specifies a center-justified column of -minimum width 10, followed by vertical bar, followed by a left-justified -column of minimum width 10, another vertical bar, then a column using -bold font justified about the decimal point in numbers: -.Pp -.Dl c10 | l10 | nfB +If a modifier consists of decimal digits, +it specifies a minimum spacing in units of +.Cm n +between this column and the next column to the right. +The default is 3. +If there is a vertical line, it is drawn inside the spacing. .Ss Data -The data section follows the last layout row. -By default, cells in a data section are delimited by a tab. -This behaviour may be changed with the +The data section follows the last +.Sx Layout +line. +Each data line consists of one or more data cells, delimited by .Cm tab -option. -If -.Cm _ +characters. +.Pp +If a data cells contains only the single character +.Ql _ or -.Cm = -is specified, a single or double line, respectively, is drawn across the -data field. -If -.Cm \e- +.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 +.Ql \e_ +or +.Ql \e= , +a single or double horizontal line is drawn inside the cell, +not joining its neighbours. +If a data line contains nothing but the single character +.Ql _ or -.Cm \e= -is specified, a line is drawn within the data field (i.e. terminating -within the cell and not draw to the border). -If the last cell of a line is -.Cm T{ , -all subsequent lines are included as part of the cell until -.Cm T} -is specified as its own data cell. -It may then be followed by a tab -.Pq or as designated by Cm tab -or an end-of-line to terminate the row. +.Ql = , +a horizontal line across the whole table is inserted +without consuming a layout row. +.Pp +In place of any data cell, a text block can be used. +It starts with +.Ic \&T{ +at the end of a physical input line. +Input line breaks inside the text block +neither end the text block nor its data cell. +It only ends if +.Ic \&T} +occurs at the beginning of a physical input line and is followed +by an end-of-cell indicator. +If the +.Ic \&T} +is followed by the end of the physical input line, the text block, +the data cell, and the data line ends at this point. +If the +.Ic \&T} +is followed by the +.Cm tab +character, only the text block and the data cell end, +but the data line continues with the data cell following the +.Cm tab +character. +If +.Ic \&T} +is followed by any other character, it does not end the text block, +which instead continues to the following physical input line. +.Sh EXAMPLES +String justification and font selection: +.Bd -literal -offset indent +\&.TS +rb c lb +r ci l. +r center l +ri ce le +right c left +\&.TE +.Ed +.Bd -filled -offset indent +.TS +rb c lb +r ci l. +r center l +ri ce le +right c left +.TE +.Ed +.Pp +Some ports in +.Ox 6.1 +to show number alignment and line drawing: +.Bd -literal -offset indent +\&.TS +box tab(:); +r| l +r n. +software:version +_ +AFL:2.39b +Mutt:1.8.0 +Ruby:1.8.7.374 +TeX Live:2015 +\&.TE +.Ed +.Bd -filled -offset indent +.TS +box tab(:); +r| l +r n. +software:version +_ +AFL:2.39b +Mutt:1.8.0 +Ruby:1.8.7.374 +TeX Live:2015 +.TE +.Ed +.sp 2v +Spans and skipping width calculations: +.Bd -literal -offset indent +\&.TS +box tab(:); +lz s | rt +lt| cb| ^ +^ | rz s. +left:r +l:center: +:right +\&.TE +.Ed +.Bd -filled -offset indent +.TS +box tab(:); +lz s | rt +lt| cb| ^ +^ | rz s. +left:r +l:center: +:right +.TE +.Ed +.sp 2v +Text blocks, specifying spacings and specifying and equalizing +column widths, putting lines into individual cells, and overriding +.Cm allbox : +.Bd -literal -offset indent +\&.TS +allbox tab(:); +le le||7 lw10. +The fourth line:_:line 1 +of this column:=:line 2 +determines:\_:line 3 +the column width.:T{ +This text is too wide to fit into a column of width 17. +T}:line 4 +T{ +No break here. +T}::line 5 +\&.TE +.Ed +.Bd -filled -offset indent +.TS +allbox tab(:); +le le||7 lw10. +The fourth line:_:line 1 +of this column:=:line 2 +determines:\_:line 3 +the column width.:T{ +This text is too wide to fit into a column of width 17. +T}:line 4 +T{ +No break here. +T}::line 5 +.TE +.Ed +.sp 2v +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. .Sh COMPATIBILITY The .Xr mandoc 1 @@ -364,4 +427,6 @@ utility. This .Nm reference was written by -.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . +.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv +and +.An Ingo Schwarze Aq Mt schwarze@openbsd.org . diff --git a/contrib/mdocml/tbl.c b/contrib/mdocml/tbl.c index ed6fbd8..3fb8e52 100644 --- a/contrib/mdocml/tbl.c +++ b/contrib/mdocml/tbl.c @@ -1,4 +1,4 @@ -/* $Id: tbl.c,v 1.40 2015/10/06 18:32:20 schwarze Exp $ */ +/* $Id: tbl.c,v 1.42 2017/07/08 17:52:50 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@openbsd.org> @@ -31,7 +31,7 @@ #include "libroff.h" -enum rofferr +void tbl_read(struct tbl_node *tbl, int ln, const char *p, int pos) { const char *cp; @@ -66,7 +66,7 @@ tbl_read(struct tbl_node *tbl, int ln, const char *p, int pos) if (*cp == ';') { tbl_option(tbl, ln, p, &pos); if (p[pos] == '\0') - return ROFF_IGN; + return; } } @@ -75,15 +75,14 @@ tbl_read(struct tbl_node *tbl, int ln, const char *p, int pos) switch (tbl->part) { case TBL_PART_LAYOUT: tbl_layout(tbl, ln, p, pos); - return ROFF_IGN; + break; case TBL_PART_CDATA: - return tbl_cdata(tbl, ln, p, pos) ? ROFF_TBL : ROFF_IGN; + tbl_cdata(tbl, ln, p, pos); + break; default: + tbl_data(tbl, ln, p, pos); break; } - - tbl_data(tbl, ln, p, pos); - return ROFF_TBL; } struct tbl_node * @@ -114,6 +113,7 @@ tbl_free(struct tbl_node *tbl) while (rp->first != NULL) { cp = rp->first; rp->first = cp->next; + free(cp->wstr); free(cp); } free(rp); @@ -159,14 +159,10 @@ tbl_span(struct tbl_node *tbl) } int -tbl_end(struct tbl_node **tblp) +tbl_end(struct tbl_node *tbl) { - struct tbl_node *tbl; struct tbl_span *sp; - tbl = *tblp; - *tblp = NULL; - if (tbl->part == TBL_PART_CDATA) mandoc_msg(MANDOCERR_TBLDATA_BLK, tbl->parse, tbl->line, tbl->pos, "TE"); diff --git a/contrib/mdocml/tbl_data.c b/contrib/mdocml/tbl_data.c index 40b756a0..ae1906e 100644 --- a/contrib/mdocml/tbl_data.c +++ b/contrib/mdocml/tbl_data.c @@ -1,7 +1,7 @@ -/* $Id: tbl_data.c,v 1.41 2015/10/06 18:32:20 schwarze Exp $ */ +/* $Id: tbl_data.c,v 1.45 2017/07/08 17:52:50 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,17 +51,26 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, cp = cp->next; /* - * Stop processing when we reach the end of the available layout - * cells. This means that we have extra input. + * If the current layout row is out of cells, allocate + * a new cell if another row of the table has at least + * this number of columns, or discard the input if we + * are beyond the last column of the table as a whole. */ if (cp == NULL) { - mandoc_msg(MANDOCERR_TBLDATA_EXTRA, tbl->parse, - ln, *pos, p + *pos); - /* Skip to the end... */ - while (p[*pos]) - (*pos)++; - return; + if (dp->layout->last->col + 1 < dp->opts->cols) { + cp = mandoc_calloc(1, sizeof(*cp)); + cp->pos = TBL_CELL_LEFT; + dp->layout->last->next = cp; + 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]) + (*pos)++; + return; + } } dat = mandoc_calloc(1, sizeof(*dat)); @@ -119,7 +128,7 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, tbl->parse, ln, sv, dat->string); } -int +void tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) { struct tbl_dat *dat; @@ -134,16 +143,17 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) pos++; while (p[pos] != '\0') getdata(tbl, tbl->last_span, ln, p, &pos); - return 1; + return; } else if (p[pos] == '\0') { tbl->part = TBL_PART_DATA; - return 1; + return; } /* Fallthrough: T} is part of a word. */ } dat->pos = TBL_DATA_DATA; + dat->block = 1; if (dat->string != NULL) { sz = strlen(p + pos) + strlen(dat->string) + 2; @@ -156,8 +166,6 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) if (dat->layout->pos == TBL_CELL_DOWN) mandoc_msg(MANDOCERR_TBLDATA_SPAN, tbl->parse, ln, pos, dat->string); - - return 0; } static struct tbl_span * @@ -184,58 +192,50 @@ newspan(struct tbl_node *tbl, int line, struct tbl_row *rp) void tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) { - struct tbl_span *dp; struct tbl_row *rp; + struct tbl_cell *cp; + struct tbl_span *sp; - /* - * Choose a layout row: take the one following the last parsed - * span's. If that doesn't exist, use the last parsed span's. - * If there's no last parsed span, use the first row. Lastly, - * if the last span was a horizontal line, use the same layout - * (it doesn't "consume" the layout). - */ - - if (tbl->last_span != NULL) { - if (tbl->last_span->pos == TBL_SPAN_DATA) { - for (rp = tbl->last_span->layout->next; - rp != NULL && rp->first != NULL; - rp = rp->next) { - switch (rp->first->pos) { - case TBL_CELL_HORIZ: - dp = newspan(tbl, ln, rp); - dp->pos = TBL_SPAN_HORIZ; - continue; - case TBL_CELL_DHORIZ: - dp = newspan(tbl, ln, rp); - dp->pos = TBL_SPAN_DHORIZ; - continue; - default: - break; - } - break; - } - } else - rp = tbl->last_span->layout; - - if (rp == NULL) - rp = tbl->last_span->layout; - } else - rp = tbl->first_row; - - assert(rp); + rp = (sp = tbl->last_span) == NULL ? tbl->first_row : + sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ? + sp->layout->next : sp->layout; - dp = newspan(tbl, ln, rp); + assert(rp != NULL); if ( ! strcmp(p, "_")) { - dp->pos = TBL_SPAN_HORIZ; + sp = newspan(tbl, ln, rp); + sp->pos = TBL_SPAN_HORIZ; return; } else if ( ! strcmp(p, "=")) { - dp->pos = TBL_SPAN_DHORIZ; + sp = newspan(tbl, ln, rp); + sp->pos = TBL_SPAN_DHORIZ; return; } - dp->pos = TBL_SPAN_DATA; + /* + * If the layout row contains nothing but horizontal lines, + * allocate an empty span for it and assign the current span + * to the next layout row accepting data. + */ + + while (rp->next != NULL) { + if (rp->last->col + 1 < tbl->opts.cols) + break; + for (cp = rp->first; cp != NULL; cp = cp->next) + if (cp->pos != TBL_CELL_HORIZ && + cp->pos != TBL_CELL_DHORIZ) + break; + if (cp != NULL) + break; + sp = newspan(tbl, ln, rp); + sp->pos = TBL_SPAN_DATA; + rp = rp->next; + } + + /* Process a real data row. */ + sp = newspan(tbl, ln, rp); + sp->pos = TBL_SPAN_DATA; while (p[pos] != '\0') - getdata(tbl, dp, ln, p, &pos); + getdata(tbl, sp, ln, p, &pos); } diff --git a/contrib/mdocml/tbl_html.c b/contrib/mdocml/tbl_html.c index 962d900..06fae5e 100644 --- a/contrib/mdocml/tbl_html.c +++ b/contrib/mdocml/tbl_html.c @@ -1,4 +1,4 @@ -/* $Id: tbl_html.c,v 1.19 2017/01/17 01:47:51 schwarze Exp $ */ +/* $Id: tbl_html.c,v 1.22 2017/06/12 20:14:18 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -31,40 +31,68 @@ static void html_tblopen(struct html *, const struct tbl_span *); static size_t html_tbl_len(size_t, void *); static size_t html_tbl_strlen(const char *, void *); +static size_t html_tbl_sulen(const struct roffsu *, void *); static size_t html_tbl_len(size_t sz, void *arg) { - return sz; } static size_t html_tbl_strlen(const char *p, void *arg) { - return strlen(p); } +static size_t +html_tbl_sulen(const struct roffsu *su, void *arg) +{ + switch (su->unit) { + case SCALE_FS: /* 2^16 basic units */ + return su->scale * 65536.0 / 24.0; + case SCALE_IN: /* 10 characters per inch */ + return su->scale * 10.0; + case SCALE_CM: /* 2.54 cm per inch */ + return su->scale * 10.0 / 2.54; + case SCALE_PC: /* 6 pica per inch */ + case SCALE_VS: + return su->scale * 10.0 / 6.0; + case SCALE_EN: + case SCALE_EM: + return su->scale; + case SCALE_PT: /* 12 points per pica */ + return su->scale * 10.0 / 6.0 / 12.0; + case SCALE_BU: /* 24 basic units per character */ + return su->scale / 24.0; + case SCALE_MM: /* 1/1000 inch */ + return su->scale / 100.0; + default: + abort(); + } +} + static void html_tblopen(struct html *h, const struct tbl_span *sp) { + struct tag *t; int ic; if (h->tbl.cols == NULL) { h->tbl.len = html_tbl_len; h->tbl.slen = html_tbl_strlen; - tblcalc(&h->tbl, sp, 0); + h->tbl.sulen = html_tbl_sulen; + tblcalc(&h->tbl, sp, 0, 0); } assert(NULL == h->tblt); h->tblt = print_otag(h, TAG_TABLE, "c", "tbl"); + t = print_otag(h, TAG_COLGROUP, ""); for (ic = 0; ic < sp->opts->cols; ic++) print_otag(h, TAG_COL, "shw", h->tbl.cols[ic].width); - - print_otag(h, TAG_TBODY, ""); + print_tagq(h, t); } void diff --git a/contrib/mdocml/tbl_layout.c b/contrib/mdocml/tbl_layout.c index c0eafbd..42fc0e8 100644 --- a/contrib/mdocml/tbl_layout.c +++ b/contrib/mdocml/tbl_layout.c @@ -1,7 +1,7 @@ -/* $Id: tbl_layout.c,v 1.41 2015/10/12 00:08:16 schwarze Exp $ */ +/* $Id: tbl_layout.c,v 1.44 2017/06/27 18:25:02 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012, 2014, 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 @@ -20,6 +20,7 @@ #include <sys/types.h> #include <ctype.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -62,6 +63,7 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp, int ln, const char *p, int *pos) { char *endptr; + size_t sz; mod: while (p[*pos] == ' ' || p[*pos] == '\t') @@ -127,7 +129,22 @@ mod: case 'u': cp->flags |= TBL_CELL_UP; goto mod; - case 'w': /* XXX for now, ignore minimal column width */ + case 'w': + sz = 0; + if (p[*pos] == '(') { + (*pos)++; + while (p[*pos + sz] != '\0' && p[*pos + sz] != ')') + sz++; + } else + while (isdigit((unsigned char)p[*pos + sz])) + sz++; + if (sz) { + free(cp->wstr); + cp->wstr = mandoc_strndup(p + *pos, sz); + *pos += sz; + if (p[*pos] == ')') + (*pos)++; + } goto mod; case 'x': cp->flags |= TBL_CELL_WMAX; @@ -282,6 +299,8 @@ tbl_layout(struct tbl_node *tbl, int ln, const char *p, int pos) tbl->parse, ln, pos, NULL); cell_alloc(tbl, tbl->first_row, TBL_CELL_LEFT); + if (tbl->opts.lvert < tbl->first_row->vert) + tbl->opts.lvert = tbl->first_row->vert; return; } @@ -339,6 +358,7 @@ cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) struct tbl_cell *p, *pp; p = mandoc_calloc(1, sizeof(*p)); + p->spacing = SIZE_MAX; p->pos = pos; if ((pp = rp->last) != NULL) { diff --git a/contrib/mdocml/tbl_term.c b/contrib/mdocml/tbl_term.c index eceaa4b..fd03b81 100644 --- a/contrib/mdocml/tbl_term.c +++ b/contrib/mdocml/tbl_term.c @@ -1,7 +1,7 @@ -/* $Id: tbl_term.c,v 1.43 2015/10/12 00:08:16 schwarze Exp $ */ +/* $Id: tbl_term.c,v 1.56 2017/07/08 13:43:15 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011, 2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2012,2014,2015,2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,10 +28,15 @@ #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_literal(struct termp *, const struct tbl_dat *, @@ -44,37 +49,36 @@ static void tbl_word(struct termp *, const struct tbl_dat *); static size_t -term_tbl_strlen(const char *p, void *arg) +term_tbl_sulen(const struct roffsu *su, void *arg) { + return term_hen((const struct termp *)arg, su); +} +static size_t +term_tbl_strlen(const char *p, void *arg) +{ return term_strlen((const struct termp *)arg, p); } static size_t 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; + const struct tbl_cell *cp, *cpn, *cpp; const struct tbl_dat *dp; static size_t offset; - size_t rmargin, maxrmargin, tsz; - int ic, horiz, spans, vert; - - rmargin = tp->rmargin; - maxrmargin = tp->maxrmargin; - - tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN; + size_t coloff, tsz; + int ic, horiz, spans, vert, more; + char fc; /* Inhibit printing of spaces: we do padding ourselves. */ - tp->flags |= TERMP_NONOSPACE; - tp->flags |= TERMP_NOSPACE; + tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE; /* * The first time we're invoked for a given table block, @@ -84,165 +88,413 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) if (tp->tbl.cols == NULL) { tp->tbl.len = term_tbl_len; tp->tbl.slen = term_tbl_strlen; + tp->tbl.sulen = term_tbl_sulen; tp->tbl.arg = tp; - tblcalc(&tp->tbl, sp, rmargin - tp->offset); + tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin); + + /* Tables leak .ta settings to subsequent text. */ + + term_tab_set(tp, NULL); + coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || + sp->opts->lvert; + for (ic = 0; ic < sp->opts->cols; ic++) { + coloff += tp->tbl.cols[ic].width; + term_tab_iset(coloff); + coloff += tp->tbl.cols[ic].spacing; + } /* Center the table as a whole. */ - offset = tp->offset; + offset = tp->tcol->offset; if (sp->opts->opts & TBL_OPT_CENTRE) { tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ? 2 : !!sp->opts->lvert + !!sp->opts->rvert; - for (ic = 0; ic < sp->opts->cols; ic++) - tsz += tp->tbl.cols[ic].width + 3; - tsz -= 3; - if (offset + tsz > rmargin) + for (ic = 0; ic + 1 < sp->opts->cols; ic++) + tsz += tp->tbl.cols[ic].width + + tp->tbl.cols[ic].spacing; + if (sp->opts->cols) + tsz += tp->tbl.cols[sp->opts->cols - 1].width; + if (offset + tsz > tp->tcol->rmargin) tsz -= 1; - tp->offset = (offset + rmargin > tsz) ? - (offset + rmargin - tsz) / 2 : 0; + tp->tcol->offset = offset + tp->tcol->rmargin > tsz ? + (offset + tp->tcol->rmargin - tsz) / 2 : 0; } /* Horizontal frame at the start of boxed tables. */ if (sp->opts->opts & TBL_OPT_DBOX) - tbl_hrule(tp, sp, 2); + tbl_hrule(tp, sp, 3); if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) - tbl_hrule(tp, sp, 1); + tbl_hrule(tp, sp, 2); } - /* Vertical frame at the start of each row. */ + /* Set up the columns. */ - horiz = sp->pos == TBL_SPAN_HORIZ || sp->pos == TBL_SPAN_DHORIZ; + tp->flags |= TERMP_MULTICOL; + horiz = 0; + switch (sp->pos) { + case TBL_SPAN_HORIZ: + case TBL_SPAN_DHORIZ: + horiz = 1; + term_setcol(tp, 1); + break; + case TBL_SPAN_DATA: + term_setcol(tp, sp->opts->cols + 2); + coloff = tp->tcol->offset; - if (sp->layout->vert || - (sp->prev != NULL && sp->prev->layout->vert) || - sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)) - term_word(tp, horiz ? "+" : "|"); - else if (sp->opts->lvert) - tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1); + /* Set up a column for a left vertical frame. */ - /* - * Now print the actual data itself depending on the span type. - * Match data cells to column numbers. - */ + if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || + sp->opts->lvert) + coloff++; + tp->tcol->rmargin = coloff; + + /* Set up the data columns. */ - if (sp->pos == TBL_SPAN_DATA) { - cp = sp->layout->first; dp = sp->first; spans = 0; for (ic = 0; ic < sp->opts->cols; ic++) { + if (spans == 0) { + tp->tcol++; + tp->tcol->offset = coloff; + } + coloff += tp->tbl.cols[ic].width; + tp->tcol->rmargin = coloff; + if (ic + 1 < sp->opts->cols) + coloff += tp->tbl.cols[ic].spacing; + if (spans) { + spans--; + continue; + } + if (dp == NULL) + continue; + spans = dp->spans; + if (ic || sp->layout->first->pos != TBL_CELL_SPAN) + dp = dp->next; + } - /* - * Remeber whether we need a vertical bar - * after this cell. - */ + /* Set up a column for a right vertical frame. */ - vert = cp == NULL ? 0 : cp->vert; + tp->tcol++; + tp->tcol->offset = coloff + 1; + tp->tcol->rmargin = tp->maxrmargin; - /* - * Print the data and advance to the next cell. - */ + /* Spans may have reduced the number of columns. */ - if (spans == 0) { - tbl_data(tp, sp->opts, dp, tp->tbl.cols + ic); + tp->lasttcol = tp->tcol - tp->tcols; + + /* Fill the buffers for all data columns. */ + + tp->tcol = tp->tcols; + cp = cpn = sp->layout->first; + dp = sp->first; + spans = 0; + for (ic = 0; ic < sp->opts->cols; ic++) { + if (cpn != NULL) { + cp = cpn; + cpn = cpn->next; + } + if (spans) { + spans--; + continue; + } + tp->tcol++; + tp->col = 0; + tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic); + if (dp == NULL) + continue; + spans = dp->spans; + if (cp->pos != TBL_CELL_SPAN) + dp = dp->next; + } + break; + } + + do { + /* 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') { + (*tp->advance)(tp, tp->tcols->offset); + (*tp->letter)(tp, fc); + tp->viscol = tp->tcol->offset + 1; + } + + /* Print the data cells. */ + + more = 0; + if (horiz) { + tbl_hrule(tp, sp, 0); + term_flushln(tp); + } 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; + for (ic = 0; ic < sp->opts->cols; ic++) { + + /* + * Figure out whether to print a + * vertical line after this cell + * and advance to next layout cell. + */ + + if (cp != NULL) { + vert = cp->vert; + switch (cp->pos) { + case TBL_CELL_HORIZ: + fc = '-'; + break; + case TBL_CELL_DHORIZ: + fc = '='; + break; + default: + fc = ' '; + break; + } + } else { + vert = 0; + fc = ' '; + } + if (cpp != NULL) { + if (vert == 0 && + cp != NULL && + ((IS_HORIZ(cp) && + !IS_HORIZ(cpp)) || + (cp->next != NULL && + cpp->next != NULL && + IS_HORIZ(cp->next) && + !IS_HORIZ(cpp->next)))) + vert = cpp->vert; + cpp = cpp->next; + } + if (vert == 0 && + sp->opts->opts & TBL_OPT_ALLBOX) + vert = 1; + if (cpn != NULL) { + if (vert == 0) + vert = cpn->vert; + cpn = cpn->next; + } + if (cp != NULL) + cp = cp->next; + + /* + * Skip later cells in a span, + * figure out whether to start a span, + * and advance to next data cell. + */ + + if (spans) { + spans--; + continue; + } if (dp != NULL) { spans = dp->spans; - dp = dp->next; + if (ic || sp->layout->first->pos + != TBL_CELL_SPAN) + dp = dp->next; } - } else - spans--; - if (cp != NULL) - cp = cp->next; - /* - * Separate columns, except in the middle - * of spans and after the last cell. - */ + /* + * Print one line of text in the cell + * and remember whether there is more. + */ + + tp->tcol++; + if (tp->tcol->col < tp->tcol->lastcol) + term_flushln(tp); + if (tp->tcol->col < tp->tcol->lastcol) + more = 1; + + /* + * Vertical frames between data cells, + * but not after the last column. + */ + + if (fc == ' ' && ((vert == 0 && + (cp == NULL || !IS_HORIZ(cp))) || + tp->tcol + 1 == tp->tcols + tp->lasttcol)) + continue; + + if (tp->viscol < tp->tcol->rmargin) { + (*tp->advance)(tp, tp->tcol->rmargin + - tp->viscol); + tp->viscol = tp->tcol->rmargin; + } + while (tp->viscol < tp->tcol->rmargin + + tp->tbl.cols[ic].spacing / 2) { + (*tp->letter)(tp, fc); + tp->viscol++; + } - if (ic + 1 == sp->opts->cols || spans) - continue; + 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++; + } - tbl_char(tp, ASCII_NBRSP, 1); - if (vert > 0) - tbl_char(tp, '|', vert); - if (vert < 2) - tbl_char(tp, ASCII_NBRSP, 2 - vert); + 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++; + } + } } - } else if (horiz) - tbl_hrule(tp, sp, 0); - - /* Vertical frame at the end of each row. */ - if (sp->layout->last->vert || - (sp->prev != NULL && sp->prev->layout->last->vert) || - (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX))) - term_word(tp, horiz ? "+" : " |"); - else if (sp->opts->rvert) - tbl_char(tp, horiz ? '-' : ASCII_NBRSP, 1); - term_flushln(tp); + /* 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') { + 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); + } + (*tp->letter)(tp, fc); + } + (*tp->endline)(tp); + tp->viscol = 0; + } while (more); /* - * If we're the last row, clean up after ourselves: clear the - * existing table configuration and set it to NULL. + * Clean up after this row. If it is the last line + * of the table, print the box line and clean up + * column data; otherwise, print the allbox line. */ + term_setcol(tp, 1); + tp->flags &= ~TERMP_MULTICOL; + tp->tcol->rmargin = tp->maxrmargin; if (sp->next == NULL) { if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) { - tbl_hrule(tp, sp, 1); + tbl_hrule(tp, sp, 2); tp->skipvsp = 1; } if (sp->opts->opts & TBL_OPT_DBOX) { - tbl_hrule(tp, sp, 2); + tbl_hrule(tp, sp, 3); tp->skipvsp = 2; } assert(tp->tbl.cols); free(tp->tbl.cols); tp->tbl.cols = NULL; - tp->offset = offset; - } + 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); tp->flags &= ~TERMP_NONOSPACE; - tp->rmargin = rmargin; - tp->maxrmargin = maxrmargin; } /* * Kinds of horizontal rulers: * 0: inside the table (single or double line with crossings) - * 1: inner frame (single line with crossings and ends) - * 2: outer frame (single line without crossings with ends) + * 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) { - const struct tbl_cell *c1, *c2; + const struct tbl_cell *cp, *cpn, *cpp; + const struct roffcol *col; int vert; char line, cross; - line = (kind == 0 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-'; - cross = (kind < 2) ? '+' : '-'; + line = (kind < 2 && TBL_SPAN_DHORIZ == sp->pos) ? '=' : '-'; + cross = (kind < 3) ? '+' : '-'; if (kind) term_word(tp, "+"); - c1 = sp->layout->first; - c2 = sp->prev == NULL ? NULL : sp->prev->layout->first; - if (c2 == c1) - c2 = NULL; + 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 (;;) { - tbl_char(tp, line, tp->tbl.cols[c1->col].width + 1); - vert = c1->vert; - if ((c1 = c1->next) == NULL) + col = tp->tbl.cols + cp->col; + tbl_char(tp, line, col->width + col->spacing / 2); + vert = cp->vert; + if ((cp = cp->next) == NULL) break; - if (c2 != NULL) { - if (vert < c2->vert) - vert = c2->vert; - c2 = c2->next; + if (cpp != NULL) { + if (vert < cpp->vert) + vert = cpp->vert; + cpp = cpp->next; } - if (vert) - tbl_char(tp, cross, vert); - if (vert < 2) - tbl_char(tp, line, 2 - vert); + if (cpn != NULL) { + if (vert < cpn->vert) + vert = cpn->vert; + cpn = cpn->next; + } + if (sp->opts->opts & TBL_OPT_ALLBOX && !vert) + vert = 1; + if (col->spacing) + tbl_char(tp, vert ? cross : line, 1); + if (col->spacing > 2) + tbl_char(tp, vert > 1 ? cross : line, 1); + if (col->spacing > 4) + tbl_char(tp, line, (col->spacing - 3) / 2); } if (kind) { term_word(tp, "+"); @@ -252,18 +504,25 @@ tbl_hrule(struct termp *tp, const struct tbl_span *sp, int kind) static void tbl_data(struct termp *tp, const struct tbl_opts *opts, - const struct tbl_dat *dp, - const struct roffcol *col) + const struct tbl_cell *cp, const struct tbl_dat *dp, + const struct roffcol *col) { - - if (dp == NULL) { - tbl_char(tp, ASCII_NBRSP, col->width); + switch (cp->pos) { + case TBL_CELL_HORIZ: + tbl_char(tp, '-', col->width); return; + case TBL_CELL_DHORIZ: + tbl_char(tp, '=', col->width); + return; + default: + break; } + if (dp == NULL) + return; + switch (dp->pos) { case TBL_DATA_NONE: - tbl_char(tp, ASCII_NBRSP, col->width); return; case TBL_DATA_HORIZ: case TBL_DATA_NHORIZ: @@ -277,13 +536,7 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts, break; } - switch (dp->layout->pos) { - case TBL_CELL_HORIZ: - tbl_char(tp, '-', col->width); - break; - case TBL_CELL_DHORIZ: - tbl_char(tp, '=', col->width); - break; + switch (cp->pos) { case TBL_CELL_LONG: case TBL_CELL_CENTRE: case TBL_CELL_LEFT: @@ -294,7 +547,7 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts, tbl_number(tp, opts, dp, col); break; case TBL_CELL_DOWN: - tbl_char(tp, ASCII_NBRSP, col->width); + case TBL_CELL_SPAN: break; default: abort(); diff --git a/contrib/mdocml/term.c b/contrib/mdocml/term.c index 1217d47..f67fcf9 100644 --- a/contrib/mdocml/term.c +++ b/contrib/mdocml/term.c @@ -1,4 +1,4 @@ -/* $Id: term.c,v 1.259 2017/01/08 18:16:58 schwarze Exp $ */ +/* $Id: term.c,v 1.274 2017/07/28 14:25:48 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org> @@ -32,17 +32,31 @@ #include "main.h" static size_t cond_width(const struct termp *, int, int *); -static void adjbuf(struct termp *p, size_t); +static void adjbuf(struct termp_col *, size_t); 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 *); void -term_free(struct termp *p) +term_setcol(struct termp *p, size_t maxtcol) { + if (maxtcol > p->maxtcol) { + p->tcols = mandoc_recallocarray(p->tcols, + p->maxtcol, maxtcol, sizeof(*p->tcols)); + p->maxtcol = maxtcol; + } + p->lasttcol = maxtcol - 1; + p->tcol = p->tcols; +} - free(p->buf); +void +term_free(struct termp *p) +{ + for (p->tcol = p->tcols; p->tcol < p->tcols + p->maxtcol; p->tcol++) + free(p->tcol->buf); + free(p->tcols); free(p->fontq); free(p); } @@ -84,69 +98,54 @@ term_end(struct termp *p) * 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_DANGLE: Do not break the output line at the right margin, + * - 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_HANG: Like TERMP_DANGLE, and also suppress padding before - * the next chunk if this column is not full. + * - TERMP_NOPAD: Start writing at the current position, + * do not pad with blank characters up to the offset. */ void term_flushln(struct termp *p) { - size_t i; /* current input position in p->buf */ - int ntab; /* number of tabs to prepend */ 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->buf */ + 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 */ - - /* - * First, establish the maximum columns of "visible" content. - * This is usually the difference between the right-margin and - * an indentation, but can be, for tagged lists or columns, a - * small set of values. - * - * The following unsigned-signed subtractions look strange, - * but they are actually correct. If the int p->overstep - * is negative, it gets sign extended. Subtracting that - * very large size_t effectively adds a small number to dv. - */ - dv = p->rmargin > p->offset ? p->rmargin - p->offset : 0; - maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; - - if (p->flags & TERMP_NOBREAK) { - dv = p->maxrmargin > p->offset ? - p->maxrmargin - p->offset : 0; - bp = (int)dv > p->overstep ? - dv - (size_t)p->overstep : 0; - } else - bp = maxvis; - - /* - * Calculate the required amount of padding. - */ - vbl = p->offset + p->overstep > p->viscol ? - p->offset + p->overstep - p->viscol : 0; - + int ntab; /* number of tabs to prepend */ + int breakline; /* after this word */ + + 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; - i = 0; - while (i < p->col) { + if ((p->flags & TERMP_MULTICOL) == 0) + p->tcol->col = 0; + while (p->tcol->col < p->tcol->lastcol) { + /* * Handle literal tab characters: collapse all * subsequent tabs into a single huge set of spaces. */ + ntab = 0; - while (i < p->col && '\t' == p->buf[i]) { - vend = (vis / p->tabwidth + 1) * p->tabwidth; + 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++; - i++; + p->tcol->col++; } /* @@ -156,84 +155,98 @@ term_flushln(struct termp *p) * space is printed according to regular spacing rules). */ - for (j = i, jhy = 0; j < p->col; j++) { - if (' ' == p->buf[j] || '\t' == p->buf[j]) + 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 (8 == p->buf[j]) { + if (p->tcol->buf[j] == '\b') { assert(j); - vend -= (*p->width)(p, p->buf[j - 1]); + 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 && - (ASCII_HYPH == p->buf[j] || - ASCII_BREAK == p->buf[j])) + (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 (ASCII_HYPH == p->buf[j]) - p->buf[j] = '-'; + if (p->tcol->buf[j] == ASCII_HYPH) + p->tcol->buf[j] = '-'; - vend += (*p->width)(p, p->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 && 0 == jhy && vis > 0) { + + if (vend > bp && jhy == 0 && vis > 0 && + (p->flags & TERMP_BRNEVER) == 0) { + if (p->flags & TERMP_MULTICOL) + return; + + endline(p); vend -= vis; - (*p->endline)(p); - p->viscol = 0; - if (TERMP_BRIND & p->flags) { - vbl = p->rmargin; - vend += p->rmargin; - vend -= p->offset; - } else - vbl = p->offset; - /* use pending tabs on the new line */ + /* Use pending tabs on the new line. */ - if (0 < ntab) - vbl += ntab * p->tabwidth; + vbl = 0; + while (ntab--) + vbl = term_tab_next(vbl); - /* - * Remove the p->overstep width. - * Again, if p->overstep is negative, - * sign extension does the right thing. - */ + /* Re-establish indentation. */ - bp += (size_t)p->overstep; - p->overstep = 0; + 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 [remaining] word. */ - for ( ; i < p->col; i++) { - if (vend > bp && jhy > 0 && i > jhy) + /* + * 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 ('\t' == p->buf[i]) + if (p->tcol->buf[p->tcol->col] == '\n') + continue; + if (p->tcol->buf[p->tcol->col] == '\t') break; - if (' ' == p->buf[i]) { - j = i; - while (i < p->col && ' ' == p->buf[i]) - i++; - dv = (i - j) * (*p->width)(p, ' '); + 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 (ASCII_NBRSP == p->buf[i]) { + if (p->tcol->buf[p->tcol->col] == ASCII_NBRSP) { vbl += (*p->width)(p, ' '); continue; } - if (ASCII_BREAK == p->buf[i]) + if (p->tcol->buf[p->tcol->col] == ASCII_BREAK) continue; /* @@ -247,61 +260,83 @@ term_flushln(struct termp *p) vbl = 0; } - (*p->letter)(p, p->buf[i]); - if (8 == p->buf[i]) - p->viscol -= (*p->width)(p, p->buf[i-1]); + (*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->buf[i]); + 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; + + /* Re-establish indentation. */ + + vbl = p->tcol->offset; + maxvis = p->tcol->rmargin > vbl ? + p->tcol->rmargin - vbl : 0; + bp = !(p->flags & TERMP_NOBREAK) ? maxvis : + p->maxrmargin > vbl ? p->maxrmargin - vbl : 0; } /* * If there was trailing white space, it was not printed; * so reset the cursor position accordingly. */ + if (vis > vbl) vis -= vbl; else vis = 0; - p->col = 0; - p->overstep = 0; - p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE); + p->col = p->tcol->col = p->tcol->lastcol = 0; + p->minbl = p->trailspace; + p->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE | TERMP_NOPAD); - if ( ! (TERMP_NOBREAK & p->flags)) { - p->viscol = 0; - (*p->endline)(p); - return; - } - - if (TERMP_HANG & p->flags) { - p->overstep += (int)(p->offset + vis - p->rmargin + - p->trailspace * (*p->width)(p, ' ')); - - /* - * If we have overstepped the margin, temporarily move - * it to the right and flag the rest of the line to be - * shorter. - * If there is a request to keep the columns together, - * allow negative overstep when the column is not full. - */ - if (p->trailspace && p->overstep < 0) - p->overstep = 0; - return; - - } else if (TERMP_DANGLE & p->flags) + if (p->flags & TERMP_MULTICOL) return; /* Trailing whitespace is significant in some columns. */ + if (vis && vbl && (TERMP_BRTRSP & p->flags)) vis += vbl; /* If the column was overrun, break the line. */ - if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) { - (*p->endline)(p); - p->viscol = 0; + if ((p->flags & TERMP_NOBREAK) == 0 || + ((p->flags & TERMP_HANG) == 0 && + vis + p->trailspace * (*p->width)(p, ' ') > maxvis)) + endline(p); +} + +static void +endline(struct termp *p) +{ + if ((p->flags & (TERMP_NEWMC | TERMP_ENDMC)) == TERMP_ENDMC) { + p->mc = NULL; + p->flags &= ~TERMP_ENDMC; + } + if (p->mc != NULL) { + if (p->viscol && p->maxrmargin >= p->viscol) + (*p->advance)(p, p->maxrmargin - p->viscol + 1); + p->flags |= TERMP_NOBUF | TERMP_NOSPACE; + term_word(p, p->mc); + p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC); } + p->viscol = 0; + p->minbl = 0; + (*p->endline)(p); } /* @@ -314,7 +349,7 @@ term_newln(struct termp *p) { p->flags |= TERMP_NOSPACE; - if (p->col || p->viscol) + if (p->tcol->lastcol || p->viscol) term_flushln(p); } @@ -330,6 +365,7 @@ term_vspace(struct termp *p) term_newln(p); p->viscol = 0; + p->minbl = 0; if (0 < p->skipvsp) p->skipvsp--; else @@ -397,30 +433,31 @@ term_fontpop(struct termp *p) void term_word(struct termp *p, const char *word) { + struct roffsu su; const char nbrsp[2] = { ASCII_NBRSP, 0 }; const char *seq, *cp; int sz, uc; - size_t ssz; + size_t csz, lsz, ssz; enum mandoc_esc esc; - if ( ! (TERMP_NOSPACE & p->flags)) { - if ( ! (TERMP_KEEP & p->flags)) { - bufferc(p, ' '); - if (TERMP_SENTENCE & p->flags) + if ((p->flags & TERMP_NOBUF) == 0) { + if ((p->flags & TERMP_NOSPACE) == 0) { + if ((p->flags & TERMP_KEEP) == 0) { bufferc(p, ' '); - } else - bufferc(p, ASCII_NBRSP); + if (p->flags & TERMP_SENTENCE) + bufferc(p, ' '); + } else + bufferc(p, ASCII_NBRSP); + } + if (p->flags & TERMP_PREKEEP) + p->flags |= TERMP_KEEP; + if (p->flags & TERMP_NONOSPACE) + p->flags |= TERMP_NOSPACE; + else + p->flags &= ~TERMP_NOSPACE; + p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); + p->skipvsp = 0; } - if (TERMP_PREKEEP & p->flags) - p->flags |= TERMP_KEEP; - - if ( ! (p->flags & TERMP_NONOSPACE)) - p->flags &= ~TERMP_NOSPACE; - else - p->flags |= TERMP_NOSPACE; - - p->flags &= ~(TERMP_SENTENCE | TERMP_NONEWLINE); - p->skipvsp = 0; while ('\0' != *word) { if ('\\' != *word) { @@ -479,12 +516,88 @@ term_word(struct termp *p, const char *word) case ESCAPE_FONTPREV: term_fontlast(p); continue; + case ESCAPE_BREAK: + bufferc(p, '\n'); + continue; case ESCAPE_NOSPACE: if (p->flags & TERMP_BACKAFTER) p->flags &= ~TERMP_BACKAFTER; else if (*word == '\0') p->flags |= (TERMP_NOSPACE | TERMP_NONEWLINE); continue; + case ESCAPE_HORIZ: + if (*seq == '|') { + seq++; + uc = -p->col; + } else + uc = 0; + if (a2roffsu(seq, &su, SCALE_EM) == NULL) + continue; + uc += term_hen(p, &su); + if (uc > 0) + while (uc-- > 0) + bufferc(p, ASCII_NBRSP); + else if (p->col > (size_t)(-uc)) + p->col += uc; + else { + uc += p->col; + p->col = 0; + if (p->tcol->offset > (size_t)(-uc)) { + p->ti += uc; + p->tcol->offset += uc; + } else { + p->ti -= p->tcol->offset; + p->tcol->offset = 0; + } + } + continue; + case ESCAPE_HLINE: + if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL) + continue; + uc = term_hen(p, &su); + if (uc <= 0) { + if (p->tcol->rmargin <= p->tcol->offset) + continue; + lsz = p->tcol->rmargin - p->tcol->offset; + } else + lsz = uc; + if (*cp == seq[-1]) + uc = -1; + else if (*cp == '\\') { + seq = cp + 1; + esc = mandoc_escape(&seq, &cp, &sz); + switch (esc) { + case ESCAPE_UNICODE: + uc = mchars_num2uc(cp + 1, sz - 1); + break; + case ESCAPE_NUMBERED: + uc = mchars_num2char(cp, sz); + break; + case ESCAPE_SPECIAL: + uc = mchars_spec2cp(cp, sz); + break; + default: + uc = -1; + break; + } + } else + uc = *cp; + if (uc < 0x20 || (uc > 0x7E && uc < 0xA0)) + uc = '_'; + if (p->enc == TERMENC_ASCII) { + cp = ascii_uc2str(uc); + csz = term_strlen(p, cp); + ssz = strlen(cp); + } else + csz = (*p->width)(p, uc); + while (lsz >= csz) { + if (p->enc == TERMENC_ASCII) + encode(p, cp, ssz); + else + encode1(p, uc); + lsz -= csz; + } + continue; case ESCAPE_SKIPCHAR: p->flags |= TERMP_BACKAFTER; continue; @@ -504,10 +617,12 @@ term_word(struct termp *p, const char *word) } } /* Trim trailing backspace/blank pair. */ - if (p->col > 2 && - (p->buf[p->col - 1] == ' ' || - p->buf[p->col - 1] == '\t')) - p->col -= 2; + if (p->tcol->lastcol > 2 && + (p->tcol->buf[p->tcol->lastcol - 1] == ' ' || + p->tcol->buf[p->tcol->lastcol - 1] == '\t')) + p->tcol->lastcol -= 2; + if (p->col > p->tcol->lastcol) + p->col = p->tcol->lastcol; continue; default: continue; @@ -532,25 +647,28 @@ term_word(struct termp *p, const char *word) } static void -adjbuf(struct termp *p, size_t sz) +adjbuf(struct termp_col *c, size_t sz) { - - if (0 == p->maxcols) - p->maxcols = 1024; - while (sz >= p->maxcols) - p->maxcols <<= 2; - - p->buf = mandoc_reallocarray(p->buf, p->maxcols, sizeof(int)); + if (c->maxcols == 0) + c->maxcols = 1024; + while (c->maxcols <= sz) + c->maxcols <<= 2; + c->buf = mandoc_reallocarray(c->buf, c->maxcols, sizeof(*c->buf)); } static void bufferc(struct termp *p, char c) { - - if (p->col + 1 >= p->maxcols) - adjbuf(p, p->col + 1); - - p->buf[p->col++] = c; + if (p->flags & TERMP_NOBUF) { + (*p->letter)(p, c); + return; + } + if (p->col + 1 >= p->tcol->maxcols) + adjbuf(p->tcol, p->col + 1); + if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) + p->tcol->buf[p->col] = c; + if (p->tcol->lastcol < ++p->col) + p->tcol->lastcol = p->col; } /* @@ -563,31 +681,40 @@ encode1(struct termp *p, int c) { enum termfont f; - if (p->col + 7 >= p->maxcols) - adjbuf(p, p->col + 7); + if (p->flags & TERMP_NOBUF) { + (*p->letter)(p, c); + return; + } + + if (p->col + 7 >= p->tcol->maxcols) + adjbuf(p->tcol, p->col + 7); f = (c == ASCII_HYPH || c > 127 || isgraph(c)) ? p->fontq[p->fonti] : TERMFONT_NONE; if (p->flags & TERMP_BACKBEFORE) { - if (p->buf[p->col - 1] == ' ' || p->buf[p->col - 1] == '\t') + if (p->tcol->buf[p->col - 1] == ' ' || + p->tcol->buf[p->col - 1] == '\t') p->col--; else - p->buf[p->col++] = 8; + p->tcol->buf[p->col++] = '\b'; p->flags &= ~TERMP_BACKBEFORE; } - if (TERMFONT_UNDER == f || TERMFONT_BI == f) { - p->buf[p->col++] = '_'; - p->buf[p->col++] = 8; + if (f == TERMFONT_UNDER || f == TERMFONT_BI) { + p->tcol->buf[p->col++] = '_'; + p->tcol->buf[p->col++] = '\b'; } - if (TERMFONT_BOLD == f || TERMFONT_BI == f) { - if (ASCII_HYPH == c) - p->buf[p->col++] = '-'; + if (f == TERMFONT_BOLD || f == TERMFONT_BI) { + if (c == ASCII_HYPH) + p->tcol->buf[p->col++] = '-'; else - p->buf[p->col++] = c; - p->buf[p->col++] = 8; + p->tcol->buf[p->col++] = c; + p->tcol->buf[p->col++] = '\b'; } - p->buf[p->col++] = c; + if (p->tcol->lastcol <= p->col || (c != ' ' && c != ASCII_NBRSP)) + p->tcol->buf[p->col] = c; + if (p->tcol->lastcol < ++p->col) + p->tcol->lastcol = p->col; if (p->flags & TERMP_BACKAFTER) { p->flags |= TERMP_BACKBEFORE; p->flags &= ~TERMP_BACKAFTER; @@ -599,15 +726,24 @@ encode(struct termp *p, const char *word, size_t sz) { size_t i; - if (p->col + 2 + (sz * 5) >= p->maxcols) - adjbuf(p, p->col + 2 + (sz * 5)); + if (p->flags & TERMP_NOBUF) { + for (i = 0; i < sz; i++) + (*p->letter)(p, word[i]); + return; + } + + if (p->col + 2 + (sz * 5) >= p->tcol->maxcols) + adjbuf(p->tcol, p->col + 2 + (sz * 5)); for (i = 0; i < sz; i++) { if (ASCII_HYPH == word[i] || isgraph((unsigned char)word[i])) encode1(p, word[i]); else { - p->buf[p->col++] = word[i]; + if (p->tcol->lastcol <= p->col || + (word[i] != ' ' && word[i] != ASCII_NBRSP)) + p->tcol->buf[p->col] = word[i]; + p->col++; /* * Postpone the effect of \z while handling @@ -621,6 +757,8 @@ encode(struct termp *p, const char *word, size_t sz) } } } + if (p->tcol->lastcol < p->col) + p->tcol->lastcol = p->col; } void @@ -644,7 +782,7 @@ term_setwidth(struct termp *p, const char *wstr) default: break; } - if (a2roffsu(wstr, &su, SCALE_MAX)) + if (a2roffsu(wstr, &su, SCALE_MAX) != NULL) width = term_hspan(p, &su); else iop = 0; @@ -833,7 +971,7 @@ term_vspan(const struct termp *p, const struct roffsu *su) } /* - * Convert a scaling width to basic units, rounding down. + * Convert a scaling width to basic units, rounding towards 0. */ int term_hspan(const struct termp *p, const struct roffsu *su) @@ -841,3 +979,17 @@ term_hspan(const struct termp *p, const struct roffsu *su) return (*p->hspan)(p, su); } + +/* + * Convert a scaling width to basic units, rounding to closest. + */ +int +term_hen(const struct termp *p, const struct roffsu *su) +{ + int bu; + + if ((bu = (*p->hspan)(p, su)) >= 0) + return (bu + 11) / 24; + else + return -((-bu + 11) / 24); +} diff --git a/contrib/mdocml/term.h b/contrib/mdocml/term.h index fabc117..493191d 100644 --- a/contrib/mdocml/term.h +++ b/contrib/mdocml/term.h @@ -1,7 +1,7 @@ -/* $Id: term.h,v 1.118 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: term.h,v 1.130 2017/07/08 14:51:05 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -36,9 +36,10 @@ enum termfont { TERMFONT__MAX }; -#define TERM_MAXMARGIN 100000 /* FIXME */ - +struct eqn_box; struct roff_meta; +struct roff_node; +struct tbl_span; struct termp; typedef void (*term_margin)(struct termp *, const struct roff_meta *); @@ -48,24 +49,33 @@ struct termp_tbl { int decimal; /* decimal point position */ }; +struct termp_col { + int *buf; /* Output buffer. */ + size_t maxcols; /* Allocated bytes in buf. */ + size_t lastcol; /* Last byte in buf. */ + size_t col; /* Byte in buf to be written. */ + size_t rmargin; /* Current right margin. */ + size_t offset; /* Current left margin. */ +}; + struct termp { - enum termtype type; - struct rofftbl tbl; /* table configuration */ - int synopsisonly; /* print the synopsis only */ - int mdocstyle; /* imitate mdoc(7) output */ + struct rofftbl tbl; /* Table configuration. */ + struct termp_col *tcols; /* Array of table columns. */ + struct termp_col *tcol; /* Current table column. */ + size_t maxtcol; /* Allocated table columns. */ + size_t lasttcol; /* Last column currently used. */ size_t line; /* Current output line number. */ size_t defindent; /* Default indent for text. */ size_t defrmargin; /* Right margin of the device. */ size_t lastrmargin; /* Right margin before the last ll. */ - size_t rmargin; /* Current right margin. */ size_t maxrmargin; /* Max right margin. */ - size_t maxcols; /* Max size of buf. */ - size_t offset; /* Margin offest. */ - size_t tabwidth; /* Distance of tab positions. */ - size_t col; /* Bytes in buf. */ + size_t col; /* Byte position in buf. */ size_t viscol; /* Chars on current line. */ - size_t trailspace; /* See termp_flushln(). */ - int overstep; /* See termp_flushln(). */ + size_t trailspace; /* See term_flushln(). */ + size_t minbl; /* Minimum blanks before next field. */ + int synopsisonly; /* Print the synopsis only. */ + int mdocstyle; /* Imitate mdoc(7) output. */ + int ti; /* Temporary indent for one line. */ int skipvsp; /* Vertical space to skip. */ int flags; #define TERMP_SENTENCE (1 << 0) /* Space before a sentence. */ @@ -79,12 +89,17 @@ struct termp { #define TERMP_NOBREAK (1 << 8) /* See term_flushln(). */ #define TERMP_BRTRSP (1 << 9) /* See term_flushln(). */ #define TERMP_BRIND (1 << 10) /* See term_flushln(). */ -#define TERMP_DANGLE (1 << 11) /* See term_flushln(). */ -#define TERMP_HANG (1 << 12) /* See term_flushln(). */ +#define TERMP_HANG (1 << 11) /* See term_flushln(). */ +#define TERMP_NOPAD (1 << 12) /* See term_flushln(). */ #define TERMP_NOSPLIT (1 << 13) /* Do not break line before .An. */ #define TERMP_SPLIT (1 << 14) /* Break line before .An. */ #define TERMP_NONEWLINE (1 << 15) /* No line break in nofill mode. */ - int *buf; /* Output buffer. */ +#define TERMP_BRNEVER (1 << 16) /* Don't even break at maxrmargin. */ +#define TERMP_NOBUF (1 << 17) /* Bypass output buffer. */ +#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. */ + enum termtype type; /* Terminal, PS, or PDF. */ enum termenc enc; /* Type of encoding. */ enum termfont fontl; /* Last font set. */ enum termfont *fontq; /* Symmetric fonts. */ @@ -102,18 +117,19 @@ struct termp { int (*hspan)(const struct termp *, const struct roffsu *); const void *argf; /* arg for headf/footf */ + const char *mc; /* Margin character. */ struct termp_ps *ps; }; -struct tbl_span; -struct eqn; - const char *ascii_uc2str(int); -void term_eqn(struct termp *, const struct eqn *); +void roff_term_pre(struct termp *, const struct roff_node *); + +void term_eqn(struct termp *, const struct eqn_box *); void term_tbl(struct termp *, const struct tbl_span *); void term_free(struct termp *); +void term_setcol(struct termp *, size_t); void term_newln(struct termp *); void term_vspace(struct termp *); void term_word(struct termp *, const char *); @@ -124,10 +140,15 @@ void term_end(struct termp *); void term_setwidth(struct termp *, const char *); int term_hspan(const struct termp *, const struct roffsu *); +int term_hen(const struct termp *, const struct roffsu *); int term_vspan(const struct termp *, const struct roffsu *); size_t term_strlen(const struct termp *, const char *); size_t term_len(const struct termp *, size_t); +void term_tab_set(const struct termp *, const char *); +void term_tab_iset(size_t); +size_t term_tab_next(size_t); + void term_fontpush(struct termp *, enum termfont); void term_fontpop(struct termp *); void term_fontpopq(struct termp *, int); diff --git a/contrib/mdocml/term_ascii.c b/contrib/mdocml/term_ascii.c index df5ff13..e819c0e 100644 --- a/contrib/mdocml/term_ascii.c +++ b/contrib/mdocml/term_ascii.c @@ -1,7 +1,7 @@ -/* $Id: term_ascii.c,v 1.54 2016/07/31 09:29:13 schwarze Exp $ */ +/* $Id: term_ascii.c,v 1.58 2017/06/14 14:24:20 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -65,13 +65,14 @@ ascii_init(enum termenc enc, const struct manoutput *outopts) #endif struct termp *p; - p = mandoc_calloc(1, sizeof(struct termp)); + p = mandoc_calloc(1, sizeof(*p)); + p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol)); + p->maxtcol = 1; p->line = 1; - p->tabwidth = 5; p->defrmargin = p->lastrmargin = 78; p->fontq = mandoc_reallocarray(NULL, - (p->fontsz = 8), sizeof(enum termfont)); + (p->fontsz = 8), sizeof(*p->fontq)); p->fontq[0] = p->fontl = TERMFONT_NONE; p->begin = ascii_begin; @@ -149,7 +150,7 @@ ascii_setwidth(struct termp *p, int iop, int width) { width /= 24; - p->rmargin = p->defrmargin; + p->tcol->rmargin = p->defrmargin; if (iop > 0) p->defrmargin += width; else if (iop == 0) @@ -158,8 +159,8 @@ ascii_setwidth(struct termp *p, int iop, int width) p->defrmargin -= width; else p->defrmargin = 0; - p->lastrmargin = p->rmargin; - p->rmargin = p->maxrmargin = p->defrmargin; + p->lastrmargin = p->tcol->rmargin; + p->tcol->rmargin = p->maxrmargin = p->defrmargin; } void @@ -216,6 +217,8 @@ ascii_endline(struct termp *p) { p->line++; + p->tcol->offset -= p->ti; + p->ti = 0; putchar('\n'); } @@ -290,7 +293,7 @@ ascii_uc2str(int uc) "<80>", "<81>", "<82>", "<83>", "<84>", "<85>", "<86>", "<87>", "<88>", "<89>", "<8A>", "<8B>", "<8C>", "<8D>", "<8E>", "<8F>", "<90>", "<91>", "<92>", "<93>", "<94>", "<95>", "<96>", "<97>", - "<99>", "<99>", "<9A>", "<9B>", "<9C>", "<9D>", "<9E>", "<9F>", + "<98>", "<99>", "<9A>", "<9B>", "<9C>", "<9D>", "<9E>", "<9F>", nbrsp, "!", "/\bc", "GBP", "o\bx", "=\bY", "|", "<sec>", "\"", "(C)", "_\ba", "<<", "~", "", "(R)", "-", "<deg>","+-", "2", "3", "'", ",\bu", "<par>",".", @@ -370,6 +373,8 @@ locale_endline(struct termp *p) { p->line++; + p->tcol->offset -= p->ti; + p->ti = 0; putwchar(L'\n'); } diff --git a/contrib/mdocml/term_ps.c b/contrib/mdocml/term_ps.c index 286a89f..9638ae4 100644 --- a/contrib/mdocml/term_ps.c +++ b/contrib/mdocml/term_ps.c @@ -1,7 +1,7 @@ -/* $Id: term_ps.c,v 1.82 2016/08/10 11:03:43 schwarze Exp $ */ +/* $Id: term_ps.c,v 1.85 2017/06/07 17:38:26 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -104,7 +104,7 @@ static void ps_pclose(struct termp *); static void ps_plast(struct termp *); static void ps_pletter(struct termp *, int); static void ps_printf(struct termp *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); + __attribute__((__format__ (__printf__, 2, 3))); static void ps_putchar(struct termp *, char); static void ps_setfont(struct termp *, enum termfont); static void ps_setwidth(struct termp *, int, int); @@ -538,12 +538,15 @@ pspdf_alloc(const struct manoutput *outopts) size_t marginx, marginy, lineheight; const char *pp; - p = mandoc_calloc(1, sizeof(struct termp)); + p = mandoc_calloc(1, sizeof(*p)); + p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol)); + p->maxtcol = 1; + p->enc = TERMENC_ASCII; p->fontq = mandoc_reallocarray(NULL, - (p->fontsz = 8), sizeof(enum termfont)); + (p->fontsz = 8), sizeof(*p->fontq)); p->fontq[0] = p->fontl = TERMFONT_NONE; - p->ps = mandoc_calloc(1, sizeof(struct termp_ps)); + p->ps = mandoc_calloc(1, sizeof(*p->ps)); p->advance = ps_advance; p->begin = ps_begin; @@ -1219,6 +1222,9 @@ ps_endline(struct termp *p) } ps_closepage(p); + + p->tcol->offset -= p->ti; + p->ti = 0; } static void diff --git a/contrib/mdocml/term_tab.c b/contrib/mdocml/term_tab.c new file mode 100644 index 0000000..5251a84 --- /dev/null +++ b/contrib/mdocml/term_tab.c @@ -0,0 +1,128 @@ +/* $OpenBSD: term.c,v 1.119 2017/01/08 18:08:44 schwarze Exp $ */ +/* + * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/types.h> + +#include <stddef.h> + +#include "mandoc_aux.h" +#include "out.h" +#include "term.h" + +struct tablist { + size_t *t; /* Allocated array of tab positions. */ + size_t s; /* Allocated number of positions. */ + size_t n; /* Currently used number of positions. */ +}; + +static struct { + struct tablist a; /* All tab positions for lookup. */ + struct tablist p; /* Periodic tab positions to add. */ + size_t d; /* Default tab width in units of n. */ +} tabs; + + +void +term_tab_set(const struct termp *p, const char *arg) +{ + static int recording_period; + + struct roffsu su; + struct tablist *tl; + size_t pos; + int add; + + /* Special arguments: clear all tabs or switch lists. */ + + if (arg == NULL) { + tabs.a.n = tabs.p.n = 0; + recording_period = 0; + if (tabs.d == 0) { + a2roffsu(".8i", &su, SCALE_IN); + tabs.d = term_hen(p, &su); + } + return; + } + if (arg[0] == 'T' && arg[1] == '\0') { + recording_period = 1; + return; + } + + /* Parse the sign, the number, and the unit. */ + + if (*arg == '+') { + add = 1; + arg++; + } else + add = 0; + if (a2roffsu(arg, &su, SCALE_EM) == NULL) + return; + + /* Select the list, and extend it if it is full. */ + + tl = recording_period ? &tabs.p : &tabs.a; + if (tl->n >= tl->s) { + tl->s += 8; + tl->t = mandoc_reallocarray(tl->t, tl->s, sizeof(*tl->t)); + } + + /* Append the new position. */ + + pos = term_hen(p, &su); + tl->t[tl->n] = pos; + if (add && tl->n) + tl->t[tl->n] += tl->t[tl->n - 1]; + tl->n++; +} + +/* + * Simplified version without a parser, + * never incremental, never periodic, for use by tbl(7). + */ +void +term_tab_iset(size_t inc) +{ + if (tabs.a.n >= tabs.a.s) { + tabs.a.s += 8; + tabs.a.t = mandoc_reallocarray(tabs.a.t, tabs.a.s, + sizeof(*tabs.a.t)); + } + tabs.a.t[tabs.a.n++] = inc; +} + +size_t +term_tab_next(size_t prev) +{ + size_t i, j; + + for (i = 0;; i++) { + if (i == tabs.a.n) { + if (tabs.p.n == 0) + return prev; + tabs.a.n += tabs.p.n; + if (tabs.a.s < tabs.a.n) { + tabs.a.s = tabs.a.n; + tabs.a.t = mandoc_reallocarray(tabs.a.t, + tabs.a.s, sizeof(*tabs.a.t)); + } + for (j = 0; j < tabs.p.n; j++) + tabs.a.t[i + j] = tabs.p.t[j] + + (i ? tabs.a.t[i - 1] : 0); + } + if (prev < tabs.a.t[i]) + return tabs.a.t[i]; + } +} diff --git a/contrib/mdocml/test-O_DIRECTORY.c b/contrib/mdocml/test-O_DIRECTORY.c new file mode 100644 index 0000000..e44d51c --- /dev/null +++ b/contrib/mdocml/test-O_DIRECTORY.c @@ -0,0 +1,7 @@ +#include <fcntl.h> + +int +main(void) +{ + return open(".", O_RDONLY | O_DIRECTORY) == -1; +} diff --git a/contrib/mdocml/test-cmsg.c b/contrib/mdocml/test-cmsg.c new file mode 100644 index 0000000..b5441f9 --- /dev/null +++ b/contrib/mdocml/test-cmsg.c @@ -0,0 +1,13 @@ +#include <stddef.h> +#include <sys/socket.h> + +int +main(void) +{ + struct msghdr msg; + + msg.msg_control = NULL; + msg.msg_controllen = 0; + + return CMSG_FIRSTHDR(&msg) != NULL; +} diff --git a/contrib/mdocml/test-recallocarray.c b/contrib/mdocml/test-recallocarray.c new file mode 100644 index 0000000..e0c60d7 --- /dev/null +++ b/contrib/mdocml/test-recallocarray.c @@ -0,0 +1,11 @@ +#include <stdlib.h> + +int +main(void) +{ + void *p; + + if ((p = calloc(2, 2)) == NULL) + return 1; + return !recallocarray(p, 2, 3, 2); +} diff --git a/contrib/mdocml/test-recvmsg.c b/contrib/mdocml/test-recvmsg.c new file mode 100644 index 0000000..d026c70 --- /dev/null +++ b/contrib/mdocml/test-recvmsg.c @@ -0,0 +1,8 @@ +#include <sys/socket.h> +#include <stddef.h> + +int +main(void) +{ + return recvmsg(-1, NULL, 0) != -1; +} diff --git a/contrib/mdocml/tree.c b/contrib/mdocml/tree.c index 9e68b69..7d18b9d 100644 --- a/contrib/mdocml/tree.c +++ b/contrib/mdocml/tree.c @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.72 2017/01/12 17:29:33 schwarze Exp $ */ +/* $Id: tree.c,v 1.77 2017/07/08 14:51:05 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2013, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -129,23 +129,23 @@ print_mdoc(const struct roff_node *n, int indent) p = n->string; break; case ROFFT_BODY: - p = mdoc_macronames[n->tok]; + p = roff_name[n->tok]; break; case ROFFT_HEAD: - p = mdoc_macronames[n->tok]; + p = roff_name[n->tok]; break; case ROFFT_TAIL: - p = mdoc_macronames[n->tok]; + p = roff_name[n->tok]; break; case ROFFT_ELEM: - p = mdoc_macronames[n->tok]; + p = roff_name[n->tok]; if (n->args) { argv = n->args->argv; argc = n->args->argc; } break; case ROFFT_BLOCK: - p = mdoc_macronames[n->tok]; + p = roff_name[n->tok]; if (n->args) { argv = n->args->argv; argc = n->args->argc; @@ -192,6 +192,8 @@ print_mdoc(const struct roff_node *n, int indent) putchar(')'); if (NODE_EOS & n->flags) putchar('.'); + if (NODE_BROKEN & n->flags) + printf(" BROKEN"); if (NODE_NOSRC & n->flags) printf(" NOSRC"); if (NODE_NOPRT & n->flags) @@ -200,7 +202,7 @@ print_mdoc(const struct roff_node *n, int indent) } if (n->eqn) - print_box(n->eqn->root->first, indent + 4); + print_box(n->eqn->first, indent + 4); if (n->child) print_mdoc(n->child, indent + (n->type == ROFFT_BLOCK ? 2 : 4)); @@ -255,7 +257,7 @@ print_man(const struct roff_node *n, int indent) case ROFFT_BLOCK: case ROFFT_HEAD: case ROFFT_BODY: - p = man_macronames[n->tok]; + p = roff_name[n->tok]; break; case ROFFT_ROOT: p = "root"; @@ -285,7 +287,7 @@ print_man(const struct roff_node *n, int indent) } if (n->eqn) - print_box(n->eqn->root->first, indent + 4); + print_box(n->eqn->first, indent + 4); if (n->child) print_man(n->child, indent + (n->type == ROFFT_BLOCK ? 2 : 4)); @@ -311,10 +313,6 @@ print_box(const struct eqn_box *ep, int indent) t = NULL; switch (ep->type) { - case EQN_ROOT: - t = "eqn-root"; - break; - case EQN_LISTONE: case EQN_LIST: t = "eqn-list"; break; |