diff options
-rw-r--r-- | INSTALL | 17 | ||||
-rw-r--r-- | LICENSE | 6 | ||||
-rw-r--r-- | Makefile | 64 | ||||
-rw-r--r-- | Makefile.depend | 38 | ||||
-rw-r--r-- | NEWS | 201 | ||||
-rw-r--r-- | TODO | 142 | ||||
-rw-r--r-- | apropos.1 | 15 | ||||
-rw-r--r-- | arch.c | 4 | ||||
-rw-r--r-- | att.c | 4 | ||||
-rw-r--r-- | catman.c | 4 | ||||
-rw-r--r-- | cgi.c | 72 | ||||
-rw-r--r-- | chars.c | 7 | ||||
-rw-r--r-- | compat_err.c | 13 | ||||
-rw-r--r-- | compat_fts.c | 29 | ||||
-rw-r--r-- | compat_fts.h | 3 | ||||
-rw-r--r-- | compat_getline.c | 13 | ||||
-rw-r--r-- | compat_getsubopt.c | 13 | ||||
-rw-r--r-- | compat_isblank.c | 14 | ||||
-rw-r--r-- | compat_mkdtemp.c | 23 | ||||
-rw-r--r-- | compat_mkstemps.c | 63 | ||||
-rw-r--r-- | compat_ohash.c | 13 | ||||
-rw-r--r-- | compat_progname.c | 15 | ||||
-rw-r--r-- | compat_reallocarray.c | 15 | ||||
-rw-r--r-- | compat_recallocarray.c | 25 | ||||
-rw-r--r-- | compat_strcasestr.c | 13 | ||||
-rw-r--r-- | compat_stringlist.c | 82 | ||||
-rw-r--r-- | compat_stringlist.h | 43 | ||||
-rw-r--r-- | compat_strlcat.c | 62 | ||||
-rw-r--r-- | compat_strlcpy.c | 49 | ||||
-rw-r--r-- | compat_strndup.c | 16 | ||||
-rw-r--r-- | compat_strsep.c | 15 | ||||
-rw-r--r-- | compat_strtonum.c | 15 | ||||
-rw-r--r-- | compat_vasprintf.c | 13 | ||||
-rwxr-xr-x | configure | 313 | ||||
-rw-r--r-- | configure.local.example | 71 | ||||
-rw-r--r-- | dba_array.c | 4 | ||||
-rw-r--r-- | dba_read.c | 4 | ||||
-rw-r--r-- | eqn.7 | 55 | ||||
-rw-r--r-- | eqn.c | 14 | ||||
-rw-r--r-- | html.c | 337 | ||||
-rw-r--r-- | html.h | 36 | ||||
-rw-r--r-- | libmandoc.h | 14 | ||||
-rw-r--r-- | main.c | 956 | ||||
-rw-r--r-- | man.1 | 98 | ||||
-rw-r--r-- | man.7 | 55 | ||||
-rw-r--r-- | man.conf.5 | 14 | ||||
-rw-r--r-- | man_html.c | 43 | ||||
-rw-r--r-- | man_macro.c | 12 | ||||
-rw-r--r-- | man_term.c | 132 | ||||
-rw-r--r-- | man_validate.c | 157 | ||||
-rw-r--r-- | manconf.h | 10 | ||||
-rw-r--r-- | mandoc.1 | 141 | ||||
-rw-r--r-- | mandoc.c | 85 | ||||
-rw-r--r-- | mandoc.css | 7 | ||||
-rw-r--r-- | mandoc.h | 22 | ||||
-rw-r--r-- | mandoc_char.7 | 21 | ||||
-rw-r--r-- | mandoc_headers.3 | 79 | ||||
-rw-r--r-- | mandoc_html.3 | 270 | ||||
-rw-r--r-- | mandoc_malloc.3 | 41 | ||||
-rw-r--r-- | mandoc_msg.c | 17 | ||||
-rw-r--r-- | mandoc_ohash.c | 4 | ||||
-rw-r--r-- | mandoc_parse.h | 3 | ||||
-rw-r--r-- | mandoc_xr.c | 4 | ||||
-rw-r--r-- | mandocd.c | 4 | ||||
-rw-r--r-- | mandocdb.c | 247 | ||||
-rw-r--r-- | manpath.c | 48 | ||||
-rw-r--r-- | mdoc.7 | 183 | ||||
-rw-r--r-- | mdoc.c | 13 | ||||
-rw-r--r-- | mdoc_html.c | 385 | ||||
-rw-r--r-- | mdoc_macro.c | 7 | ||||
-rw-r--r-- | mdoc_man.c | 124 | ||||
-rw-r--r-- | mdoc_markdown.c | 55 | ||||
-rw-r--r-- | mdoc_state.c | 5 | ||||
-rw-r--r-- | mdoc_term.c | 440 | ||||
-rw-r--r-- | mdoc_validate.c | 527 | ||||
-rw-r--r-- | out.c | 75 | ||||
-rw-r--r-- | out.h | 6 | ||||
-rw-r--r-- | read.c | 31 | ||||
-rw-r--r-- | roff.7 | 8 | ||||
-rw-r--r-- | roff.c | 233 | ||||
-rw-r--r-- | roff.h | 17 | ||||
-rw-r--r-- | roff_html.c | 4 | ||||
-rw-r--r-- | roff_int.h | 5 | ||||
-rw-r--r-- | roff_term.c | 56 | ||||
-rw-r--r-- | roff_validate.c | 10 | ||||
-rw-r--r-- | soelim.c | 8 | ||||
-rw-r--r-- | tag.c | 415 | ||||
-rw-r--r-- | tag.h | 35 | ||||
-rw-r--r-- | tbl.7 | 13 | ||||
-rw-r--r-- | tbl.h | 7 | ||||
-rw-r--r-- | tbl_data.c | 53 | ||||
-rw-r--r-- | tbl_html.c | 42 | ||||
-rw-r--r-- | tbl_layout.c | 63 | ||||
-rw-r--r-- | tbl_term.c | 60 | ||||
-rw-r--r-- | term.c | 25 | ||||
-rw-r--r-- | term_ascii.c | 32 | ||||
-rw-r--r-- | term_ps.c | 9 | ||||
-rw-r--r-- | term_tab.c | 4 | ||||
-rw-r--r-- | term_tag.c | 227 | ||||
-rw-r--r-- | term_tag.h | 34 | ||||
-rw-r--r-- | test-attribute.c | 48 | ||||
-rw-r--r-- | test-mkstemps.c | 12 | ||||
-rw-r--r-- | tree.c | 201 |
103 files changed, 5023 insertions, 2888 deletions
@@ -1,4 +1,4 @@ -$Id: INSTALL,v 1.23 2019/03/06 15:58:10 schwarze Exp $ +$Id: INSTALL,v 1.24 2021/09/20 13:25:42 schwarze Exp $ About the portable mandoc distribution -------------------------------------- @@ -18,7 +18,7 @@ tech@ mailing list, too. Enjoy using the mandoc toolset! -Ingo Schwarze, Karlsruhe, March 2019 +Ingo Schwarze, Karlsruhe, September 2021 Installation @@ -65,10 +65,15 @@ installed to the intended places. Otherwise, put some *DIR or *NM* 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. In particular, regarding Solaris systems, look at the BUGS -section of that manual page. +Basically, that amounts to "make regress" to do a standard regression +run, running all tests. For more fine-grained control, +read "./mandoc -l regress/regress.pl.1", +then run "cd regress && ./regress.pl" with optional arguments. +The regression suite requires a reasonably modern Perl interpreter. +Examples of systems that are too old to run the regression suite +include Solaris 9, Solaris 10, and Mac OS X 10.4 Tiger. +On Solaris 11, the suite does run, but some tests fail; +look at the BUGS section of that manual page. 8. Run "sudo make install". If you intend to build a binary package using some kind of fake root mechanism, you may need a @@ -1,11 +1,11 @@ -$Id: LICENSE,v 1.21 2018/11/26 17:11:11 schwarze Exp $ +$Id: LICENSE,v 1.22 2021/09/19 11:02:09 schwarze Exp $ With the exceptions noted below, all non-trivial files contained in the mandoc toolkit are protected by the Copyright of the following developers: Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> -Copyright (c) 2010-2018 Ingo Schwarze <schwarze@openbsd.org> +Copyright (c) 2010-2021 Ingo Schwarze <schwarze@openbsd.org> Copyright (c) 1999, 2004, 2017 Marc Espie <espie@openbsd.org> Copyright (c) 2009, 2010, 2011, 2012 Joerg Sonnenberger <joerg@netbsd.org> Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> @@ -13,7 +13,7 @@ Copyright (c) 2014 Baptiste Daroussin <bapt@freebsd.org> Copyright (c) 2016 Ed Maste <emaste@freebsd.org> Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> Copyright (c) 2017 Anthony Bentley <bentley@openbsd.org> -Copyright (c) 1998, 2004, 2010 Todd C. Miller <Todd.Miller@courtesan.com> +Copyright (c) 1998, 2004, 2010, 2015 Todd C. Miller <Todd.Miller@courtesan.com> Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net> Copyright (c) 2004 Ted Unangst <tedu@openbsd.org> Copyright (c) 1994 Christos Zoulas <christos@netbsd.org> @@ -1,7 +1,7 @@ -# $Id: Makefile,v 1.530 2019/03/06 16:08:41 schwarze Exp $ +# $Id: Makefile,v 1.540 2021/09/21 11:04:40 schwarze Exp $ # +# Copyright (c) 2011, 2013-2021 Ingo Schwarze <schwarze@openbsd.org> # Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> -# Copyright (c) 2011, 2013-2019 Ingo Schwarze <schwarze@openbsd.org> # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -15,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.5 +VERSION = 1.14.6 # === LIST OF FILES ==================================================== -TESTSRCS = test-be32toh.c \ +TESTSRCS = test-attribute.c \ + test-be32toh.c \ test-cmsg.c \ test-dirent-namlen.c \ test-EFTYPE.c \ @@ -29,6 +30,7 @@ TESTSRCS = test-be32toh.c \ test-getsubopt.c \ test-isblank.c \ test-mkdtemp.c \ + test-mkstemps.c \ test-nanosleep.c \ test-noop.c \ test-ntohl.c \ @@ -65,6 +67,7 @@ SRCS = arch.c \ compat_getsubopt.c \ compat_isblank.c \ compat_mkdtemp.c \ + compat_mkstemps.c \ compat_ohash.c \ compat_progname.c \ compat_reallocarray.c \ @@ -134,6 +137,7 @@ SRCS = arch.c \ term_ascii.c \ term_ps.c \ term_tab.c \ + term_tag.c \ tree.c DISTFILES = INSTALL \ @@ -209,6 +213,7 @@ DISTFILES = INSTALL \ tbl_int.h \ tbl_parse.h \ term.h \ + term_tag.h \ $(SRCS) \ $(TESTSRCS) @@ -245,19 +250,22 @@ LIBMANDOC_OBJS = $(LIBMAN_OBJS) \ mandoc_xr.o \ msec.o \ preconv.o \ - read.o + read.o \ + tag.o -COMPAT_OBJS = compat_err.o \ +ALL_COBJS = compat_err.o \ compat_fts.o \ compat_getline.o \ compat_getsubopt.o \ compat_isblank.o \ compat_mkdtemp.o \ + compat_mkstemps.o \ compat_ohash.o \ compat_progname.o \ compat_reallocarray.o \ compat_recallocarray.o \ compat_strcasestr.o \ + compat_stringlist.o \ compat_strlcat.o \ compat_strlcpy.o \ compat_strndup.o \ @@ -280,6 +288,7 @@ MANDOC_TERM_OBJS = eqn_term.o \ term_ascii.o \ term_ps.o \ term_tab.o \ + term_tag.o \ tbl_term.o DBM_OBJS = dbm.o \ @@ -302,7 +311,6 @@ MAIN_OBJS = $(MANDOC_HTML_OBJS) \ mdoc_man.o \ mdoc_markdown.o \ out.o \ - tag.o \ tree.o CGI_OBJS = $(MANDOC_HTML_OBJS) \ @@ -313,18 +321,10 @@ CGI_OBJS = $(MANDOC_HTML_OBJS) \ MANDOCD_OBJS = $(MANDOC_HTML_OBJS) \ $(MANDOC_TERM_OBJS) \ mandocd.o \ - out.o \ - tag.o + out.o DEMANDOC_OBJS = demandoc.o -SOELIM_OBJS = soelim.o \ - compat_err.o \ - compat_getline.o \ - compat_progname.o \ - compat_reallocarray.o \ - compat_stringlist.o - WWW_MANS = apropos.1.html \ demandoc.1.html \ man.1.html \ @@ -373,7 +373,7 @@ include Makefile.local # === DEPENDENCY HANDLING ============================================== -all: mandoc demandoc soelim $(BUILD_TARGETS) Makefile.local +all: mandoc man demandoc soelim $(BUILD_TARGETS) Makefile.local install: base-install $(INSTALL_TARGETS) @@ -392,13 +392,14 @@ distclean: clean rm -f Makefile.local config.h config.h.old config.log config.log.old clean: - rm -f libmandoc.a $(LIBMANDOC_OBJS) $(COMPAT_OBJS) - rm -f mandoc $(MAIN_OBJS) + rm -f libmandoc.a $(LIBMANDOC_OBJS) $(ALL_COBJS) + rm -f mandoc man $(MAIN_OBJS) rm -f man.cgi $(CGI_OBJS) rm -f mandocd catman catman.o $(MANDOCD_OBJS) rm -f demandoc $(DEMANDOC_OBJS) - rm -f soelim $(SOELIM_OBJS) + rm -f soelim soelim.o rm -f $(WWW_MANS) $(WWW_INCS) mandoc*.tar.gz mandoc*.sha256 + rm -f Makefile.tmp1 Makefile.tmp2 rm -rf *.dSYM base-install: mandoc demandoc soelim @@ -511,12 +512,15 @@ Makefile.local config.h: configure $(TESTSRCS) @echo "$@ is out of date; please run ./configure" @exit 1 -libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS) - ar rs $@ $(COMPAT_OBJS) $(LIBMANDOC_OBJS) +libmandoc.a: $(MANDOC_COBJS) $(LIBMANDOC_OBJS) + $(AR) rs $@ $(MANDOC_COBJS) $(LIBMANDOC_OBJS) mandoc: $(MAIN_OBJS) libmandoc.a $(CC) -o $@ $(LDFLAGS) $(MAIN_OBJS) libmandoc.a $(LDADD) +man: mandoc + $(LN) mandoc man + man.cgi: $(CGI_OBJS) libmandoc.a $(CC) $(STATIC) -o $@ $(LDFLAGS) $(CGI_OBJS) libmandoc.a $(LDADD) @@ -529,8 +533,8 @@ catman: catman.o libmandoc.a demandoc: $(DEMANDOC_OBJS) libmandoc.a $(CC) -o $@ $(LDFLAGS) $(DEMANDOC_OBJS) libmandoc.a $(LDADD) -soelim: $(SOELIM_OBJS) - $(CC) -o $@ $(LDFLAGS) $(SOELIM_OBJS) +soelim: $(SOELIM_COBJS) soelim.o + $(CC) -o $@ $(LDFLAGS) $(SOELIM_COBJS) soelim.o # --- maintainer targets --- @@ -540,11 +544,13 @@ www-install: www $(INSTALL_DATA) $(WWW_INCS) $(HTDOCDIR)/includes depend: config.h - mkdep -f Makefile.depend $(CFLAGS) $(SRCS) + ./configure -depend + mkdep -f Makefile.tmp1 $(CFLAGS) $(SRCS) perl -e 'undef $$/; $$_ = <>; s|/usr/include/\S+||g; \ s|\\\n||g; s| +| |g; s| $$||mg; print;' \ - Makefile.depend > Makefile.tmp - mv Makefile.tmp Makefile.depend + Makefile.tmp1 > Makefile.tmp2 + rm Makefile.tmp1 + mv Makefile.tmp2 Makefile.depend regress-distclean: @find regress \ @@ -597,7 +603,7 @@ dist-install: dist .h.h.html: highlight -I $< > $@ -.1.1.html .3.3.html .5.5.html .7.7.html .8.8.html: mandoc - mandoc -Thtml -Wwarning,stop \ +.1.1.html .3.3.html .5.5.html .7.7.html .8.8.html: + ./mandoc -Thtml -Wwarning,stop \ -O 'style=/mandoc.css,man=/man/%N.%S.html;https://man.openbsd.org/%N.%S,includes=/includes/%I.html' \ $< > $@ diff --git a/Makefile.depend b/Makefile.depend index 3540aeda822c..d5f6556c3e7e 100644 --- a/Makefile.depend +++ b/Makefile.depend @@ -9,6 +9,7 @@ compat_getline.o: compat_getline.c config.h compat_getsubopt.o: compat_getsubopt.c config.h compat_isblank.o: compat_isblank.c config.h compat_mkdtemp.o: compat_mkdtemp.c config.h +compat_mkstemps.o: compat_mkstemps.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 @@ -22,8 +23,8 @@ compat_strsep.o: compat_strsep.c config.h compat_strtonum.o: compat_strtonum.c config.h compat_vasprintf.o: compat_vasprintf.c config.h dba.o: dba.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mansearch.h dba_write.h dba_array.h dba.h -dba_array.o: dba_array.c mandoc_aux.h dba_write.h dba_array.h -dba_read.o: dba_read.c mandoc_aux.h mansearch.h dba_array.h dba.h dbm.h +dba_array.o: dba_array.c config.h mandoc_aux.h dba_write.h dba_array.h +dba_read.o: dba_read.c config.h 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 @@ -33,17 +34,17 @@ eqn_html.o: eqn_html.c config.h mandoc.h roff.h eqn.h out.h html.h eqn_term.o: eqn_term.c config.h eqn.h out.h term.h html.o: html.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h out.h html.h manconf.h main.h lib.o: lib.c config.h roff.h libmdoc.h lib.in -main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h tag.h main.h manconf.h mansearch.h +main.o: main.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h man.h mandoc_parse.h tag.h term_tag.h main.h manconf.h mansearch.h man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h man_html.o: man_html.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h html.h main.h man_macro.o: man_macro.c config.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h -man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h tag.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 +man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h term_tag.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 tag.h mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h roff_int.h mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h mandoc_msg.o: mandoc_msg.c config.h mandoc.h -mandoc_ohash.o: mandoc_ohash.c mandoc_aux.h mandoc_ohash.h compat_ohash.h -mandoc_xr.o: mandoc_xr.c mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc_xr.h +mandoc_ohash.o: mandoc_ohash.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h +mandoc_xr.o: mandoc_xr.c config.h 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 mandoc_parse.h main.h manconf.h mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h manconf.h mansearch.h dba_array.h dba.h manpath.o: manpath.c config.h mandoc_aux.h mandoc.h manconf.h @@ -53,21 +54,21 @@ mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc. 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 roff_int.h libmdoc.h -mdoc_term.o: mdoc_term.c config.h mandoc_aux.h roff.h mdoc.h out.h term.h tag.h main.h -mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h +mdoc_markdown.o: mdoc_markdown.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h main.h +mdoc_state.o: mdoc_state.c config.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h +mdoc_term.o: mdoc_term.c config.h mandoc_aux.h roff.h mdoc.h out.h term.h term_tag.h main.h +mdoc_validate.o: mdoc_validate.c config.h mandoc_aux.h mandoc.h mandoc_xr.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h tag.h msec.o: msec.c config.h mandoc.h libmandoc.h msec.in -out.o: out.c config.h mandoc_aux.h tbl.h out.h +out.o: out.c config.h mandoc_aux.h mandoc.h tbl.h out.h preconv.o: preconv.c config.h mandoc.h roff.h mandoc_parse.h libmandoc.h -read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h +read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h libmandoc.h roff_int.h tag.h roff.o: roff.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mandoc_parse.h libmandoc.h roff_int.h tbl_parse.h eqn_parse.h predefs.in -roff_html.o: roff_html.c mandoc.h roff.h out.h html.h -roff_term.o: roff_term.c mandoc.h roff.h out.h term.h -roff_validate.o: roff_validate.c mandoc.h roff.h libmandoc.h roff_int.h +roff_html.o: roff_html.c config.h mandoc.h roff.h out.h html.h +roff_term.o: roff_term.c config.h mandoc.h roff.h out.h term.h +roff_validate.o: roff_validate.c config.h mandoc.h roff.h libmandoc.h roff_int.h soelim.o: soelim.c config.h compat_stringlist.h st.o: st.c config.h mandoc.h roff.h libmdoc.h -tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h tag.h +tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h roff.h mdoc.h roff_int.h tag.h tbl.o: tbl.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_parse.h tbl_int.h tbl_data.o: tbl_data.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h tbl_html.o: tbl_html.c config.h mandoc.h roff.h tbl.h out.h html.h @@ -77,5 +78,6 @@ tbl_term.o: tbl_term.c config.h mandoc.h tbl.h out.h term.h term.o: term.c config.h mandoc.h mandoc_aux.h out.h term.h main.h term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h out.h term.h manconf.h main.h term_ps.o: term_ps.c config.h mandoc_aux.h out.h term.h manconf.h main.h -term_tab.o: term_tab.c mandoc_aux.h out.h term.h +term_tab.o: term_tab.c config.h mandoc_aux.h out.h term.h +term_tag.o: term_tag.c config.h mandoc.h roff.h roff_int.h tag.h term_tag.h tree.o: tree.c config.h mandoc.h roff.h mdoc.h man.h tbl.h eqn.h main.h @@ -1,7 +1,206 @@ -$Id: NEWS,v 1.34 2019/03/10 09:32:00 schwarze Exp $ +$Id: NEWS,v 1.40 2021/09/23 18:03:00 schwarze Exp $ This file lists the most important changes in the mandoc.bsd.lv distribution. +Changes in version 1.14.6, released on September 23, 2021 + + --- MAJOR NEW FEATURES --- + * mdoc(7): automatic tagging improved in many respects + * mdoc(7): new .Tg (tag) macro to explicitly mark a place as defining a term + * man(7): implement some automatic tagging support + * man(1): let -w without argument show the manpath, like in man-db and man-1.6 + * -T html: wrap text and phrasing elements in paragraphs unless already + contained in flow containers; never put them directly into sections. + This helps to format paragraphs with the CSS class selector .Pp. + * man.conf(5): remove support for the "_whatdb" configuration directive + that was deprecated in 2015; please use "manpath" instead + --- MINOR NEW FEATURES --- + * man(1): switch the default pager from "more -s" to "less" + * man(1): in the fallback code to look for manual pages without using + mandoc.db(5), accept files "man<one-digit-section>/<name>.<full-section>" + in addition to the already supported "man<full-section>/name.[01-9]*" + * if messages are shown and output is printed without a pager, display + a heads-up on stderr at the end because otherwise, users may easily + miss the messages + * man.cgi(8): add a Content-Security-Policy HTTP header + * man.cgi(8): switch off autocomplete and autocapitalize + * mandoc.css: support prefers-color-scheme: dark + * -T html: add meta viewport element to help mobile devices + * -T html -O tag: let this pass a file:// URI to the pager + * tbl(7): implement the "nospaces" option + * tbl(7) -T html: implement the "a" (em indent) layout specification + * tbl(7) -T html: implement the "b" (bold) and "i" (italic) layout modifiers + * tbl(7): support two-character font names in the layout font modifier + * tbl(7) -T html: support horinzontal rulers in individual cells + * tbl(7) -T tree: print more details about columns, options, rows, and cells + * roff(7): implement the .break request (break out of a .while loop) + * roff(7): support the CB and CI fonts in \f and .ft + * -T lint: new STYLE message if a file name extension contradicts .Dt/.TH + * -T lint: new STYLE message about overlong text lines + * -W style: check .Xr links along the full manpath + --- RELIABILITY BUGFIXES --- + * man(1): do not segfault if /tmp/ is not writeable + * man(1): do not access a NULL pointer when both -l and -w are given + * makewhatis(8): do not crash when a manpath directory contains + a symbolic link that points to a directory + * man(7): fix an assertion failure caused by doubly nested next-line scopes + * tbl(7): fix a crash when the last column is only reached by spans + * tbl(7): fix a NULL pointer access in some cases of two spans on one row + * tbl(7) -T ascii: fix a NULL pointer access on empty data cells + * tbl(7) -T ascii: fix a NULL pointer access on a line next to a short row + * tbl(7): fix an assertion failure caused by excessive spacing modifiers + * tbl(7): fix an infinite loop for some overlapping horizontal spans + * roff(7): fix a rare case of writing one byte past the end of the input buffer + * roff(7): do not call abort(3) when \*[.T] is encountered + * roff(7): fix an assertion failure caused by a macro inside .ce .if + * roff(7): fix assertion failures for .ti and .po with excessive arguments + * roff(7): avoid near-infinte output for .ce inside explicit no-fill mode + * -T ascii/utf8: fix assertion failures caused by excessive spacing + * -T html: fix an assertion failure caused by .ft in rare situations + * -T man: fix an assertion failure caused by tbl(7) and eqn(7) input + --- PORTABILITY IMPROVEMENTS --- + * rename HOMEBREWDIR to READ_ALLOWED_PATH, allow it to contain more than + one directory, and explain how to use that for NixOS and GNU Guix Linux + * configure: stop trying to ask make(1) what the default compiler is + because that test was too fragile; just use "cc" by default + * configure: various simplifications and improved robustness + * configure: only compile compat_*.c implementations that are needed + * configure: provide feature tests for __attribute__(()) and mkstemps(3) + * compat_*: sync with upstreams for security, functionality, and style + * in regress.pl, avoid the non-portable options sed(1) -i and echo(1) -n + * in the regression suite, avoid file names that differ only by case + --- MINOR FUNCTIONAL IMPROVEMENTS --- + * man(1) -h: for pages lacking a SYNOPSIS, show the NAME section + * man(1): when the first argument starts with a digit, optionally + followed by a letter, and at least one more argument follows, + interpret the first argument as a section name even when additional + characters follow after the digit and letter + * man(1): with a specific section requested, try harder to find + the best match; use this order of preference: + 1. The section in both the directory name and the file name matches exactly. + 2. The section in the file name matches exactly. + 3. The section in the directory name matches exactly. + 4. Neither of them matches exactly. + * man(1): if no tags were generated at all, unlink(2) the empty tags file + as soon as the condition can be detected and do not pass it to less(1) + * makewhatis(8): handle both dangling symlinks and .so links + in manual page directories more gracefully + * man.cgi(8): for invalid queries and for valid queries returning + no result, return the appropriate 40x status code rather than 200 + * mdoc(7): let .Dd concatenate all arguments and default to the empty string + * mdoc(7): convert ".Fl Fl" to ".Fl \-" during validation, improving -T html + * mdoc(7): improve output of .At 32v + * man(7): no longer print multiple blank lines before NAME and page footer + * tbl(7) -T utf8: improved rendering of horizontal lines + * tbl(7) -T html: in "n" cells, align by padding numbers on the right + * tbl(7): no longer leak tabulator settings to subsequent roff(7) code + * mdoc(7) -T html: for .Bl -tag, use "column-count: 1" rather + than "overflow: auto" to avoid the ugly side effects + * mdoc(7) -T html: render .Bd -unfilled in proportionally-spaced font + * mdoc(7) -T html: format .Nd with <span> rather than <div> + * mdoc(7) -T lint: do not warn about Mdocdate without an actual date + * mdoc(7) -T lint: do not complain about function types of the + form "ret_type (fname)(args)", but otherwise check names more strictly + * -T html: append .html suffix to temporary files to please browsers + * -T markdown: print a BAGARG message if called on man(7) input + --- MINOR BUGFIXES --- + * man(1): do the search for each name independently, and + show the results in the order of the command line argument + * man(1): escape shell wildcard characters in name arguments before glob(3) + * man(1): when asking for a single manual page by name, prefer file name + matches over .Dt/.TH matches over first NAME matches over later NAME + matches, but do not change the ordering for apropos(1) nor for man -a + * man(1): correctly extract the section name from the file name extension + of gzipped manual page files + * makewhatis(8): fix file type tests putting wrong data into mandoc.db(5) + * man.cgi(8): fix section number in the <title> element for preformatted pages + * tbl(7): correct handling of T& after horizontal rulers in the layout + * tbl(7): correct column widths if rows have different numbers of cells + * tbl(7): empty columns are 1n wide rather than 0n + * tbl(7): correctly calculate required column widths for tables containing + cells that horizontally span columns which contains "n" (number) formatted + cells on other rows + * tbl(7): skip escape sequences when looking for column separators + * eqn(7): skip whitespace before tokens + * roff(7): when calling an empty macro, do not clobber existing arguments + * roff(7): recognize \} on lines closing a macro definition request + * roff(7): do not throw a bogus warning for "'br\}" and similar lines + * roff(7): stop generating comment nodes when encountering the first content + * mandoc_char(7): make \0 (digit-width space) non-breaking + * mdoc(7) .Bl -column: parse Macro in .It "word<tab>word" Ta word Macro<eol> + * mdoc(7) -T html: display straight quotes, not curly quotes, for .Qq/.Qo + * -T html: remove some spurious line breaks, in particular inside <pre> + * -T html: use <br/> for a space character at the beginning of an input line + * -T html: use ~%d for ordinal fragment suffixes, reserve '~' for that purpose + --- STRUCTURAL IMPROVEMENTS --- + * introduce the concept of semantically transparent syntax tree nodes, + allowing improved decisions in various validators and formatters + * move some code out of the giant main() into separate functions + doing one well-defined task each + * clearly separate parser state (struct curparse) and formatter state + (struct outstate), don't mix them in the same struct + * in the HTML formatter, assert(3) that no HTML nesting violation occurs + * let html_close_paragraph() close any phrasing context + --- THANKS TO --- + * Anthony Bentley and Klemens Nanni (OpenBSD) for many patches and bug + reports, for useful discussions, and for checking patches + * Anton Lindqvist (OpenBSD) for two patches and a bug report + * Marc Espie (OpenBSD) for a patch, many bug reports, and useful discussions + * Lukas Epple (NixOS) for a patch, bug reports, suggesting a minor + portability feature, checking patches, and extensive release testing + * Abel Romero Perez for a patch, a bug report, and suggesting a new feature + * nabijaczleweli for a patch and for suggesting feature improvements + * Jonathan Gray (OpenBSD) for a patch and for bug reports + * Otto Moerbeek (OpenBSD) and Alexander Gromnitsky for a patch + * Armin Besirovic for a contribution to mandoc.css + * Jason McIntyre (OpenBSD) for manual page patches, suggesting a new feature, + checking many patches, and useful discussions + * Martin Vahlensieck for a manual page patch and reporting a code style issue + * Frederic Cambus and Ian Sutton (OpenBSD) for a manual page patch + * Jan Schreiber for many bug reports found with afl(1) + * G. Branden Robinson (GNU troff) for several bug reports, feature + suggestions, and for checking many groff patches + * Michael Stapelberg (Debian) for several bug reports and feature + suggestions, and for extensive release testing + * Ian Ropers, Lorenzo Beretta, and Oliver Corff for several bug reports + and feature suggestions + * Stephen Gregoratto for several bug reports + * Theo de Raadt (OpenBSD) for two bug reports, checking a patch, + and a useful discussion + * Thomas Klausner (NetBSD) for two bug reports and for release testing + * Andreas Kahari and Jason A. Donenfeld for two bug reports + * Soeren Tempel (Alpine Linux) for a bug report, suggesting a feature + improvement, and checking two patches + * Aman Verma, Jan Stary, and John Gardner for a bug report + and for suggesting a feature impovement + * Todd Miller (OpenBSD) for a bug report, checking a patch, + and a useful discussion + * Andrew Fresh, Brian Callahan, Christian Weisgerber, Paul de Weerd (OpenBSD), + Havard Eidnes, Jason Thorpe (NetBSD), Yuri Pankov (FreeBSD), + Bjarni Ingi Gislason, Chris Bennett, Edgar Pettijohn, Eldred Habert, + Jamie Landeg-Jones, Kazuo KUROI, and Wynn Wolf Arbor for a bug report + * Theo Buehler (OpenBSD) for suggesting two feature impovements + and for checking a patch + * Leah Neukirchen (Void Linux) for suggesting a feature impovement + and for release testing + * Colin Watson (Debian) for suggesting a feature impovement + and for checking groff patches + * Matej Cepl (SUSE Linux), Matthew Martin, Steffen Nurpmeso, + and Tim Baumgard for suggesting a feature impovement + * Christos Zoulas (NetBSD) for a report regarding portability + * Daniel Dickman (OpenBSD) for suggesting a portability improvement + * Werner Lemberg (GNU troff) and Douglas McIlroy + for reporting bugs in manual pages + * Baptiste Daroussin and Eygene Ryabinkin (FreeBSD) + for an additional regression test + * Michal Nowak for reporting several code style issues + * TJ Townsend (OpenBSD) for help with CSS + * Sevan Janiyan (NetBSD) and Robert Mustacchi (Illumos) + for extensive release testing + * Job Snijders, Kinichiro INOGUCHI, and Martijn van Duren (OpenBSD) + for checking patches + * Bertrand Garrigues and Ralph Corderoy (GNU troff) for checking groff patches + Changes in version 1.14.5, released on March 10, 2019 --- MAJOR NEW FEATURES --- @@ -1,6 +1,6 @@ ************************************************************************ * Official mandoc TODO. -* $Id: TODO,v 1.295 2019/06/11 16:04:36 schwarze Exp $ +* $Id: TODO,v 1.319 2021/09/21 17:58:13 schwarze Exp $ ************************************************************************ Many issues are annotated for difficulty as follows: @@ -33,6 +33,43 @@ Obviously, as the issues have not been solved yet, these annotations are mere guesses, and some may be wrong. ************************************************************************ +* assertion failures +************************************************************************ + +- .if n .ce in the middle of .TS data + afl case f1/id:000103,sig:06,src:009024+009105,op:splice,rep:2 (jes@) + While roff_parseln() prevents .ce and similar requests in the middle + of a tbl, the guard is no longer effective when the .ce is wrapped + in a roff block, for example a conditional. The resulting assertion + has never been seen in any real-world manual page. + This is too dangerous to fix before release because it requires + reorganizing the very delicate internals of roff_parseln(), + which risks causing more severe bugs. + loc * exist *** algo *** size * imp * + + +************************************************************************ +* bugs: invalid output +************************************************************************ + +- wrong number of layout columns in tbl(7) code generated by -T man + https://savannah.gnu.org/bugs/?57720 + The reason likely is that tbl(7) does not support the -Bl -column + feature of not explicitly specifying the last table column. + loc ** exist * algo ** size * imp *** + +- eqn(7) delimiters cause conditional lines to misbehave + nabijaczleweli 8 Sep 2021 15:24:48 +0200 + loc * exist *** algo *** size * imp * + +- roff.c, roff_expand() should not remove blanks before comments + to Oliver Corff, Sep 7, 2021 + loc * exist * algo * size * imp * + but watch out for regressions in the high-level parsers + maybe it should not even remove comments? - consider T{\" + + +************************************************************************ * missing features ************************************************************************ @@ -83,8 +120,39 @@ are mere guesses, and some may be wrong. Jan Stary 20 Apr 2019 20:16:54 +0200 loc * exist *** algo *** size ** imp * +- mandoc replaces all ASCII control characters except tab and line feed + with '?' during input. It would be better to replace them with + Unicode escapes in preconv_encode() or somewhere in the vicinity, + such that the already existing better replacement strings show + up in the output. Emulating groff is not desirable: groff replaces + 0x00, 0x0b, and 0x0d to 0x1f with the empty string (bad because + that's easy to overlook for the document author), 0x01 with '.' + (very confusing), and passes through 0x02 to 0x08, 0x0c, and 0x7f + raw (bad because that is insecure output). Remember that 0x07 may + need special handling because it is sometimes used for certain + delimiters, so it may need handling *after* roff.c rather than before. + reminded by John Gardner 16 Jun 2020 14:26:28 +1000 + Actually, more ASCII control characters than just 0x07 may need + later handling because they can for example be used in macro names. + So they may need handling after roff(7) processing. + pointed out by John Gardner 23 Jun 2020 18:28:08 +1000 + more info from John Gardner 29 Jun 2020 19:54:04 +1000 + loc ** exist ** algo ** size ** imp * + +- many missing features used in old groff_char(7), + some can possibly be supported + kamil at netbsd 12 Nov 2020 17:27:09 +0100 + reply + +- \s with arbitrary arg delimiters as already supported for other escapes + found following jmc@'s mail 28 Apr 2021 18:31:41 +0100 + loc * exist * algo * size * imp * + --- missing mdoc features ---------------------------------------------- +- .Sh and .Ss should be parsed and partially callable, see groff_mdoc(7) + reed at reedmedia dot net Sat, 21 Dec 2019 17:13:07 -0600 + loc ** exist ** algo ** size ** imp * + - .Bl -column .Xo support is missing ultimate goal: restore .Xr and .Dv to @@ -153,6 +221,13 @@ are mere guesses, and some may be wrong. --- missing man features ----------------------------------------------- +- MANWIDTH + Markus Waldeck <waldeck at gmx dot de> 9 Jun 2015 05:49:56 +0200 + Laura Morales <lauretas at mail dot com> 26 Apr 2018 08:15:55 +0200 + Kamil Rytarowski <kamil at netbsd> 13 Nov 2020 00:19:36 +0100 + patch from Kamil 13 Nov 2020 22:37:07 +0100 + loc * exist * algo * size * imp * + - groff_www(7) .MTO and .URL These macros were used by the GNU grep(1) man page. The groff_www(7) manual page itself uses them, too. @@ -217,6 +292,21 @@ are mere guesses, and some may be wrong. --- missing misc features ---------------------------------------------- +- conisder whether man(1) fallback code in main.c/fs_*() can find files + like man3c/fopen.3c (illumos, Solaris) and man3p/fopen.3p (POSIX) + discussed with Robert Mustacchi 21 Sep 2021 10:39:40 -0700 + loc * exist * algo ** size * imp ** + +- let makewhatis(8) follow symbolic links to dirs below READ_ALLOWED_PATH + this may be feasible using fts_set(FTS_FOLLOW) + mail to sternenseemann 19 Aug 2021 19:11:50 +0200 + loc * exist ** algo ** size * imp ** + +- -T man does not handle eqn(7) and tbl(7) + Stephen Gregoratto 16 Feb 2020 01:28:07 +1100 + also https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=901636 + loc ** exist ** algo ** size *** imp ** + - man -ks 1,8 route; kn@ Jul 13, 2018 orally - italic correction (\/) in PostScript mode @@ -291,6 +381,10 @@ are mere guesses, and some may be wrong. - check features of the Slackware man.conf(5) format Carsten Kunze Wed, 11 Mar 2015 17:57:24 +0100 +- look at http://www.snake.net/software/troffcvt/ (troff to HTML) + mentioned by Oliver Corff 22 Jan 2021 01:36:49 +0100 + + ************************************************************************ * formatting issues: ugly output ************************************************************************ @@ -370,10 +464,29 @@ are mere guesses, and some may be wrong. 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 ** + loc ** exist ** algo ** size * imp ** --- HTML issues -------------------------------------------------------- +- make the HTML scaffolding customozable with -O skip=... + mail to Oliver Corff 3 Jun 2021 17:28:02 +0200 + more feedback from Oliver 3 Jun 2021 18:27:56 +0200 + more feedback from Oliver 3 Jun 2021 23:37:18 +0200 + +- .Bd -unfilled should not use monospaced font + anton@ 4 Mar 2021 08:19:35 +0100 + loc ** exist * algo * size * imp ** + +- HTML formatting of .nf should avoid <br/> + and not close and re-open <pre> on .P + my mail to ports@ 27 Jun 2021 16:09:20 +0200 + loc ** exist ** algo * size * imp ** + +- get rid of the last handful of style= attributes such that + Content-Security-Policy: can be enabled without unsafe-inline + suggested by bentley@ Nov 10, 2019 at 06:02:49AM -0700 + loc * exist * algo * size * imp ** + - .Bf at the beginning of a paragraph inserts a bogus 1ex horizontal space, see for example random(3). Introduced in http://mdocml.bsd.lv/cgi-bin/cvsweb/mdoc_html.c.diff?r1=1.91&r2=1.92 @@ -386,6 +499,11 @@ are mere guesses, and some may be wrong. https://github.com/Debian/debiman/issues/15 loc * exist * algo ** size ** imp ** +- space characters can end up in href= attributes, for example coming + from the first .Xr argument (where they make no sense, but still); + does this affect other characters, other source macros...? + Jackson Pauls 29 Aug 2017 16:56:27 +0100 + - The tables used to render the three-part page headers actually force the width of the <body> to the max-width given for <html>. Not yet sure how to fix that... @@ -470,6 +588,10 @@ are mere guesses, and some may be wrong. * warning issues ************************************************************************ +- shorten/simplify error messages for usage errors + To: deraadt@ 25 Oct 2020 23:37:01 +0100 + loc ** exist * algo * size ** imp *** + - warn about duplicate .Sh/.Ss heads gre(4): Rename duplicate sections 20 Apr 2018 15:27:33 +0200 loc * exist * algo * size * imp ** @@ -505,6 +627,10 @@ are mere guesses, and some may be wrong. output without intervening whitespace, in particular after a macro line (from the mdoclint TODO) +- report double .TH in man(7) as an ERROR and let the first win + kristaps@ 28 Mar 2021 13:30:41 +0200 + loc * exist * algo * size * imp * + - makewhatis -p complains about language subdirectories: /usr/local/man//ru: Unknown directory part @@ -553,6 +679,9 @@ are mere guesses, and some may be wrong. * CGI issues ************************************************************************ + - Inspect httpd(8) logs on man.openbsd.org and consider + whether logging can be improved, where bad syntax comes from, + and what needs to be done to get rid of COMPAT_OLDURI. - Enable HTTP compression by detecting gzip encoding and filtering output through libz. - Privilege separation (see OpenSSH). @@ -562,6 +691,15 @@ are mere guesses, and some may be wrong. * to improve in the groff_mdoc(7) macros ************************************************************************ +- delete OS release verification from .Dx, .Fx, .Nx, .Ox etc. + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=629161 + also Branden Robinson 18 Dec 2019 00:59:52 +1100 + +- Can the distinction between .Vt and .Va be made stricter, + recommending .Vt extern char * Ns Va optarg ; ? + What about the block macro properties of .Vt in the SYNOPSIS? + zeurkous 25 Dec 2019 08:48:36 +0100 + - .Cd # arch1, arch2 in section 4 pages: find better way to indicate multiple architectures, maybe: allow .Dt vgafb 4 "macppc sparc64" diff --git a/apropos.1 b/apropos.1 index 01d2a6bfa4f4..9129ae31f126 100644 --- a/apropos.1 +++ b/apropos.1 @@ -1,4 +1,4 @@ -.\" $Id: apropos.1,v 1.49 2018/11/22 12:33:52 schwarze Exp $ +.\" $Id: apropos.1,v 1.51 2020/10/01 22:50:00 schwarze Exp $ .\" .\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2011,2012,2014,2017,2018 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: November 22 2018 $ +.Dd $Mdocdate: October 1 2020 $ .Dt APROPOS 1 .Os .Sh NAME @@ -73,7 +73,7 @@ would. If the standard output is a terminal device and .Fl c is not specified, use -.Xr more 1 +.Xr less 1 to paginate them. In .Fl a @@ -340,7 +340,7 @@ types appearing in function arguments in the SYNOPSIS Any non-empty value of the environment variable .Ev MANPAGER is used instead of the standard pagination program, -.Xr more 1 ; +.Xr less 1 ; see .Xr man 1 for details. @@ -363,8 +363,7 @@ Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, -.Xr more 1 -.Fl s +.Xr less 1 is used. Only used if .Fl a @@ -404,6 +403,10 @@ Search in names and descriptions using a case-sensitive regular expression: .Pp .Dl $ apropos \(aq\(tiset.?[ug]id\(aq .Pp +Search for all manual pages in a given section: +.Pp +.Dl $ apropos \-s 9 \&. +.Pp Search for manuals in the library section mentioning both the .Qq optind and the @@ -1,4 +1,4 @@ -/* $Id: arch.c,v 1.15 2019/05/21 07:52:00 schwarze Exp $ */ +/* $Id: arch.c,v 1.17 2021/05/13 13:33:11 schwarze Exp $ */ /* * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org> * @@ -26,7 +26,7 @@ arch_valid(const char *arch, enum mandoc_os os) const char *openbsd_arch[] = { "alpha", "amd64", "arm64", "armv7", "hppa", "i386", "landisk", "loongson", "luna88k", "macppc", "mips64", - "octeon", "sgi", "sparc64", NULL + "octeon", "powerpc64", "riscv64", "sparc64", NULL }; const char *netbsd_arch[] = { "acorn26", "acorn32", "algor", "alpha", "amiga", @@ -1,4 +1,4 @@ -/* $Id: att.c,v 1.18 2018/12/13 11:55:46 schwarze Exp $ */ +/* $Id: att.c,v 1.19 2021/09/04 20:26:43 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -37,7 +37,7 @@ mdoc_a2att(const char *p) LINE("v5", "Version\\~5 AT&T UNIX"); LINE("v6", "Version\\~6 AT&T UNIX"); LINE("v7", "Version\\~7 AT&T UNIX"); - LINE("32v", "Version\\~32V AT&T UNIX"); + LINE("32v", "Version\\~7 AT&T UNIX/32V"); LINE("III", "AT&T System\\~III UNIX"); LINE("V", "AT&T System\\~V UNIX"); LINE("V.1", "AT&T System\\~V Release\\~1 UNIX"); @@ -1,4 +1,4 @@ -/* $Id: catman.c,v 1.21 2017/02/18 12:24:24 schwarze Exp $ */ +/* $Id: catman.c,v 1.22 2020/06/14 23:40:31 schwarze Exp $ */ /* * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> @@ -17,7 +17,7 @@ */ #include "config.h" -#if HAVE_CMSG_XPG42 +#if NEED_XPG4_2 #define _XPG4_2 #endif @@ -1,7 +1,7 @@ -/* $Id: cgi.c,v 1.167 2019/07/10 12:49:20 schwarze Exp $ */ +/* $Id: cgi.c,v 1.175 2021/08/19 15:23:36 schwarze Exp $ */ /* + * Copyright (c) 2014-2019, 2021 Ingo Schwarze <schwarze@usta.de> * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2016, 2017, 2018 Ingo Schwarze <schwarze@usta.de> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Implementation of the man.cgi(8) program. */ #include "config.h" @@ -70,14 +72,15 @@ enum focus { static void html_print(const char *); static void html_putchar(char); static int http_decode(char *); -static void http_encode(const char *p); +static void http_encode(const char *); static void parse_manpath_conf(struct req *); -static void parse_path_info(struct req *req, const char *path); +static void parse_path_info(struct req *, const char *); static void parse_query_string(struct req *, const char *); 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_noresult(const struct req *, int, const char *, + 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 *, @@ -119,16 +122,18 @@ 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", "arm64", - "hppa", "i386", "landisk", - "loongson", "luna88k", "macppc", "mips64", - "octeon", "sgi", "socppc", "sparc64", + "amd64", "alpha", "armv7", "arm64", + "hppa", "i386", "landisk", "loongson", + "luna88k", "macppc", "mips64", "octeon", + "powerpc64", "riscv64", "sparc64", + "amiga", "arc", "armish", "arm32", "atari", "aviion", "beagle", "cats", "hppa64", "hp300", "ia64", "mac68k", "mvme68k", "mvme88k", "mvmeppc", "palm", "pc532", "pegasos", - "pmax", "powerpc", "solbourne", "sparc", + "pmax", "powerpc", "sgi", "socppc", + "solbourne", "sparc", "sun3", "vax", "wgrisc", "x68k", "zaurus" }; @@ -339,6 +344,8 @@ resp_begin_http(int code, const char *msg) printf("Content-Type: text/html; charset=utf-8\r\n" "Cache-Control: no-cache\r\n" + "Content-Security-Policy: default-src 'none'; " + "style-src 'self' 'unsafe-inline'\r\n" "Pragma: no-cache\r\n" "\r\n"); @@ -363,7 +370,8 @@ resp_copy(const char *filename) static void resp_begin_html(int code, const char *msg, const char *file) { - char *cp; + const char *name, *sec, *cp; + int namesz, secsz; resp_begin_http(code, msg); @@ -378,12 +386,27 @@ resp_begin_html(int code, const char *msg, const char *file) " <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); + cp = strrchr(file, '/'); + name = cp == NULL ? file : cp + 1; + cp = strrchr(name, '.'); + namesz = cp == NULL ? strlen(name) : cp - name; + sec = NULL; + if (cp != NULL && cp[1] != '0') { + sec = cp + 1; + secsz = strlen(sec); + } else if (name - file > 1) { + for (cp = name - 2; cp >= file; cp--) { + if (*cp < '1' || *cp > '9') + continue; + sec = cp; + secsz = name - cp - 1; + break; + } + } + printf("%.*s", namesz, name); + if (sec != NULL) + printf("(%.*s)", secsz, sec); + fputs(" - ", stdout); } printf("%s</title>\n" "</head>\n" @@ -408,7 +431,8 @@ resp_searchform(const struct req *req, enum focus focus) { int i; - printf("<form action=\"/%s\" method=\"get\">\n" + printf("<form action=\"/%s\" method=\"get\" " + "autocomplete=\"off\" autocapitalize=\"none\">\n" " <fieldset>\n" " <legend>Manual Page Search Parameters</legend>\n", scriptname); @@ -546,12 +570,13 @@ pg_index(const struct req *req) } static void -pg_noresult(const struct req *req, const char *msg) +pg_noresult(const struct req *req, int code, const char *http_msg, + const char *user_msg) { - resp_begin_html(200, NULL, NULL); + resp_begin_html(code, http_msg, NULL); resp_searchform(req, FOCUS_QUERY); puts("<p>"); - puts(msg); + puts(user_msg); puts("</p>"); resp_end_html(); } @@ -1016,9 +1041,10 @@ pg_search(const struct req *req) 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."); + pg_noresult(req, 400, "Bad Request", + "You entered an invalid query."); else if (ressz == 0) - pg_noresult(req, "No results found."); + pg_noresult(req, 404, "Not Found", "No results found."); else pg_searchres(req, res, ressz); @@ -1,7 +1,8 @@ -/* $Id: chars.c,v 1.78 2018/12/15 19:30:26 schwarze Exp $ */ +/* $Id: chars.c,v 1.79 2020/02/13 16:18:29 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2020 + * Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -47,7 +48,7 @@ static struct ln lines[] = { /* Spacing. */ { " ", ascii_nbrsp, 0x00a0 }, { "~", ascii_nbrsp, 0x00a0 }, - { "0", " ", 0x2002 }, + { "0", ascii_nbrsp, 0x00a0 }, { ":", ascii_break, 0 }, /* Lines. */ diff --git a/compat_err.c b/compat_err.c index d8b09cb2a307..31994cbfaee7 100644 --- a/compat_err.c +++ b/compat_err.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_ERR - -int dummy; - -#else - -/* $Id: compat_err.c,v 1.4 2015/11/26 07:42:11 schwarze Exp $ */ +/* $Id: compat_err.c,v 1.5 2020/06/15 01:37:14 schwarze Exp $ */ /* * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. @@ -35,6 +27,7 @@ int dummy; * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include "config.h" #include <errno.h> #include <stdarg.h> @@ -108,5 +101,3 @@ warnx(const char *fmt, ...) va_end(ap); fputc('\n', stderr); } - -#endif diff --git a/compat_fts.c b/compat_fts.c index 3859111a52f8..e44298df02b8 100644 --- a/compat_fts.c +++ b/compat_fts.c @@ -1,13 +1,5 @@ -#include "config.h" - -#if HAVE_FTS - -int dummy; - -#else - -/* $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 $ */ +/* $Id: compat_fts.c,v 1.17 2020/06/15 01:37:14 schwarze Exp $ */ +/* $OpenBSD: fts.c,v 1.59 2019/06/28 13:32:41 deraadt Exp $ */ /*- * Copyright (c) 1990, 1993, 1994 @@ -37,6 +29,7 @@ int dummy; * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include "config.h" #include <sys/stat.h> #include <sys/types.h> @@ -62,6 +55,8 @@ static int fts_palloc(FTS *, size_t); static FTSENT *fts_sort(FTS *, FTSENT *, int); static unsigned short fts_stat(FTS *, FTSENT *); +typedef int (*qsort_compar_proto)(const void *, const void *); + #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) #ifndef O_CLOEXEC #define O_CLOEXEC 0 @@ -573,19 +568,20 @@ fts_sort(FTS *sp, FTSENT *head, int nitems) if (nitems > sp->fts_nitems) { struct _ftsent **a; - sp->fts_nitems = nitems + 40; if ((a = reallocarray(sp->fts_array, - sp->fts_nitems, sizeof(FTSENT *))) == NULL) { + nitems + 40, sizeof(FTSENT *))) == NULL) { free(sp->fts_array); sp->fts_array = NULL; sp->fts_nitems = 0; return (head); } + sp->fts_nitems = nitems + 40; sp->fts_array = a; } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; - qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); + qsort(sp->fts_array, nitems, sizeof(FTSENT *), + (qsort_compar_proto)sp->fts_compar); for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; @@ -648,13 +644,14 @@ fts_palloc(FTS *sp, size_t more) errno = ENAMETOOLONG; return (1); } - sp->fts_pathlen += more; - p = realloc(sp->fts_path, sp->fts_pathlen); + p = recallocarray(sp->fts_path, sp->fts_pathlen, + sp->fts_pathlen + more, 1); if (p == NULL) { free(sp->fts_path); sp->fts_path = NULL; return (1); } + sp->fts_pathlen += more; sp->fts_path = p; return (0); } @@ -697,5 +694,3 @@ fts_maxarglen(char * const *argv) max = len; return (max + 1); } - -#endif diff --git a/compat_fts.h b/compat_fts.h index f4a97a4c3a56..ca3f3a7ed804 100644 --- a/compat_fts.h +++ b/compat_fts.h @@ -43,7 +43,8 @@ typedef struct { char *fts_path; /* path for this descent */ size_t fts_pathlen; /* sizeof(path) */ int fts_nitems; /* elements in the sort array */ - int (*fts_compar)(); /* compare function */ + int (*fts_compar)(const struct _ftsent **, const struct _ftsent **); + /* compare function */ #define FTS_NOCHDIR 0x0004 /* don't change directories */ #define FTS_PHYSICAL 0x0010 /* physical walk */ diff --git a/compat_getline.c b/compat_getline.c index aed4754ffdae..843a94a6bb93 100644 --- a/compat_getline.c +++ b/compat_getline.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_GETLINE - -int dummy; - -#else - -/* $Id: compat_getline.c,v 1.1 2015/11/07 20:52:52 schwarze Exp $ */ +/* $Id: compat_getline.c,v 1.2 2020/06/15 01:37:14 schwarze Exp $ */ /* * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -22,6 +14,7 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> #include <errno.h> @@ -64,5 +57,3 @@ getline(char **buf, size_t *bufsz, FILE *fp) return pos; } } - -#endif diff --git a/compat_getsubopt.c b/compat_getsubopt.c index 880f2f789d4a..c442f87ecab7 100644 --- a/compat_getsubopt.c +++ b/compat_getsubopt.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_GETSUBOPT - -int dummy; - -#else - -/* $Id: compat_getsubopt.c,v 1.5 2014/08/17 20:53:50 schwarze Exp $ */ +/* $Id: compat_getsubopt.c,v 1.6 2020/06/15 01:37:15 schwarze Exp $ */ /* $OpenBSD: getsubopt.c,v 1.4 2005/08/08 08:05:36 espie Exp $ */ /*- @@ -37,6 +29,7 @@ int dummy; * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include "config.h" #include <unistd.h> #include <stdlib.h> @@ -92,5 +85,3 @@ getsubopt(char **optionp, char * const *tokens, char **valuep) return(cnt); return(-1); } - -#endif diff --git a/compat_isblank.c b/compat_isblank.c index 9e3c74706c3d..38d1d2839eb8 100644 --- a/compat_isblank.c +++ b/compat_isblank.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_ISBLANK - -int dummy; - -#else - -/* $Id: compat_isblank.c,v 1.2 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: compat_isblank.c,v 1.3 2020/06/15 01:37:15 schwarze Exp $ */ /* * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -22,12 +14,10 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" int isblank(int c) { - return c == ' ' || c == '\t'; } - -#endif diff --git a/compat_mkdtemp.c b/compat_mkdtemp.c index 1fcb325f4f9a..c2edfcb1927b 100644 --- a/compat_mkdtemp.c +++ b/compat_mkdtemp.c @@ -1,14 +1,6 @@ -#include "config.h" - -#if HAVE_MKDTEMP - -int dummy; - -#else - -/* $Id: compat_mkdtemp.c,v 1.2 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: compat_mkdtemp.c,v 1.4 2021/09/19 15:02:55 schwarze Exp $ */ /* - * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2015, 2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,6 +17,7 @@ int dummy; * The algorithm of this function is inspired by OpenBSD mkdtemp(3) * by Theo de Raadt and Todd Miller, but the code differs. */ +#include "config.h" #include <sys/stat.h> #include <errno.h> @@ -43,19 +36,15 @@ mkdtemp(char *path) start--; for (tries = INT_MAX; tries; tries--) { - if (mktemp(path) == NULL) { - errno = EEXIST; + if (mktemp(path) == NULL) return NULL; - } if (mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) == 0) return path; - if (errno != EEXIST) - return NULL; for (cp = start; *cp != '\0'; cp++) *cp = 'X'; + if (errno != EEXIST) + return NULL; } errno = EEXIST; return NULL; } - -#endif diff --git a/compat_mkstemps.c b/compat_mkstemps.c new file mode 100644 index 000000000000..32394ffa682e --- /dev/null +++ b/compat_mkstemps.c @@ -0,0 +1,63 @@ +/* $Id: compat_mkstemps.c,v 1.1 2021/09/19 15:05:39 schwarze Exp $ */ +/* + * Copyright (c) 2015, 2021 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * 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. + * + * Parts of the algorithm of this function are inspired by OpenBSD + * mkdtemp(3) by Theo de Raadt and Todd Miller, but the code differs. + */ +#include "config.h" + +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +int +mkstemps(char *path, int suffixlen) +{ + char *start, *end, *cp; + int fd, tries; + char backup; + + end = strchr(path, '\0'); + if (suffixlen < 0 || suffixlen > end - path - 6) { + errno = EINVAL; + return -1; + } + end -= suffixlen; + for (start = end; start > path; start--) + if (start[-1] != 'X') + break; + + backup = *end; + for (tries = INT_MAX; tries; tries--) { + *end = '\0'; + cp = mktemp(path); + *end = backup; + if (cp == NULL) + return -1; + fd = open(path, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); + if (fd != -1) + return fd; + for (cp = start; cp < end; cp++) + *cp = 'X'; + if (errno != EEXIST) + return -1; + } + errno = EEXIST; + return -1; +} diff --git a/compat_ohash.c b/compat_ohash.c index cbd6052182f2..f29c086c6d7d 100644 --- a/compat_ohash.c +++ b/compat_ohash.c @@ -1,11 +1,4 @@ -#include "config.h" - -#if HAVE_OHASH - -int dummy; - -#else - +/* $Id: compat_ohash.c,v 1.7 2020/06/15 01:37:15 schwarze Exp $ */ /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */ /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> @@ -22,9 +15,9 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> - #include <stddef.h> #include <stdint.h> #include <stdlib.h> @@ -335,5 +328,3 @@ ohash_qlookupi(struct ohash *h, const char *s, const char **e) hv = ohash_interval(s, e); return ohash_lookup_interval(h, s, *e, hv); } - -#endif /*!HAVE_OHASH*/ diff --git a/compat_progname.c b/compat_progname.c index 9840cc7c6c61..e067e489c6b7 100644 --- a/compat_progname.c +++ b/compat_progname.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_PROGNAME - -int dummy; - -#else - -/* $Id: compat_progname.c,v 1.1 2015/11/06 16:30:33 schwarze Exp $ */ +/* $Id: compat_progname.c,v 1.2 2020/06/15 01:37:15 schwarze Exp $ */ /* * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -22,21 +14,18 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" static const char *progname; void setprogname(const char *name) { - progname = name; } const char * getprogname(void) { - return progname; } - -#endif diff --git a/compat_reallocarray.c b/compat_reallocarray.c index 66151904257d..a12cec1344cb 100644 --- a/compat_reallocarray.c +++ b/compat_reallocarray.c @@ -1,13 +1,5 @@ -#include "config.h" - -#if HAVE_REALLOCARRAY - -int dummy; - -#else - -/* $Id: compat_reallocarray.c,v 1.4 2014/12/11 09:05:01 schwarze Exp $ */ -/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ +/* $Id: compat_reallocarray.c,v 1.5 2020/06/15 01:37:15 schwarze Exp $ */ +/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> * @@ -23,6 +15,7 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> #include <errno.h> @@ -45,5 +38,3 @@ reallocarray(void *optr, size_t nmemb, size_t size) } return realloc(optr, size * nmemb); } - -#endif /*!HAVE_REALLOCARRAY*/ diff --git a/compat_recallocarray.c b/compat_recallocarray.c index d86dc5091a0a..723e145c0446 100644 --- a/compat_recallocarray.c +++ b/compat_recallocarray.c @@ -1,15 +1,7 @@ -#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 $ */ +/* $Id: compat_recallocarray.c,v 1.2 2020/06/15 01:37:15 schwarze Exp $ */ +/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */ /* - * Copyright (c) 2017 Otto Moerbeek <otto@drijf.net> + * Copyright (c) 2008, 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 @@ -23,18 +15,19 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> #include <errno.h> -#include <stdint.h> #include <stdlib.h> +#include <stdint.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)) +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) /* * Even though specified in POSIX, the PAGESIZE and PAGE_SIZE @@ -42,7 +35,7 @@ int dummy; * to avoid free() overhead for small shrinking, simply pick * an arbitrary number. */ -#define MALLOC_PAGESIZE (1UL << 12) +#define getpagesize() (1UL << 12) void * @@ -75,7 +68,7 @@ recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) if (newsize <= oldsize) { size_t d = oldsize - newsize; - if (d < oldsize / 2 && d < MALLOC_PAGESIZE) { + if (d < oldsize / 2 && d < getpagesize()) { memset((char *)ptr + newsize, 0, d); return ptr; } @@ -104,5 +97,3 @@ recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) return newptr; } - -#endif /* !HAVE_RECALLOCARRAY */ diff --git a/compat_strcasestr.c b/compat_strcasestr.c index 62c0ff740c81..5eba8257fdb0 100644 --- a/compat_strcasestr.c +++ b/compat_strcasestr.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_STRCASESTR - -int dummy; - -#else - -/* $Id: compat_strcasestr.c,v 1.4 2014/12/11 09:19:32 schwarze Exp $ */ +/* $Id: compat_strcasestr.c,v 1.5 2020/06/15 01:37:15 schwarze Exp $ */ /* $NetBSD: strcasestr.c,v 1.3 2005/11/29 03:12:00 christos Exp $ */ /*- @@ -40,6 +32,7 @@ int dummy; * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include "config.h" #include <sys/types.h> #include <ctype.h> @@ -69,5 +62,3 @@ strcasestr(const char *s, const char *find) } return __UNCONST(s); } - -#endif diff --git a/compat_stringlist.c b/compat_stringlist.c index 17eba7724af5..fb8e56aebaa4 100644 --- a/compat_stringlist.c +++ b/compat_stringlist.c @@ -1,16 +1,13 @@ -#include "config.h" - -#if HAVE_STRINGLIST - -int dummy; - -#else +/* $Id: compat_stringlist.c,v 1.8 2020/06/15 21:48:09 schwarze Exp $ */ +/* $NetBSD: stringlist.c,v 1.14 2015/05/21 01:29:13 christos Exp $ */ -/* $Id: compat_stringlist.c,v 1.6 2015/11/07 14:22:29 schwarze Exp $ */ -/* - * Copyright (c) 1994 Christos Zoulas <christos@netbsd.org> +/*- + * Copyright (c) 1994, 1999 The NetBSD Foundation, Inc. * All rights reserved. * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -20,22 +17,20 @@ int dummy; * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" -#if HAVE_ERR -#include <err.h> -#endif #include <stdlib.h> #include <string.h> #include "compat_stringlist.h" @@ -52,13 +47,15 @@ sl_init(void) sl = malloc(sizeof(StringList)); if (sl == NULL) - err(1, "stringlist"); + return NULL; sl->sl_cur = 0; sl->sl_max = _SL_CHUNKSIZE; sl->sl_str = reallocarray(NULL, sl->sl_max, sizeof(char *)); - if (sl->sl_str == NULL) - err(1, "stringlist"); + if (sl->sl_str == NULL) { + free(sl); + sl = NULL; + } return sl; } @@ -70,14 +67,17 @@ int sl_add(StringList *sl, char *name) { if (sl->sl_cur == sl->sl_max - 1) { + char **new; + + new = reallocarray(sl->sl_str, (sl->sl_max + _SL_CHUNKSIZE), + sizeof(char *)); + if (new == NULL) + return -1; sl->sl_max += _SL_CHUNKSIZE; - sl->sl_str = reallocarray(sl->sl_str, - sl->sl_max, sizeof(char *)); - if (sl->sl_str == NULL) - return (-1); + sl->sl_str = new; } sl->sl_str[sl->sl_cur++] = name; - return (0); + return 0; } @@ -116,4 +116,20 @@ sl_find(StringList *sl, const char *name) return NULL; } -#endif +int +sl_delete(StringList *sl, const char *name, int all) +{ + size_t i, j; + + for (i = 0; i < sl->sl_cur; i++) + if (strcmp(sl->sl_str[i], name) == 0) { + if (all) + free(sl->sl_str[i]); + for (j = i + 1; j < sl->sl_cur; j++) + sl->sl_str[j - 1] = sl->sl_str[j]; + sl->sl_str[--sl->sl_cur] = NULL; + return 0; + } + return -1; +} + diff --git a/compat_stringlist.h b/compat_stringlist.h index f04e8435047d..5777b1cb2871 100644 --- a/compat_stringlist.h +++ b/compat_stringlist.h @@ -1,10 +1,13 @@ -/* $Id: compat_stringlist.h,v 1.4 2015/11/07 14:01:16 schwarze Exp $ */ -/* $NetBSD: stringlist.h,v 1.2 1997/01/17 06:11:36 lukem Exp $ */ +/* $Id: compat_stringlist.h,v 1.5 2020/06/15 21:48:09 schwarze Exp $ */ +/* $NetBSD: stringlist.h,v 1.7 2008/04/28 20:22:54 martin Exp $ */ -/* - * Copyright (c) 1994 Christos Zoulas <christos@netbsd.org> +/*- + * Copyright (c) 1994 The NetBSD Foundation, Inc. * All rights reserved. * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -14,17 +17,17 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ #include <sys/types.h> @@ -38,8 +41,8 @@ typedef struct _stringlist { size_t sl_cur; } StringList; - -StringList *sl_init(void); -int sl_add(StringList *, char *); -void sl_free(StringList *, int); -char *sl_find(StringList *, const char *); +StringList *sl_init(void); +int sl_add(StringList *, char *); +void sl_free(StringList *, int); +char *sl_find(StringList *, const char *); +int sl_delete(StringList *, const char *, int); diff --git a/compat_strlcat.c b/compat_strlcat.c index acaae4fb1506..dc0ec5e79064 100644 --- a/compat_strlcat.c +++ b/compat_strlcat.c @@ -1,15 +1,8 @@ -#include "config.h" - -#if HAVE_STRLCAT - -int dummy; - -#else - -/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ +/* $Id: compat_strlcat.c,v 1.6 2020/06/15 20:49:57 schwarze Exp $ */ +/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */ /* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * Copyright (c) 1998, 2015 Todd C. Miller <millert@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,43 +16,42 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> #include <string.h> /* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. */ size_t -strlcat(char *dst, const char *src, size_t siz) +strlcat(char *dst, const char *src, size_t dsize) { - char *d = dst; - const char *s = src; - size_t n = siz; + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; size_t dlen; - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; n--; } - s++; + src++; } - *d = '\0'; + *dst = '\0'; - return(dlen + (s - src)); /* count does not include NUL */ + return(dlen + (src - osrc)); /* count does not include NUL */ } - -#endif diff --git a/compat_strlcpy.c b/compat_strlcpy.c index a00d511817a3..2e7201a64953 100644 --- a/compat_strlcpy.c +++ b/compat_strlcpy.c @@ -1,15 +1,8 @@ -#include "config.h" - -#if HAVE_STRLCPY - -int dummy; - -#else - -/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ +/* $Id: compat_strlcpy.c,v 1.6 2020/06/15 20:49:57 schwarze Exp $ */ +/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ /* - * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * Copyright (c) 1998, 2015 Todd C. Miller <millert@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,39 +16,37 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> #include <string.h> /* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t -strlcpy(char *dst, const char *src, size_t siz) +strlcpy(char *dst, const char *src, size_t dsize) { - char *d = dst; - const char *s = src; - size_t n = siz; + const char *osrc = src; + size_t nleft = dsize; - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') break; } } - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) ; } - return(s - src - 1); /* count does not include NUL */ + return(src - osrc - 1); /* count does not include NUL */ } - -#endif diff --git a/compat_strndup.c b/compat_strndup.c index 5e127364f5c8..89693e795ce9 100644 --- a/compat_strndup.c +++ b/compat_strndup.c @@ -1,15 +1,8 @@ -#include "config.h" - -#if HAVE_STRNDUP - -int dummy; +/* $Id: compat_strndup.c,v 1.3 2020/06/15 20:19:39 schwarze Exp $ */ +/* $OpenBSD: strndup.c,v 1.3 2019/01/25 00:19:25 millert Exp $ */ -#else - -/* $Id: compat_strndup.c,v 1.1 2018/02/27 11:16:23 schwarze Exp $ */ -/* OpenBSD: strndup.c,v 1.2 2015/08/31 02:53:57 guenther Exp */ /* - * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com> + * Copyright (c) 2010 Todd C. Miller <millert@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +16,7 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <sys/types.h> @@ -46,5 +40,3 @@ strndup(const char *str, size_t maxlen) return copy; } - -#endif diff --git a/compat_strsep.c b/compat_strsep.c index 1df57582802b..9765ac823eeb 100644 --- a/compat_strsep.c +++ b/compat_strsep.c @@ -1,13 +1,5 @@ -#include "config.h" - -#if HAVE_STRSEP - -int dummy; - -#else - -/* $Id: compat_strsep.c,v 1.4 2014/12/11 09:05:01 schwarze Exp $ */ -/* $OpenBSD: strsep.c,v 1.7 2014/02/05 20:42:32 stsp Exp $ */ +/* $Id: compat_strsep.c,v 1.5 2020/06/15 01:37:15 schwarze Exp $ */ +/* $OpenBSD: strsep.c,v 1.8 2015/08/31 02:53:57 guenther Exp $ */ /*- * Copyright (c) 1990, 1993 @@ -37,6 +29,7 @@ int dummy; * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include "config.h" /* * Get next token from string *stringp, where tokens are possibly-empty @@ -75,5 +68,3 @@ strsep(char **stringp, const char *delim) } /* NOTREACHED */ } - -#endif diff --git a/compat_strtonum.c b/compat_strtonum.c index 628e5d51b86b..ce0459fa8825 100644 --- a/compat_strtonum.c +++ b/compat_strtonum.c @@ -1,13 +1,5 @@ -#include "config.h" - -#if HAVE_STRTONUM - -int dummy; - -#else - -/* $Id: compat_strtonum.c,v 1.1 2015/02/16 14:56:22 schwarze Exp $ */ -/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ +/* $Id: compat_strtonum.c,v 1.2 2020/06/15 01:37:15 schwarze Exp $ */ +/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller @@ -25,6 +17,7 @@ int dummy; * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" #include <errno.h> #include <limits.h> @@ -72,5 +65,3 @@ strtonum(const char *numstr, long long minval, long long maxval, return (ll); } - -#endif /* !HAVE_STRTONUM */ diff --git a/compat_vasprintf.c b/compat_vasprintf.c index 9040822b0097..11556c0155b8 100644 --- a/compat_vasprintf.c +++ b/compat_vasprintf.c @@ -1,12 +1,4 @@ -#include "config.h" - -#if HAVE_VASPRINTF - -int dummy; - -#else - -/* $Id: compat_vasprintf.c,v 1.3 2015/10/06 18:32:19 schwarze Exp $ */ +/* $Id: compat_vasprintf.c,v 1.4 2020/06/15 01:37:15 schwarze Exp $ */ /* * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -28,6 +20,7 @@ int dummy; * printf(3) or completely reimplementing printf(3), i can't think * of another portable solution. */ +#include "config.h" #include <stdarg.h> #include <stdio.h> @@ -52,5 +45,3 @@ vasprintf(char **ret, const char *format, va_list ap) *ret = NULL; return -1; } - -#endif diff --git a/configure b/configure index 2df028da0510..5cf4e081c2cb 100755 --- a/configure +++ b/configure @@ -1,8 +1,8 @@ #!/bin/sh # -# $Id: configure,v 1.71 2019/07/01 22:56:24 schwarze Exp $ +# $Id: configure,v 1.81 2021/09/20 10:19:51 schwarze Exp $ # -# Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org> +# Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -33,7 +33,7 @@ echo "file config.log: writing..." # Initialize all variables here, # such that nothing can leak in from the environment. -SOURCEDIR=`dirname "$0"` +SOURCEDIR=`dirname "${0}"` MANPATH_BASE="/usr/share/man:/usr/X11R6/man" MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man" @@ -41,8 +41,10 @@ OSENUM= OSNAME= UTF8_LOCALE= -CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | env -i make -sf -` +AR=ar +CC=cc CFLAGS= +FATAL=0 LDADD= LDFLAGS= LD_NANOSLEEP= @@ -54,8 +56,8 @@ BUILD_CGI=0 BUILD_CATMAN=0 INSTALL_LIBMANDOC=0 +HAVE_ATTRIBUTE= HAVE_CMSG= -HAVE_CMSG_XPG42=0 HAVE_DIRENT_NAMLEN= HAVE_EFTYPE= HAVE_ENDIAN= @@ -67,6 +69,7 @@ HAVE_GETSUBOPT= HAVE_ISBLANK= HAVE_LESS_T= HAVE_MKDTEMP= +HAVE_MKSTEMPS= HAVE_NANOSLEEP= HAVE_NTOHL= HAVE_O_DIRECTORY= @@ -94,6 +97,10 @@ HAVE_WCHAR= NEED_GNU_SOURCE=0 NEED_OPENBSD_SOURCE=0 +NEED_XPG4_2=0 + +MANDOC_COBJS= +SOELIM_COBJS= PREFIX="/usr/local" BINDIR= @@ -102,7 +109,7 @@ BIN_FROM_SBIN= INCLUDEDIR= LIBDIR= MANDIR= -HOMEBREWDIR= +READ_ALLOWED_PATH= WWWPREFIX="/var/www" HTDOCDIR= @@ -114,6 +121,7 @@ BINM_MAKEWHATIS="makewhatis" BINM_MAN="man" BINM_SOELIM="soelim" BINM_WHATIS="whatis" +BINM_PAGER= MANM_MAN="man" MANM_MANCONF="man.conf" MANM_MDOC="mdoc" @@ -159,16 +167,17 @@ ismanual() { # In case of failure, do not decide anything yet. # Arguments: test file name, test var name, additional CFLAGS singletest() { - n=${1}${3}${4} + n=${1}${3} cat 1>&3 << __HEREDOC__ testing ${n} ... -${COMP} -o test-${1} test-${1}.c ${3} ${4} +${COMP} -o test-${1} test-${1}.c ${3} __HEREDOC__ - if ${COMP} -o "test-${1}" "${SOURCEDIR}/test-${1}.c" ${3} ${4} 1>&3 2>&3 + if ${COMP} -o "test-${1}" "${SOURCEDIR}/test-${1}.c" ${3} 1>&3 2>&3 then echo "partial result of ${n}: ${CC} succeeded" 1>&3 else + echo "tested ${n}: no (compilation failed)" 1>&2 echo "result of ${n}: ${CC} failed with exit status $?" 1>&3 echo "result of compiling ${n}: no" 1>&3 echo 1>&3 @@ -180,11 +189,16 @@ __HEREDOC__ echo "result of running ${n}: yes" 1>&3 echo 1>&3 eval HAVE_${2}=1 - [ "X$3" = "X-D_GNU_SOURCE" ] && NEED_GNU_SOURCE=1 - [ "X$3" = "X-D_OPENBSD_SOURCE" ] && NEED_OPENBSD_SOURCE=1 + [ "${3}" = "-D_GNU_SOURCE" ] && NEED_GNU_SOURCE=1 + [ "${3}" = "-D_OPENBSD_SOURCE" ] && NEED_OPENBSD_SOURCE=1 + [ "${3}" = "-D_XPG4_2" ] && NEED_XPG4_2=1 + [ "${3}" = "-lrt" ] && LD_NANOSLEEP="-lrt" + [ "${3}" = "-lsocket" ] && LD_RECVMSG="-lsocket" + [ "${3}" = "-lutil" ] && LD_OHASH="-lutil" rm "test-${1}" return 0 else + echo "tested ${n}: no (execution failed)" 1>&2 echo "result of ${n}: execution failed with exit status $?" 1>&3 echo "result of running ${n}: no" 1>&3 echo 1>&3 @@ -196,11 +210,12 @@ __HEREDOC__ # Run a complete autoconfiguration test, including the check for # a manual override and disabling the feature on failure. # Arguments: test file name, test var name, additional CFLAGS +# The final argument can optionally be repeated a second time. runtest() { eval _manual=\${HAVE_${2}} ismanual "${1}" "${2}" "${_manual}" && return 0 - singletest "${1}" "${2}" "${3}" "${4}" && return 0 - echo "tested ${1}${3}${4}: no" 1>&2 + singletest "${1}" "${2}" "${3}" && return 0 + [ -n "${4}" ] && singletest "${1}" "${2}" "${4}" && return 0 eval HAVE_${2}=0 return 1 } @@ -208,7 +223,7 @@ runtest() { # Select a UTF-8 locale. get_locale() { [ -n "${HAVE_WCHAR}" ] && [ "${HAVE_WCHAR}" -eq 0 ] && return 0 - ismanual UTF8_LOCALE UTF8_LOCALE "$UTF8_LOCALE" && return 0 + ismanual UTF8_LOCALE UTF8_LOCALE "${UTF8_LOCALE}" && return 0 echo "testing UTF8_LOCALE ..." 1>&3 UTF8_LOCALE=`locale -a | grep -i '^en_US\.UTF-*8$' | head -n 1` if [ -z "${UTF8_LOCALE}" ]; then @@ -228,9 +243,9 @@ if [ -n "${OSENUM}" ]; then echo "OSENUM specified manually: ${OSENUM}" 1>&3 else OSDETECT=`uname` - if [ "X${OSDETECT}" = "XNetBSD" ]; then + if [ "${OSDETECT}" = "NetBSD" ]; then OSENUM=MANDOC_OS_NETBSD - elif [ "X${OSDETECT}" = "XOpenBSD" ]; then + elif [ "${OSDETECT}" = "OpenBSD" ]; then OSENUM=MANDOC_OS_OPENBSD else OSENUM=MANDOC_OS_OTHER @@ -250,8 +265,8 @@ if [ -n "${CFLAGS}" ]; then else COMP="${CC} ${DEFCFLAGS} -Wno-unused -Werror" fi -echo -n "tested ${CC} -W: " 1>&2 -echo -n "testing ${CC} -W: " 1>&3 +printf "%s" "tested ${CC} -W: " 1>&2 +printf "%s" "testing ${CC} -W: " 1>&3 runtest noop WFLAG || true if [ -n "${CFLAGS}" ]; then @@ -283,51 +298,48 @@ fi # --- tests for config.h ---------------------------------------------- # --- library functions --- +runtest attribute ATTRIBUTE || true +runtest cmsg CMSG "" "-D_XPG4_2" || true runtest dirent-namlen DIRENT_NAMLEN || true runtest be32toh ENDIAN || true runtest be32toh SYS_ENDIAN -DSYS_ENDIAN || true runtest EFTYPE EFTYPE || true runtest err ERR || true runtest getline GETLINE || true -singletest getsubopt GETSUBOPT || \ - runtest getsubopt GETSUBOPT -D_GNU_SOURCE || true +runtest getsubopt GETSUBOPT "" -D_GNU_SOURCE || true runtest isblank ISBLANK || true runtest mkdtemp MKDTEMP || true +runtest mkstemps MKSTEMPS || true +runtest nanosleep NANOSLEEP "${LD_NANOSLEEP}" "-lrt" || 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 -singletest reallocarray REALLOCARRAY || \ - runtest reallocarray REALLOCARRAY -D_OPENBSD_SOURCE || true -singletest recallocarray RECALLOCARRAY || \ - runtest recallocarray RECALLOCARRAY -D_OPENBSD_SOURCE || true +runtest reallocarray REALLOCARRAY "" -D_OPENBSD_SOURCE || true +runtest recallocarray RECALLOCARRAY "" -D_OPENBSD_SOURCE || true +runtest recvmsg RECVMSG "${LD_RECVMSG}" "-lsocket" || true runtest rewb-bsd REWB_BSD || true runtest rewb-sysv REWB_SYSV || true -singletest strcasestr STRCASESTR || \ - runtest strcasestr STRCASESTR -D_GNU_SOURCE || true +runtest strcasestr STRCASESTR "" -D_GNU_SOURCE || true runtest stringlist STRINGLIST || true runtest strlcat STRLCAT || true runtest strlcpy STRLCPY || true runtest strndup STRNDUP || true -singletest strptime STRPTIME || \ - runtest strptime STRPTIME -D_GNU_SOURCE || true +runtest strptime STRPTIME "" -D_GNU_SOURCE || true runtest strsep STRSEP || true -singletest strtonum STRTONUM || \ - runtest strtonum STRTONUM -D_OPENBSD_SOURCE || true -singletest vasprintf VASPRINTF || \ - runtest vasprintf VASPRINTF -D_GNU_SOURCE || true - -if [ ${HAVE_ENDIAN} -eq 0 -a \ - ${HAVE_SYS_ENDIAN} -eq 0 -a \ - ${HAVE_NTOHL} -eq 0 ]; then - echo "FATAL: no endian conversion functions found" 1>&2 - echo "FATAL: no endian conversion functions found" 1>&3 - exit 1 -fi +runtest strtonum STRTONUM "" -D_OPENBSD_SOURCE || true +runtest vasprintf VASPRINTF "" -D_GNU_SOURCE || true -if ismanual fts FTS ${HAVE_FTS}; then +# --- fts --- +if [ "${1}" = "-depend" ]; then + HAVE_FTS=0 + HAVE_FTS_COMPARE_CONST=0 + echo "tested fts: HAVE_FTS=0 (for make depend)" 1>&2 + echo "tested fts: HAVE_FTS=0 (for make depend)" 1>&3 + echo 1>&3 +elif ismanual fts FTS ${HAVE_FTS}; then HAVE_FTS_COMPARE_CONST=0 elif runtest fts FTS_COMPARE_CONST -DFTS_COMPARE_CONST; then HAVE_FTS=1 @@ -335,25 +347,41 @@ else runtest fts FTS || true fi -if ismanual "less -T" LESS_T ${HAVE_LESS_T}; then +# --- pager --- +manual= +if [ -n "${BINM_PAGER}" ]; then + manual=" (manual)" +elif less test-noop.c 1>/dev/null 2>&3; then + BINM_PAGER=less + echo "tested less: yes" 1>&2 + echo "tested less: yes" 1>&3 +else + BINM_PAGER=more + echo "tested less: no" 1>&2 + echo "tested less: no" 1>&3 +fi +echo "selected BINM_PAGER=${BINM_PAGER}${manual}" 1>&2 +echo "selected BINM_PAGER=${BINM_PAGER}${manual}" 1>&3 + +# --- tagging support in the pager --- +if ismanual "${BINM_PAGER} -T" LESS_T ${HAVE_LESS_T}; then : -elif less -ET /dev/null test-noop.c 1>/dev/null 2>&3; then +elif ${BINM_PAGER} -T /dev/null test-noop.c 1>/dev/null 2>&3; then HAVE_LESS_T=1 - echo "tested less -T: yes" 1>&2 - echo "tested less -T: yes" 1>&3 + echo "tested ${BINM_PAGER} -T: yes" 1>&2 + echo "tested ${BINM_PAGER} -T: yes" 1>&3 echo 1>&3 else HAVE_LESS_T=0 - echo "tested less -T: no" 1>&2 - echo "tested less -T: no" 1>&3 + echo "tested ${BINM_PAGER} -T: no" 1>&2 + echo "tested ${BINM_PAGER} -T: no" 1>&3 echo 1>&3 fi # --- wide character and locale support --- if get_locale; then - singletest wchar WCHAR -DUTF8_LOCALE=\"${UTF8_LOCALE}\" || \ - runtest wchar WCHAR -D_GNU_SOURCE \ - -DUTF8_LOCALE=\"${UTF8_LOCALE}\" || true + runtest wchar WCHAR "-DUTF8_LOCALE=\"${UTF8_LOCALE}\"" \ + "-D_GNU_SOURCE -DUTF8_LOCALE=\"${UTF8_LOCALE}\"" || true else HAVE_WCHAR=0 echo "tested wchar: no (no UTF8_LOCALE)" 1>&2 @@ -361,63 +389,45 @@ else echo 1>&3 fi -# --- nanosleep --- -if [ -n "${LD_NANOSLEEP}" ]; then - runtest nanosleep NANOSLEEP "${LD_NANOSLEEP}" || true -elif singletest nanosleep NANOSLEEP; then - : -elif runtest nanosleep NANOSLEEP "-lrt"; then - LD_NANOSLEEP="-lrt" +# --- ohash --- +if [ "${1}" = "-depend" ]; then + HAVE_OHASH=0 + echo "tested ohash: HAVE_OHASH=0 (for make depend)" 1>&2 + echo "tested ohash: HAVE_OHASH=0 (for make depend)" 1>&3 + echo 1>&3 +else + runtest ohash OHASH "${LD_OHASH}" "-lutil" || true +fi +if [ "${HAVE_OHASH}" -eq 0 ]; then + LD_OHASH= +fi + +# --- required functions --- +if [ ${HAVE_ENDIAN} -eq 0 -a \ + ${HAVE_SYS_ENDIAN} -eq 0 -a \ + ${HAVE_NTOHL} -eq 0 ]; then + echo "FATAL: no endian conversion functions found" 1>&2 + echo "FATAL: no endian conversion functions found" 1>&3 + FATAL=1 fi if [ "${HAVE_NANOSLEEP}" -eq 0 ]; then echo "FATAL: nanosleep: no" 1>&2 echo "FATAL: nanosleep: no" 1>&3 - exit 1 + FATAL=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 - : -elif [ -n "${LD_OHASH}" ]; then - runtest ohash OHASH "${LD_OHASH}" || true -elif singletest ohash OHASH; then - : -elif runtest ohash OHASH "-lutil"; then - LD_OHASH="-lutil" +if [ ${BUILD_CATMAN} -gt 0 -a "${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 + FATAL=1 fi -if [ "${HAVE_OHASH}" -eq 0 ]; then - LD_OHASH= +if [ ${BUILD_CATMAN} -gt 0 -a "${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 + FATAL=1 fi +[ "${FATAL}" -eq 0 ] || exit 1 # --- LDADD --- LDADD="${LDADD} ${LD_NANOSLEEP} ${LD_RECVMSG} ${LD_OHASH} -lz" @@ -434,10 +444,6 @@ cat << __HEREDOC__ #error "Do not use C++. See the INSTALL file." #endif -#if !defined(__GNUC__) || (__GNUC__ < 4) -#define __attribute__(x) -#endif - __HEREDOC__ [ ${NEED_GNU_SOURCE} -eq 0 ] || echo "#define _GNU_SOURCE" @@ -458,7 +464,9 @@ echo "#define MANPATH_DEFAULT \"${MANPATH_DEFAULT}\"" echo "#define OSENUM ${OSENUM}" [ -n "${OSNAME}" ] && echo "#define OSNAME \"${OSNAME}\"" [ -n "${UTF8_LOCALE}" ] && echo "#define UTF8_LOCALE \"${UTF8_LOCALE}\"" -[ -n "${HOMEBREWDIR}" ] && echo "#define HOMEBREWDIR \"${HOMEBREWDIR}\"" +[ -n "${READ_ALLOWED_PATH}" ] \ + && echo "#define READ_ALLOWED_PATH \"${READ_ALLOWED_PATH}\"" +[ ${HAVE_ATTRIBUTE} -eq 0 ] && echo "#define __attribute__(x)" [ ${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" @@ -466,9 +474,8 @@ if [ ${HAVE_ENDIAN} -eq 0 -a ${HAVE_SYS_ENDIAN} -eq 0 ]; then echo "#define be32toh ntohl" echo "#define htobe32 htonl" 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} @@ -479,6 +486,7 @@ cat << __HEREDOC__ #define HAVE_ISBLANK ${HAVE_ISBLANK} #define HAVE_LESS_T ${HAVE_LESS_T} #define HAVE_MKDTEMP ${HAVE_MKDTEMP} +#define HAVE_MKSTEMPS ${HAVE_MKSTEMPS} #define HAVE_NTOHL ${HAVE_NTOHL} #define HAVE_PLEDGE ${HAVE_PLEDGE} #define HAVE_PROGNAME ${HAVE_PROGNAME} @@ -499,6 +507,7 @@ cat << __HEREDOC__ #define HAVE_VASPRINTF ${HAVE_VASPRINTF} #define HAVE_WCHAR ${HAVE_WCHAR} #define HAVE_OHASH ${HAVE_OHASH} +#define NEED_XPG4_2 ${NEED_XPG4_2} #define BINM_APROPOS "${BINM_APROPOS}" #define BINM_CATMAN "${BINM_CATMAN}" @@ -506,6 +515,7 @@ cat << __HEREDOC__ #define BINM_MAN "${BINM_MAN}" #define BINM_SOELIM "${BINM_SOELIM}" #define BINM_WHATIS "${BINM_WHATIS}" +#define BINM_PAGER "${BINM_PAGER}" __HEREDOC__ @@ -514,52 +524,82 @@ if [ ${HAVE_ERR} -eq 0 ]; then echo "extern void errx(int, const char *, ...);" echo "extern void warn(const char *, ...);" echo "extern void warnx(const char *, ...);" + MANDOC_COBJS="${MANDOC_COBJS} compat_err.o" + SOELIM_COBJS="${SOELIM_COBJS} compat_err.o" fi - -[ ${HAVE_GETLINE} -eq 0 ] && \ +if [ ${HAVE_FTS} -eq 0 ]; then + MANDOC_COBJS="${MANDOC_COBJS} compat_fts.o" +fi +if [ ${HAVE_GETLINE} -eq 0 ]; then echo "extern ssize_t getline(char **, size_t *, FILE *);" - -[ ${HAVE_GETSUBOPT} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_getline.o" + SOELIM_COBJS="${SOELIM_COBJS} compat_getline.o" +fi +if [ ${HAVE_GETSUBOPT} -eq 0 ]; then echo "extern int getsubopt(char **, char * const *, char **);" - -[ ${HAVE_ISBLANK} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_getsubopt.o" +fi +if [ ${HAVE_ISBLANK} -eq 0 ]; then echo "extern int isblank(int);" - -[ ${HAVE_MKDTEMP} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_isblank.o" +fi +if [ ${HAVE_MKDTEMP} -eq 0 ]; then echo "extern char *mkdtemp(char *);" - + MANDOC_COBJS="${MANDOC_COBJS} compat_mkdtemp.o" +fi +if [ ${HAVE_MKSTEMPS} -eq 0 ]; then + echo "extern int mkstemps(char *, int);" + MANDOC_COBJS="${MANDOC_COBJS} compat_mkstemps.o" +fi +if [ ${HAVE_OHASH} -eq 0 ]; then + MANDOC_COBJS="${MANDOC_COBJS} compat_ohash.o" +fi if [ ${HAVE_PROGNAME} -eq 0 ]; then echo "extern const char *getprogname(void);" echo "extern void setprogname(const char *);" + MANDOC_COBJS="${MANDOC_COBJS} compat_progname.o" + SOELIM_COBJS="${SOELIM_COBJS} compat_progname.o" fi - -[ ${HAVE_REALLOCARRAY} -eq 0 ] && \ +if [ ${HAVE_REALLOCARRAY} -eq 0 ]; then echo "extern void *reallocarray(void *, size_t, size_t);" - -[ ${HAVE_RECALLOCARRAY} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_reallocarray.o" + SOELIM_COBJS="${SOELIM_COBJS} compat_reallocarray.o" +fi +if [ ${HAVE_RECALLOCARRAY} -eq 0 ]; then echo "extern void *recallocarray(void *, size_t, size_t, size_t);" - -[ ${HAVE_STRCASESTR} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_recallocarray.o" +fi +if [ ${HAVE_STRCASESTR} -eq 0 ]; then echo "extern char *strcasestr(const char *, const char *);" - -[ ${HAVE_STRLCAT} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_strcasestr.o" +fi +if [ ${HAVE_STRINGLIST} -eq 0 ]; then + SOELIM_COBJS="${SOELIM_COBJS} compat_stringlist.o" +fi +if [ ${HAVE_STRLCAT} -eq 0 ]; then echo "extern size_t strlcat(char *, const char *, size_t);" - -[ ${HAVE_STRLCPY} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_strlcat.o" +fi +if [ ${HAVE_STRLCPY} -eq 0 ]; then echo "extern size_t strlcpy(char *, const char *, size_t);" - -[ ${HAVE_STRNDUP} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_strlcpy.o" +fi +if [ ${HAVE_STRNDUP} -eq 0 ]; then echo "extern char *strndup(const char *, size_t);" - -[ ${HAVE_STRSEP} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_strndup.o" +fi +if [ ${HAVE_STRSEP} -eq 0 ]; then echo "extern char *strsep(char **, const char *);" - -[ ${HAVE_STRTONUM} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_strsep.o" +fi +if [ ${HAVE_STRTONUM} -eq 0 ]; then echo "extern long long strtonum(const char *, long long, long long, const char **);" - -[ ${HAVE_VASPRINTF} -eq 0 ] && \ + MANDOC_COBJS="${MANDOC_COBJS} compat_strtonum.o" +fi +if [ ${HAVE_VASPRINTF} -eq 0 ]; then echo "extern int vasprintf(char **, const char *, va_list);" - + MANDOC_COBJS="${MANDOC_COBJS} compat_vasprintf.o" +fi echo "file config.h: written" 1>&2 echo "file config.h: written" 1>&3 @@ -595,10 +635,13 @@ INSTALL_TARGETS= cat << __HEREDOC__ BUILD_TARGETS = ${BUILD_TARGETS} INSTALL_TARGETS = ${INSTALL_TARGETS} +AR = ${AR} CC = ${CC} CFLAGS = ${CFLAGS} LDADD = ${LDADD} LDFLAGS = ${LDFLAGS} +MANDOC_COBJS = ${MANDOC_COBJS} +SOELIM_COBJS = ${SOELIM_COBJS} STATIC = ${STATIC} PREFIX = ${PREFIX} BINDIR = ${BINDIR} diff --git a/configure.local.example b/configure.local.example index 4a456aade7c3..1050f4a1d9ab 100644 --- a/configure.local.example +++ b/configure.local.example @@ -1,6 +1,6 @@ -# $Id: configure.local.example,v 1.36 2019/03/06 10:18:58 schwarze Exp $ +# $Id: configure.local.example,v 1.43 2021/09/20 13:25:42 schwarze Exp $ # -# Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org> +# Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above @@ -28,6 +28,19 @@ # --- user settings relevant for all builds ---------------------------- +# By default, "cc" is used as the C compiler, but it can be overridden. +# For example, the system compiler in SunOS 5.9 may not provide <stdint.h>, +# which may require this line: +CC=gcc + +# IBM AIX may need: +CC=xlc + +# By default, "ar" is used as the library archive builder, but it +# can be overridden. For example, NixOS may not have ar(1) in the +# PATH, but may want to specify an absolute path instead. +AR=ar + # For -Tutf8 and -Tlocale operation, mandoc(1) requires <locale.h> # providing setlocale(3) and <wchar.h> providing wcwidth(3) and # putwchar(3) with a wchar_t storing UCS-4 values. Theoretically, @@ -88,7 +101,7 @@ OSENUM=MANDOC_OS_OTHER # If you do not want uname(3) to be called but instead want a fixed # string to be used, use the following line: -OSNAME="OpenBSD 6.5" +OSNAME="OpenBSD 7.0" # The following installation directories are used. # It is possible to set only one or a few of these variables, @@ -143,6 +156,14 @@ BINM_WHATIS=mwhatis # default is "whatis" BINM_MAKEWHATIS=mandocdb # default is "makewhatis" BINM_SOELIM=msoelim # default is "soelim" +# If less(1) is available, it is used as the default manual pager. +# Otherwise, more(1) is used: its existence is required by POSIX. +# It is possible to force using a different default pager, either +# by giving the name of a program found in the PATH, or by giving +# an absolute path. + +BINM_PAGER=pg # default is "less" or "more" + # Some distributions do not want hardlinks # between installed binary programs. # Set the following variable to use symbolic links instead. @@ -193,14 +214,28 @@ INSTALL_LIB="${INSTALL} -m 0444" INSTALL_MAN="${INSTALL} -m 0444" INSTALL_DATA="${INSTALL} -m 0444" -# When using the "homebrew" package manager on Mac OS X, the actual -# manuals are located in a so-called "cellar" and only symlinked -# into the manual trees. To allow mandoc to follow such symlinks, -# you have to specify the physical location of the cellar as returned -# by realpath(3), for example: +# By default, makewhatis(8) can only read from the paths passed on the +# command line or configured in man.conf(5). +# But some package managers on some operating systems store manual pages +# in separate "cellar" or "store" directories and only symlink them +# into the manual trees. +# To support one or more such package managers, give makewhatis(8) +# read access to the cellars and stores on your system, in the form +# of a colon-separated path: +# Homebrow package manager on Mac OS X: PREFIX="/usr/local" -HOMEBREWDIR="${PREFIX}/Cellar" +READ_ALLOWED_PATH="${PREFIX}/Cellar" + +# Nix package manager and/or NixOS Linux distribution: +READ_ALLOWED_PATH="/nix/store" + +# GNU Guix package manager and/or GNU Guix Linux distribution: +READ_ALLOWED_PATH="/gnu/store" + +# If multiple package managers are used concurrently: +PREFIX="/usr/local" +READ_ALLOWED_PATH="/nix/store:${PREFIX}/Cellar" # --- user settings for the mandoc(3) library -------------------------- @@ -256,6 +291,8 @@ CGIBINDIR="${WWWPREFIX}/cgi-bin" # 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). +# It may not work on old releases of Mac OS X either. For example, +# Mac OS X 10.4 Tiger provides neither mkdirat(2) nor openat(2). BUILD_CATMAN=1 @@ -268,21 +305,6 @@ BINM_CATMAN=mcatman # default is "catman" # Do not set these variables unless you really need to. -# You can manually override the compiler to be used. -# But that's rarely useful because ./configure asks your make(1) -# which compiler to use, and that answer will hardly be wrong. - -CC=cc - -# Because the system compiler may not provide <stdint.h>, -# SunOS 5.9 may need: - -CC=gcc - -# IBM AIX may need: - -CC=xlc - # Normally, leave CFLAGS unset. In that case, -g will automatically # be used, and various -W options will be added if the compiler # supports them. If you define CFLAGS manually, it will be used @@ -295,6 +317,7 @@ CFLAGS="-g" # and will be regarded as failed) or 1 (test will not be run and will # be regarded as successful). +HAVE_ATTRIBUTE=0 HAVE_DIRENT_NAMLEN=0 HAVE_ENDIAN=0 HAVE_EFTYPE=0 diff --git a/dba_array.c b/dba_array.c index 18c9f09f1a21..aaf02baa7c39 100644 --- a/dba_array.c +++ b/dba_array.c @@ -1,4 +1,4 @@ -/* $Id: dba_array.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */ +/* $Id: dba_array.c,v 1.2 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -17,6 +17,8 @@ * Allocation-based arrays for the mandoc database, for read-write access. * The interface is defined in "dba_array.h". */ +#include "config.h" + #include <assert.h> #include <stdint.h> #include <stdlib.h> diff --git a/dba_read.c b/dba_read.c index e976057064ab..4fc3ee5eddcb 100644 --- a/dba_read.c +++ b/dba_read.c @@ -1,4 +1,4 @@ -/* $Id: dba_read.c,v 1.4 2016/08/17 20:46:56 schwarze Exp $ */ +/* $Id: dba_read.c,v 1.5 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> * @@ -19,6 +19,8 @@ * The interface is defined in "dba.h". * This file is seperate from dba.c because this also uses "dbm.h". */ +#include "config.h" + #include <regex.h> #include <stdint.h> #include <stdlib.h> @@ -1,4 +1,4 @@ -.\" $Id: eqn.7,v 1.38 2019/04/23 17:57:49 schwarze Exp $ +.\" $Id: eqn.7,v 1.39 2020/01/10 11:55:04 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 23 2019 $ +.Dd $Mdocdate: January 10 2020 $ .Dt EQN 7 .Os .Sh NAME @@ -44,28 +44,16 @@ specification (see .Sx SEE ALSO for references). .Pp -Equations within -.Xr mdoc 7 -or -.Xr man 7 -documents are enclosed by the standalone -.Sq \&.EQ -and -.Sq \&.EN -tags. -Equations are multi-line blocks consisting of formulas and control -statements. -.Sh EQUATION STRUCTURE -Each equation is bracketed by -.Sq \&.EQ -and -.Sq \&.EN -strings. -.Em Note : -these are not the same as -.Xr roff 7 -macros, and may only be invoked as -.Sq \&.EQ . +An equation starts with an input line containing exactly the characters +.Sq \&.EQ , +may contain multiple input lines, and ends with an input line +containing exactly the characters +.Sq \&.EN . +Equivalently, an equation can be given in the middle of a single +text input line by surrounding it with the equation delimiters +defined with the +.Cm delim +statement. .Pp The equation grammar is as follows, where quoted strings are case-sensitive literals in the input: @@ -178,6 +166,25 @@ statement is a synonym for while .Cm tdefine is discarded. +.It Cm delim +This statement takes a string argument consisting of two bytes, +to be used as the opening and closing delimiters for equations +in the middle of text input lines. +Conventionally, the dollar sign is used for both delimiters, +as follows: +.Bd -literal -offset indent +\&.EQ +delim $$ +\&.EN +An equation like $sin pi = 0$ can now be entered +in the middle of a text input line. +.Ed +.Pp +The special statement +.Cm delim off +temporarily disables previously declared delimiters and +.Cm delim on +reenables them. .It Cm gfont Set the default font of subsequent output. Its syntax is as follows: @@ -1,7 +1,7 @@ -/* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */ +/* $Id: eqn.c,v 1.84 2020/01/08 12:16:24 schwarze Exp $ */ /* * Copyright (c) 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -399,6 +399,14 @@ eqn_next(struct eqn_node *ep, enum parse_mode mode) case '"': quoted = 1; break; + case ' ': + case '\t': + case '~': + case '^': + if (quoted) + break; + ep->start++; + continue; default: break; } @@ -669,7 +677,7 @@ eqn_parse(struct eqn_node *ep) if (ep->data == NULL) return; - ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); + ep->start = ep->end = ep->data; next_tok: tok = eqn_next(ep, MODE_TOK); @@ -1,7 +1,7 @@ -/* $Id: html.c,v 1.255 2019/04/30 15:53:00 schwarze Exp $ */ +/* $Id: html.c,v 1.275 2021/09/09 14:47:24 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Common functions for mandoc(1) HTML formatters. + * For use by individual formatters and by the main program. */ #include "config.h" @@ -42,34 +45,30 @@ struct htmldata { const char *name; int flags; -#define HTML_NOSTACK (1 << 0) -#define HTML_AUTOCLOSE (1 << 1) -#define HTML_NLBEFORE (1 << 2) -#define HTML_NLBEGIN (1 << 3) -#define HTML_NLEND (1 << 4) -#define HTML_NLAFTER (1 << 5) +#define HTML_INPHRASE (1 << 0) /* Can appear in phrasing context. */ +#define HTML_TOPHRASE (1 << 1) /* Establishes phrasing context. */ +#define HTML_NOSTACK (1 << 2) /* Does not have an end tag. */ +#define HTML_NLBEFORE (1 << 3) /* Output line break before opening. */ +#define HTML_NLBEGIN (1 << 4) /* Output line break after opening. */ +#define HTML_NLEND (1 << 5) /* Output line break before closing. */ +#define HTML_NLAFTER (1 << 6) /* Output line break after closing. */ #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) -#define HTML_INDENT (1 << 6) -#define HTML_NOINDENT (1 << 7) +#define HTML_INDENT (1 << 7) /* Indent content by two spaces. */ +#define HTML_NOINDENT (1 << 8) /* Exception: never indent content. */ }; static const struct htmldata htmltags[TAG_MAX] = { {"html", HTML_NLALL}, {"head", HTML_NLALL | HTML_INDENT}, - {"body", HTML_NLALL}, - {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, + {"meta", HTML_NOSTACK | HTML_NLALL}, + {"link", HTML_NOSTACK | HTML_NLALL}, + {"style", HTML_NLALL | HTML_INDENT}, {"title", HTML_NLAROUND}, + {"body", HTML_NLALL}, {"div", HTML_NLAROUND}, - {"div", 0}, {"section", HTML_NLALL}, - {"h1", HTML_NLAROUND}, - {"h2", HTML_NLAROUND}, - {"span", 0}, - {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, - {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, - {"a", 0}, {"table", HTML_NLALL | HTML_INDENT}, {"tr", HTML_NLALL | HTML_INDENT}, {"td", HTML_NLAROUND}, @@ -79,16 +78,22 @@ static const struct htmldata htmltags[TAG_MAX] = { {"dl", HTML_NLALL | HTML_INDENT}, {"dt", HTML_NLAROUND}, {"dd", HTML_NLAROUND | HTML_INDENT}, - {"p", HTML_NLAROUND | HTML_INDENT}, - {"pre", HTML_NLALL | HTML_NOINDENT}, - {"var", 0}, - {"cite", 0}, - {"b", 0}, - {"i", 0}, - {"code", 0}, - {"small", 0}, - {"style", HTML_NLALL | HTML_INDENT}, - {"math", HTML_NLALL | HTML_INDENT}, + {"h1", HTML_TOPHRASE | HTML_NLAROUND}, + {"h2", HTML_TOPHRASE | HTML_NLAROUND}, + {"p", HTML_TOPHRASE | HTML_NLAROUND | HTML_INDENT}, + {"pre", HTML_TOPHRASE | HTML_NLAROUND | HTML_NOINDENT}, + {"a", HTML_INPHRASE | HTML_TOPHRASE}, + {"b", HTML_INPHRASE | HTML_TOPHRASE}, + {"cite", HTML_INPHRASE | HTML_TOPHRASE}, + {"code", HTML_INPHRASE | HTML_TOPHRASE}, + {"i", HTML_INPHRASE | HTML_TOPHRASE}, + {"small", HTML_INPHRASE | HTML_TOPHRASE}, + {"span", HTML_INPHRASE | HTML_TOPHRASE}, + {"var", HTML_INPHRASE | HTML_TOPHRASE}, + {"br", HTML_INPHRASE | HTML_NOSTACK | HTML_NLALL}, + {"hr", HTML_INPHRASE | HTML_NOSTACK}, + {"mark", HTML_INPHRASE }, + {"math", HTML_INPHRASE | HTML_NLALL | HTML_INDENT}, {"mrow", 0}, {"mi", 0}, {"mn", 0}, @@ -108,6 +113,11 @@ static const struct htmldata htmltags[TAG_MAX] = { }; /* Avoid duplicate HTML id= attributes. */ + +struct id_entry { + int ord; /* Ordinal number of the latest occurrence. */ + char id[]; /* The id= attribute without any ordinal suffix. */ +}; static struct ohash id_unique; static void html_reset_internal(struct html *); @@ -131,6 +141,7 @@ html_alloc(const struct manoutput *outopts) h = mandoc_calloc(1, sizeof(struct html)); h->tag = NULL; + h->metac = h->metal = ESCAPE_FONTROMAN; h->style = outopts->style; if ((h->base_man1 = outopts->man) == NULL) h->base_man2 = NULL; @@ -142,7 +153,7 @@ html_alloc(const struct manoutput *outopts) if (outopts->toc) h->oflags |= HTML_TOC; - mandoc_ohash_init(&id_unique, 4, 0); + mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); return h; } @@ -151,17 +162,17 @@ static void html_reset_internal(struct html *h) { struct tag *tag; - char *cp; + struct id_entry *entry; unsigned int slot; while ((tag = h->tag) != NULL) { h->tag = tag->next; free(tag); } - cp = ohash_first(&id_unique, &slot); - while (cp != NULL) { - free(cp); - cp = ohash_next(&id_unique, &slot); + entry = ohash_first(&id_unique, &slot); + while (entry != NULL) { + free(entry); + entry = ohash_next(&id_unique, &slot); } ohash_delete(&id_unique); } @@ -170,7 +181,7 @@ void html_reset(void *p) { html_reset_internal(p); - mandoc_ohash_init(&id_unique, 4, 0); + mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); } void @@ -186,6 +197,8 @@ print_gen_head(struct html *h) struct tag *t; print_otag(h, TAG_META, "?", "charset", "utf-8"); + print_otag(h, TAG_META, "??", "name", "viewport", + "content", "width=device-width, initial-scale=1.0"); if (h->style != NULL) { print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", h->style, "type", "text/css", "media", "all"); @@ -203,23 +216,18 @@ print_gen_head(struct html *h) print_endline(h); print_text(h, "td.head-vol { text-align: center; }"); print_endline(h); - print_text(h, "div.Pp { margin: 1ex 0ex; }"); + print_text(h, ".Nd, .Bf, .Op { display: inline; }"); print_endline(h); - print_text(h, "div.Nd, div.Bf, div.Op { display: inline; }"); + print_text(h, ".Pa, .Ad { font-style: italic; }"); print_endline(h); - print_text(h, "span.Pa, span.Ad { font-style: italic; }"); + print_text(h, ".Ms { font-weight: bold; }"); print_endline(h); - print_text(h, "span.Ms { font-weight: bold; }"); - print_endline(h); - print_text(h, "dl.Bl-diag "); + print_text(h, ".Bl-diag "); print_byte(h, '>'); print_text(h, " dt { font-weight: bold; }"); print_endline(h); - print_text(h, "code.Nm, code.Fl, code.Cm, code.Ic, " - "code.In, code.Fd, code.Fn,"); - print_endline(h); - print_text(h, "code.Cd { font-weight: bold; " - "font-family: inherit; }"); + print_text(h, "code.Nm, .Fl, .Cm, .Ic, code.In, .Fd, .Fn, .Cd " + "{ font-weight: bold; font-family: inherit; }"); print_tagq(h, t); } @@ -233,8 +241,10 @@ html_setfont(struct html *h, enum mandoc_esc font) case ESCAPE_FONTITALIC: case ESCAPE_FONTBOLD: case ESCAPE_FONTBI: - case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: break; case ESCAPE_FONT: font = ESCAPE_FONTROMAN; @@ -265,9 +275,17 @@ print_metaf(struct html *h) h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); break; + case ESCAPE_FONTCB: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + print_otag(h, TAG_B, ""); + break; + case ESCAPE_FONTCI: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + print_otag(h, TAG_I, ""); + break; default: break; } @@ -276,21 +294,18 @@ print_metaf(struct html *h) void html_close_paragraph(struct html *h) { - struct tag *t; + struct tag *this, *next; + int flags; - for (t = h->tag; t != NULL && t->closed == 0; t = t->next) { - switch(t->tag) { - case TAG_P: - case TAG_PRE: - print_tagq(h, t); + this = h->tag; + for (;;) { + next = this->next; + flags = htmltags[this->tag].flags; + if (flags & (HTML_INPHRASE | HTML_TOPHRASE)) + print_ctag(h, this); + if ((flags & HTML_INPHRASE) == 0) break; - case TAG_A: - print_tagq(h, t); - continue; - default: - continue; - } - break; + this = next; } } @@ -328,33 +343,66 @@ html_fillmode(struct html *h, enum roff_tok want) return had; } +/* + * Allocate a string to be used for the "id=" attribute of an HTML + * element and/or as a segment identifier for a URI in an <a> element. + * The function may fail and return NULL if the node lacks text data + * to create the attribute from. + * The caller is responsible for free(3)ing the returned string. + * + * If the "unique" argument is non-zero, the "id_unique" ohash table + * is used for de-duplication. If the "unique" argument is 1, + * it is the first time the function is called for this tag and + * location, so if an ordinal suffix is needed, it is incremented. + * If the "unique" argument is 2, it is the second time the function + * is called for this tag and location, so the ordinal suffix + * remains unchanged. + */ char * html_make_id(const struct roff_node *n, int unique) { const struct roff_node *nch; - char *buf, *bufs, *cp; + struct id_entry *entry; + char *buf, *cp; + size_t len; unsigned int slot; - int suffix; - for (nch = n->child; nch != NULL; nch = nch->next) - if (nch->type != ROFFT_TEXT) - return NULL; - - buf = NULL; - deroff(&buf, n); - if (buf == NULL) - return NULL; + if (n->tag != NULL) + buf = mandoc_strdup(n->tag); + else { + switch (n->tok) { + case MDOC_Sh: + case MDOC_Ss: + case MDOC_Sx: + case MAN_SH: + case MAN_SS: + for (nch = n->child; nch != NULL; nch = nch->next) + if (nch->type != ROFFT_TEXT) + return NULL; + buf = NULL; + deroff(&buf, n); + if (buf == NULL) + return NULL; + break; + default: + if (n->child == NULL || n->child->type != ROFFT_TEXT) + return NULL; + buf = mandoc_strdup(n->child->string); + break; + } + } /* * In ID attributes, only use ASCII characters that are * permitted in URL-fragment strings according to the * explicit list at: * https://url.spec.whatwg.org/#url-fragment-string + * In addition, reserve '~' for ordinal suffixes. */ for (cp = buf; *cp != '\0'; cp++) if (isalnum((unsigned char)*cp) == 0 && - strchr("!$&'()*+,-./:;=?@_~", *cp) == NULL) + strchr("!$&'()*+,-./:;=?@_", *cp) == NULL) *cp = '_'; if (unique == 0) @@ -362,25 +410,21 @@ html_make_id(const struct roff_node *n, int unique) /* Avoid duplicate HTML id= attributes. */ - bufs = NULL; - suffix = 1; slot = ohash_qlookup(&id_unique, buf); - cp = ohash_find(&id_unique, slot); - if (cp != NULL) { - while (cp != NULL) { - free(bufs); - if (++suffix > 127) { - free(buf); - return NULL; - } - mandoc_asprintf(&bufs, "%s_%d", buf, suffix); - slot = ohash_qlookup(&id_unique, bufs); - cp = ohash_find(&id_unique, slot); - } - free(buf); - buf = bufs; + if ((entry = ohash_find(&id_unique, slot)) == NULL) { + len = strlen(buf) + 1; + entry = mandoc_malloc(sizeof(*entry) + len); + entry->ord = 1; + memcpy(entry->id, buf, len); + ohash_insert(&id_unique, slot, entry); + } else if (unique == 1) + entry->ord++; + + if (entry->ord > 1) { + cp = buf; + mandoc_asprintf(&buf, "%s~%d", cp, entry->ord); + free(cp); } - ohash_insert(&id_unique, slot, buf); return buf; } @@ -470,8 +514,10 @@ print_encode(struct html *h, const char *p, const char *pend, int norecurse) case ESCAPE_FONTBOLD: case ESCAPE_FONTITALIC: case ESCAPE_FONTBI: - case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: if (0 == norecurse) { h->flags |= HTML_NOSPACE; if (html_setfont(h, esc)) @@ -589,6 +635,25 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) tflags = htmltags[tag].flags; + /* Flow content is not allowed in phrasing context. */ + + if ((tflags & HTML_INPHRASE) == 0) { + for (t = h->tag; t != NULL; t = t->next) { + if (t->closed) + continue; + assert((htmltags[t->tag].flags & HTML_TOPHRASE) == 0); + break; + } + + /* + * Always wrap phrasing elements in a paragraph + * unless already contained in some flow container; + * never put them directly into a section. + */ + + } else if (tflags & HTML_TOPHRASE && h->tag->tag == TAG_SECTION) + print_otag(h, TAG_P, "c", "Pp"); + /* Push this tag onto the stack of open scopes. */ if ((tflags & HTML_NOSTACK) == 0) { @@ -706,7 +771,7 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) /* Accommodate for "well-formed" singleton escaping. */ - if (HTML_AUTOCLOSE & htmltags[tag].flags) + if (htmltags[tag].flags & HTML_NOSTACK) print_byte(h, '/'); print_byte(h, '>'); @@ -724,6 +789,49 @@ print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) return t; } +/* + * Print an element with an optional "id=" attribute. + * If the element has phrasing content and an "id=" attribute, + * also add a permalink: outside if it can be in phrasing context, + * inside otherwise. + */ +struct tag * +print_otag_id(struct html *h, enum htmltag elemtype, const char *cattr, + struct roff_node *n) +{ + struct roff_node *nch; + struct tag *ret, *t; + char *id, *href; + + ret = NULL; + id = href = NULL; + if (n->flags & NODE_ID) + id = html_make_id(n, 1); + if (n->flags & NODE_HREF) + href = id == NULL ? html_make_id(n, 2) : id; + if (href != NULL && htmltags[elemtype].flags & HTML_INPHRASE) + ret = print_otag(h, TAG_A, "chR", "permalink", href); + t = print_otag(h, elemtype, "ci", cattr, id); + if (ret == NULL) { + ret = t; + if (href != NULL && (nch = n->child) != NULL) { + /* man(7) is safe, it tags phrasing content only. */ + if (n->tok > MDOC_MAX || + htmltags[elemtype].flags & HTML_TOPHRASE) + nch = NULL; + else /* For mdoc(7), beware of nested blocks. */ + while (nch != NULL && nch->type == ROFFT_TEXT) + nch = nch->next; + if (nch == NULL) + print_otag(h, TAG_A, "chR", "permalink", href); + } + } + free(id); + if (id == NULL) + free(href); + return ret; +} + static void print_ctag(struct html *h, struct tag *tag) { @@ -793,6 +901,25 @@ print_gen_comment(struct html *h, struct roff_node *n) void print_text(struct html *h, const char *word) { + print_tagged_text(h, word, NULL); +} + +void +print_tagged_text(struct html *h, const char *word, struct roff_node *n) +{ + struct tag *t; + char *href; + + /* + * Always wrap text in a paragraph unless already contained in + * some flow container; never put it directly into a section. + */ + + if (h->tag->tag == TAG_SECTION) + print_otag(h, TAG_P, "c", "Pp"); + + /* Output whitespace before this text? */ + if (h->col && (h->flags & HTML_NOSPACE) == 0) { if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) @@ -802,9 +929,21 @@ print_text(struct html *h, const char *word) print_word(h, " "); } + /* + * Optionally switch fonts, optionally write a permalink, then + * print the text, optionally surrounded by HTML whitespace. + */ + assert(h->metaf == NULL); print_metaf(h); print_indent(h); + + if (n != NULL && (href = html_make_id(n, 2)) != NULL) { + t = print_otag(h, TAG_A, "chR", "permalink", href); + free(href); + } else + t = NULL; + if ( ! print_encode(h, word, NULL, 0)) { if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; @@ -815,7 +954,8 @@ print_text(struct html *h, const char *word) if (h->metaf != NULL) { print_tagq(h, h->metaf); h->metaf = NULL; - } + } else if (t != NULL) + print_tagq(h, t); h->flags &= ~HTML_IGNDELIM; } @@ -942,15 +1082,12 @@ print_indent(struct html *h) { size_t i; - if (h->col) + if (h->col || h->noindent) return; - if (h->noindent == 0) { - h->col = h->indent * 2; - for (i = 0; i < h->col; i++) - putchar(' '); - } - h->flags &= ~HTML_NOSPACE; + h->col = h->indent * 2; + for (i = 0; i < h->col; i++) + putchar(' '); } /* @@ -1,7 +1,7 @@ -/* $Id: html.h,v 1.103 2019/04/30 15:53:00 schwarze Exp $ */ +/* $Id: html.h,v 1.109 2021/09/09 14:47:24 schwarze Exp $ */ /* + * Copyright (c) 2017, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,23 +14,21 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces for mandoc(1) HTML formatters. + * For use by the individual HTML formatters only. */ enum htmltag { TAG_HTML, TAG_HEAD, - TAG_BODY, TAG_META, + TAG_LINK, + TAG_STYLE, TAG_TITLE, + TAG_BODY, TAG_DIV, - TAG_IDIV, TAG_SECTION, - TAG_H1, - TAG_H2, - TAG_SPAN, - TAG_LINK, - TAG_BR, - TAG_A, TAG_TABLE, TAG_TR, TAG_TD, @@ -40,15 +38,21 @@ enum htmltag { TAG_DL, TAG_DT, TAG_DD, + TAG_H1, + TAG_H2, TAG_P, TAG_PRE, - TAG_VAR, - TAG_CITE, + TAG_A, TAG_B, - TAG_I, + TAG_CITE, TAG_CODE, + TAG_I, TAG_SMALL, - TAG_STYLE, + TAG_SPAN, + TAG_VAR, + TAG_BR, + TAG_HR, + TAG_MARK, TAG_MATH, TAG_MROW, TAG_MI, @@ -120,8 +124,12 @@ void print_gen_comment(struct html *, struct roff_node *); void print_gen_decls(struct html *); void print_gen_head(struct html *); struct tag *print_otag(struct html *, enum htmltag, const char *, ...); +struct tag *print_otag_id(struct html *, enum htmltag, const char *, + struct roff_node *); void print_tagq(struct html *, const struct tag *); void print_stagq(struct html *, const struct tag *); +void print_tagged_text(struct html *, const char *, + struct roff_node *); void print_text(struct html *, const char *); void print_tblclose(struct html *); void print_tbl(struct html *, const struct tbl_span *); diff --git a/libmandoc.h b/libmandoc.h index ff6f4692f062..ab7c29be510f 100644 --- a/libmandoc.h +++ b/libmandoc.h @@ -1,7 +1,7 @@ -/* $Id: libmandoc.h,v 1.77 2018/12/21 17:15:18 schwarze Exp $ */ +/* $Id: libmandoc.h,v 1.80 2021/06/27 17:57:54 schwarze Exp $ */ /* + * Copyright (c) 2013-2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces for parser utilities needed by multiple parsers + * and the top-level functions to call the mdoc, man, and roff parsers. */ /* @@ -47,8 +50,9 @@ struct buf { struct roff; struct roff_man; +struct roff_node; -char *mandoc_normdate(struct roff_man *, char *, int, int); +char *mandoc_normdate(struct roff_node *, struct roff_node *); int mandoc_eos(const char *, size_t); int mandoc_strntoi(const char *, size_t, int); const char *mandoc_a2msec(const char*); @@ -69,10 +73,10 @@ void roff_reset(struct roff *); void roff_man_free(struct roff_man *); struct roff_man *roff_man_alloc(struct roff *, const char *, int); void roff_man_reset(struct roff_man *); -int roff_parseln(struct roff *, int, struct buf *, int *); +int roff_parseln(struct roff *, int, struct buf *, int *, size_t); void roff_userret(struct roff *); void roff_endparse(struct roff *); -void roff_setreg(struct roff *, const char *, int, char sign); +void roff_setreg(struct roff *, const char *, int, char); int roff_getreg(struct roff *, const char *); char *roff_strdup(const struct roff *, const char *); char *roff_getarg(struct roff *, char **, int, int *); @@ -1,7 +1,7 @@ -/* $Id: main.c,v 1.332 2019/07/19 20:27:25 schwarze Exp $ */ +/* $Id: main.c,v 1.358 2021/09/04 22:38:46 schwarze Exp $ */ /* + * Copyright (c) 2010-2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1). */ #include "config.h" @@ -32,6 +34,7 @@ #include <errno.h> #include <fcntl.h> #include <glob.h> +#include <limits.h> #if HAVE_SANDBOX_INIT #include <sandbox.h> #endif @@ -52,6 +55,7 @@ #include "man.h" #include "mandoc_parse.h" #include "tag.h" +#include "term_tag.h" #include "main.h" #include "manconf.h" #include "mansearch.h" @@ -77,33 +81,39 @@ enum outt { OUTT_PDF /* -Tpdf */ }; -struct curparse { - struct mparse *mp; - struct manoutput *outopts; /* output options */ +struct outstate { + struct tag_files *tag_files; /* Tagging state variables. */ void *outdata; /* data for output */ - char *os_s; /* operating system for display */ + int use_pager; int wstop; /* stop after a file with a warning */ - enum mandoc_os os_e; /* check base system conventions */ + int had_output; /* Some output was generated. */ enum outt outtype; /* which output to use */ }; int mandocdb(int, char *[]); -static void check_xr(void); -static int fs_lookup(const struct manpaths *, - size_t ipath, const char *, - const char *, const char *, +static void check_xr(struct manpaths *); +static void fs_append(char **, size_t, int, + size_t, const char *, enum form, + struct manpage **, size_t *); +static int fs_lookup(const struct manpaths *, size_t, + const char *, const char *, const char *, struct manpage **, size_t *); static int fs_search(const struct mansearch *, - const struct manpaths *, int, char**, + const struct manpaths *, const char *, struct manpage **, size_t *); -static void outdata_alloc(struct curparse *); -static void parse(struct curparse *, int, const char *); +static void glob_esc(char **, const char *, const char *); +static void outdata_alloc(struct outstate *, struct manoutput *); +static void parse(struct mparse *, int, const char *, + struct outstate *, struct manconf *); static void passthrough(int, int); -static pid_t spawn_pager(struct tag_files *); +static void process_onefile(struct mparse *, struct manpage *, + int, struct outstate *, struct manconf *); +static void run_pager(struct outstate *, char *); +static pid_t spawn_pager(struct outstate *, char *); static void usage(enum argmode) __attribute__((__noreturn__)); -static int woptions(struct curparse *, char *); +static int woptions(char *, enum mandoc_os *, int *); static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static char help_arg[] = "help"; @@ -113,26 +123,31 @@ static char *help_argv[] = {help_arg, NULL}; int main(int argc, char *argv[]) { - struct manconf conf; - struct mansearch search; - struct curparse curp; - struct winsize ws; - struct tag_files *tag_files; - struct manpage *res, *resp; - const char *progname, *sec, *thisarg; - char *conf_file, *defpaths, *auxpaths; - char *oarg, *tagarg; + struct manconf conf; /* Manpaths and output options. */ + struct outstate outst; /* Output state. */ + struct winsize ws; /* Result of ioctl(TIOCGWINSZ). */ + struct mansearch search; /* Search options. */ + struct manpage *res; /* Complete list of search results. */ + struct manpage *resn; /* Search results for one name. */ + struct mparse *mp; /* Opaque parser object. */ + const char *conf_file; /* -C: alternate config file. */ + const char *os_s; /* -I: Operating system for display. */ + const char *progname, *sec, *ep; + char *defpaths; /* -M: override manpaths. */ + char *auxpaths; /* -m: additional manpaths. */ + char *oarg; /* -O: output option string. */ + char *tagarg; /* -O tag: default value. */ unsigned char *uc; - size_t i, sz, ssz; + size_t ressz; /* Number of elements in res[]. */ + size_t resnsz; /* Number of elements in resn[]. */ + size_t i, ib, ssz; + int options; /* Parser options. */ + int show_usage; /* Invalid argument: give up. */ int prio, best_prio; - enum outmode outmode; - int fd, startdir; - int show_usage; - int options; - int use_pager; - int status, signum; + int startdir; int c; - pid_t pager_pid, tc_pgid, man_pgid, pid; + enum mandoc_os os_e; /* Check base system conventions. */ + enum outmode outmode; /* According to command line. */ #if HAVE_PROGNAME progname = getprogname(); @@ -152,7 +167,7 @@ main(int argc, char *argv[]) return mandocdb(argc, argv); #if HAVE_PLEDGE - if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) { + if (pledge("stdio rpath wpath cpath tmppath tty proc exec", NULL) == -1) { mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); return mandoc_msg_getrc(); } @@ -165,8 +180,8 @@ main(int argc, char *argv[]) /* Search options. */ memset(&conf, 0, sizeof(conf)); - conf_file = defpaths = NULL; - auxpaths = NULL; + conf_file = NULL; + defpaths = auxpaths = NULL; memset(&search, 0, sizeof(struct mansearch)); search.outkey = "Nd"; @@ -183,15 +198,19 @@ main(int argc, char *argv[]) else search.argmode = ARG_FILE; - /* Parser and formatter options. */ + /* Parser options. */ - memset(&curp, 0, sizeof(struct curparse)); - curp.outtype = OUTT_LOCALE; - curp.outopts = &conf.output; options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; + os_e = MANDOC_OS_OTHER; + os_s = NULL; + + /* Formatter options. */ + + memset(&outst, 0, sizeof(outst)); + outst.tag_files = NULL; + outst.outtype = OUTT_LOCALE; + outst.use_pager = 1; - use_pager = 1; - tag_files = NULL; show_usage = 0; outmode = OUTMODE_DEF; @@ -209,14 +228,14 @@ main(int argc, char *argv[]) conf_file = optarg; break; case 'c': - use_pager = 0; + outst.use_pager = 0; break; case 'f': search.argmode = ARG_WORD; break; case 'h': conf.output.synopsisonly = 1; - use_pager = 0; + outst.use_pager = 0; outmode = OUTMODE_ALL; break; case 'I': @@ -225,12 +244,12 @@ main(int argc, char *argv[]) "-I %s", optarg); return mandoc_msg_getrc(); } - if (curp.os_s != NULL) { + if (os_s != NULL) { mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, "-I %s", optarg); return mandoc_msg_getrc(); } - curp.os_s = mandoc_strdup(optarg + 3); + os_s = optarg + 3; break; case 'K': options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); @@ -268,27 +287,27 @@ main(int argc, char *argv[]) break; case 'T': if (strcmp(optarg, "ascii") == 0) - curp.outtype = OUTT_ASCII; + outst.outtype = OUTT_ASCII; else if (strcmp(optarg, "lint") == 0) { - curp.outtype = OUTT_LINT; + outst.outtype = OUTT_LINT; mandoc_msg_setoutfile(stdout); mandoc_msg_setmin(MANDOCERR_BASE); } else if (strcmp(optarg, "tree") == 0) - curp.outtype = OUTT_TREE; + outst.outtype = OUTT_TREE; else if (strcmp(optarg, "man") == 0) - curp.outtype = OUTT_MAN; + outst.outtype = OUTT_MAN; else if (strcmp(optarg, "html") == 0) - curp.outtype = OUTT_HTML; + outst.outtype = OUTT_HTML; else if (strcmp(optarg, "markdown") == 0) - curp.outtype = OUTT_MARKDOWN; + outst.outtype = OUTT_MARKDOWN; else if (strcmp(optarg, "utf8") == 0) - curp.outtype = OUTT_UTF8; + outst.outtype = OUTT_UTF8; else if (strcmp(optarg, "locale") == 0) - curp.outtype = OUTT_LOCALE; + outst.outtype = OUTT_LOCALE; else if (strcmp(optarg, "ps") == 0) - curp.outtype = OUTT_PS; + outst.outtype = OUTT_PS; else if (strcmp(optarg, "pdf") == 0) - curp.outtype = OUTT_PDF; + outst.outtype = OUTT_PDF; else { mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-T %s", optarg); @@ -296,7 +315,7 @@ main(int argc, char *argv[]) } break; case 'W': - if (woptions(&curp, optarg) == -1) + if (woptions(optarg, &os_e, &outst.wstop) == -1) return mandoc_msg_getrc(); break; case 'w': @@ -313,11 +332,12 @@ main(int argc, char *argv[]) /* Postprocess options. */ - if (outmode == OUTMODE_DEF) { + switch (outmode) { + case OUTMODE_DEF: switch (search.argmode) { case ARG_FILE: outmode = OUTMODE_ALL; - use_pager = 0; + outst.use_pager = 0; break; case ARG_NAME: outmode = OUTMODE_ONE; @@ -326,6 +346,16 @@ main(int argc, char *argv[]) outmode = OUTMODE_LST; break; } + break; + case OUTMODE_FLN: + if (search.argmode == ARG_FILE) + outmode = OUTMODE_ALL; + break; + case OUTMODE_ALL: + break; + case OUTMODE_LST: + case OUTMODE_ONE: + abort(); } if (oarg != NULL) { @@ -340,15 +370,17 @@ main(int argc, char *argv[]) } } - if (curp.outtype != OUTT_TREE || !curp.outopts->noval) + if (outst.outtype != OUTT_TREE || conf.output.noval == 0) options |= MPARSE_VALIDATE; if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST || - !isatty(STDOUT_FILENO)) - use_pager = 0; + (conf.output.outfilename == NULL && + conf.output.tagfilename == NULL && + isatty(STDOUT_FILENO) == 0)) + outst.use_pager = 0; - if (use_pager && + if (outst.use_pager && (conf.output.width == 0 || conf.output.indent == 0) && ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 1) { @@ -359,12 +391,16 @@ main(int argc, char *argv[]) } #if HAVE_PLEDGE - if (use_pager == 0) { - if (pledge("stdio rpath", NULL) == -1) { - mandoc_msg(MANDOCERR_PLEDGE, 0, 0, - "%s", strerror(errno)); - return mandoc_msg_getrc(); - } + if (outst.use_pager == 0) + c = pledge("stdio rpath", NULL); + else if (conf.output.outfilename != NULL || + conf.output.tagfilename != NULL) + c = pledge("stdio rpath wpath cpath", NULL); + else + c = pledge("stdio rpath tmppath tty proc exec", NULL); + if (c == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); + return mandoc_msg_getrc(); } #endif @@ -374,11 +410,10 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; } - resp = NULL; /* - * Quirks for help(1) - * and for a man(1) section argument without -s. + * Quirks for help(1) and man(1), + * in particular for a section argument without -s. */ if (search.argmode == ARG_NAME) { @@ -402,6 +437,8 @@ main(int argc, char *argv[]) if (search.arch == NULL) search.arch = MACHINE; #endif + if (outmode == OUTMODE_ONE) + search.firstmatch = 1; } /* @@ -415,80 +452,78 @@ main(int argc, char *argv[]) conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; } - /* man(1), whatis(1), apropos(1) */ - - if (search.argmode != ARG_FILE) { - if (search.argmode == ARG_NAME && - outmode == OUTMODE_ONE) - search.firstmatch = 1; - - /* Access the mandoc database. */ + /* Read the configuration file. */ + if (search.argmode != ARG_FILE || + mandoc_msg_getmin() == MANDOCERR_STYLE) manconf_parse(&conf, conf_file, defpaths, auxpaths); - if ( ! mansearch(&search, &conf.manpath, - argc, argv, &res, &sz)) - usage(search.argmode); - if (sz == 0 && search.argmode == ARG_NAME) - (void)fs_search(&search, &conf.manpath, - argc, argv, &res, &sz); + /* man(1): Resolve each name individually. */ - if (search.argmode == ARG_NAME) { - for (c = 0; c < argc; c++) { - if (strchr(argv[c], '/') == NULL) - continue; - if (access(argv[c], R_OK) == -1) { - mandoc_msg_setinfilename(argv[c]); + if (search.argmode == ARG_NAME) { + if (argc < 1) { + if (outmode != OUTMODE_FLN) + usage(ARG_NAME); + if (conf.manpath.sz == 0) { + warnx("The manpath is empty."); + mandoc_msg_setrc(MANDOCLEVEL_BADARG); + } else { + for (i = 0; i + 1 < conf.manpath.sz; i++) + printf("%s:", conf.manpath.paths[i]); + printf("%s\n", conf.manpath.paths[i]); + } + manconf_free(&conf); + return (int)mandoc_msg_getrc(); + } + for (res = NULL, ressz = 0; argc > 0; argc--, argv++) { + (void)mansearch(&search, &conf.manpath, + 1, argv, &resn, &resnsz); + if (resnsz == 0) + (void)fs_search(&search, &conf.manpath, + *argv, &resn, &resnsz); + if (resnsz == 0 && strchr(*argv, '/') == NULL) { + if (search.arch != NULL && + arch_valid(search.arch, OSENUM) == 0) + warnx("Unknown architecture \"%s\".", + search.arch); + else if (search.sec != NULL) + warnx("No entry for %s in " + "section %s of the manual.", + *argv, search.sec); + else + warnx("No entry for %s in " + "the manual.", *argv); + mandoc_msg_setrc(MANDOCLEVEL_BADARG); + continue; + } + if (resnsz == 0) { + if (access(*argv, R_OK) == -1) { + mandoc_msg_setinfilename(*argv); mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "%s", strerror(errno)); mandoc_msg_setinfilename(NULL); continue; } + resnsz = 1; + resn = mandoc_calloc(resnsz, sizeof(*res)); + resn->file = mandoc_strdup(*argv); + resn->ipath = SIZE_MAX; + resn->form = FORM_SRC; + } + if (outmode != OUTMODE_ONE || resnsz == 1) { res = mandoc_reallocarray(res, - sz + 1, sizeof(*res)); - res[sz].file = mandoc_strdup(argv[c]); - res[sz].names = NULL; - res[sz].output = NULL; - res[sz].bits = 0; - res[sz].ipath = SIZE_MAX; - res[sz].sec = 10; - res[sz].form = FORM_SRC; - sz++; + ressz + resnsz, sizeof(*res)); + memcpy(res + ressz, resn, + sizeof(*resn) * resnsz); + ressz += resnsz; + continue; } - } - if (sz == 0) { - if (search.argmode != ARG_NAME) - warnx("nothing appropriate"); - mandoc_msg_setrc(MANDOCLEVEL_BADARG); - goto out; - } + /* Search for the best section. */ - /* - * For standard man(1) and -a output mode, - * prepare for copying filename pointers - * into the program parameter array. - */ - - if (outmode == OUTMODE_ONE) { - argc = 1; best_prio = 40; - } else if (outmode == OUTMODE_ALL) - argc = (int)sz; - - /* Iterate all matching manuals. */ - - resp = res; - for (i = 0; i < sz; i++) { - if (outmode == OUTMODE_FLN) - puts(res[i].file); - else if (outmode == OUTMODE_LST) - printf("%s - %s\n", res[i].names, - res[i].output == NULL ? "" : - res[i].output); - else if (outmode == OUTMODE_ONE) { - /* Search for the best section. */ - sec = res[i].file; + for (ib = i = 0; i < resnsz; i++) { + sec = resn[i].file; sec += strcspn(sec, "123456789"); if (sec[0] == '\0') continue; /* No section at all. */ @@ -501,45 +536,66 @@ main(int argc, char *argv[]) sec++; /* Prefer without suffix. */ if (*sec != '/') prio += 10; /* Wrong dir name. */ - if (search.sec != NULL && - (strlen(sec) <= ssz + 3 || - strcmp(sec + strlen(sec) - ssz, - search.sec) != 0)) - prio += 20; /* Wrong file ext. */ + if (search.sec != NULL) { + ep = strchr(sec, '\0'); + if (ep - sec > 3 && + strncmp(ep - 3, ".gz", 3) == 0) + ep -= 3; + if ((size_t)(ep - sec) < ssz + 3 || + strncmp(ep - ssz, search.sec, + ssz) != 0) /* Wrong file */ + prio += 20; /* extension. */ + } if (prio >= best_prio) continue; best_prio = prio; - resp = res + i; + ib = i; } + res = mandoc_reallocarray(res, ressz + 1, + sizeof(*res)); + memcpy(res + ressz++, resn + ib, sizeof(*resn)); } - /* - * For man(1), -a and -i output mode, fall through - * to the main mandoc(1) code iterating files - * and running the parsers on each of them. - */ + /* apropos(1), whatis(1): Process the full search expression. */ + + } else if (search.argmode != ARG_FILE) { + if (mansearch(&search, &conf.manpath, + argc, argv, &res, &ressz) == 0) + usage(search.argmode); - if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) + if (ressz == 0) { + warnx("nothing appropriate"); + mandoc_msg_setrc(MANDOCLEVEL_BADARG); goto out; - } + } - /* mandoc(1) */ + /* mandoc(1): Take command line arguments as file names. */ -#if HAVE_PLEDGE - if (use_pager) { - if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) { - mandoc_msg(MANDOCERR_PLEDGE, 0, 0, - "%s", strerror(errno)); - return mandoc_msg_getrc(); - } } else { - if (pledge("stdio rpath", NULL) == -1) { - mandoc_msg(MANDOCERR_PLEDGE, 0, 0, - "%s", strerror(errno)); - return mandoc_msg_getrc(); + ressz = argc > 0 ? argc : 1; + res = mandoc_calloc(ressz, sizeof(*res)); + for (i = 0; i < ressz; i++) { + if (argc > 0) + res[i].file = mandoc_strdup(argv[i]); + res[i].ipath = SIZE_MAX; + res[i].form = FORM_SRC; } } -#endif + + switch (outmode) { + case OUTMODE_FLN: + for (i = 0; i < ressz; i++) + puts(res[i].file); + goto out; + case OUTMODE_LST: + for (i = 0; i < ressz; i++) + printf("%s - %s\n", res[i].names, + res[i].output == NULL ? "" : + res[i].output); + goto out; + default: + break; + } if (search.argmode == ARG_FILE && auxpaths != NULL) { if (strcmp(auxpaths, "doc") == 0) @@ -549,19 +605,7 @@ main(int argc, char *argv[]) } mchars_alloc(); - curp.mp = mparse_alloc(options, curp.os_e, curp.os_s); - - if (argc < 1) { - if (use_pager) { - tag_files = tag_init(); - if (tag_files != NULL) - tag_files->tagname = conf.output.tag; - } - thisarg = "<stdin>"; - mandoc_msg_setinfilename(thisarg); - parse(&curp, STDIN_FILENO, thisarg); - mandoc_msg_setinfilename(NULL); - } + mp = mparse_alloc(options, os_e, os_s); /* * Remember the original working directory, if possible. @@ -571,164 +615,53 @@ main(int argc, char *argv[]) * readable: Maybe it won't be needed after all. */ startdir = open(".", O_RDONLY | O_DIRECTORY); - - while (argc > 0) { - - /* - * Changing directories is not needed in ARG_FILE mode. - * Do it on a best-effort basis. Even in case of - * failure, some functionality may still work. - */ - if (resp != NULL) { - if (resp->ipath != SIZE_MAX) - (void)chdir(conf.manpath.paths[resp->ipath]); - else if (startdir != -1) - (void)fchdir(startdir); - thisarg = resp->file; - } else - thisarg = *argv; - - mandoc_msg_setinfilename(thisarg); - fd = mparse_open(curp.mp, thisarg); - if (fd != -1) { - if (use_pager) { - use_pager = 0; - tag_files = tag_init(); - if (tag_files != NULL) - tag_files->tagname = conf.output.tag; - } - - if (resp == NULL || resp->form == FORM_SRC) - parse(&curp, fd, thisarg); - else - passthrough(fd, conf.output.synopsisonly); - - if (ferror(stdout)) { - if (tag_files != NULL) { - mandoc_msg(MANDOCERR_WRITE, 0, 0, - "%s: %s", tag_files->ofn, - strerror(errno)); - tag_unlink(); - tag_files = NULL; - } else - mandoc_msg(MANDOCERR_WRITE, 0, 0, - "%s", strerror(errno)); - break; - } - - if (argc > 1 && curp.outtype <= OUTT_UTF8) { - if (curp.outdata == NULL) - outdata_alloc(&curp); - terminal_sepline(curp.outdata); - } - } else - mandoc_msg(resp == NULL ? MANDOCERR_BADARG_BAD : - MANDOCERR_OPEN, 0, 0, "%s", strerror(errno)); - - mandoc_msg_setinfilename(NULL); - - if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) + for (i = 0; i < ressz; i++) { + process_onefile(mp, res + i, startdir, &outst, &conf); + if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) break; - - if (resp != NULL) - resp++; - else - argv++; - if (--argc) - mparse_reset(curp.mp); } if (startdir != -1) { (void)fchdir(startdir); close(startdir); } - - if (curp.outdata != NULL) { - switch (curp.outtype) { + if (conf.output.tag != NULL && conf.output.tag_found == 0) { + mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", conf.output.tag); + conf.output.tag = NULL; + } + if (outst.outdata != NULL) { + switch (outst.outtype) { case OUTT_HTML: - html_free(curp.outdata); + html_free(outst.outdata); break; case OUTT_UTF8: case OUTT_LOCALE: case OUTT_ASCII: - ascii_free(curp.outdata); + ascii_free(outst.outdata); break; case OUTT_PDF: case OUTT_PS: - pspdf_free(curp.outdata); + pspdf_free(outst.outdata); break; default: break; } } mandoc_xr_free(); - mparse_free(curp.mp); + mparse_free(mp); mchars_free(); out: - if (search.argmode != ARG_FILE) { + mansearch_free(res, ressz); + if (search.argmode != ARG_FILE) manconf_free(&conf); - mansearch_free(res, sz); - } - - free(curp.os_s); - - /* - * When using a pager, finish writing both temporary files, - * fork it, wait for the user to close it, and clean up. - */ - - if (tag_files != NULL) { - fclose(stdout); - tag_write(); - man_pgid = getpgid(0); - tag_files->tcpgid = man_pgid == getpid() ? - getpgid(getppid()) : man_pgid; - pager_pid = 0; - signum = SIGSTOP; - for (;;) { - - /* Stop here until moved to the foreground. */ - - tc_pgid = tcgetpgrp(tag_files->ofd); - if (tc_pgid != man_pgid) { - if (tc_pgid == pager_pid) { - (void)tcsetpgrp(tag_files->ofd, - man_pgid); - if (signum == SIGTTIN) - continue; - } else - tag_files->tcpgid = tc_pgid; - kill(0, signum); - continue; - } - - /* Once in the foreground, activate the pager. */ - - if (pager_pid) { - (void)tcsetpgrp(tag_files->ofd, pager_pid); - kill(pager_pid, SIGCONT); - } else - pager_pid = spawn_pager(tag_files); - - /* Wait for the pager to stop or exit. */ - while ((pid = waitpid(pager_pid, &status, - WUNTRACED)) == -1 && errno == EINTR) - continue; - - if (pid == -1) { - mandoc_msg(MANDOCERR_WAIT, 0, 0, - "%s", strerror(errno)); - break; - } - if (!WIFSTOPPED(status)) - break; - - signum = WSTOPSIG(status); - } - tag_unlink(); - } else if (curp.outtype != OUTT_LINT && - (search.argmode == ARG_FILE || sz > 0)) + if (outst.tag_files != NULL) { + if (term_tag_close() != -1 && + conf.output.outfilename == NULL && + conf.output.tagfilename == NULL) + run_pager(&outst, conf.output.tag); + term_tag_unlink(); + } else if (outst.had_output && outst.outtype != OUTT_LINT) mandoc_msg_summary(); return (int)mandoc_msg_getrc(); @@ -762,6 +695,42 @@ usage(enum argmode argmode) exit((int)MANDOCLEVEL_BADARG); } +static void +glob_esc(char **dst, const char *src, const char *suffix) +{ + while (*src != '\0') { + if (strchr("*?[", *src) != NULL) + *(*dst)++ = '\\'; + *(*dst)++ = *src++; + } + while (*suffix != '\0') + *(*dst)++ = *suffix++; +} + +static void +fs_append(char **file, size_t filesz, int copy, size_t ipath, + const char *sec, enum form form, struct manpage **res, size_t *ressz) +{ + struct manpage *page; + + *res = mandoc_reallocarray(*res, *ressz + filesz, sizeof(**res)); + page = *res + *ressz; + *ressz += filesz; + for (;;) { + page->file = copy ? mandoc_strdup(*file) : *file; + page->names = NULL; + page->output = NULL; + page->bits = NAME_FILE & NAME_MASK; + page->ipath = ipath; + page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; + page->form = form; + if (--filesz == 0) + break; + file++; + page++; + } +} + static int fs_lookup(const struct manpaths *paths, size_t ipath, const char *sec, const char *arch, const char *name, @@ -769,12 +738,19 @@ fs_lookup(const struct manpaths *paths, size_t ipath, { struct stat sb; glob_t globinfo; - struct manpage *page; - char *file; + char *file, *cp, secnum[2]; int globres; enum form form; + const char *const slman = "/man"; + const char *const slash = "/"; + const char *const sglob = ".[01-9]*"; + const char *const dot = "."; + const char *const aster = "*"; + + memset(&globinfo, 0, sizeof(globinfo)); form = FORM_SRC; + mandoc_asprintf(&file, "%s/man%s/%s.%s", paths->paths[ipath], sec, name, sec); if (stat(file, &sb) != -1) @@ -797,21 +773,46 @@ fs_lookup(const struct manpaths *paths, size_t ipath, free(file); } - mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", - paths->paths[ipath], sec, name); + cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + + strlen(slman) + strlen(sec) * 2 + strlen(slash) + + strlen(name) * 2 + strlen(sglob) + 1); + glob_esc(&cp, paths->paths[ipath], slman); + glob_esc(&cp, sec, slash); + glob_esc(&cp, name, sglob); + *cp = '\0'; globres = glob(file, 0, NULL, &globinfo); if (globres != 0 && globres != GLOB_NOMATCH) mandoc_msg(MANDOCERR_GLOB, 0, 0, "%s: %s", file, strerror(errno)); free(file); + file = NULL; if (globres == 0) - file = mandoc_strdup(*globinfo.gl_pathv); + goto found; globfree(&globinfo); - if (globres == 0) { - if (stat(file, &sb) != -1) - goto found; + + if (sec[1] != '\0' && *ressz == 0) { + secnum[0] = sec[0]; + secnum[1] = '\0'; + cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + + strlen(slman) + strlen(secnum) * 2 + strlen(slash) + + strlen(name) * 2 + strlen(dot) + + strlen(sec) * 2 + strlen(aster) + 1); + glob_esc(&cp, paths->paths[ipath], slman); + glob_esc(&cp, secnum, slash); + glob_esc(&cp, name, dot); + glob_esc(&cp, sec, aster); + *cp = '\0'; + globres = glob(file, 0, NULL, &globinfo); + if (globres != 0 && globres != GLOB_NOMATCH) + mandoc_msg(MANDOCERR_GLOB, 0, 0, + "%s: %s", file, strerror(errno)); free(file); + file = NULL; + if (globres == 0) + goto found; + globfree(&globinfo); } + if (res != NULL || ipath + 1 != paths->sz) return -1; @@ -823,81 +824,134 @@ fs_lookup(const struct manpaths *paths, size_t ipath, found: warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); - if (res == NULL) { + if (res == NULL) free(file); - return 0; - } - *res = mandoc_reallocarray(*res, ++*ressz, sizeof(**res)); - page = *res + (*ressz - 1); - page->file = file; - page->names = NULL; - page->output = NULL; - page->bits = NAME_FILE & NAME_MASK; - page->ipath = ipath; - page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; - page->form = form; + else if (file == NULL) + fs_append(globinfo.gl_pathv, globinfo.gl_pathc, 1, + ipath, sec, form, res, ressz); + else + fs_append(&file, 1, 0, ipath, sec, form, res, ressz); + globfree(&globinfo); return 0; } static int fs_search(const struct mansearch *cfg, const struct manpaths *paths, - int argc, char **argv, struct manpage **res, size_t *ressz) + const char *name, struct manpage **res, size_t *ressz) { const char *const sections[] = {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; const size_t nsec = sizeof(sections)/sizeof(sections[0]); - size_t ipath, isec, lastsz; + size_t ipath, isec; assert(cfg->argmode == ARG_NAME); - if (res != NULL) *res = NULL; - *ressz = lastsz = 0; - while (argc) { - for (ipath = 0; ipath < paths->sz; ipath++) { - if (cfg->sec != NULL) { - if (fs_lookup(paths, ipath, cfg->sec, - cfg->arch, *argv, res, ressz) != -1 && - cfg->firstmatch) - return 0; - } else for (isec = 0; isec < nsec; isec++) + *ressz = 0; + for (ipath = 0; ipath < paths->sz; ipath++) { + if (cfg->sec != NULL) { + if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, + name, res, ressz) != -1 && cfg->firstmatch) + return 0; + } else { + for (isec = 0; isec < nsec; isec++) if (fs_lookup(paths, ipath, sections[isec], - cfg->arch, *argv, res, ressz) != -1 && + cfg->arch, name, res, ressz) != -1 && cfg->firstmatch) return 0; } - if (res != NULL && *ressz == lastsz && - strchr(*argv, '/') == NULL) { - if (cfg->arch != NULL && - arch_valid(cfg->arch, OSENUM) == 0) - warnx("Unknown architecture \"%s\".", - cfg->arch); - else if (cfg->sec == NULL) - warnx("No entry for %s in the manual.", - *argv); - else - warnx("No entry for %s in section %s " - "of the manual.", *argv, cfg->sec); - } - lastsz = *ressz; - argv++; - argc--; } return -1; } static void -parse(struct curparse *curp, int fd, const char *file) +process_onefile(struct mparse *mp, struct manpage *resp, int startdir, + struct outstate *outst, struct manconf *conf) { - struct roff_meta *meta; + int fd; + + /* + * Changing directories is not needed in ARG_FILE mode. + * Do it on a best-effort basis. Even in case of + * failure, some functionality may still work. + */ + if (resp->ipath != SIZE_MAX) + (void)chdir(conf->manpath.paths[resp->ipath]); + else if (startdir != -1) + (void)fchdir(startdir); + + mandoc_msg_setinfilename(resp->file); + if (resp->file != NULL) { + if ((fd = mparse_open(mp, resp->file)) == -1) { + mandoc_msg(resp->ipath == SIZE_MAX ? + MANDOCERR_BADARG_BAD : MANDOCERR_OPEN, + 0, 0, "%s", strerror(errno)); + mandoc_msg_setinfilename(NULL); + return; + } + } else + fd = STDIN_FILENO; + + if (outst->use_pager) { + outst->use_pager = 0; + outst->tag_files = term_tag_init(conf->output.outfilename, + outst->outtype == OUTT_HTML ? ".html" : "", + conf->output.tagfilename); +#if HAVE_PLEDGE + if ((conf->output.outfilename != NULL || + conf->output.tagfilename != NULL) && + pledge("stdio rpath cpath", NULL) == -1) { + mandoc_msg(MANDOCERR_PLEDGE, 0, 0, + "%s", strerror(errno)); + exit(mandoc_msg_getrc()); + } +#endif + } + if (outst->had_output && outst->outtype <= OUTT_UTF8) { + if (outst->outdata == NULL) + outdata_alloc(outst, &conf->output); + terminal_sepline(outst->outdata); + } - /* Begin by parsing the file itself. */ + if (resp->form == FORM_SRC) + parse(mp, fd, resp->file, outst, conf); + else { + passthrough(fd, conf->output.synopsisonly); + outst->had_output = 1; + } + + if (ferror(stdout)) { + if (outst->tag_files != NULL) { + mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s", + outst->tag_files->ofn, strerror(errno)); + term_tag_unlink(); + outst->tag_files = NULL; + } else + mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s", + strerror(errno)); + } + mandoc_msg_setinfilename(NULL); +} + +static void +parse(struct mparse *mp, int fd, const char *file, + struct outstate *outst, struct manconf *conf) +{ + static struct manpaths basepaths; + static int previous; + struct roff_meta *meta; - assert(file); assert(fd >= 0); + if (file == NULL) + file = "<stdin>"; - mparse_readfd(curp->mp, fd, file); + if (previous) + mparse_reset(mp); + else + previous = 1; + + mparse_readfd(mp, fd, file); if (fd != STDIN_FILENO) close(fd); @@ -906,81 +960,89 @@ parse(struct curparse *curp, int fd, const char *file) * level, do not produce output. */ - if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) + if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) return; - if (curp->outdata == NULL) - outdata_alloc(curp); - else if (curp->outtype == OUTT_HTML) - html_reset(curp); + if (outst->outdata == NULL) + outdata_alloc(outst, &conf->output); + else if (outst->outtype == OUTT_HTML) + html_reset(outst->outdata); mandoc_xr_reset(); - meta = mparse_result(curp->mp); + meta = mparse_result(mp); /* Execute the out device, if it exists. */ + outst->had_output = 1; if (meta->macroset == MACROSET_MDOC) { - switch (curp->outtype) { + switch (outst->outtype) { case OUTT_HTML: - html_mdoc(curp->outdata, meta); + html_mdoc(outst->outdata, meta); break; case OUTT_TREE: - tree_mdoc(curp->outdata, meta); + tree_mdoc(outst->outdata, meta); break; case OUTT_MAN: - man_mdoc(curp->outdata, meta); + man_mdoc(outst->outdata, meta); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: - terminal_mdoc(curp->outdata, meta); + terminal_mdoc(outst->outdata, meta); break; case OUTT_MARKDOWN: - markdown_mdoc(curp->outdata, meta); + markdown_mdoc(outst->outdata, meta); break; default: break; } } if (meta->macroset == MACROSET_MAN) { - switch (curp->outtype) { + switch (outst->outtype) { case OUTT_HTML: - html_man(curp->outdata, meta); + html_man(outst->outdata, meta); break; case OUTT_TREE: - tree_man(curp->outdata, meta); + tree_man(outst->outdata, meta); break; case OUTT_MAN: - mparse_copy(curp->mp); + mparse_copy(mp); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: - terminal_man(curp->outdata, meta); + terminal_man(outst->outdata, meta); + break; + case OUTT_MARKDOWN: + mandoc_msg(MANDOCERR_MAN_TMARKDOWN, 0, 0, NULL); break; default: break; } } - if (mandoc_msg_getmin() < MANDOCERR_STYLE) - check_xr(); + if (conf->output.tag != NULL && conf->output.tag_found == 0 && + tag_exists(conf->output.tag)) + conf->output.tag_found = 1; + + if (mandoc_msg_getmin() < MANDOCERR_STYLE) { + if (basepaths.sz == 0) + manpath_base(&basepaths); + check_xr(&basepaths); + } else if (mandoc_msg_getmin() < MANDOCERR_WARNING) + check_xr(&conf->manpath); } static void -check_xr(void) +check_xr(struct manpaths *paths) { - static struct manpaths paths; struct mansearch search; struct mandoc_xr *xr; size_t sz; - if (paths.sz == 0) - manpath_base(&paths); - for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { if (xr->line == -1) continue; @@ -989,9 +1051,9 @@ check_xr(void) search.outkey = NULL; search.argmode = ARG_NAME; search.firstmatch = 1; - if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) + if (mansearch(&search, paths, 1, &xr->name, NULL, &sz)) continue; - if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz) != -1) + if (fs_search(&search, paths, xr->name, NULL, &sz) != -1) continue; if (xr->count == 1) mandoc_msg(MANDOCERR_XR_BAD, xr->line, @@ -1004,26 +1066,26 @@ check_xr(void) } static void -outdata_alloc(struct curparse *curp) +outdata_alloc(struct outstate *outst, struct manoutput *outconf) { - switch (curp->outtype) { + switch (outst->outtype) { case OUTT_HTML: - curp->outdata = html_alloc(curp->outopts); + outst->outdata = html_alloc(outconf); break; case OUTT_UTF8: - curp->outdata = utf8_alloc(curp->outopts); + outst->outdata = utf8_alloc(outconf); break; case OUTT_LOCALE: - curp->outdata = locale_alloc(curp->outopts); + outst->outdata = locale_alloc(outconf); break; case OUTT_ASCII: - curp->outdata = ascii_alloc(curp->outopts); + outst->outdata = ascii_alloc(outconf); break; case OUTT_PDF: - curp->outdata = pdf_alloc(curp->outopts); + outst->outdata = pdf_alloc(outconf); break; case OUTT_PS: - curp->outdata = ps_alloc(curp->outopts); + outst->outdata = ps_alloc(outconf); break; default: break; @@ -1093,7 +1155,7 @@ done: } static int -woptions(struct curparse *curp, char *arg) +woptions(char *arg, enum mandoc_os *os_e, int *wstop) { char *v, *o; const char *toks[11]; @@ -1114,7 +1176,7 @@ woptions(struct curparse *curp, char *arg) o = arg; switch (getsubopt(&arg, (char * const *)toks, &v)) { case 0: - curp->wstop = 1; + *wstop = 1; break; case 1: case 2: @@ -1137,11 +1199,11 @@ woptions(struct curparse *curp, char *arg) break; case 8: mandoc_msg_setmin(MANDOCERR_BASE); - curp->os_e = MANDOC_OS_OPENBSD; + *os_e = MANDOC_OS_OPENBSD; break; case 9: mandoc_msg_setmin(MANDOCERR_BASE); - curp->os_e = MANDOC_OS_NETBSD; + *os_e = MANDOC_OS_NETBSD; break; default: mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); @@ -1151,8 +1213,66 @@ woptions(struct curparse *curp, char *arg) return 0; } +/* + * Wait until moved to the foreground, + * then fork the pager and wait for the user to close it. + */ +static void +run_pager(struct outstate *outst, char *tag_target) +{ + int signum, status; + pid_t man_pgid, tc_pgid; + pid_t pager_pid, wait_pid; + + man_pgid = getpgid(0); + outst->tag_files->tcpgid = + man_pgid == getpid() ? getpgid(getppid()) : man_pgid; + pager_pid = 0; + signum = SIGSTOP; + + for (;;) { + /* Stop here until moved to the foreground. */ + + tc_pgid = tcgetpgrp(STDOUT_FILENO); + if (tc_pgid != man_pgid) { + if (tc_pgid == pager_pid) { + (void)tcsetpgrp(STDOUT_FILENO, man_pgid); + if (signum == SIGTTIN) + continue; + } else + outst->tag_files->tcpgid = tc_pgid; + kill(0, signum); + continue; + } + + /* Once in the foreground, activate the pager. */ + + if (pager_pid) { + (void)tcsetpgrp(STDOUT_FILENO, pager_pid); + kill(pager_pid, SIGCONT); + } else + pager_pid = spawn_pager(outst, tag_target); + + /* Wait for the pager to stop or exit. */ + + while ((wait_pid = waitpid(pager_pid, &status, + WUNTRACED)) == -1 && errno == EINTR) + continue; + + if (wait_pid == -1) { + mandoc_msg(MANDOCERR_WAIT, 0, 0, + "%s", strerror(errno)); + break; + } + if (!WIFSTOPPED(status)) + break; + + signum = WSTOPSIG(status); + } +} + static pid_t -spawn_pager(struct tag_files *tag_files) +spawn_pager(struct outstate *outst, char *tag_target) { const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ #define MAX_PAGER_ARGS 16 @@ -1165,11 +1285,14 @@ spawn_pager(struct tag_files *tag_files) int argc, use_ofn; pid_t pager_pid; + assert(outst->tag_files->ofd == -1); + assert(outst->tag_files->tfs == NULL); + pager = getenv("MANPAGER"); if (pager == NULL || *pager == '\0') pager = getenv("PAGER"); if (pager == NULL || *pager == '\0') - pager = "more -s"; + pager = BINM_PAGER; cp = mandoc_strdup(pager); /* @@ -1194,21 +1317,28 @@ spawn_pager(struct tag_files *tag_files) use_ofn = 1; #if HAVE_LESS_T - if (*tag_files->tfn != '\0' && (cmdlen = strlen(argv[0])) >= 4) { + if (*outst->tag_files->tfn != '\0' && + (cmdlen = strlen(argv[0])) >= 4) { cp = argv[0] + cmdlen - 4; if (strcmp(cp, "less") == 0) { argv[argc++] = mandoc_strdup("-T"); - argv[argc++] = tag_files->tfn; - if (tag_files->tagname != NULL) { + argv[argc++] = outst->tag_files->tfn; + if (tag_target != NULL) { argv[argc++] = mandoc_strdup("-t"); - argv[argc++] = tag_files->tagname; + argv[argc++] = tag_target; use_ofn = 0; } } } #endif - if (use_ofn) - argv[argc++] = tag_files->ofn; + if (use_ofn) { + if (outst->outtype == OUTT_HTML && tag_target != NULL) + mandoc_asprintf(&argv[argc], "file://%s#%s", + outst->tag_files->ofn, tag_target); + else + argv[argc] = outst->tag_files->ofn; + argc++; + } argv[argc] = NULL; switch (pager_pid = fork()) { @@ -1219,7 +1349,7 @@ spawn_pager(struct tag_files *tag_files) break; default: (void)setpgid(pager_pid, 0); - (void)tcsetpgrp(tag_files->ofd, pager_pid); + (void)tcsetpgrp(STDOUT_FILENO, pager_pid); #if HAVE_PLEDGE if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { mandoc_msg(MANDOCERR_PLEDGE, 0, 0, @@ -1227,20 +1357,14 @@ spawn_pager(struct tag_files *tag_files) exit(mandoc_msg_getrc()); } #endif - tag_files->pager_pid = pager_pid; + outst->tag_files->pager_pid = pager_pid; return pager_pid; } - /* The child process becomes the pager. */ - - if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) { - mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); - _exit(mandoc_msg_getrc()); - } - close(tag_files->ofd); - assert(tag_files->tfd == -1); - - /* Do not start the pager before controlling the terminal. */ + /* + * The child process becomes the pager. + * Do not start it before controlling the terminal. + */ while (tcgetpgrp(STDOUT_FILENO) != getpid()) nanosleep(&timeout, NULL); @@ -1,9 +1,9 @@ -.\" $Id: man.1,v 1.35 2019/03/09 15:55:01 schwarze Exp $ +.\" $Id: man.1,v 1.40 2020/07/20 16:57:30 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-2018 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010, 2011, 2014-2020 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: March 9 2019 $ +.Dd $Mdocdate: July 20 2020 $ .Dt MAN 1 .Os .Sh NAME @@ -51,7 +51,7 @@ The .Nm utility displays the -manual pages entitled +manual page entitled .Ar name . Pages may be selected according to a specific category @@ -64,7 +64,6 @@ The options are as follows: .Bl -tag -width Ds .It Fl a Display all matching manual pages. -Normally, only the first page found is displayed. .It Fl C Ar file Use the specified .Ar file @@ -75,7 +74,7 @@ See for a description of the contents of this file. .It Fl c Copy the manual page to the standard output instead of using -.Xr more 1 +.Xr less 1 to paginate it. This is done by default if the standard output is not a terminal device. .Pp @@ -129,31 +128,31 @@ are ignored. This option implies .Fl a . .It Fl M Ar path -Override the list of standard directories which -.Nm -searches for manual pages. +Override the list of directories to search for manual pages. The supplied .Ar path must be a colon .Pq Ql \&: separated list of directories. -This search path may also be set using the environment variable -.Ev MANPATH . +This option also overrides the environment variable +.Ev MANPATH +and any directories specified in the +.Xr man.conf 5 +file. .It Fl m Ar path -Augment the list of standard directories which -.Nm -searches for manual pages. +Augment the list of directories to search for manual pages. The supplied .Ar path must be a colon .Pq Ql \&: separated list of directories. -These directories will be searched before the standard directories or -the directories specified using the +These directories will be searched before those specified using the .Fl M -option or the +option, the .Ev MANPATH -environment variable. +environment variable, the +.Xr man.conf 5 +file, or the default directories. .It Fl S Ar subsection Only show pages for the specified .Xr machine 1 @@ -168,6 +167,7 @@ architecture whilst using another. This option overrides the .Ev MACHINE environment variable. +.Tg s .It Oo Fl s Oc Ar section Only select manuals from the specified .Ar section . @@ -197,13 +197,12 @@ System maintenance and operation commands. .It 9 Kernel internals. .El -.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 all matching manual pages instead of displaying any of them. +If no +.Ar name +is given, list the directories that would be searched. .El .Pp The options @@ -214,9 +213,23 @@ The options .Fl fkl are mutually exclusive and override each other. .Pp -Guidelines for writing -man pages can be found in -.Xr mdoc 7 . +The search starts with the +.Fl m +argument if provided, then continues with the +.Fl M +argument, the +.Ev MANPATH +variable, the +.Ic manpath +entries in the +.Xr man.conf 5 +file, or with +.Pa /usr/share/man : Ns Pa /usr/X11R6/man : Ns Pa /usr/local/man +by default. +Within each of these, directories are searched in the order provided. +Within each directory, the search proceeds according to the following +list of sections: 1, 8, 6, 2, 3, 5, 7, 4, 9, 3p. +The first match found is shown. .Pp The .Xr mandoc.db 5 @@ -236,6 +249,10 @@ The database is kept up to date with which is run by the .Xr weekly 8 maintenance script. +.Pp +Guidelines for writing +man pages can be found in +.Xr mdoc 7 . .Sh ENVIRONMENT .Bl -tag -width MANPATHX .It Ev MACHINE @@ -258,7 +275,7 @@ is case insensitive. Any non-empty value of the environment variable .Ev MANPAGER is used instead of the standard pagination program, -.Xr more 1 . +.Xr less 1 . If .Xr less 1 is used, the interactive @@ -286,15 +303,15 @@ manual opens a manual page at the definition of a specific .Ar term rather than at the beginning. .It Ev MANPATH -The standard search path used by -.Nm -may be changed by specifying a path in the +Override the standard search path which is either specified in +.Xr man.conf 5 +or the default path. +The format of .Ev MANPATH -environment variable. -The format of the path is a colon +is a colon .Pq Ql \&: separated list of directories. -Invalid paths are ignored. +Invalid directories are ignored. Overridden by .Fl M , ignored if @@ -303,25 +320,24 @@ 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; +begins with a colon, it is appended to the standard path; +if it ends with a colon, it is prepended to the standard path; 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. +the standard path is inserted between the colons. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, -.Xr more 1 -.Fl s +.Xr less 1 is used. .El .Sh FILES .Bl -tag -width /etc/man.conf -compact .It Pa /etc/man.conf -default man configuration file +default +.Nm +configuration file .El .Sh EXIT STATUS .Ex -std man @@ -365,7 +381,7 @@ are extensions to that specification. A .Nm command first appeared in -.At v3 . +.At v2 . .Pp The .Fl w @@ -1,7 +1,7 @@ -.\" $Id: man.7,v 1.144 2019/07/09 03:46:59 schwarze Exp $ +.\" $Id: man.7,v 1.148 2021/08/05 14:31:14 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2011-2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2011-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> .\" Copyright (c) 2017 Anthony Bentley <bentley@openbsd.org> .\" Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> .\" @@ -17,7 +17,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 9 2019 $ +.Dd $Mdocdate: August 5 2021 $ .Dt MAN 7 .Os .Sh NAME @@ -127,6 +127,8 @@ Sets the volume for the footer for compatibility with man pages from .At releases. The optional arguments specify which release it is from. +This macro is an extension that first appeared in +.Bx 4.3 . .It Ic B Text is rendered in bold face. .It Ic BI @@ -244,7 +246,7 @@ link description to be shown .Ed .It Ic OP Optional command-line argument. -This is a non-standard GNU extension. +This is a non-standard DWB extension. It has the following syntax: .Pp .D1 Pf . Ic OP Ar key Op Ar value @@ -255,8 +257,12 @@ is usually a command-line flag and .Ar value its argument. .It Ic P -A synonym for -.Ic PP . +This synonym for +.Ic PP +is an +.At III +extension later adopted by +.Bx 4.3 . .It Ic PD Specify the vertical space to be inserted before each new paragraph. .br @@ -343,6 +349,9 @@ See also .It Ic SB Text is rendered in small size (one point smaller than the default font) bold face. +This macro is an extension that probably first appeared in SunOS 4.0 +and was later adopted by GNU and by +.Bx 4.4 . .It Ic SH Begin a section. The scope of a section is only closed by another section or the end of @@ -435,6 +444,8 @@ Sets the volume for the footer for compatibility with man pages from .Bx releases. The optional first argument specifies which release it is from. +This macro is an extension that first appeared in +.Bx 3 . .It Ic UE End a uniform resource identifier block started with .Ic UR . @@ -505,7 +516,7 @@ The syntax is as follows: .It Ic I Ta n Ta next-line Ta \& .It Ic IB Ta n Ta current Ta \& .It Ic IR Ta n Ta current Ta \& -.It Ic OP Ta >=1 Ta current Ta GNU +.It Ic OP Ta >=1 Ta current Ta DWB .It Ic PD Ta 1 Ta current Ta \& .It Ic RB Ta n Ta current Ta \& .It Ic RI Ta n Ta current Ta \& @@ -601,16 +612,32 @@ The language first appeared as a macro package for the roff typesetting system in .At v7 . -It was later rewritten by James Clark as a macro package for groff. -Eric S. Raymond wrote the extended -.Nm -macros for groff in 2007. +.Pp The stand-alone implementation that is part of the .Xr mandoc 1 -utility written by Kristaps Dzonsons appeared in +utility first appeared in .Ox 4.6 . .Sh AUTHORS -This +.An -nosplit +.An Douglas McIlroy Aq Mt m.douglas.mcilroy@dartmouth.edu +designed and implemented the original version of these macros, +wrote the original version of this manual page, +and was the first to use them when he edited volume 1 of the +.At v7 +manual pages. +.Pp +.An James Clark +later rewrote the macros for groff. +.An Eric S. Raymond Aq Mt esr@thyrsus.com +and +.An Werner Lemberg Aq Mt wl@gnu.org +added the extended +.Nm +macros to groff in 2007. +.Pp +The +.Xr mandoc 1 +program and this .Nm -reference was written by +reference were written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . diff --git a/man.conf.5 b/man.conf.5 index 0ffe868ad764..ffe9fcc07058 100644 --- a/man.conf.5 +++ b/man.conf.5 @@ -1,4 +1,4 @@ -.\" $Id: man.conf.5,v 1.6 2018/10/02 14:56:47 schwarze Exp $ +.\" $Id: man.conf.5,v 1.8 2020/02/10 14:42:10 schwarze Exp $ .\" .\" Copyright (c) 2015, 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: October 2 2018 $ +.Dd $Mdocdate: February 10 2020 $ .Dt MAN.CONF 5 .Os .Sh NAME @@ -101,15 +101,11 @@ manual. .It Ic toc Ta none Ta Cm html Ta print table of contents .It Ic width Ta integer Ta Cm ascii , utf8 Ta right margin .El -.It Ic _whatdb Ar path Ns Cm /whatis.db -This directive provides the same functionality as -.Ic manpath , -but using a historic and misleading syntax. -It is kept for backward compatibility for now, -but will eventually be removed. .El .Sh FILES -.Pa /etc/man.conf +.Bl -tag -width /etc/examples/man.conf -compact +.It Pa /etc/man.conf +.El .Sh EXAMPLES The following configuration file reproduces the defaults: installing it is equivalent to not having a diff --git a/man_html.c b/man_html.c index c0a429c5d6c9..147c20e46443 100644 --- a/man_html.c +++ b/man_html.c @@ -1,7 +1,7 @@ -/* $Id: man_html.c,v 1.174 2019/04/30 15:53:00 schwarze Exp $ */ +/* $Id: man_html.c,v 1.179 2020/10/16 17:22:43 schwarze Exp $ */ /* + * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * HTML formatter for man(7) used by mandoc(1). */ #include "config.h" @@ -34,7 +36,7 @@ #include "main.h" #define MAN_ARGS const struct roff_meta *man, \ - const struct roff_node *n, \ + struct roff_node *n, \ struct html *h struct man_html_act { @@ -167,7 +169,12 @@ print_man_node(MAN_ARGS) if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; - html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); + if ((n->flags & NODE_NOFILL) == 0) + html_fillmode(h, ROFF_fi); + else if (html_fillmode(h, ROFF_nf) == ROFF_nf && + n->tok != ROFF_fi && n->flags & NODE_LINE && + (n->prev == NULL || n->prev->tok != MAN_YS)) + print_endline(h); child = 1; switch (n->type) { @@ -178,7 +185,7 @@ print_man_node(MAN_ARGS) } if (*n->string == ' ' && n->flags & NODE_LINE && (h->flags & HTML_NONEWLINE) == 0) - print_endline(h); + print_otag(h, TAG_BR, ""); else if (n->flags & NODE_DELIMC) h->flags |= HTML_NOSPACE; t = h->tag; @@ -244,20 +251,13 @@ print_man_node(MAN_ARGS) * Close the list if no further item of the same type * follows; otherwise, close the item only. */ - if (list_continues(n, n->next) == '\0') { + if (list_continues(n, roff_node_next(n)) == '\0') { print_tagq(h, t); t = NULL; } } if (t != NULL) print_stagq(h, t); - - if (n->flags & NODE_NOFILL && n->tok != MAN_YS && - (n->next != NULL && n->next->flags & NODE_LINE)) { - /* In .nf = <pre>, print even empty lines. */ - h->col++; - print_endline(h); - } } static void @@ -310,7 +310,6 @@ static int man_SH_pre(MAN_ARGS) { const char *class; - char *id; enum htmltag tag; if (n->tok == MAN_SH) { @@ -326,10 +325,7 @@ man_SH_pre(MAN_ARGS) print_otag(h, TAG_SECTION, "c", class); break; case ROFFT_HEAD: - id = html_make_id(n, 1); - print_otag(h, tag, "ci", class, id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); + print_otag_id(h, tag, class, n); break; case ROFFT_BODY: break; @@ -445,15 +441,17 @@ list_continues(const struct roff_node *n1, const struct roff_node *n2) static int man_IP_pre(MAN_ARGS) { - const struct roff_node *nn; + struct roff_node *nn; const char *list_class; enum htmltag list_elem, body_elem; char list_type; nn = n->type == ROFFT_BLOCK ? n : n->parent; - if ((list_type = list_continues(nn->prev, nn)) == '\0') { + list_type = list_continues(roff_node_prev(nn), nn); + if (list_type == '\0') { /* Start a new list. */ - if ((list_type = list_continues(nn, nn->next)) == '\0') + list_type = list_continues(nn, roff_node_next(nn)); + if (list_type == '\0') list_type = ' '; switch (list_type) { case ' ': @@ -487,7 +485,7 @@ man_IP_pre(MAN_ARGS) case ROFFT_HEAD: if (body_elem == TAG_LI) return 0; - print_otag(h, TAG_DT, ""); + print_otag_id(h, TAG_DT, NULL, n); break; case ROFFT_BODY: print_otag(h, body_elem, ""); @@ -495,7 +493,6 @@ man_IP_pre(MAN_ARGS) default: abort(); } - switch(n->tok) { case MAN_IP: /* Only print the first header element. */ if (n->child != NULL) diff --git a/man_macro.c b/man_macro.c index d195576dee28..b3c3a3cb488b 100644 --- a/man_macro.c +++ b/man_macro.c @@ -1,7 +1,7 @@ -/* $Id: man_macro.c,v 1.144 2019/01/05 18:59:46 schwarze Exp $ */ +/* $Id: man_macro.c,v 1.145 2020/09/09 17:01:10 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> * * Permission to use, copy, modify, and distribute this software for any @@ -107,9 +107,11 @@ man_unscope(struct roff_man *man, const struct roff_node *to) mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, "EOF breaks %s", roff_name[n->tok]); - if (man->flags & MAN_ELINE) - man->flags &= ~MAN_ELINE; - else { + if (man->flags & MAN_ELINE) { + if ((man_macro(n->parent->tok)->flags & + MAN_ESCOPED) == 0) + man->flags &= ~MAN_ELINE; + } else { assert(n->type == ROFFT_HEAD); n = n->parent; man->flags &= ~MAN_BLINE; diff --git a/man_term.c b/man_term.c index d9ee14ad22ba..d289f2d12bfb 100644 --- a/man_term.c +++ b/man_term.c @@ -1,7 +1,7 @@ -/* $Id: man_term.c,v 1.232 2019/07/23 17:53:35 schwarze Exp $ */ +/* $Id: man_term.c,v 1.236 2021/06/28 19:50:15 schwarze Exp $ */ /* + * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Plain text formatter for man(7), used by mandoc(1) + * for ASCII, UTF-8, PostScript, and PDF output. */ #include "config.h" @@ -32,7 +35,7 @@ #include "man.h" #include "out.h" #include "term.h" -#include "tag.h" +#include "term_tag.h" #include "main.h" #define MAXMARGINS 64 /* maximum number of indented scopes */ @@ -64,7 +67,7 @@ static void print_man_head(struct termp *, static void print_man_foot(struct termp *, const struct roff_meta *); static void print_bvspace(struct termp *, - const struct roff_node *, int); + struct roff_node *, int); static int pre_B(DECL_ARGS); static int pre_DT(DECL_ARGS); @@ -94,8 +97,6 @@ static void post_SY(DECL_ARGS); static void post_TP(DECL_ARGS); static void post_UR(DECL_ARGS); -static void tag_man(struct termp *, struct roff_node *); - static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = { { NULL, NULL, 0 }, /* TH */ { pre_SH, post_SH, 0 }, /* SH */ @@ -205,19 +206,20 @@ terminal_man(void *arg, const struct roff_meta *man) * first, print it. */ static void -print_bvspace(struct termp *p, const struct roff_node *n, int pardist) +print_bvspace(struct termp *p, struct roff_node *n, int pardist) { - int i; + struct roff_node *nch; + int i; term_newln(p); - if (n->body != NULL && n->body->child != NULL) - if (n->body->child->type == ROFFT_TBL) - return; + if (n->body != NULL && + (nch = roff_node_child(n->body)) != NULL && + nch->type == ROFFT_TBL) + return; - if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) - if (n->prev == NULL) - return; + if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL) + return; for (i = 0; i < pardist; i++) term_vspace(p); @@ -538,10 +540,8 @@ pre_IP(DECL_ARGS) case ROFFT_HEAD: p->tcol->offset = mt->offset; p->tcol->rmargin = mt->offset + len; - if (n->child != NULL) { + if (n->child != NULL) print_man_node(p, mt, n->child, meta); - tag_man(p, n->child); - } return 0; case ROFFT_BODY: p->tcol->offset = mt->offset + len; @@ -621,18 +621,6 @@ pre_TP(DECL_ARGS) while (nn != NULL && (nn->flags & NODE_LINE) == 0) nn = nn->next; - if (nn == NULL) - return 0; - - if (nn->type == ROFFT_TEXT) - tag_man(p, nn); - else if (nn->child != NULL && - nn->child->type == ROFFT_TEXT && - (nn->tok == MAN_B || nn->tok == MAN_BI || - nn->tok == MAN_BR || nn->tok == MAN_I || - nn->tok == MAN_IB || nn->tok == MAN_IR)) - tag_man(p, nn->child); - while (nn != NULL) { print_man_node(p, mt, nn, meta); nn = nn->next; @@ -683,12 +671,8 @@ pre_SS(DECL_ARGS) * and after an empty subsection. */ - do { - n = n->prev; - } while (n != NULL && n->tok >= MAN_TH && - man_term_act(n->tok)->flags & MAN_NOTEXT); - if (n == NULL || n->type == ROFFT_COMMENT || - (n->tok == MAN_SS && n->body->child == NULL)) + if ((n = roff_node_prev(n)) == NULL || + (n->tok == MAN_SS && roff_node_child(n->body) == NULL)) break; for (i = 0; i < mt->pardist; i++) @@ -728,12 +712,8 @@ pre_SH(DECL_ARGS) * and after an empty section. */ - do { - n = n->prev; - } while (n != NULL && n->tok >= MAN_TH && - man_term_act(n->tok)->flags & MAN_NOTEXT); - if (n == NULL || n->type == ROFFT_COMMENT || - (n->tok == MAN_SH && n->body->child == NULL)) + if ((n = roff_node_prev(n)) == NULL || + (n->tok == MAN_SH && roff_node_child(n->body) == NULL)) break; for (i = 0; i < mt->pardist; i++) @@ -839,7 +819,7 @@ pre_SY(DECL_ARGS) switch (n->type) { case ROFFT_BLOCK: - if (n->prev == NULL || n->prev->tok != MAN_SY) + if ((nn = roff_node_prev(n)) == NULL || nn->tok != MAN_SY) print_bvspace(p, n, mt->pardist); return 1; case ROFFT_HEAD: @@ -920,6 +900,9 @@ print_man_node(DECL_ARGS) const struct man_term_act *act; int c; + if (n->flags & NODE_ID) + term_tag_write(n, p->line); + switch (n->type) { case ROFFT_TEXT: /* @@ -1038,10 +1021,6 @@ print_man_foot(struct termp *p, const struct roff_meta *meta) */ if ( ! p->mdocstyle) { - if (meta->hasbody) { - term_vspace(p); - term_vspace(p); - } mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); } else if (meta->os != NULL) { @@ -1160,66 +1139,5 @@ print_man_head(struct termp *p, const struct roff_meta *meta) */ term_vspace(p); - if ( ! p->mdocstyle) { - term_vspace(p); - term_vspace(p); - } free(title); } - -/* - * Skip leading whitespace, dashes, backslashes, and font escapes, - * then create a tag if the first following byte is a letter. - * Priority is high unless whitespace is present. - */ -static void -tag_man(struct termp *p, struct roff_node *n) -{ - const char *cp, *arg; - int prio, sz; - - assert(n->type == ROFFT_TEXT); - cp = n->string; - prio = 1; - for (;;) { - switch (*cp) { - case ' ': - case '\t': - prio = INT_MAX; - /* FALLTHROUGH */ - case '-': - cp++; - break; - case '\\': - cp++; - switch (mandoc_escape(&cp, &arg, &sz)) { - case ESCAPE_FONT: - case ESCAPE_FONTROMAN: - case ESCAPE_FONTITALIC: - case ESCAPE_FONTBOLD: - case ESCAPE_FONTPREV: - case ESCAPE_FONTBI: - break; - case ESCAPE_SPECIAL: - if (sz != 1) - return; - switch (*arg) { - case '&': - case '-': - case 'e': - break; - default: - return; - } - break; - default: - return; - } - break; - default: - if (isalpha((unsigned char)*cp)) - tag_put(cp, prio, p->line); - return; - } - } -} diff --git a/man_validate.c b/man_validate.c index 0aa550bd08d9..404b223f2b54 100644 --- a/man_validate.c +++ b/man_validate.c @@ -1,7 +1,7 @@ -/* $Id: man_validate.c,v 1.149 2019/06/27 15:07:30 schwarze Exp $ */ +/* $Id: man_validate.c,v 1.156 2021/08/10 12:55:03 schwarze Exp $ */ /* + * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Validation module for man(7) syntax trees used by mandoc(1). */ #include "config.h" @@ -36,6 +38,7 @@ #include "libmandoc.h" #include "roff_int.h" #include "libman.h" +#include "tag.h" #define CHKARGS struct roff_man *man, struct roff_node *n @@ -45,6 +48,7 @@ static void check_abort(CHKARGS) __attribute__((__noreturn__)); static void check_par(CHKARGS); static void check_part(CHKARGS); static void check_root(CHKARGS); +static void check_tag(struct roff_node *, struct roff_node *); static void check_text(CHKARGS); static void post_AT(CHKARGS); @@ -54,6 +58,7 @@ static void post_IP(CHKARGS); static void post_OP(CHKARGS); static void post_SH(CHKARGS); static void post_TH(CHKARGS); +static void post_TP(CHKARGS); static void post_UC(CHKARGS); static void post_UR(CHKARGS); static void post_in(CHKARGS); @@ -62,8 +67,8 @@ static const v_check man_valids[MAN_MAX - MAN_TH] = { post_TH, /* TH */ post_SH, /* SH */ post_SH, /* SS */ - NULL, /* TP */ - NULL, /* TQ */ + post_TP, /* TP */ + post_TP, /* TQ */ check_abort,/* LP */ check_par, /* PP */ check_abort,/* P */ @@ -185,7 +190,7 @@ check_root(CHKARGS) man->meta.title = mandoc_strdup(""); man->meta.msec = mandoc_strdup(""); - man->meta.date = mandoc_normdate(man, NULL, n->line, n->pos); + man->meta.date = mandoc_normdate(NULL, NULL); } if (man->meta.os_e && @@ -201,6 +206,68 @@ check_abort(CHKARGS) abort(); } +/* + * Skip leading whitespace, dashes, backslashes, and font escapes, + * then create a tag if the first following byte is a letter. + * Priority is high unless whitespace is present. + */ +static void +check_tag(struct roff_node *n, struct roff_node *nt) +{ + const char *cp, *arg; + int prio, sz; + + if (nt == NULL || nt->type != ROFFT_TEXT) + return; + + cp = nt->string; + prio = TAG_STRONG; + for (;;) { + switch (*cp) { + case ' ': + case '\t': + prio = TAG_WEAK; + /* FALLTHROUGH */ + case '-': + cp++; + break; + case '\\': + cp++; + switch (mandoc_escape(&cp, &arg, &sz)) { + case ESCAPE_FONT: + case ESCAPE_FONTBOLD: + case ESCAPE_FONTITALIC: + case ESCAPE_FONTBI: + case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: + case ESCAPE_FONTPREV: + case ESCAPE_IGNORE: + break; + case ESCAPE_SPECIAL: + if (sz != 1) + return; + switch (*arg) { + case '-': + case 'e': + break; + default: + return; + } + break; + default: + return; + } + break; + default: + if (isalpha((unsigned char)*cp)) + tag_put(cp, prio, n); + return; + } + } +} + static void check_text(CHKARGS) { @@ -246,9 +313,32 @@ static void post_SH(CHKARGS) { struct roff_node *nc; + char *cp, *tag; - if (n->type != ROFFT_BODY || (nc = n->child) == NULL) + nc = n->child; + switch (n->type) { + case ROFFT_HEAD: + tag = NULL; + deroff(&tag, n); + if (tag != NULL) { + for (cp = tag; *cp != '\0'; cp++) + if (*cp == ' ') + *cp = '_'; + if (nc != NULL && nc->type == ROFFT_TEXT && + strcmp(nc->string, tag) == 0) + tag_put(NULL, TAG_STRONG, n); + else + tag_put(tag, TAG_FALLBACK, n); + free(tag); + } + return; + case ROFFT_BODY: + if (nc != NULL) + break; return; + default: + return; + } if (nc->tok == MAN_PP && nc->body->child != NULL) { while (nc->body->last != NULL) { @@ -332,12 +422,14 @@ check_par(CHKARGS) static void post_IP(CHKARGS) { - switch (n->type) { case ROFFT_BLOCK: if (n->head->child == NULL && n->body->child == NULL) roff_node_delete(man, n); break; + case ROFFT_HEAD: + check_tag(n, n->child); + break; case ROFFT_BODY: if (n->parent->head->child == NULL && n->child == NULL) mandoc_msg(MANDOCERR_PAR_SKIP, n->line, n->pos, @@ -348,6 +440,37 @@ post_IP(CHKARGS) } } +/* + * The first next-line element in the head is the tag. + * If that's a font macro, use its first child instead. + */ +static void +post_TP(CHKARGS) +{ + struct roff_node *nt; + + if (n->type != ROFFT_HEAD || (nt = n->child) == NULL) + return; + + while ((nt->flags & NODE_LINE) == 0) + if ((nt = nt->next) == NULL) + return; + + switch (nt->tok) { + case MAN_B: + case MAN_BI: + case MAN_BR: + case MAN_I: + case MAN_IB: + case MAN_IR: + nt = nt->child; + break; + default: + break; + } + check_tag(n, nt); +} + static void post_TH(CHKARGS) { @@ -389,9 +512,14 @@ post_TH(CHKARGS) if (n != NULL) n = n->next; - if (n != NULL && n->string != NULL) + if (n != NULL && n->string != NULL) { man->meta.msec = mandoc_strdup(n->string); - else { + if (man->filesec != '\0' && + man->filesec != *n->string && + *n->string >= '1' && *n->string <= '9') + mandoc_msg(MANDOCERR_MSEC_FILE, n->line, n->pos, + "*.%c vs TH ... %c", man->filesec, *n->string); + } else { man->meta.msec = mandoc_strdup(""); mandoc_msg(MANDOCERR_MSEC_MISSING, nb->line, nb->pos, "TH %s", man->meta.title); @@ -401,15 +529,10 @@ post_TH(CHKARGS) if (n != NULL) n = n->next; - if (n != NULL && n->string != NULL && n->string[0] != '\0') - man->meta.date = mandoc_normdate(man, - n->string, n->line, n->pos); - else { + if (man->quick && n != NULL) man->meta.date = mandoc_strdup(""); - mandoc_msg(MANDOCERR_DATE_MISSING, - n == NULL ? nb->line : n->line, - n == NULL ? nb->pos : n->pos, "TH"); - } + else + man->meta.date = mandoc_normdate(n, nb); /* TITLE MSEC DATE ->OS<- VOL */ diff --git a/manconf.h b/manconf.h index bb3761998c88..c84409d7b570 100644 --- a/manconf.h +++ b/manconf.h @@ -1,6 +1,6 @@ -/* $Id: manconf.h,v 1.7 2018/11/22 11:30:23 schwarze Exp $ */ +/* $OpenBSD: manconf.h,v 1.7 2018/11/22 11:30:15 schwarze Exp $ */ /* - * Copyright (c) 2011, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Public interface to man(1) configuration management. + * For use by the main program and by the formatters. */ /* List of unique, absolute paths to manual trees. */ @@ -28,15 +31,18 @@ struct manpaths { struct manoutput { char *includes; char *man; + char *outfilename; char *paper; char *style; char *tag; + char *tagfilename; size_t indent; size_t width; int fragment; int mdoc; int noval; int synopsisonly; + int tag_found; int toc; }; @@ -1,7 +1,7 @@ -.\" $Id: mandoc.1,v 1.240 2019/07/10 19:39:01 schwarze Exp $ +.\" $OpenBSD: mandoc.1,v 1.166 2020/02/15 15:28:01 schwarze Exp $ .\" +.\" Copyright (c) 2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org> .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 10 2019 $ +.Dd $Mdocdate: August 14 2021 $ .Dt MANDOC 1 .Os .Sh NAME @@ -52,13 +52,13 @@ The options are as follows: If the standard output is a terminal device and .Fl c is not specified, use -.Xr more 1 +.Xr less 1 to paginate the output, just like .Xr man 1 would. .It Fl c Copy the formatted manual pages to the standard output without using -.Xr more 1 +.Xr less 1 to paginate them. This is the default. It can be specified to override @@ -301,8 +301,8 @@ Format input files in .Xr mdoc 7 output style. -Specifically, this suppresses the two additional blank lines near the -top and the bottom of each page, and it implies +This prints the operating system name rather than the page title +on the right side of the footer line, and it implies .Fl O Cm indent Ns =5 . One useful application is for checking that .Fl T Cm man @@ -410,6 +410,30 @@ The file is used for an external style-sheet. This must be a valid absolute or relative URI. +.It Cm tag Ns Op = Ns Ar term +Same syntax and semantics as for +.Sx ASCII Output . +This is implemented by passing a +.Ic file:// +URI ending in a fragment identifier to the pager +rather than passing merely a file name. +When using this argument, use a pager supporting such URIs, for example +.Bd -literal -offset 3n +MANPAGER='lynx -force_html' man -T html -O tag=MANPAGER man +MANPAGER='w3m -T text/html' man -T html -O tag=toc mandoc +.Ed +.Pp +Consequently, for HTML output, this argument does not work with +.Xr more 1 +or +.Xr less 1 . +For example, +.Ql MANPAGER=less man -T html -O tag=toc mandoc +does not work because +.Xr less 1 +does not support +.Ic file:// +URIs. .It Cm toc If an input file contains at least two non-standard sections, print a table of contents near the beginning of the output. @@ -443,13 +467,15 @@ This is useful for distributing manual sources to legacy systems lacking .Xr mdoc 7 formatters. +Embedded +.Xr eqn 7 +and +.Xr tbl 7 +code is not supported. .Pp If the input format of a file is .Xr man 7 , -the input is copied to the output, expanding any -.Xr roff 7 -.Ic so -requests. +the input is copied to the output. The parser is also run, and as usual, the .Fl W level controls which @@ -628,7 +654,7 @@ It never affects the interpretation of input files. Any non-empty value of the environment variable .Ev MANPAGER is used instead of the standard pagination program, -.Xr more 1 ; +.Xr less 1 ; see .Xr man 1 for details. @@ -642,8 +668,7 @@ Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, -.Xr more 1 -.Fl s +.Xr less 1 is used. Only used if .Fl a @@ -897,14 +922,6 @@ generated by CVS or .Ic NetBSD keyword substitution as conventionally used in these operating systems. -.It Sy "referenced manual not found" -.Pq mdoc -An -.Ic \&Xr -macro references a manual page that is not found in the base system. -The path to look for base system manuals is configurable at compile -time and defaults to -.Pa /usr/share/man : /usr/X11R6/man . .El .Ss Style suggestions .Bl -ohang @@ -991,6 +1008,35 @@ list contains two consecutive entries describing the same .Ic \&Er number. +.It Sy "referenced manual not found" +.Pq mdoc +An +.Ic \&Xr +macro references a manual page that was not found. +When running with +.Fl W Cm base , +the search is restricted to the base system, by default to +.Pa /usr/share/man : Ns Pa /usr/X11R6/man . +This path can be configured at compile time using the +.Dv MANPATH_BASE +preprocessor macro. +When running with +.Fl W Cm style , +the search is done along the full search path as described in the +.Xr man 1 +manual page, respecting the +.Fl m +and +.Fl M +command line options, the +.Ev MANPATH +environment variable, the +.Xr man.conf 5 +file and falling back to the default of +.Pa /usr/share/man : Ns Pa /usr/X11R6/man : Ns Pa /usr/local/man , +also configurable at compile time using the +.Dv MANPATH_DEFAULT +preprocessor macro. .It Sy "trailing delimiter" .Pq mdoc The last argument of an @@ -1020,6 +1066,9 @@ An request occurs even though the document already switched to no-fill mode and did not switch back to fill mode yet. It has no effect. +.It Sy "input text line longer than 80 bytes" +Consider breaking the input text line +at one of the blank characters before column 80. .It Sy "verbatim \(dq--\(dq, maybe consider using \e(em" .Pq mdoc Even though the ASCII output device renders an em-dash as @@ -1073,7 +1122,21 @@ macro lacks the mandatory section argument. The section number in a .Ic \&Dt line is invalid, but still used. -.It Sy "missing date, using today's date" +.It Sy "filename/section mismatch" +.Pq mdoc , man +The name of the input file being processed is known and its file +name extension starts with a non-zero digit, but the +.Ic \&Dt +or +.Ic \&TH +macro contains a +.Ar section +argument that starts with a different non-zero digit. +The +.Ar section +argument is used as provided anyway. +Consider checking whether the file name or the argument need a correction. +.It Sy "missing date, using \(dq\(dq" .Pq mdoc, man The document was parsed as .Xr mdoc 7 @@ -1811,6 +1874,10 @@ The invalid character is discarded. A table layout specification contains an opening parenthesis, but no matching closing parenthesis. The rest of the input line, starting from the parenthesis, has no effect. +.It Sy "ignoring excessive spacing in tbl layout" +.Pq tbl +A spacing modifier in a table layout is unreasonably large. +The default spacing of 3n is used instead. .It Sy "tbl without any data cells" .Pq tbl A table does not contain any data cells. @@ -2247,6 +2314,26 @@ or macro or of an undefined macro. The macro is ignored, and its arguments are handled as if they were a text line. +.It Sy "skipping tbl in -Tman mode" +.Pq mdoc , tbl +An input file contains the +.Ic \&TS +macro. +This message is only generated in +.Fl T Cm man +output mode, where +.Xr tbl 7 +input is not supported. +.It Sy "skipping eqn in -Tman mode" +.Pq mdoc , eqn +An input file contains the +.Ic \&EQ +macro. +This message is only generated in +.Fl T Cm man +output mode, where +.Xr eqn 7 +input is not supported. .El .Ss Bad command line arguments .Bl -ohang @@ -2284,6 +2371,14 @@ The .Fl O Cm tag option was specified but the tag was not found in any of the displayed manual pages. +.It Sy "\-Tmarkdown unsupported for man(7) input" +.Pq man +The +.Fl T Cm markdown +option was specified but an input file uses the +.Xr man 7 +language. +No output is produced for that input file. .El .Sh SEE ALSO .Xr apropos 1 , @@ -1,7 +1,7 @@ -/* $Id: mandoc.c,v 1.116 2019/06/27 15:07:30 schwarze Exp $ */ +/* $Id: mandoc.c,v 1.119 2021/08/10 12:55:03 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -74,12 +74,12 @@ mandoc_font(const char *cp, int sz) case 'C': switch (cp[1]) { case 'B': - return ESCAPE_FONTBOLD; + return ESCAPE_FONTCB; case 'I': - return ESCAPE_FONTITALIC; + return ESCAPE_FONTCI; case 'R': case 'W': - return ESCAPE_FONTCW; + return ESCAPE_FONTCR; default: return ESCAPE_ERROR; } @@ -203,7 +203,18 @@ mandoc_escape(const char **end, const char **start, int *sz) case 'O': case 'V': case 'Y': - gly = (*start)[-1] == 'f' ? ESCAPE_FONT : ESCAPE_IGNORE; + case '*': + switch ((*start)[-1]) { + case 'f': + gly = ESCAPE_FONT; + break; + case '*': + gly = ESCAPE_DEVICE; + break; + default: + gly = ESCAPE_IGNORE; + break; + } switch (**start) { case '(': if ((*start)[-1] == 'O') @@ -238,13 +249,6 @@ mandoc_escape(const char **end, const char **start, int *sz) break; } break; - case '*': - if (strncmp(*start, "(.T", 3) != 0) - abort(); - gly = ESCAPE_DEVICE; - *start = ++*end; - *sz = 2; - break; /* * These escapes are of the form \X'Y', where 'X' is the trigger @@ -459,6 +463,9 @@ mandoc_escape(const char **end, const char **start, int *sz) + 1 == *sz) gly = ESCAPE_UNICODE; break; + case ESCAPE_DEVICE: + assert(*sz == 2 && (*start)[0] == '.' && (*start)[1] == 'T'); + break; default: break; } @@ -536,45 +543,59 @@ fail: } char * -mandoc_normdate(struct roff_man *man, char *in, int ln, int pos) +mandoc_normdate(struct roff_node *nch, struct roff_node *nbl) { char *cp; time_t t; - if (man->quick) - return mandoc_strdup(in == NULL ? "" : in); - - /* No date specified: use today's date. */ + /* No date specified. */ - if (in == NULL || *in == '\0') - mandoc_msg(MANDOCERR_DATE_MISSING, ln, pos, NULL); - if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) + if (nch == NULL) { + if (nbl == NULL) + mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL); + else + mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line, + nbl->pos, "%s", roff_name[nbl->tok]); + return mandoc_strdup(""); + } + if (*nch->string == '\0') { + mandoc_msg(MANDOCERR_DATE_MISSING, nch->line, + nch->pos, "%s", roff_name[nbl->tok]); + return mandoc_strdup(""); + } + if (strcmp(nch->string, "$" "Mdocdate$") == 0) return time2a(time(NULL)); /* Valid mdoc(7) date format. */ - if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) || - a2time(&t, "%b %d, %Y", in)) { + if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) || + a2time(&t, "%b %d, %Y", nch->string)) { cp = time2a(t); if (t > time(NULL) + 86400) - mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", cp); - else if (*in != '$' && strcmp(in, cp) != 0) - mandoc_msg(MANDOCERR_DATE_NORM, ln, pos, "%s", cp); + mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, + nch->pos, "%s %s", roff_name[nbl->tok], cp); + else if (*nch->string != '$' && + strcmp(nch->string, cp) != 0) + mandoc_msg(MANDOCERR_DATE_NORM, nch->line, + nch->pos, "%s %s", roff_name[nbl->tok], cp); return cp; } /* In man(7), do not warn about the legacy format. */ - if (a2time(&t, "%Y-%m-%d", in) == 0) - mandoc_msg(MANDOCERR_DATE_BAD, ln, pos, "%s", in); + if (a2time(&t, "%Y-%m-%d", nch->string) == 0) + mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos, + "%s %s", roff_name[nbl->tok], nch->string); else if (t > time(NULL) + 86400) - mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", in); - else if (man->meta.macroset == MACROSET_MDOC) - mandoc_msg(MANDOCERR_DATE_LEGACY, ln, pos, "Dd %s", in); + mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos, + "%s %s", roff_name[nbl->tok], nch->string); + else if (nbl->tok == MDOC_Dd) + mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos, + "Dd %s", nch->string); /* Use any non-mdoc(7) date verbatim. */ - return mandoc_strdup(in); + return mandoc_strdup(nch->string); } int diff --git a/mandoc.css b/mandoc.css index d75700a28ba7..ceac503a79f6 100644 --- a/mandoc.css +++ b/mandoc.css @@ -1,4 +1,4 @@ -/* $Id: mandoc.css,v 1.46 2019/06/02 16:57:13 schwarze Exp $ */ +/* $Id: mandoc.css,v 1.48 2021/03/30 19:26:20 schwarze Exp $ */ /* * Standard style sheet for mandoc(1) -Thtml and man.cgi(8). * @@ -31,6 +31,7 @@ td { vertical-align: top; ul, ol, dl { margin-top: 0em; margin-bottom: 0em; } li, dt { margin-top: 1em; } +pre { font-family: inherit; } .permalink { border-bottom: thin dotted; color: inherit; @@ -135,12 +136,12 @@ h2.Ss { margin-top: 1.2em; vertical-align: top; } .Bl-tag > dd { clear: right; + column-count: 1; /* Force block formatting context. */ width: 100%; margin-top: 0em; margin-left: 0em; margin-bottom: 0.6em; - vertical-align: top; - overflow: auto; } + vertical-align: top; } .Bl-compact { margin-top: 0em; } .Bl-compact > dd { margin-bottom: 0em; } @@ -1,7 +1,7 @@ -/* $Id: mandoc.h,v 1.264 2019/07/14 18:16:13 schwarze Exp $ */ +/* $Id: mandoc.h,v 1.274 2021/08/14 13:53:08 schwarze Exp $ */ /* + * Copyright (c) 2012-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,6 +16,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Error handling, escape sequence, and character utilities. + * Can be used by all code in the mandoc package. */ #define ASCII_NBRSP 31 /* non-breaking space */ @@ -53,7 +54,6 @@ enum mandocerr { MANDOCERR_ARCH_BAD, /* unknown architecture: Dt ... arch */ MANDOCERR_OS_ARG, /* operating system explicitly specified: Os ... */ MANDOCERR_RCS_MISSING, /* RCS id missing */ - MANDOCERR_XR_BAD, /* referenced manual not found: Xr name sec */ MANDOCERR_STYLE, /* ===== start of style suggestions ===== */ @@ -67,10 +67,12 @@ enum mandocerr { MANDOCERR_BX, /* consider using OS macro: macro */ MANDOCERR_ER_ORDER, /* errnos out of order: Er ... */ MANDOCERR_ER_REP, /* duplicate errno: Er ... */ + MANDOCERR_XR_BAD, /* referenced manual not found: Xr name sec */ MANDOCERR_DELIM, /* trailing delimiter: macro ... */ MANDOCERR_DELIM_NB, /* no blank before trailing delimiter: macro ... */ MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */ MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */ + MANDOCERR_TEXT_LONG, /* input text line longer than 80 bytes */ MANDOCERR_DASHDASH, /* verbatim "--", maybe consider using \(em */ MANDOCERR_FUNC, /* function name without markup: name() */ MANDOCERR_SPACE_EOL, /* whitespace at end of input line */ @@ -83,7 +85,8 @@ enum mandocerr { MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */ MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */ MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */ - MANDOCERR_DATE_MISSING, /* missing date, using today's date */ + MANDOCERR_MSEC_FILE, /* filename/section mismatch: ... */ + MANDOCERR_DATE_MISSING, /* missing date, using "": [macro] */ MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */ MANDOCERR_DATE_FUTURE, /* date in the future, using it anyway: date */ MANDOCERR_OS_MISSING, /* missing Os macro, using "" */ @@ -187,6 +190,7 @@ enum mandocerr { MANDOCERR_TBLLAYOUT_NONE, /* empty tbl layout */ MANDOCERR_TBLLAYOUT_CHAR, /* invalid character in tbl layout: char */ MANDOCERR_TBLLAYOUT_PAR, /* unmatched parenthesis in tbl layout */ + MANDOCERR_TBLLAYOUT_SPC, /* ignoring excessive spacing in tbl layout */ MANDOCERR_TBLDATA_NONE, /* tbl without any data cells */ MANDOCERR_TBLDATA_SPAN, /* ignoring data in spanned tbl cell: data */ MANDOCERR_TBLDATA_EXTRA, /* ignoring extra tbl data cells: data */ @@ -223,6 +227,7 @@ enum mandocerr { MANDOCERR_SHIFT, /* excessive shift: ..., but max is ... */ MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */ MANDOCERR_SO_FAIL, /* .so request failed */ + MANDOCERR_TG_SPC, /* skipping tag containing whitespace: tag */ MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */ MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */ MANDOCERR_DIVZERO, /* divide by zero */ @@ -240,6 +245,8 @@ enum mandocerr { MANDOCERR_TBLOPT_EQN, /* eqn delim option in tbl: arg */ MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */ MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */ + MANDOCERR_TBL_TMAN, /* skipping tbl in -Tman mode */ + MANDOCERR_EQN_TMAN, /* skipping eqn in -Tman mode */ MANDOCERR_BADARG, /* ===== start of bad invocations ===== */ @@ -250,6 +257,7 @@ enum mandocerr { MANDOCERR_BADVAL_BAD, /* bad argument value */ MANDOCERR_BADVAL_DUPE, /* duplicate argument value */ MANDOCERR_TAG, /* no such tag */ + MANDOCERR_MAN_TMARKDOWN, /* -Tmarkdown unsupported for man(7) input */ MANDOCERR_SYSERR, /* ===== start of system errors ===== */ @@ -284,7 +292,9 @@ enum mandoc_esc { ESCAPE_FONTITALIC, /* italic font mode */ ESCAPE_FONTBI, /* bold italic font mode */ ESCAPE_FONTROMAN, /* roman font mode */ - ESCAPE_FONTCW, /* constant width font mode */ + ESCAPE_FONTCR, /* constant width font mode */ + ESCAPE_FONTCB, /* constant width bold font mode */ + ESCAPE_FONTCI, /* constant width italic font mode */ ESCAPE_FONTPREV, /* previous font mode */ ESCAPE_NUMBERED, /* a numbered glyph */ ESCAPE_UNICODE, /* a unicode codepoint */ @@ -298,7 +308,7 @@ enum mandoc_esc { }; -enum mandoc_esc mandoc_font(const char *, int sz); +enum mandoc_esc mandoc_font(const char *, int); enum mandoc_esc mandoc_escape(const char **, const char **, int *); void mandoc_msg_setoutfile(FILE *); const char *mandoc_msg_getinfilename(void); diff --git a/mandoc_char.7 b/mandoc_char.7 index b46575d4d250..eb9e65acfc86 100644 --- a/mandoc_char.7 +++ b/mandoc_char.7 @@ -1,8 +1,8 @@ -.\" $Id: mandoc_char.7,v 1.76 2019/03/31 19:17:26 schwarze Exp $ +.\" $Id: mandoc_char.7,v 1.78 2020/10/31 11:45:16 schwarze Exp $ .\" .\" Copyright (c) 2003 Jason McIntyre <jmc@openbsd.org> .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2011,2013,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2011,2013,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 31 2019 $ +.Dd $Mdocdate: October 31 2020 $ .Dt MANDOC_CHAR 7 .Os .Sh NAME @@ -261,15 +261,15 @@ subsection of the .Xr roff 7 manual. .Pp -Spacing: +Spaces, non-breaking unless stated otherwise: .Bl -column "Input" "Description" -offset indent -compact .It Em Input Ta Em Description -.It Sq \e\ \& Ta unpaddable non-breaking space -.It \e\(ti Ta paddable non-breaking space -.It \e0 Ta digit-width space allowing line break +.It Sq \e\ \& Ta unpaddable space +.It \e\(ti Ta paddable space +.It \e0 Ta digit-width space .It \e| Ta one-sixth \e(em narrow space, zero width in nroff mode .It \e^ Ta one-twelfth \e(em half-narrow space, zero width in nroff -.It \e& Ta zero-width non-breaking space +.It \e& Ta zero-width space .It \e) Ta zero-width space transparent to end-of-sentence detection .It \e% Ta zero-width space allowing hyphenation .It \e: Ta zero-width space allowing line break @@ -709,11 +709,6 @@ Their syntax is similar to special characters, using and .Sq \e*[N] .Pq N-character . -For details, see the -.Em Predefined Strings -subsection of the -.Xr roff 7 -manual. .Bl -column "Input" "Rendered" "Description" -offset indent .It Em Input Ta Em Rendered Ta Em Description .It \e*(Ba Ta \*(Ba Ta vertical bar diff --git a/mandoc_headers.3 b/mandoc_headers.3 index 2cda75cba8b3..7fe6d379f864 100644 --- a/mandoc_headers.3 +++ b/mandoc_headers.3 @@ -1,6 +1,6 @@ -.\" $Id: mandoc_headers.3,v 1.31 2019/03/17 18:21:45 schwarze Exp $ +.\" $Id: mandoc_headers.3,v 1.34 2021/08/10 12:55:03 schwarze Exp $ .\" -.\" Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -14,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: March 17 2019 $ +.Dd $Mdocdate: August 10 2021 $ .Dt MANDOC_HEADERS 3 .Os .Sh NAME @@ -167,7 +167,11 @@ parse tree; can be used everywhere. Requires .In sys/types.h for -.Vt size_t . +.Vt size_t +and +.Qq Pa mandoc.h +for +.Vt enum mandoc_esc . .Pp Provides .Vt enum tbl_cellt , @@ -232,6 +236,30 @@ and the functions .Fn mandoc_xr_get , and .Fn mandoc_xr_free . +.It Qq Pa tag.h +Internal interfaces to tag syntax tree nodes, +for use by validation modules only. +.Pp +Requires +.In limits.h +for +.Dv INT_MAX . +.Pp +Provides the functions +.Fn tag_alloc , +.Fn tag_put , +.Fn tag_check , +and +.Fn tag_free +and some +.Dv TAG_* +constants. +.Pp +Uses the type +.Vt struct roff_node +from +.Qq Pa roff.h +as an opaque type for function prototypes. .El .Pp The following two require @@ -587,6 +615,33 @@ When this header is included, the same file should not include .Qq Pa html.h or .Qq Pa mansearch.h . +.It Qq Pa tag_term.h +Requires +.In sys/types.h +for +.Vt size_t +and +.In stdio.h +for +.Vt FILE . +.Pp +Provides an interface to generate +.Xr ctags 1 +files for the +.Ic :t +functionality mentioned in +.Xr man 1 . +.Pp +Uses the type +.Vt struct roff_node +from +.Qq Pa roff.h +as an opaque type for function prototypes. +.Pp +When this header is included, the same file should not include +.Qq Pa html.h +or +.Qq Pa mansearch.h . .It Qq Pa html.h Requires .In sys/types.h @@ -629,21 +684,10 @@ from as opaque types for function prototypes. .Pp When this header is included, the same file should not include -.Qq Pa term.h +.Qq Pa term.h , +.Qq Pa tab_term.h , or .Qq Pa mansearch.h . -.It Qq Pa tag.h -Requires -.In sys/types.h -for -.Vt size_t . -.Pp -Provides an interface to generate -.Xr ctags 1 -files for the -.Ic :t -functionality mentioned in -.Xr man 1 . .It Qq Pa main.h Provides the top level steering functions for all formatters. .Pp @@ -696,6 +740,7 @@ as an opaque type for function prototypes. When this header is included, the same file should not include .Qq Pa out.h , .Qq Pa term.h , +.Qq Pa tab_term.h , or .Qq Pa html.h . .El diff --git a/mandoc_html.3 b/mandoc_html.3 index 32407574a814..a77d0e04fce4 100644 --- a/mandoc_html.3 +++ b/mandoc_html.3 @@ -1,4 +1,4 @@ -.\" $Id: mandoc_html.3,v 1.19 2019/01/11 12:56:43 schwarze Exp $ +.\" $Id: mandoc_html.3,v 1.23 2020/04/24 13:13:06 schwarze Exp $ .\" .\" Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> .\" @@ -14,14 +14,18 @@ .\" 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 11 2019 $ +.Dd $Mdocdate: April 24 2020 $ .Dt MANDOC_HTML 3 .Os .Sh NAME .Nm mandoc_html .Nd internals of the mandoc HTML formatter .Sh SYNOPSIS -.In "html.h" +.In sys/types.h +.Fd #include """mandoc.h""" +.Fd #include """roff.h""" +.Fd #include """out.h""" +.Fd #include """html.h""" .Ft void .Fn print_gen_decls "struct html *h" .Ft void @@ -46,18 +50,42 @@ .Fa "const struct tag *suntil" .Fc .Ft void +.Fn html_close_paragraph "struct html *h" +.Ft enum roff_tok +.Fo html_fillmode +.Fa "struct html *h" +.Fa "enum roff_tok tok" +.Fc +.Ft int +.Fo html_setfont +.Fa "struct html *h" +.Fa "enum mandoc_esc font" +.Fc +.Ft void .Fo print_text .Fa "struct html *h" .Fa "const char *word" .Fc +.Ft void +.Fo print_tagged_text +.Fa "struct html *h" +.Fa "const char *word" +.Fa "struct roff_node *n" +.Fc .Ft char * .Fo html_make_id .Fa "const struct roff_node *n" +.Fa "int unique" .Fc -.Ft int -.Fo html_strlen -.Fa "const char *cp" +.Ft struct tag * +.Fo print_otag_id +.Fa "struct html *h" +.Fa "enum htmltag tag" +.Fa "const char *cattr" +.Fa "struct roff_node *n" .Fc +.Ft void +.Fn print_endline "struct html *h" .Sh DESCRIPTION The mandoc HTML formatter is not a formal library. However, as it is compiled into more than one program, in particular @@ -96,7 +124,7 @@ These structures are declared in Internal state of the HTML formatter. .It Vt struct tag One entry for the LIFO stack of HTML elements. -Members are +Members include .Fa "enum htmltag tag" and .Fa "struct tag *next" . @@ -105,10 +133,8 @@ and The function .Fn print_gen_decls prints the opening -.Ao Pf \&? Ic xml ? Ac -and .Aq Pf \&! Ic DOCTYPE -declarations required for the current document type. +declaration. .Pp The function .Fn print_gen_comment @@ -235,6 +261,59 @@ is used to close out all open elements up to and including .Fn print_stagq is a variant to close out all open elements up to but excluding .Fa suntil . +The function +.Fn html_close_paragraph +closes all open elements that establish phrasing context, +thus returning to the innermost flow context. +.Pp +The function +.Fn html_fillmode +switches to fill mode if +.Fa want +is +.Dv ROFF_fi +or to no-fill mode if +.Fa want +is +.Dv ROFF_nf . +Switching from fill mode to no-fill mode closes the current paragraph +and opens a +.Aq Ic PRE +element. +Switching in the opposite direction closes the +.Aq Ic PRE +element, but does not open a new paragraph. +If +.Fa want +matches the mode that is already active, no elements are closed nor opened. +If +.Fa want +is +.Dv TOKEN_NONE , +the mode remains as it is. +.Pp +The function +.Fn html_setfont +selects the +.Fa font , +which can be +.Dv ESCAPE_FONTROMAN , +.Dv ESCAPE_FONTBOLD , +.Dv ESCAPE_FONTITALIC , +.Dv ESCAPE_FONTBI , +or +.Dv ESCAPE_FONTCW , +for future text output and internally remembers +the font that was active before the change. +If the +.Fa font +argument is +.Dv ESCAPE_FONTPREV , +the current and the previous font are exchanged. +This function only changes the internal state of the +.Fa h +object; no HTML elements are written yet. +Subsequent text output will write font elements when needed. .Pp The function .Fn print_text @@ -256,24 +335,118 @@ and 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 +.Fn print_tagged_text +is a variant of +.Fn print_text +that wraps +.Fa word +in an +.Aq Ic A +element of class +.Qq permalink +if .Fa n -contains any non-text child node, +is not +.Dv NULL +and yields a segment identifier when passed to +.Fn html_make_id . +.Pp +The function .Fn html_make_id -returns +allocates a string to be used for the +.Cm id +attribute of an HTML element and/or as a segment identifier for a URI in an +.Aq Ic A +element. +If +.Fa n +contains a +.Fa tag +attribute, it is used; otherwise, child nodes are used. +If +.Fa n +is an +.Ic \&Sh , +.Ic \&Ss , +.Ic \&Sx , +.Ic SH , +or +.Ic SS +node, the resulting string is the concatenation of the child strings; +for other node types, only the first child is used. +Bytes not permitted in URI-fragment strings are replaced by underscores. +If any of the children to be used is not a text node, +no string is generated and .Dv NULL -instead. -The caller is responsible for freeing the returned string. +is returned instead. +If the +.Fa unique +argument is non-zero, deduplication is performed by appending an +underscore and a decimal integer, if necessary. +If the +.Fa unique +argument is 1, this is assumed to be the first call for this tag +at this location, typically for use by +.Dv NODE_ID , +so the integer is incremented before use. +If the +.Fa unique +argument is 2, this is ssumed to be the second call for this tag +at this location, typically for use by +.Dv NODE_HREF , +so the existing integer, if any, is used without incrementing it. .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. +.Fn print_otag_id +opens a +.Fa tag +element of class +.Fa cattr +for the node +.Fa n . +If the flag +.Dv NODE_ID +is set in +.Fa n , +it attempts to generate an +.Cm id +attribute with +.Fn html_make_id . +If the flag +.Dv NODE_HREF +is set in +.Fa n , +an +.Aq Ic A +element of class +.Qq permalink +is added: +outside if +.Fa n +generates an element that can only occur in phrasing context, +or inside otherwise. +This function is a wrapper around +.Fn html_make_id +and +.Fn print_otag , +automatically chosing the +.Fa unique +argument appropriately and setting the +.Fa fmt +arguments to +.Qq chR +and +.Qq ci , +respectively. +.Pp +The function +.Fn print_endline +makes sure subsequent output starts on a new HTML output line. +If nothing was printed on the current output line yet, it has no effect. +Otherwise, it appends any buffered text to the current output line, +ends the line, and updates the internal state of the +.Fa h +object. .Pp The functions .Fn print_eqn , @@ -281,6 +454,46 @@ The functions and .Fn print_tblclose are not yet documented. +.Sh RETURN VALUES +The functions +.Fn print_otag +and +.Fn print_otag_id +return a pointer to a new element on the stack of HTML elements. +When +.Fn print_otag_id +opens two elements, a pointer to the outer one is returned. +The memory pointed to is owned by the library and is automatically +.Xr free 3 Ns d +when +.Fn print_tagq +is called on it or when +.Fn print_stagq +is called on a parent element. +.Pp +The function +.Fn html_fillmode +returns +.Dv ROFF_fi +if fill mode was active before the call or +.Dv ROFF_nf +otherwise. +.Pp +The function +.Fn html_make_id +returns a newly allocated string or +.Dv NULL +if +.Fa n +lacks text data to create the attribute from. +The caller is responsible for +.Xr free 3 Ns ing +the returned string after using it. +.Pp +In case of +.Xr malloc 3 +failure, these functions do not return but call +.Xr err 3 . .Sh FILES .Bl -tag -width mandoc_aux.c -compact .It Pa main.h @@ -303,6 +516,17 @@ HTML formatter .It Pa eqn_html.c .Xr eqn 7 HTML formatter +.It Pa roff_html.c +.Xr roff 7 +HTML formatter, handling requests like +.Ic br , +.Ic ce , +.Ic fi , +.Ic ft , +.Ic nf , +.Ic rj , +and +.Ic sp . .It Pa out.h declarations of data types and private functions for shared use by all mandoc formatters, diff --git a/mandoc_malloc.3 b/mandoc_malloc.3 index ae3ca16aa4a8..d73438575065 100644 --- a/mandoc_malloc.3 +++ b/mandoc_malloc.3 @@ -1,4 +1,4 @@ -.\" $Id: mandoc_malloc.3,v 1.2 2016/07/07 19:19:01 schwarze Exp $ +.\" $Id: mandoc_malloc.3,v 1.3 2021/09/17 18:50:21 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: July 7 2016 $ +.Dd $Mdocdate: September 17 2021 $ .Dt MANDOC_MALLOC 3 .Os .Sh NAME @@ -22,6 +22,7 @@ .Nm mandoc_realloc , .Nm mandoc_reallocarray , .Nm mandoc_calloc , +.Nm mandoc_recallocarray , .Nm mandoc_strdup , .Nm mandoc_strndup , .Nm mandoc_asprintf @@ -49,6 +50,13 @@ .Fa "size_t nmemb" .Fa "size_t size" .Fc +.Ft "void *" +.Fo mandoc_recallocarray +.Fa "void *ptr" +.Fa "size_t oldnmemb" +.Fa "size_t nmemb" +.Fa "size_t size" +.Fc .Ft "char *" .Fo mandoc_strdup .Fa "const char *s" @@ -82,12 +90,15 @@ The function .Fn mandoc_malloc allocates one new object, leaving the memory uninitialized. The functions -.Fn mandoc_realloc +.Fn mandoc_realloc , +.Fn mandoc_reallocarray , and -.Fn mandoc_reallocarray +.Fn mandoc_recallocarray change the size of an existing object or array, possibly moving it. When shrinking the size, existing data is truncated; when growing, -the additional memory is not initialized. +only +.Fn mandoc_recallocarray +initializes the new elements to zero. The function .Fn mandoc_calloc allocates a new array, initializing it to zero. @@ -99,6 +110,9 @@ The argument .Fa nmemb is the new number of objects in the array. The argument +.Fa oldnmemb +is the number of objects in the array before the call. +The argument .Fa ptr is a pointer to the existing object or array to be resized; if it is .Dv NULL , @@ -168,9 +182,13 @@ is a widespread extension that first appeared in the GNU C library. The function .Fn reallocarray is an extension that first appeared in -.Ox 5.6 . -If it is not provided by the operating system, the mandoc build system -uses a bundled portable implementation. +.Ox 5.6 , +and +.Fn recallocarray +in +.Ox 6.1 . +If these two are not provided by the operating system, +the mandoc build system uses bundled portable implementations. .Sh HISTORY The functions .Fn mandoc_malloc , @@ -181,11 +199,12 @@ and have been available since mandoc 1.9.12, .Fn mandoc_strndup since 1.11.5, -and .Fn mandoc_asprintf -and +since 1.12.4, .Fn mandoc_reallocarray -since 1.12.4 and 1.13.0. +since 1.13.0, and +.Fn mandoc_recallocarray +since 1.14.2. .Sh AUTHORS .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .An Ingo Schwarze Aq Mt schwarze@openbsd.org diff --git a/mandoc_msg.c b/mandoc_msg.c index e73dcfd830ff..beec5059a28c 100644 --- a/mandoc_msg.c +++ b/mandoc_msg.c @@ -1,7 +1,7 @@ -/* $Id: mandoc_msg.c,v 1.8 2019/07/14 18:16:13 schwarze Exp $ */ +/* $OpenBSD: mandoc_msg.c,v 1.8 2020/01/19 17:59:01 schwarze Exp $ */ /* + * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Implementation of warning and error messages for mandoc(1). */ #include "config.h" @@ -53,7 +55,6 @@ static const char *const type_message[MANDOCERR_MAX] = { "unknown architecture", "operating system explicitly specified", "RCS id missing", - "referenced manual not found", "generic style suggestion", @@ -67,10 +68,12 @@ static const char *const type_message[MANDOCERR_MAX] = { "consider using OS macro", "errnos out of order", "duplicate errno", + "referenced manual not found", "trailing delimiter", "no blank before trailing delimiter", "fill mode already enabled, skipping", "fill mode already disabled, skipping", + "input text line longer than 80 bytes", "verbatim \"--\", maybe consider using \\(em", "function name without markup", "whitespace at end of input line", @@ -83,7 +86,8 @@ static const char *const type_message[MANDOCERR_MAX] = { "missing manual title, using \"\"", "missing manual section, using \"\"", "unknown manual section", - "missing date, using today's date", + "filename/section mismatch", + "missing date, using \"\"", "cannot parse date, using it verbatim", "date in the future, using it anyway", "missing Os macro, using \"\"", @@ -187,6 +191,7 @@ static const char *const type_message[MANDOCERR_MAX] = { "empty tbl layout", "invalid character in tbl layout", "unmatched parenthesis in tbl layout", + "ignoring excessive spacing in tbl layout", "tbl without any data cells", "ignoring data in spanned tbl cell", "ignoring extra tbl data cells", @@ -223,6 +228,7 @@ static const char *const type_message[MANDOCERR_MAX] = { "excessive shift", "NOT IMPLEMENTED: .so with absolute path or \"..\"", ".so request failed", + "skipping tag containing whitespace", "skipping all arguments", "skipping excess arguments", "divide by zero", @@ -239,6 +245,8 @@ static const char *const type_message[MANDOCERR_MAX] = { "eqn delim option in tbl", "unsupported tbl layout modifier", "ignoring macro in table", + "skipping tbl in -Tman mode", + "skipping eqn in -Tman mode", /* bad command line arguments */ NULL, @@ -249,6 +257,7 @@ static const char *const type_message[MANDOCERR_MAX] = { "bad option value", "duplicate option value", "no such tag", + "-Tmarkdown unsupported for man(7) input", /* system errors */ NULL, diff --git a/mandoc_ohash.c b/mandoc_ohash.c index 0627b469c60b..c4ebfcd428ed 100644 --- a/mandoc_ohash.c +++ b/mandoc_ohash.c @@ -1,4 +1,4 @@ -/* $Id: mandoc_ohash.c,v 1.2 2015/10/19 18:58:47 schwarze Exp $ */ +/* $Id: mandoc_ohash.c,v 1.3 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2014, 2015 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <stddef.h> #include <stdint.h> diff --git a/mandoc_parse.h b/mandoc_parse.h index 61341f0d7f39..b8b29dd931c9 100644 --- a/mandoc_parse.h +++ b/mandoc_parse.h @@ -1,4 +1,4 @@ -/* $Id: mandoc_parse.h,v 1.4 2018/12/30 00:49:55 schwarze Exp $ */ +/* $Id: mandoc_parse.h,v 1.5 2019/11/09 14:39:49 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org> @@ -29,6 +29,7 @@ #define MPARSE_UTF8 (1 << 4) /* accept UTF-8 input */ #define MPARSE_LATIN1 (1 << 5) /* accept ISO-LATIN-1 input */ #define MPARSE_VALIDATE (1 << 6) /* call validation functions */ +#define MPARSE_COMMENT (1 << 7) /* save comments in the tree */ struct roff_meta; diff --git a/mandoc_xr.c b/mandoc_xr.c index da0a7f0cf23b..6d1b1a8d8ad2 100644 --- a/mandoc_xr.c +++ b/mandoc_xr.c @@ -1,4 +1,4 @@ -/* $Id: mandoc_xr.c,v 1.3 2017/07/02 21:18:29 schwarze Exp $ */ +/* $Id: mandoc_xr.c,v 1.4 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <assert.h> diff --git a/mandocd.c b/mandocd.c index 1a597b3867bc..60e40fed8023 100644 --- a/mandocd.c +++ b/mandocd.c @@ -1,4 +1,4 @@ -/* $Id: mandocd.c,v 1.11 2019/03/03 13:02:11 schwarze Exp $ */ +/* $Id: mandocd.c,v 1.12 2020/06/14 23:40:31 schwarze Exp $ */ /* * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org> * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org> @@ -17,7 +17,7 @@ */ #include "config.h" -#if HAVE_CMSG_XPG42 +#if NEED_XPG4_2 #define _XPG4_2 #endif diff --git a/mandocdb.c b/mandocdb.c index 64ac10115877..a3360fe44d01 100644 --- a/mandocdb.c +++ b/mandocdb.c @@ -1,7 +1,7 @@ -/* $Id: mandocdb.c,v 1.263 2019/05/03 18:17:12 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.269 2021/08/19 16:55:31 schwarze Exp $ */ /* + * Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2016 Ed Maste <emaste@freebsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Implementation of the makewhatis(8) program. */ #include "config.h" @@ -118,7 +120,7 @@ struct mdoc_handler { int mandocdb(int, char *[]); static void dbadd(struct dba *, struct mpage *); -static void dbadd_mlink(const struct mlink *mlink); +static void dbadd_mlink(const struct mlink *); static void dbprune(struct dba *); static void dbwrite(struct dba *); static void filescan(const char *); @@ -163,6 +165,9 @@ static void putkey(const struct mpage *, char *, uint64_t); static void putkeys(const struct mpage *, char *, size_t, uint64_t); static void putmdockey(const struct mpage *, const struct roff_node *, uint64_t, int); +#ifdef READ_ALLOWED_PATH +static int read_allowed(const char *); +#endif static int render_string(char **, size_t *); static void say(const char *, const char *, ...) __attribute__((__format__ (__printf__, 2, 3))); @@ -179,6 +184,7 @@ static int write_utf8; /* write UTF-8 output; else ASCII */ static int exitcode; /* to be returned by main */ static enum op op; /* operational mode */ static char basedir[PATH_MAX]; /* current base directory */ +static size_t basedir_len; /* strlen(basedir) */ static struct mpage *mpage_head; /* list of distinct manual pages */ static struct ohash mpages; /* table of distinct manual pages */ static struct ohash mlinks; /* table of directory entries */ @@ -342,7 +348,7 @@ mandocdb(int argc, char *argv[]) * clobber each other. */ #define CHECKOP(_op, _ch) do \ - if (OP_DEFAULT != (_op)) { \ + if ((_op) != OP_DEFAULT) { \ warnx("-%c: Conflicting option", (_ch)); \ goto usage; \ } while (/*CONSTCOND*/0) @@ -351,7 +357,7 @@ mandocdb(int argc, char *argv[]) path_arg = NULL; op = OP_DEFAULT; - while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v"))) + while ((ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")) != -1) switch (ch) { case 'a': use_all = 1; @@ -379,7 +385,7 @@ mandocdb(int argc, char *argv[]) mparse_options |= MPARSE_QUICK; break; case 'T': - if (strcmp(optarg, "utf8")) { + if (strcmp(optarg, "utf8") != 0) { warnx("-T%s: Unsupported output format", optarg); goto usage; @@ -416,7 +422,7 @@ mandocdb(int argc, char *argv[]) } #endif - if (OP_CONFFILE == op && argc > 0) { + if (op == OP_CONFFILE && argc > 0) { warnx("-C: Too many arguments"); goto usage; } @@ -427,13 +433,13 @@ mandocdb(int argc, char *argv[]) mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); - if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { + if (op == OP_UPDATE || op == OP_DELETE || op == OP_TEST) { /* * Most of these deal with a specific directory. * Jump into that directory first. */ - if (OP_TEST != op && 0 == set_basedir(path_arg, 1)) + if (op != OP_TEST && set_basedir(path_arg, 1) == 0) goto out; dba = nodb ? dba_new(128) : dba_read(MANDOC_DB); @@ -454,11 +460,11 @@ mandocdb(int argc, char *argv[]) " from scratch", strerror(errno)); exitcode = (int)MANDOCLEVEL_OK; op = OP_DEFAULT; - if (0 == treescan()) + if (treescan() == 0) goto out; dba = dba_new(128); } - if (OP_DELETE != op) + if (op != OP_DELETE) mpages_merge(dba, mp); if (nodb == 0) dbwrite(dba); @@ -492,7 +498,7 @@ mandocdb(int argc, char *argv[]) sz = strlen(conf.manpath.paths[j]); if (sz && conf.manpath.paths[j][sz - 1] == '/') conf.manpath.paths[j][--sz] = '\0'; - if (0 == sz) + if (sz == 0) continue; if (j) { @@ -502,9 +508,9 @@ mandocdb(int argc, char *argv[]) offsetof(struct mlink, file)); } - if ( ! set_basedir(conf.manpath.paths[j], argc > 0)) + if (set_basedir(conf.manpath.paths[j], argc > 0) == 0) continue; - if (0 == treescan()) + if (treescan() == 0) continue; dba = dba_new(128); mpages_merge(dba, mp); @@ -608,9 +614,9 @@ treescan(void) say(path, "&realpath"); continue; } - if (strstr(buf, basedir) != buf -#ifdef HOMEBREWDIR - && strstr(buf, HOMEBREWDIR) != buf + if (strncmp(buf, basedir, basedir_len) != 0 +#ifdef READ_ALLOWED_PATH + && !read_allowed(buf) #endif ) { if (warnings) say("", @@ -623,6 +629,8 @@ treescan(void) say(path, "&stat"); continue; } + if ((ff->fts_statp->st_mode & S_IFMT) != S_IFREG) + continue; /* FALLTHROUGH */ /* @@ -777,17 +785,17 @@ treescan(void) * See treescan() for the fts(3) version of this. */ static void -filescan(const char *file) +filescan(const char *infile) { - char buf[PATH_MAX]; struct stat st; struct mlink *mlink; - char *p, *start; + char *linkfile, *p, *realdir, *start, *usefile; + size_t realdir_len; assert(use_all); - if (0 == strncmp(file, "./", 2)) - file += 2; + if (strncmp(infile, "./", 2) == 0) + infile += 2; /* * We have to do lstat(2) before realpath(3) loses @@ -796,13 +804,13 @@ filescan(const char *file) * we want to use the orginal file name, while for * regular files, we want to use the real path. */ - if (-1 == lstat(file, &st)) { + if (lstat(infile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&lstat"); + say(infile, "&lstat"); return; - } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { + } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "Not a regular file"); + say(infile, "Not a regular file"); return; } @@ -810,23 +818,24 @@ filescan(const char *file) * We have to resolve the file name to the real path * in any case for the base directory check. */ - if (NULL == realpath(file, buf)) { + if ((usefile = realpath(infile, NULL)) == NULL) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&realpath"); + say(infile, "&realpath"); return; } - if (OP_TEST == op) - start = buf; - else if (strstr(buf, basedir) == buf) - start = buf + strlen(basedir); -#ifdef HOMEBREWDIR - else if (strstr(buf, HOMEBREWDIR) == buf) - start = buf; + if (op == OP_TEST) + start = usefile; + else if (strncmp(usefile, basedir, basedir_len) == 0) + start = usefile + basedir_len; +#ifdef READ_ALLOWED_PATH + else if (read_allowed(usefile)) + start = usefile; #endif else { exitcode = (int)MANDOCLEVEL_BADARG; - say("", "%s: outside base directory", buf); + say("", "%s: outside base directory", infile); + free(usefile); return; } @@ -834,25 +843,72 @@ filescan(const char *file) * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * and use the original name. - * This implies passing stuff like "cat1/../man1/foo.1" - * on the command line won't work. So don't do that. - * Note the stat(2) can still fail if the link target - * doesn't exist. */ - if (S_IFLNK & st.st_mode) { - if (-1 == stat(buf, &st)) { + do { + if (S_ISLNK(st.st_mode) == 0) + break; + + /* + * Some implementations of realpath(3) may succeed + * even if the target of the link does not exist, + * so check again for extra safety. + */ + if (stat(usefile, &st) == -1) { exitcode = (int)MANDOCLEVEL_BADARG; - say(file, "&stat"); + say(infile, "&stat"); + free(usefile); return; } - if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { - say(file, "Filename too long"); - return; + linkfile = mandoc_strdup(infile); + if (op == OP_TEST) { + free(usefile); + start = usefile = linkfile; + break; } - start = buf; - if (OP_TEST != op && strstr(buf, basedir) == buf) - start += strlen(basedir); - } + if (strncmp(infile, basedir, basedir_len) == 0) { + free(usefile); + usefile = linkfile; + start = usefile + basedir_len; + break; + } + + /* + * This symbolic link points into the basedir + * from the outside. Let's see whether any of + * the parent directories resolve to the basedir. + */ + p = strchr(linkfile, '\0'); + do { + while (*--p != '/') + continue; + *p = '\0'; + if ((realdir = realpath(linkfile, NULL)) == NULL) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(infile, "&realpath"); + free(linkfile); + free(usefile); + return; + } + realdir_len = strlen(realdir) + 1; + free(realdir); + *p = '/'; + } while (realdir_len > basedir_len); + + /* + * If one of the directories resolves to the basedir, + * use the rest of the original name. + * Otherwise, the best we can do + * is to use the filename pointed to. + */ + if (realdir_len == basedir_len) { + free(usefile); + usefile = linkfile; + start = p + 1; + } else { + free(linkfile); + start = usefile + basedir_len; + } + } while (/* CONSTCOND */ 0); mlink = mandoc_calloc(1, sizeof(struct mlink)); mlink->dform = FORM_NONE; @@ -860,6 +916,7 @@ filescan(const char *file) sizeof(mlink->file)) { say(start, "Filename too long"); free(mlink); + free(usefile); return; } @@ -868,13 +925,13 @@ filescan(const char *file) * but outside our tree, guess the base directory. */ - if (op == OP_TEST || (start == buf && *start == '/')) { - if (strncmp(buf, "man/", 4) == 0) - start = buf + 4; - else if ((start = strstr(buf, "/man/")) != NULL) + if (op == OP_TEST || (start == usefile && *start == '/')) { + if (strncmp(usefile, "man/", 4) == 0) + start = usefile + 4; + else if ((start = strstr(usefile, "/man/")) != NULL) start += 5; else - start = buf; + start = usefile; } /* @@ -883,18 +940,18 @@ filescan(const char *file) * If we find one of these and what's underneath is a directory, * assume it's an architecture. */ - if (NULL != (p = strchr(start, '/'))) { + if ((p = strchr(start, '/')) != NULL) { *p++ = '\0'; - if (0 == strncmp(start, "man", 3)) { + if (strncmp(start, "man", 3) == 0) { mlink->dform = FORM_SRC; mlink->dsec = start + 3; - } else if (0 == strncmp(start, "cat", 3)) { + } else if (strncmp(start, "cat", 3) == 0) { mlink->dform = FORM_CAT; mlink->dsec = start + 3; } start = p; - if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { + if (mlink->dsec != NULL && (p = strchr(start, '/')) != NULL) { *p++ = '\0'; mlink->arch = start; start = p; @@ -906,10 +963,10 @@ filescan(const char *file) * Suffix of `.0' indicates a catpage, `.1-9' is a manpage. */ p = strrchr(start, '\0'); - while (p-- > start && '/' != *p && '.' != *p) - /* Loop. */ ; + while (p-- > start && *p != '/' && *p != '.') + continue; - if ('.' == *p) { + if (*p == '.') { *p++ = '\0'; mlink->fsec = p; } @@ -919,11 +976,12 @@ filescan(const char *file) * Use the filename portion of the path. */ mlink->name = start; - if (NULL != (p = strrchr(start, '/'))) { + if ((p = strrchr(start, '/')) != NULL) { mlink->name = p + 1; *p = '\0'; } mlink_add(mlink, &st); + free(usefile); } static void @@ -2250,7 +2308,6 @@ set_basedir(const char *targetdir, int report_baddir) static char startdir[PATH_MAX]; static int getcwd_status; /* 1 = ok, 2 = failure */ static int chdir_status; /* 1 = changed directory */ - char *cp; /* * Remember the original working directory, if possible. @@ -2259,8 +2316,8 @@ set_basedir(const char *targetdir, int report_baddir) * Do not error out if the current directory is not * searchable: Maybe it won't be needed after all. */ - if (0 == getcwd_status) { - if (NULL == getcwd(startdir, sizeof(startdir))) { + if (getcwd_status == 0) { + if (getcwd(startdir, sizeof(startdir)) == NULL) { getcwd_status = 2; (void)strlcpy(startdir, strerror(errno), sizeof(startdir)); @@ -2273,19 +2330,20 @@ set_basedir(const char *targetdir, int report_baddir) * Do not use it any longer, not even for messages. */ *basedir = '\0'; + basedir_len = 0; /* * If and only if the directory was changed earlier and * the next directory to process is given as a relative path, * first go back, or bail out if that is impossible. */ - if (chdir_status && '/' != *targetdir) { - if (2 == getcwd_status) { + if (chdir_status && *targetdir != '/') { + if (getcwd_status == 2) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "getcwd: %s", startdir); return 0; } - if (-1 == chdir(startdir)) { + if (chdir(startdir) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&chdir %s", startdir); return 0; @@ -2297,48 +2355,71 @@ set_basedir(const char *targetdir, int report_baddir) * pathname and append a trailing slash, such that * we can reliably check whether files are inside. */ - if (NULL == realpath(targetdir, basedir)) { + if (realpath(targetdir, basedir) == NULL) { if (report_baddir || errno != ENOENT) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "&%s: realpath", targetdir); } + *basedir = '\0'; return 0; - } else if (-1 == chdir(basedir)) { + } else if (chdir(basedir) == -1) { if (report_baddir || errno != ENOENT) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "&chdir"); } + *basedir = '\0'; return 0; } chdir_status = 1; - cp = strchr(basedir, '\0'); - if ('/' != cp[-1]) { - if (cp - basedir >= PATH_MAX - 1) { + basedir_len = strlen(basedir); + if (basedir[basedir_len - 1] != '/') { + if (basedir_len >= PATH_MAX - 1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "Filename too long"); + *basedir = '\0'; + basedir_len = 0; return 0; } - *cp++ = '/'; - *cp = '\0'; + basedir[basedir_len++] = '/'; + basedir[basedir_len] = '\0'; } return 1; } +#ifdef READ_ALLOWED_PATH +static int +read_allowed(const char *candidate) +{ + const char *cp; + size_t len; + + for (cp = READ_ALLOWED_PATH;; cp += len) { + while (*cp == ':') + cp++; + if (*cp == '\0') + return 0; + len = strcspn(cp, ":"); + if (strncmp(candidate, cp, len) == 0) + return 1; + } +} +#endif + static void say(const char *file, const char *format, ...) { va_list ap; int use_errno; - if ('\0' != *basedir) + if (*basedir != '\0') fprintf(stderr, "%s", basedir); - if ('\0' != *basedir && '\0' != *file) + if (*basedir != '\0' && *file != '\0') fputc('/', stderr); - if ('\0' != *file) + if (*file != '\0') fprintf(stderr, "%s", file); use_errno = 1; - if (NULL != format) { + if (format != NULL) { switch (*format) { case '&': format++; @@ -2351,15 +2432,15 @@ say(const char *file, const char *format, ...) break; } } - if (NULL != format) { - if ('\0' != *basedir || '\0' != *file) + if (format != NULL) { + if (*basedir != '\0' || *file != '\0') fputs(": ", stderr); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } if (use_errno) { - if ('\0' != *basedir || '\0' != *file || NULL != format) + if (*basedir != '\0' || *file != '\0' || format != NULL) fputs(": ", stderr); perror(NULL); } else diff --git a/manpath.c b/manpath.c index e4578ccec364..255d748246d6 100644 --- a/manpath.c +++ b/manpath.c @@ -1,4 +1,4 @@ -/* $Id: manpath.c,v 1.40 2019/07/10 19:39:01 schwarze Exp $ */ +/* $Id: manpath.c,v 1.43 2020/08/27 14:59:47 schwarze Exp $ */ /* * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> @@ -163,7 +163,7 @@ manconf_free(struct manconf *conf) static void manconf_file(struct manconf *conf, const char *file) { - const char *const toks[] = { "manpath", "output", "_whatdb" }; + const char *const toks[] = { "manpath", "output" }; char manpath_default[] = MANPATH_DEFAULT; FILE *stream; @@ -200,13 +200,6 @@ manconf_file(struct manconf *conf, const char *file) } switch (tok) { - case 2: /* _whatdb */ - while (ep > cp && ep[-1] != '/') - ep--; - if (ep == cp) - continue; - *ep = '\0'; - /* FALLTHROUGH */ case 0: /* manpath */ manpath_add(&conf->manpath, cp, '\0'); *manpath_default = '\0'; @@ -230,8 +223,13 @@ int manconf_output(struct manoutput *conf, const char *cp, int fromfile) { const char *const toks[] = { + /* Tokens requiring an argument. */ "includes", "man", "paper", "style", "indent", "width", - "tag", "fragment", "mdoc", "noval", "toc" + "outfilename", "tagfilename", + /* Token taking an optional argument. */ + "tag", + /* Tokens not taking arguments. */ + "fragment", "mdoc", "noval", "toc" }; const size_t ntoks = sizeof(toks) / sizeof(toks[0]); @@ -252,11 +250,11 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) } } - if (tok < 6 && *cp == '\0') { + if (tok < 8 && *cp == '\0') { mandoc_msg(MANDOCERR_BADVAL_MISS, 0, 0, "-O %s=?", toks[tok]); return -1; } - if (tok > 6 && tok < ntoks && *cp != '\0') { + if (tok > 8 && tok < ntoks && *cp != '\0') { mandoc_msg(MANDOCERR_BADVAL, 0, 0, "-O %s=%s", toks[tok], cp); return -1; } @@ -313,22 +311,40 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile) "-O width=%s is %s", cp, errstr); return -1; case 6: + if (conf->outfilename != NULL) { + oldval = mandoc_strdup(conf->outfilename); + break; + } + conf->outfilename = mandoc_strdup(cp); + return 0; + case 7: + if (conf->tagfilename != NULL) { + oldval = mandoc_strdup(conf->tagfilename); + break; + } + conf->tagfilename = mandoc_strdup(cp); + return 0; + /* + * If the index of the following token changes, + * do not forget to adjust the range check above the switch. + */ + case 8: if (conf->tag != NULL) { oldval = mandoc_strdup(conf->tag); break; } conf->tag = mandoc_strdup(cp); return 0; - case 7: + case 9: conf->fragment = 1; return 0; - case 8: + case 10: conf->mdoc = 1; return 0; - case 9: + case 11: conf->noval = 1; return 0; - case 10: + case 12: conf->toc = 1; return 0; default: @@ -1,7 +1,7 @@ -.\" $Id: mdoc.7,v 1.279 2019/07/15 19:20:30 schwarze Exp $ +.\" $Id: mdoc.7,v 1.287 2021/07/29 17:32:01 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2010, 2011, 2013-2018 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010, 2011, 2013-2020 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -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 15 2019 $ +.Dd $Mdocdate: July 29 2021 $ .Dt MDOC 7 .Os .Sh NAME @@ -297,7 +297,7 @@ utility does this, that, and the other. It usually follows with a breakdown of the options (if documenting a command), such as: .Bd -literal -offset indent -The arguments are as follows: +The options are as follows: \&.Bl \-tag \-width Ds \&.It Fl v Print verbose information. @@ -449,6 +449,7 @@ in the alphabetical .It Ic \&Ss Ta subsection header (one line) .It Ic \&Sx Ta internal cross reference to a section or subsection .It Ic \&Xr Ta cross reference to another manual page: Ar name section +.It Ic \&Tg Ta tag the definition of a Ar term Pq <= 1 arguments .It Ic \&Pp Ta start a text paragraph (no arguments) .El .Ss Displays and lists @@ -631,6 +632,7 @@ Close an .Ic \&Ao block. Does not have any tail arguments. +.Tg Ad .It Ic \&Ad Ar address Memory address. Do not use this for postal addresses. @@ -638,6 +640,7 @@ Do not use this for postal addresses. Examples: .Dl \&.Ad [0,$] .Dl \&.Ad 0x00000000 +.Tg An .It Ic \&An Fl split | nosplit | Ar first_name ... last_name Author name. Can be used both for the authors of the program, function, or driver @@ -678,6 +681,7 @@ This macro is almost never useful. See .Ic \&Aq for more details. +.Tg Ap .It Ic \&Ap Inserts an apostrophe without any surrounding whitespace. This is generally used as a grammatical device when referring to the verb @@ -685,6 +689,7 @@ form of a function. .Pp Examples: .Dl \&.Fn execve \&Ap d +.Tg Aq .It Ic \&Aq Ar line Enclose the rest of the input line in angle brackets. The only important use case is for email addresses. @@ -729,6 +734,7 @@ as needed. .Pp See also .Ic \&Ao . +.Tg Ar .It Ic \&Ar Op Ar placeholder ... Command arguments. If an argument is not provided, the string @@ -747,6 +753,7 @@ for fixed strings to be passed verbatim as arguments, use .Ic \&Fl or .Ic \&Cm . +.Tg At .It Ic \&At Op Ar version Formats an .At @@ -784,6 +791,7 @@ Close a .Ic \&Bo block. Does not have any tail arguments. +.Tg Bd .It Ic \&Bd Fl Ns Ar type Oo Fl offset Ar width Oc Op Fl compact Begin a display block. Display blocks are used to select a different indentation and @@ -874,6 +882,7 @@ See also .Ic \&D1 and .Ic \&Dl . +.Tg Bf .It Ic \&Bf Fl emphasis | literal | symbolic | Cm \&Em | \&Li | \&Sy Change the font mode for a scoped block of text. The @@ -900,6 +909,7 @@ See also .Ic \&Em , and .Ic \&Sy . +.Tg Bk .It Ic \&Bk Fl words For each macro, keep its output together on the same output line, until the end of the macro or the end of the input line is reached, @@ -922,6 +932,7 @@ macro line: .Pp Be careful in using over-long lines within a keep block! Doing so will clobber the right margin. +.Tg Bl .It Xo .Ic \&Bl .Fl Ns Ar type @@ -1064,6 +1075,7 @@ Examples: .Pp See also .Ic \&Bq . +.Tg Bq .It Ic \&Bq Ar line Encloses its arguments in square brackets. .Pp @@ -1097,6 +1109,7 @@ Examples: .Pp See also .Ic \&Brq . +.Tg Brq .It Ic \&Brq Ar line Encloses its arguments in curly braces. .Pp @@ -1105,6 +1118,7 @@ Examples: .Pp See also .Ic \&Bro . +.Tg Bsx .It Ic \&Bsx Op Ar version Format the .Bsx @@ -1127,6 +1141,7 @@ and Supported only for compatibility, do not use this in new manuals. Prints .Dq is currently in beta test. +.Tg Bx .It Ic \&Bx Op Ar version Op Ar variant Format the .Bx @@ -1146,6 +1161,7 @@ See also .Ic \&Nx , and .Ic \&Ox . +.Tg Cd .It Ic \&Cd Ar line Kernel configuration declaration. This denotes strings accepted by @@ -1161,6 +1177,7 @@ whitespace and align consecutive .Ic \&Cd declarations. This practise is discouraged. +.Tg Cm .It Ic \&Cm Ar keyword ... Command modifiers. Typically used for fixed strings passed as arguments to interactive @@ -1176,6 +1193,7 @@ Examples: .Dl ".Ic set Fl o Cm vi" .Dl ".Ic lookup Cm file bind" .Dl ".Ic permit Ar identity Op Cm as Ar target" +.Tg D1 .It Ic \&D1 Ar line One-line indented display. This is formatted by the default rules and is useful for simple indented @@ -1201,8 +1219,10 @@ Close a .Ic \&Do block. Does not have any tail arguments. +.Tg Dd .It Ic \&Dd Cm $\&Mdocdate$ | Ar month day , year -Document date for display in the page footer. +Document date for display in the page footer, +by convention the date of the last change. This is the mandatory first macro of any .Nm manual. @@ -1248,6 +1268,7 @@ See also .Ic \&Dt and .Ic \&Os . +.Tg Dl .It Ic \&Dl Ar line One-line indented display. This is formatted as literal text and is useful for commands and @@ -1276,6 +1297,7 @@ April is the cruellest month .Pp See also .Ic \&Dq . +.Tg Dq .It Ic \&Dq Ar line Encloses its arguments in .Dq typographic @@ -1292,6 +1314,7 @@ See also .Ic \&Sq , and .Ic \&Do . +.Tg Dt .It Ic \&Dt Ar TITLE section Op Ar arch Document title for display in the page header. This is the mandatory second macro of any @@ -1351,6 +1374,7 @@ See also .Ic \&Dd and .Ic \&Os . +.Tg Dv .It Ic \&Dv Ar identifier ... Defined variables such as preprocessor constants, constant symbols, enumeration values, and so on. @@ -1370,6 +1394,7 @@ for variable symbols, and .Ic \&Fd for listing preprocessor variable definitions in the .Em SYNOPSIS . +.Tg Dx .It Ic \&Dx Op Ar version Format the .Dx @@ -1411,6 +1436,7 @@ End a list context started by .Ic \&Bl . See also .Ic \&It . +.Tg Em .It Ic \&Em Ar word ... Request an italic font. If the output device does not provide that, underline. @@ -1450,6 +1476,7 @@ or any of the other enclosure macros. It encloses its argument in the delimiters specified by the last .Ic \&Es macro. +.Tg Eo .It Ic \&Eo Op Ar opening_delimiter An arbitrary enclosure. The @@ -1457,6 +1484,7 @@ The argument is used as the enclosure head, for example, specifying \e(lq will emulate .Ic \&Do . +.Tg Er .It Ic \&Er Ar identifier ... Error constants for definitions of the .Va errno @@ -1479,6 +1507,7 @@ or any of the other enclosure macros. It takes two arguments, defining the delimiters to be used by subsequent .Ic \&En macros. +.Tg Ev .It Ic \&Ev Ar identifier ... Environmental variables such as those specified in .Xr environ 7 . @@ -1490,6 +1519,7 @@ Examples: See also .Ic \&Dv for general constants. +.Tg Ex .It Ic \&Ex Fl std Op Ar utility ... Insert a standard sentence regarding command exit values of 0 on success and >0 on failure. @@ -1506,6 +1536,7 @@ arguments are treated as separate utilities. .Pp See also .Ic \&Rv . +.Tg Fa .It Ic \&Fa Ar argument ... Function argument or parameter. Each argument may be a name and a type (recommended for the @@ -1543,6 +1574,7 @@ See also .It Ic \&Fc End a function context started by .Ic \&Fo . +.Tg Fd .It Ic \&Fd Pf # Ar directive Op Ar argument ... Preprocessor directive, in particular for listing it in the .Em SYNOPSIS . @@ -1563,25 +1595,33 @@ See also .Ic \&In , and .Ic \&Dv . +.Tg Fl .It Ic \&Fl Op Ar word ... Command-line flag or option. Used when listing arguments to command-line utilities. -Prints a fixed-width hyphen -.Sq \- -directly followed by each argument. -If no arguments are provided, a hyphen is printed followed by a space. -If the argument is a macro, a hyphen is prefixed to the subsequent macro -output. +For each argument, prints an ASCII hyphen-minus character +.Sq \- , +immediately followed by the argument. +If no arguments are provided, a hyphen-minus is printed followed by a space. +If the argument is a macro, a hyphen-minus is prefixed +to the subsequent macro output. .Pp Examples: -.Dl ".Fl R Op Fl H | L | P" -.Dl ".Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux" -.Dl ".Fl type Cm d Fl name Pa CVS" -.Dl ".Fl Ar signal_number" -.Dl ".Fl o Fl" +.Dl ".Nm du Op Fl H | L | P" +.Dl ".Nm ls Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux" +.Dl ".Nm route Cm add Fl inet Ar destination gateway" +.Dl ".Nm locate.updatedb Op Fl \e-fcodes Ns = Ns Ar dbfile" +.Dl ".Nm aucat Fl o Fl" +.Dl ".Nm kill Fl Ar signal_number" +.Pp +For GNU-sytle long options, escaping the additional hyphen-minus is not +strictly required, but may be safer with future versions of GNU troff; see +.Xr mandoc_char 7 +for details. .Pp See also .Ic \&Cm . +.Tg Fn .It Ic \&Fn Ar funcname Op Ar argument ... A function name. .Pp @@ -1610,6 +1650,7 @@ See also .Ic \&Fo , and .Ic \&Ft . +.Tg Fo .It Ic \&Fo Ar funcname Begin a function block. This is a multi-line version of @@ -1644,6 +1685,7 @@ This macro is obsolete. No replacement markup is needed. .Pp It was used to show numerical function return values in an italic font. +.Tg Ft .It Ic \&Ft Ar functype A function type. .Pp @@ -1663,6 +1705,7 @@ See also .Ic \&Fn , and .Ic \&Fo . +.Tg Fx .It Ic \&Fx Op Ar version Format the .Fx @@ -1685,6 +1728,7 @@ and This macro is not implemented in .Xr mandoc 1 . It was used to include the contents of a (header) file literally. +.Tg Ic .It Ic \&Ic Ar keyword ... Internal or interactive command, or configuration instruction in a configuration file. @@ -1704,6 +1748,7 @@ or is preferred for displaying code samples; the .Ic \&Ic macro is used when referring to an individual command name. +.Tg In .It Ic \&In Ar filename The name of an include file. This macro is most often used in section 2, 3, and 9 manual pages. @@ -1723,6 +1768,7 @@ Examples: .Pp See also .Sx MANUAL STRUCTURE . +.Tg It .It Ic \&It Op Ar head A list item. The syntax of this macro depends on the list type. @@ -1813,6 +1859,7 @@ but not the whitespace before the semicolon. .Pp See also .Ic \&Bl . +.Tg Lb .It Ic \&Lb Cm lib Ns Ar name Specify a library. .Pp @@ -1833,6 +1880,7 @@ section as described in Examples: .Dl \&.Lb libz .Dl \&.Lb libmandoc +.Tg Li .It Ic \&Li Ar word ... Request a typewriter (literal) font. Deprecated because on terminal output devices, this is usually @@ -1843,24 +1891,27 @@ For literal displays, use or .Ic \&Bd Fl literal Pq multi-line instead. +.Tg Lk .It Ic \&Lk Ar uri Op Ar display_name Format a hyperlink. .Pp Examples: -.Dl \&.Lk http://bsd.lv \(dqThe BSD.lv Project\(dq -.Dl \&.Lk http://bsd.lv +.Dl \&.Lk https://bsd.lv \(dqThe BSD.lv Project\(dq +.Dl \&.Lk https://bsd.lv .Pp See also .Ic \&Mt . .It Ic \&Lp Deprecated synonym for .Ic \&Pp . +.Tg Ms .It Ic \&Ms Ar name Display a mathematical symbol. .Pp Examples: .Dl \&.Ms sigma .Dl \&.Ms aleph +.Tg Mt .It Ic \&Mt Ar localpart Ns @ Ns Ar domain Format a .Dq mailto: @@ -1869,6 +1920,7 @@ hyperlink. Examples: .Dl \&.Mt discuss@manpages.bsd.lv .Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv +.Tg Nd .It Ic \&Nd Ar line A one line description of the manual's content. This is the mandatory last macro of the @@ -1891,6 +1943,7 @@ arguments and will display macros verbatim. .Pp See also .Ic \&Nm . +.Tg Nm .It Ic \&Nm Op Ar name The name of the manual page, or \(em in particular in section 1, 6, and 8 pages \(em of an additional command or feature documented in @@ -1928,6 +1981,7 @@ of section 2, 3 and 9 manual pages, use the macro rather than .Ic \&Nm to mark up the name of the manual page. +.Tg No .It Ic \&No Ar word ... Normal text. Closes the scope of any preceding in-line macro. @@ -1952,6 +2006,7 @@ See also .Ic \&Ql , and .Ic \&Sy . +.Tg Ns .It Ic \&Ns Suppress a space between the output of the preceding macro and the following text or macro. @@ -1971,6 +2026,7 @@ See also .Ic \&No and .Ic \&Sm . +.Tg Nx .It Ic \&Nx Op Ar version Format the .Nx @@ -2003,6 +2059,7 @@ Examples: \&.Op Fl flag Ns Ar value \&.Oc .Ed +.Tg Op .It Ic \&Op Ar line Optional part of a command line. Prints the argument(s) in brackets. @@ -2016,6 +2073,7 @@ Examples: .Pp See also .Ic \&Oo . +.Tg Os .It Ic \&Os Op Ar system Op Ar version Operating system version for display in the page footer. This is the mandatory third macro of @@ -2058,6 +2116,7 @@ Historical .Nm packages described it as .Dq "old function type (FORTRAN)" . +.Tg Ox .It Ic \&Ox Op Ar version Format the .Ox @@ -2076,6 +2135,7 @@ See also .Ic \&Fx , and .Ic \&Nx . +.Tg Pa .It Ic \&Pa Ar name ... An absolute or relative file system path, or a file or directory name. If an argument is not provided, the character @@ -2091,6 +2151,7 @@ See also .It Ic \&Pc Close parenthesised context opened by .Ic \&Po . +.Tg Pf .It Ic \&Pf Ar prefix macro Op Ar argument ... Removes the space between its argument and the following macro. It is equivalent to: @@ -2114,6 +2175,7 @@ and .It Ic \&Po Ar block Multi-line version of .Ic \&Pq . +.Tg Pp .It Ic \&Pp Break a paragraph. This will assert vertical space between prior and subsequent macros @@ -2130,6 +2192,7 @@ or lists unless the .Fl compact flag is given. +.Tg Pq .It Ic \&Pq Ar line Parenthesised enclosure. .Pp @@ -2138,6 +2201,7 @@ See also .It Ic \&Qc Close quoted context opened by .Ic \&Qo . +.Tg Ql .It Ic \&Ql Ar line In-line literal display. This can be used for complete command invocations and for multi-word @@ -2151,6 +2215,7 @@ and .It Ic \&Qo Ar block Multi-line version of .Ic \&Qq . +.Tg Qq .It Ic \&Qq Ar line Encloses its arguments in .Qq typewriter @@ -2168,6 +2233,7 @@ Close an .Ic \&Rs block. Does not have any tail arguments. +.Tg Rs .It Ic \&Rs Begin a bibliographic .Pq Dq reference @@ -2208,6 +2274,7 @@ If an block is used within a SEE ALSO section, a vertical space is asserted before the rendered output, else the block continues on the current line. +.Tg Rv .It Ic \&Rv Fl std Op Ar function ... Insert a standard sentence regarding a function call's return value of 0 on success and \-1 on error, with the @@ -2228,6 +2295,7 @@ See also .It Ic \&Sc Close single-quoted context opened by .Ic \&So . +.Tg Sh .It Ic \&Sh Ar TITLE LINE Begin a new section. For a list of conventional manual sections, see @@ -2246,6 +2314,7 @@ See also .Ic \&Ss , and .Ic \&Sx . +.Tg Sm .It Ic \&Sm Op Cm on | off Switches the spacing mode for output generated from macros. .Pp @@ -2264,6 +2333,7 @@ Using this is not recommended because it makes the code harder to read. .It Ic \&So Ar block Multi-line version of .Ic \&Sq . +.Tg Sq .It Ic \&Sq Ar line Encloses its arguments in .Sq typewriter @@ -2274,6 +2344,7 @@ See also .Ic \&Qq , and .Ic \&So . +.Tg Ss .It Ic \&Ss Ar Title line Begin a new subsection. Unlike with @@ -2296,6 +2367,7 @@ See also .Ic \&Sh , and .Ic \&Sx . +.Tg St .It Ic \&St Fl Ns Ar abbreviation Replace an abbreviation for a standard with the full form. The following standards are recognised. @@ -2506,6 +2578,7 @@ Ethernet local area networks. .St -ieee1275-94 .El .El +.Tg Sx .It Ic \&Sx Ar Title line Reference a section or subsection in the same manual page. The referenced section or subsection name must be identical to the @@ -2518,6 +2591,7 @@ See also .Ic \&Sh and .Ic \&Ss . +.Tg Sy .It Ic \&Sy Ar word ... Request a boldface font. .Pp @@ -2543,11 +2617,56 @@ See also .Ic \&No , and .Ic \&Ql . +.Tg Ta .It Ic \&Ta Table cell separator in .Ic \&Bl Fl column lists; can only be used below .Ic \&It . +.Tg Tg +.It Ic \&Tg Op Ar term +Announce that the next input line starts a definition of the +.Ar term . +This macro must appear alone on its own input line. +The argument defaults to the first argument of the first macro +on the next line. +The argument may not contain whitespace characters, not even when it is quoted. +This macro is a +.Xr mandoc 1 +extension and is typically ignored by other formatters. +.Pp +When viewing terminal output with +.Xr less 1 , +the interactive +.Ic :t +command can be used to go to the definition of the +.Ar term +as described for the +.Ev MANPAGER +variable in +.Xr man 1 ; +when producing HTML output, a fragment identifier +.Pq Ic id No attribute +is generated, to be used for deep linking to this place of the document. +.Pp +In most cases, adding a +.Ic \&Tg +macro would be redundant because +.Xr mandoc 1 +is able to automatically tag most definitions. +This macro is intended for cases where automatic tagging of a +.Ar term +is unsatisfactory, for example if a definition is not tagged +automatically (false negative) or if places are tagged that do +not define the +.Ar term +(false positives). +When there is at least one +.Ic \&Tg +macro for a +.Ar term , +no other places are automatically marked as definitions of that +.Ar term . .It Ic \&Tn Ar word ... Supported only for compatibility, do not use this in new manuals. Even though the macro name @@ -2562,6 +2681,7 @@ Prints out Supported only for compatibility, do not use this in new manuals. Prints out .Dq Ux . +.Tg Va .It Ic \&Va Oo Ar type Oc Ar identifier ... A variable name. .Pp @@ -2576,6 +2696,7 @@ For declarations of global variables in the .Em SYNOPSIS section, use .Ic \&Vt . +.Tg Vt .It Ic \&Vt Ar type Op Ar identifier A variable type. .Pp @@ -2619,6 +2740,7 @@ beyond the end of the input line. This macro originally existed to work around the 9-argument limit of historic .Xr roff 7 . +.Tg Xr .It Ic \&Xr Ar name section Link to another manual .Pq Qq cross-reference . @@ -2681,7 +2803,7 @@ column, if applicable, describes closure rules. .Ss Block full-explicit Multi-line scope closed by an explicit closing macro. All macros contains bodies; only -.Ic \s&Bf +.Ic \&Bf and .Pq optionally .Ic \&Bl @@ -2912,6 +3034,7 @@ then the macro accepts an arbitrary number of arguments. .It Ic \&St Ta \&No Ta Yes Ta 1 .It Ic \&Sx Ta Yes Ta Yes Ta >0 .It Ic \&Sy Ta Yes Ta Yes Ta >0 +.It Ic \&Tg Ta \&No Ta \&No Ta <2 .It Ic \&Tn Ta Yes Ta Yes Ta >0 .It Ic \&Ud Ta \&No Ta \&No Ta 0 .It Ic \&Ux Ta Yes Ta Yes Ta n @@ -3039,17 +3162,6 @@ The following problematic behaviour is found in groff: .Pp .Bl -dash -compact .It -.Ic \&Dd -with non-standard arguments behaves very strangely. -When there are three arguments, they are printed verbatim. -Any other number of arguments is replaced by the current date, -but without any arguments the string -.Dq Epoch -is printed. -.It -.Ic \&Lk -only accepts a single link-name argument; the remainder is misformatted. -.It .Ic \&Pa does not format its arguments when used in the FILES section under certain list types. @@ -3057,9 +3169,6 @@ certain list types. .Ic \&Ta can only be called by other macros, but not at the beginning of a line. .It -.Ic \&%C -is not implemented (up to and including groff-1.22.2). -.It .Sq \ef .Pq font face and @@ -3109,10 +3218,16 @@ but produces large indentations. .Xr tbl 7 .Pp The web page -.Lk http://mandoc.bsd.lv/mdoc/ "extended documentation for the mdoc language" +.Lk https://mandoc.bsd.lv/mdoc/ "extended documentation for the mdoc language" provides a few tutorial-style pages for beginners, an extensive style guide for advanced authors, and an alphabetic index helping to choose the best macros for various kinds of content. +.Pp +The manual page +.Lk https://man.voidlinux.org/groff_mdoc "groff_mdoc(7)" +contained in the +.Dq groff +package documents exactly the same language in a somewhat different style. .Sh HISTORY The .Nm @@ -1,7 +1,7 @@ -/* $Id: mdoc.c,v 1.274 2018/12/31 07:46:07 schwarze Exp $ */ +/* $Id: mdoc.c,v 1.275 2020/04/06 10:16:17 schwarze Exp $ */ /* + * Copyright (c) 2010, 2012-2018, 2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Top level and utility functions of the mdoc(7) parser for mandoc(1). */ #include "config.h" @@ -352,12 +354,13 @@ mdoc_pmacro(struct roff_man *mdoc, int ln, char *buf, int offs) mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); /* - * If an initial macro or a list invocation, divert directly - * into macro processing. + * If an initial or transparent macro or a list invocation, + * divert directly into macro processing. */ n = mdoc->last; - if (n == NULL || tok == MDOC_It || tok == MDOC_El) { + if (n == NULL || tok == MDOC_It || tok == MDOC_El || + roff_tok_transparent(tok)) { (*mdoc_macro(tok)->fp)(mdoc, tok, ln, sv, &offs, buf); return 1; } diff --git a/mdoc_html.c b/mdoc_html.c index 87bf42a72ba0..c0a0a6a56978 100644 --- a/mdoc_html.c +++ b/mdoc_html.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_html.c,v 1.328 2019/03/01 10:57:18 schwarze Exp $ */ +/* $Id: mdoc_html.c,v 1.342 2021/03/30 19:26:20 schwarze Exp $ */ /* + * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * HTML formatter for mdoc(7) used by mandoc(1). */ #include "config.h" @@ -47,13 +49,11 @@ struct mdoc_html_act { void (*post)(MDOC_ARGS); }; -static char *cond_id(const struct roff_node *); static void print_mdoc_head(const struct roff_meta *, struct html *); static void print_mdoc_node(MDOC_ARGS); static void print_mdoc_nodelist(MDOC_ARGS); -static void synopsis_pre(struct html *, - const struct roff_node *); +static void synopsis_pre(struct html *, struct roff_node *); static void mdoc_root_post(const struct roff_meta *, struct html *); @@ -73,9 +73,8 @@ static void mdoc_bk_post(MDOC_ARGS); static int mdoc_bk_pre(MDOC_ARGS); static int mdoc_bl_pre(MDOC_ARGS); static int mdoc_cd_pre(MDOC_ARGS); -static int mdoc_cm_pre(MDOC_ARGS); +static int mdoc_code_pre(MDOC_ARGS); static int mdoc_d1_pre(MDOC_ARGS); -static int mdoc_dv_pre(MDOC_ARGS); static int mdoc_fa_pre(MDOC_ARGS); static int mdoc_fd_pre(MDOC_ARGS); static int mdoc_fl_pre(MDOC_ARGS); @@ -84,20 +83,15 @@ static int mdoc_ft_pre(MDOC_ARGS); static int mdoc_em_pre(MDOC_ARGS); static void mdoc_eo_post(MDOC_ARGS); static int mdoc_eo_pre(MDOC_ARGS); -static int mdoc_er_pre(MDOC_ARGS); -static int mdoc_ev_pre(MDOC_ARGS); static int mdoc_ex_pre(MDOC_ARGS); static void mdoc_fo_post(MDOC_ARGS); static int mdoc_fo_pre(MDOC_ARGS); -static int mdoc_ic_pre(MDOC_ARGS); static int mdoc_igndelim_pre(MDOC_ARGS); static int mdoc_in_pre(MDOC_ARGS); static int mdoc_it_pre(MDOC_ARGS); static int mdoc_lb_pre(MDOC_ARGS); -static int mdoc_li_pre(MDOC_ARGS); static int mdoc_lk_pre(MDOC_ARGS); static int mdoc_mt_pre(MDOC_ARGS); -static int mdoc_ms_pre(MDOC_ARGS); static int mdoc_nd_pre(MDOC_ARGS); static int mdoc_nm_pre(MDOC_ARGS); static int mdoc_no_pre(MDOC_ARGS); @@ -115,6 +109,7 @@ static int mdoc_ss_pre(MDOC_ARGS); static int mdoc_st_pre(MDOC_ARGS); static int mdoc_sx_pre(MDOC_ARGS); static int mdoc_sy_pre(MDOC_ARGS); +static int mdoc_tg_pre(MDOC_ARGS); static int mdoc_va_pre(MDOC_ARGS); static int mdoc_vt_pre(MDOC_ARGS); static int mdoc_xr_pre(MDOC_ARGS); @@ -139,19 +134,19 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {mdoc_ap_pre, NULL}, /* Ap */ {mdoc_ar_pre, NULL}, /* Ar */ {mdoc_cd_pre, NULL}, /* Cd */ - {mdoc_cm_pre, NULL}, /* Cm */ - {mdoc_dv_pre, NULL}, /* Dv */ - {mdoc_er_pre, NULL}, /* Er */ - {mdoc_ev_pre, NULL}, /* Ev */ + {mdoc_code_pre, NULL}, /* Cm */ + {mdoc_code_pre, NULL}, /* Dv */ + {mdoc_code_pre, NULL}, /* Er */ + {mdoc_code_pre, NULL}, /* Ev */ {mdoc_ex_pre, NULL}, /* Ex */ {mdoc_fa_pre, NULL}, /* Fa */ {mdoc_fd_pre, NULL}, /* Fd */ {mdoc_fl_pre, NULL}, /* Fl */ {mdoc_fn_pre, NULL}, /* Fn */ {mdoc_ft_pre, NULL}, /* Ft */ - {mdoc_ic_pre, NULL}, /* Ic */ + {mdoc_code_pre, NULL}, /* Ic */ {mdoc_in_pre, NULL}, /* In */ - {mdoc_li_pre, NULL}, /* Li */ + {mdoc_code_pre, NULL}, /* Li */ {mdoc_nd_pre, NULL}, /* Nd */ {mdoc_nm_pre, NULL}, /* Nm */ {mdoc_quote_pre, mdoc_quote_post}, /* Op */ @@ -192,7 +187,7 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {mdoc_em_pre, NULL}, /* Em */ {mdoc_eo_pre, mdoc_eo_post}, /* Eo */ {mdoc_xx_pre, NULL}, /* Fx */ - {mdoc_ms_pre, NULL}, /* Ms */ + {mdoc_no_pre, NULL}, /* Ms */ {mdoc_no_pre, NULL}, /* No */ {mdoc_ns_pre, NULL}, /* Ns */ {mdoc_xx_pre, NULL}, /* Nx */ @@ -241,6 +236,7 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { {mdoc__x_pre, mdoc__x_post}, /* %Q */ {mdoc__x_pre, mdoc__x_post}, /* %U */ {NULL, NULL}, /* Ta */ + {mdoc_tg_pre, NULL}, /* Tg */ }; @@ -248,13 +244,15 @@ static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = { * See the same function in mdoc_term.c for documentation. */ static void -synopsis_pre(struct html *h, const struct roff_node *n) +synopsis_pre(struct html *h, struct roff_node *n) { + struct roff_node *np; - if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; - if (n->prev->tok == n->tok && + if (np->tok == n->tok && MDOC_Fo != n->tok && MDOC_Ft != n->tok && MDOC_Fn != n->tok) { @@ -262,7 +260,7 @@ synopsis_pre(struct html *h, const struct roff_node *n) return; } - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -351,30 +349,40 @@ print_mdoc_node(MDOC_ARGS) if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) return; - html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); + if ((n->flags & NODE_NOFILL) == 0) + html_fillmode(h, ROFF_fi); + else if (html_fillmode(h, ROFF_nf) == ROFF_nf && + n->tok != ROFF_fi && n->flags & NODE_LINE) + print_endline(h); child = 1; n->flags &= ~NODE_ENDED; switch (n->type) { case ROFFT_TEXT: + if (n->flags & NODE_LINE) { + switch (*n->string) { + case '\0': + h->col = 1; + print_endline(h); + return; + case ' ': + if ((h->flags & HTML_NONEWLINE) == 0 && + (n->flags & NODE_NOFILL) == 0) + print_otag(h, TAG_BR, ""); + break; + default: + break; + } + } t = h->tag; t->refcnt++; - - /* No tables in this mode... */ - assert(NULL == h->tblt); - - /* - * Make sure that if we're in a literal mode already - * (i.e., within a <PRE>) don't print the newline. - */ - if (*n->string == ' ' && n->flags & NODE_LINE && - (h->flags & HTML_NONEWLINE) == 0 && - (n->flags & NODE_NOFILL) == 0) - print_otag(h, TAG_BR, ""); - if (NODE_DELIMC & n->flags) + if (n->flags & NODE_DELIMC) h->flags |= HTML_NOSPACE; - print_text(h, n->string); - if (NODE_DELIMO & n->flags) + if (n->flags & NODE_HREF) + print_tagged_text(h, n->string, n); + else + print_text(h, n->string); + if (n->flags & NODE_DELIMO) h->flags |= HTML_NOSPACE; break; case ROFFT_EQN: @@ -439,12 +447,6 @@ print_mdoc_node(MDOC_ARGS) n->body->flags |= NODE_ENDED; break; } - - if (n->flags & NODE_NOFILL && - (n->next == NULL || n->next->flags & NODE_LINE)) { - h->col++; - print_endline(h); - } } static void @@ -502,20 +504,11 @@ mdoc_root_pre(const struct roff_meta *meta, struct html *h) return 1; } -static char * -cond_id(const struct roff_node *n) +static int +mdoc_code_pre(MDOC_ARGS) { - if (n->child != NULL && - n->child->type == ROFFT_TEXT && - (n->prev == NULL || - (n->prev->type == ROFFT_TEXT && - strcmp(n->prev->string, "|") == 0)) && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Xo && - n->parent->parent->prev == NULL && - n->parent->parent->parent->tok == MDOC_It))) - return html_make_id(n, 1); - return NULL; + print_otag_id(h, TAG_CODE, roff_name[n->tok], n); + return 1; } static int @@ -578,10 +571,7 @@ mdoc_sh_pre(MDOC_ARGS) print_otag(h, TAG_SECTION, "c", "Sh"); break; case ROFFT_HEAD: - id = html_make_id(n, 1); - print_otag(h, TAG_H1, "ci", "Sh", id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); + print_otag_id(h, TAG_H1, "Sh", n); break; case ROFFT_BODY: if (n->sec == SEC_AUTHORS) @@ -596,64 +586,43 @@ mdoc_sh_pre(MDOC_ARGS) static int mdoc_ss_pre(MDOC_ARGS) { - char *id; - switch (n->type) { case ROFFT_BLOCK: html_close_paragraph(h); print_otag(h, TAG_SECTION, "c", "Ss"); - return 1; + break; case ROFFT_HEAD: + print_otag_id(h, TAG_H2, "Ss", n); break; case ROFFT_BODY: - return 1; + break; default: abort(); } - - id = html_make_id(n, 1); - print_otag(h, TAG_H2, "ci", "Ss", id); - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); return 1; } static int mdoc_fl_pre(MDOC_ARGS) { - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Fl", id); + struct roff_node *nn; + print_otag_id(h, TAG_CODE, "Fl", n); print_text(h, "\\-"); - if (!(n->child == NULL && - (n->next == NULL || - n->next->type == ROFFT_TEXT || - n->next->flags & NODE_LINE))) + if (n->child != NULL || + ((nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && + (nn->flags & NODE_LINE) == 0)) h->flags |= HTML_NOSPACE; return 1; } static int -mdoc_cm_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Cm", id); - return 1; -} - -static int mdoc_nd_pre(MDOC_ARGS) { switch (n->type) { case ROFFT_BLOCK: - html_close_paragraph(h); return 1; case ROFFT_HEAD: return 0; @@ -663,8 +632,7 @@ mdoc_nd_pre(MDOC_ARGS) abort(); } print_text(h, "\\(em"); - /* Cannot use TAG_SPAN because it may contain blocks. */ - print_otag(h, TAG_DIV, "c", "Nd"); + print_otag(h, TAG_SPAN, "c", "Nd"); return 1; } @@ -722,6 +690,18 @@ mdoc_xr_pre(MDOC_ARGS) } static int +mdoc_tg_pre(MDOC_ARGS) +{ + char *id; + + if ((id = html_make_id(n, 1)) != NULL) { + print_tagq(h, print_otag(h, TAG_MARK, "i", id)); + free(id); + } + return 0; +} + +static int mdoc_ns_pre(MDOC_ARGS) { @@ -765,7 +745,7 @@ mdoc_it_pre(MDOC_ARGS) case ROFFT_HEAD: return 0; case ROFFT_BODY: - print_otag(h, TAG_LI, ""); + print_otag_id(h, TAG_LI, NULL, n); break; default: break; @@ -777,7 +757,7 @@ mdoc_it_pre(MDOC_ARGS) case LIST_ohang: switch (n->type) { case ROFFT_HEAD: - print_otag(h, TAG_DT, ""); + print_otag_id(h, TAG_DT, NULL, n); break; case ROFFT_BODY: print_otag(h, TAG_DD, ""); @@ -789,7 +769,7 @@ mdoc_it_pre(MDOC_ARGS) case LIST_tag: switch (n->type) { case ROFFT_HEAD: - print_otag(h, TAG_DT, ""); + print_otag_id(h, TAG_DT, NULL, n); break; case ROFFT_BODY: if (n->child == NULL) { @@ -810,7 +790,7 @@ mdoc_it_pre(MDOC_ARGS) print_otag(h, TAG_TD, ""); break; default: - print_otag(h, TAG_TR, ""); + print_otag_id(h, TAG_TR, NULL, n); } default: break; @@ -876,8 +856,8 @@ mdoc_bl_pre(MDOC_ARGS) case LIST_tag: if (bl->offs) print_otag(h, TAG_DIV, "c", "Bd-indent"); - print_otag(h, TAG_DL, "c", bl->comp ? - "Bl-tag Bl-compact" : "Bl-tag"); + print_otag_id(h, TAG_DL, + bl->comp ? "Bl-tag Bl-compact" : "Bl-tag", n->body); return 1; case LIST_column: elemtype = TAG_TABLE; @@ -890,14 +870,14 @@ mdoc_bl_pre(MDOC_ARGS) (void)strlcat(cattr, " Bd-indent", sizeof(cattr)); if (bl->comp) (void)strlcat(cattr, " Bl-compact", sizeof(cattr)); - print_otag(h, elemtype, "c", cattr); + print_otag_id(h, elemtype, cattr, n->body); return 1; } static int mdoc_ex_pre(MDOC_ARGS) { - if (n->prev) + if (roff_node_prev(n) != NULL) print_otag(h, TAG_BR, ""); return 1; } @@ -912,7 +892,7 @@ mdoc_st_pre(MDOC_ARGS) static int mdoc_em_pre(MDOC_ARGS) { - print_otag(h, TAG_I, "c", "Em"); + print_otag_id(h, TAG_I, "Em", n); return 1; } @@ -922,15 +902,15 @@ mdoc_d1_pre(MDOC_ARGS) switch (n->type) { case ROFFT_BLOCK: html_close_paragraph(h); - break; + return 1; case ROFFT_HEAD: return 0; case ROFFT_BODY: - return 1; + break; default: abort(); } - print_otag(h, TAG_DIV, "c", "Bd Bd-indent"); + print_otag_id(h, TAG_DIV, "Bd Bd-indent", n); if (n->tok == MDOC_Dl) print_otag(h, TAG_CODE, "c", "Li"); return 1; @@ -950,7 +930,7 @@ mdoc_sx_pre(MDOC_ARGS) static int mdoc_bd_pre(MDOC_ARGS) { - char buf[16]; + char buf[20]; struct roff_node *nn; int comp; @@ -974,7 +954,7 @@ mdoc_bd_pre(MDOC_ARGS) continue; if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss) comp = 1; - if (nn->prev != NULL) + if (roff_node_prev(nn) != NULL) break; } (void)strlcpy(buf, "Bd", sizeof(buf)); @@ -987,7 +967,10 @@ mdoc_bd_pre(MDOC_ARGS) strcmp(n->norm->Bd.offs, "left") != 0) (void)strlcat(buf, " Bd-indent", sizeof(buf)); - print_otag(h, TAG_DIV, "c", buf); + if (n->norm->Bd.type == DISP_literal) + (void)strlcat(buf, " Li", sizeof(buf)); + + print_otag_id(h, TAG_DIV, buf, n); return 1; } @@ -1038,45 +1021,6 @@ mdoc_cd_pre(MDOC_ARGS) } static int -mdoc_dv_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Dv", id); - return 1; -} - -static int -mdoc_ev_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Ev", id); - return 1; -} - -static int -mdoc_er_pre(MDOC_ARGS) -{ - char *id; - - id = n->sec == SEC_ERRORS && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Bq && - n->parent->parent->parent->tok == MDOC_It)) ? - html_make_id(n, 1) : NULL; - - if (id != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Er", id); - return 1; -} - -static int mdoc_fa_pre(MDOC_ARGS) { const struct roff_node *nn; @@ -1086,22 +1030,21 @@ mdoc_fa_pre(MDOC_ARGS) print_otag(h, TAG_VAR, "c", "Fa"); return 1; } - - for (nn = n->child; nn; nn = nn->next) { + for (nn = n->child; nn != NULL; nn = nn->next) { t = print_otag(h, TAG_VAR, "c", "Fa"); print_text(h, nn->string); print_tagq(h, t); - if (nn->next) { + if (nn->next != NULL) { h->flags |= HTML_NOSPACE; print_text(h, ","); } } - - if (n->child && n->next && n->next->tok == MDOC_Fa) { + if (n->child != NULL && + (nn = roff_node_next(n)) != NULL && + nn->tok == MDOC_Fa) { h->flags |= HTML_NOSPACE; print_text(h, ","); } - return 0; } @@ -1209,7 +1152,7 @@ mdoc_fn_pre(MDOC_ARGS) print_tagq(h, t); } - t = print_otag(h, TAG_CODE, "c", "Fn"); + t = print_otag_id(h, TAG_CODE, "Fn", n); if (sp) print_text(h, sp); @@ -1272,9 +1215,21 @@ mdoc_skip_pre(MDOC_ARGS) static int mdoc_pp_pre(MDOC_ARGS) { - if ((n->flags & NODE_NOFILL) == 0) { + char *id; + + if (n->flags & NODE_NOFILL) { + print_endline(h); + if (n->flags & NODE_ID) + mdoc_tg_pre(meta, n, h); + else { + h->col = 1; + print_endline(h); + } + } else { html_close_paragraph(h); - print_otag(h, TAG_P, "c", "Pp"); + id = n->flags & NODE_ID ? html_make_id(n, 1) : NULL; + print_otag(h, TAG_P, "ci", "Pp", id); + free(id); } return 0; } @@ -1324,14 +1279,12 @@ mdoc_mt_pre(MDOC_ARGS) for (n = n->child; n; n = n->next) { assert(n->type == ROFFT_TEXT); - mandoc_asprintf(&cp, "mailto:%s", n->string); t = print_otag(h, TAG_A, "ch", "Mt", cp); print_text(h, n->string); print_tagq(h, t); free(cp); } - return 0; } @@ -1340,30 +1293,30 @@ mdoc_fo_pre(MDOC_ARGS) { struct tag *t; - if (n->type == ROFFT_BODY) { + switch (n->type) { + case ROFFT_BLOCK: + synopsis_pre(h, n); + return 1; + case ROFFT_HEAD: + if (n->child != NULL) { + t = print_otag_id(h, TAG_CODE, "Fn", n); + print_text(h, n->child->string); + print_tagq(h, t); + } + return 0; + case ROFFT_BODY: h->flags |= HTML_NOSPACE; print_text(h, "("); h->flags |= HTML_NOSPACE; return 1; - } else if (n->type == ROFFT_BLOCK) { - synopsis_pre(h, n); - return 1; + default: + abort(); } - - if (n->child == NULL) - return 0; - - assert(n->child->string); - t = print_otag(h, TAG_CODE, "c", "Fn"); - print_text(h, n->child->string); - print_tagq(h, t); - return 0; } static void mdoc_fo_post(MDOC_ARGS) { - if (n->type != ROFFT_BODY) return; h->flags |= HTML_NOSPACE; @@ -1413,22 +1366,10 @@ mdoc_in_pre(MDOC_ARGS) assert(n->type == ROFFT_TEXT); print_text(h, n->string); } - return 0; } static int -mdoc_ic_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Ic", id); - return 1; -} - -static int mdoc_va_pre(MDOC_ARGS) { print_otag(h, TAG_VAR, "c", "Va"); @@ -1438,7 +1379,6 @@ mdoc_va_pre(MDOC_ARGS) static int mdoc_ap_pre(MDOC_ARGS) { - h->flags |= HTML_NOSPACE; print_text(h, "\\(aq"); h->flags |= HTML_NOSPACE; @@ -1477,20 +1417,8 @@ mdoc_bf_pre(MDOC_ARGS) } static int -mdoc_ms_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_SPAN, "ci", "Ms", id); - return 1; -} - -static int mdoc_igndelim_pre(MDOC_ARGS) { - h->flags |= HTML_IGNDELIM; return 1; } @@ -1498,7 +1426,6 @@ mdoc_igndelim_pre(MDOC_ARGS) static void mdoc_pf_post(MDOC_ARGS) { - if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) h->flags |= HTML_NOSPACE; } @@ -1527,36 +1454,23 @@ mdoc_rs_pre(MDOC_ARGS) static int mdoc_no_pre(MDOC_ARGS) { - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_SPAN, "ci", "No", id); - return 1; -} - -static int -mdoc_li_pre(MDOC_ARGS) -{ - char *id; - - if ((id = cond_id(n)) != NULL) - print_otag(h, TAG_A, "chR", "permalink", id); - print_otag(h, TAG_CODE, "ci", "Li", id); + print_otag_id(h, TAG_SPAN, roff_name[n->tok], n); return 1; } static int mdoc_sy_pre(MDOC_ARGS) { - print_otag(h, TAG_B, "c", "Sy"); + print_otag_id(h, TAG_B, "Sy", n); return 1; } static int mdoc_lb_pre(MDOC_ARGS) { - if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags && n->prev) + if (n->sec == SEC_LIBRARY && + n->flags & NODE_LINE && + roff_node_prev(n) != NULL) print_otag(h, TAG_BR, ""); print_otag(h, TAG_SPAN, "c", "Lb"); @@ -1566,17 +1480,18 @@ mdoc_lb_pre(MDOC_ARGS) static int mdoc__x_pre(MDOC_ARGS) { - const char *cattr; - enum htmltag t; + struct roff_node *nn; + const char *cattr; + enum htmltag t; t = TAG_SPAN; switch (n->tok) { case MDOC__A: cattr = "RsA"; - if (n->prev && MDOC__A == n->prev->tok) - if (NULL == n->next || MDOC__A != n->next->tok) - print_text(h, "and"); + if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A)) + print_text(h, "and"); break; case MDOC__B: t = TAG_I; @@ -1631,19 +1546,21 @@ mdoc__x_pre(MDOC_ARGS) static void mdoc__x_post(MDOC_ARGS) { + struct roff_node *nn; - if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) - if (NULL == n->next->next || MDOC__A != n->next->next->tok) - if (NULL == n->prev || MDOC__A != n->prev->tok) - return; + if (n->tok == MDOC__A && + (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) && + ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A)) + return; /* TODO: %U */ - if (NULL == n->parent || MDOC_Rs != n->parent->tok) + if (n->parent == NULL || n->parent->tok != MDOC_Rs) return; h->flags |= HTML_NOSPACE; - print_text(h, n->next ? "," : "."); + print_text(h, roff_node_next(n) ? "," : "."); } static int @@ -1700,7 +1617,7 @@ mdoc_quote_pre(MDOC_ARGS) /* * Give up on semantic markup for now. * We cannot use TAG_SPAN because .Oo may contain blocks. - * We cannot use TAG_IDIV because we might be in a + * We cannot use TAG_DIV because we might be in a * phrasing context (like .Dl or .Pp); we cannot * close out a .Pp at this point either because * that would break the line. @@ -1715,9 +1632,11 @@ mdoc_quote_pre(MDOC_ARGS) break; case MDOC_Do: case MDOC_Dq: + print_text(h, "\\(lq"); + break; case MDOC_Qo: case MDOC_Qq: - print_text(h, "\\(lq"); + print_text(h, "\""); break; case MDOC_Po: case MDOC_Pq: @@ -1773,12 +1692,14 @@ mdoc_quote_post(MDOC_ARGS) else print_text(h, n->norm->Es->child->next->string); break; - case MDOC_Qo: - case MDOC_Qq: case MDOC_Do: case MDOC_Dq: print_text(h, "\\(rq"); break; + case MDOC_Qo: + case MDOC_Qq: + print_text(h, "\""); + break; case MDOC_Po: case MDOC_Pq: print_text(h, ")"); diff --git a/mdoc_macro.c b/mdoc_macro.c index 3422945814f0..dd3885c702b7 100644 --- a/mdoc_macro.c +++ b/mdoc_macro.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_macro.c,v 1.232 2019/01/07 07:26:29 schwarze Exp $ */ +/* $Id: mdoc_macro.c,v 1.234 2020/01/19 18:02:00 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -61,7 +61,7 @@ static void rew_pending(struct roff_man *, const struct roff_node *); static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = { - { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ + { in_line_eoln, MDOC_PROLOGUE | MDOC_JOIN }, /* Dd */ { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ { in_line_eoln, MDOC_PROLOGUE }, /* Os */ { blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */ @@ -200,6 +200,7 @@ static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = { { in_line_eoln, MDOC_JOIN }, /* %Q */ { in_line_eoln, 0 }, /* %U */ { phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */ + { in_line_eoln, 0 }, /* Tg */ }; diff --git a/mdoc_man.c b/mdoc_man.c index 2e8f02ae56f2..0964cc6160a1 100644 --- a/mdoc_man.c +++ b/mdoc_man.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_man.c,v 1.132 2019/01/04 03:17:36 schwarze Exp $ */ +/* $Id: mdoc_man.c,v 1.137 2021/07/04 15:38:26 schwarze Exp $ */ /* - * Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -113,7 +113,7 @@ static int pre_sm(DECL_ARGS); static void pre_sp(DECL_ARGS); static int pre_sect(DECL_ARGS); static int pre_sy(DECL_ARGS); -static void pre_syn(const struct roff_node *); +static void pre_syn(struct roff_node *); static void pre_ta(DECL_ARGS); static int pre_vt(DECL_ARGS); static int pre_xr(DECL_ARGS); @@ -262,6 +262,7 @@ static const struct mdoc_man_act mdoc_man_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, post_percent, NULL, NULL }, /* %Q */ { NULL, NULL, post_percent, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ + { NULL, pre_skip, NULL, NULL, NULL }, /* Tg */ }; static const struct mdoc_man_act *mdoc_man_act(enum roff_tok); @@ -582,9 +583,9 @@ print_width(const struct mdoc_bl *bl, const struct roff_node *child) /* Set up the current list. */ if (chsz > sz && bl->type != LIST_tag) - print_block(".HP", 0); + print_block(".HP", MMAN_spc); else { - print_block(".TP", 0); + print_block(".TP", MMAN_spc); remain = sz + 2; } if (numeric) { @@ -649,7 +650,9 @@ print_node(DECL_ARGS) * Break the line if we were parsed subsequent the current node. * This makes the page structure be more consistent. */ - if (MMAN_spc & outflags && NODE_LINE & n->flags) + if (outflags & MMAN_spc && + n->flags & NODE_LINE && + !roff_node_transparent(n)) outflags |= MMAN_nl; act = NULL; @@ -657,7 +660,20 @@ print_node(DECL_ARGS) do_sub = 1; n->flags &= ~NODE_ENDED; - if (n->type == ROFFT_TEXT) { + switch (n->type) { + case ROFFT_EQN: + case ROFFT_TBL: + mandoc_msg(n->type == ROFFT_EQN ? MANDOCERR_EQN_TMAN : + MANDOCERR_TBL_TMAN, n->line, n->pos, NULL); + outflags |= MMAN_PP | MMAN_sp | MMAN_nl; + print_word("The"); + print_line(".B \\-T man", MMAN_nl); + print_word("output mode does not support"); + print_word(n->type == ROFFT_EQN ? "eqn(7)" : "tbl(7)"); + print_word("input."); + outflags |= MMAN_PP | MMAN_sp | MMAN_nl; + return; + case ROFFT_TEXT: /* * Make sure that we don't happen to start with a * control character at the start of a line. @@ -677,19 +693,18 @@ print_node(DECL_ARGS) outflags &= ~(MMAN_spc | MMAN_spc_force); else if (outflags & MMAN_Sm) outflags |= MMAN_spc; - } else if (n->tok < ROFF_MAX) { - (*roff_man_acts[n->tok])(meta, n); - return; - } else { - /* - * Conditionally run the pre-node action handler for a - * node. - */ + break; + default: + if (n->tok < ROFF_MAX) { + (*roff_man_acts[n->tok])(meta, n); + return; + } act = mdoc_man_act(n->tok); cond = act->cond == NULL || (*act->cond)(meta, n); if (cond && act->pre != NULL && (n->end == ENDBODY_NOT || n->child != NULL)) do_sub = (*act->pre)(meta, n); + break; } /* @@ -776,13 +791,20 @@ post_font(DECL_ARGS) static void post_percent(DECL_ARGS) { + struct roff_node *np, *nn, *nnn; if (mdoc_man_act(n->tok)->pre == pre_em) font_pop(); - if (n->next) { - print_word(","); - if (n->prev && n->prev->tok == n->tok && - n->next->tok == n->tok) + + if ((nn = roff_node_next(n)) != NULL) { + np = roff_node_prev(n); + nnn = nn == NULL ? NULL : roff_node_next(nn); + if (nn->tok != n->tok || + (np != NULL && np->tok == n->tok) || + (nnn != NULL && nnn->tok == n->tok)) + print_word(","); + if (nn->tok == n->tok && + (nnn == NULL || nnn->tok != n->tok)) print_word("and"); } else { print_word("."); @@ -850,13 +872,15 @@ post_sect(DECL_ARGS) /* See mdoc_term.c, synopsis_pre() for comments. */ static void -pre_syn(const struct roff_node *n) +pre_syn(struct roff_node *n) { + struct roff_node *np; - if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; - if (n->prev->tok == n->tok && + if (np->tok == n->tok && MDOC_Ft != n->tok && MDOC_Fo != n->tok && MDOC_Fn != n->tok) { @@ -864,7 +888,7 @@ pre_syn(const struct roff_node *n) return; } - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -940,11 +964,10 @@ static int pre_bd(DECL_ARGS) { outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br); - - if (DISP_unfilled == n->norm->Bd.type || - DISP_literal == n->norm->Bd.type) + if (n->norm->Bd.type == DISP_unfilled || + n->norm->Bd.type == DISP_literal) print_line(".nf", 0); - if (0 == n->norm->Bd.comp && NULL != n->parent->prev) + if (n->norm->Bd.comp == 0 && roff_node_prev(n->parent) != NULL) outflags |= MMAN_sp; print_offs(n->norm->Bd.offs, 1); return 1; @@ -976,7 +999,7 @@ post_bd(DECL_ARGS) } /* Maybe we are inside an enclosing list? */ - if (NULL != n->parent->next) + if (roff_node_next(n->parent) != NULL) mid_it(); } @@ -1101,16 +1124,15 @@ post_bl(DECL_ARGS) print_line(".RE", MMAN_nl); assert(Bl_stack_len); Bl_stack_len--; - assert(0 == Bl_stack[Bl_stack_len]); + assert(Bl_stack[Bl_stack_len] == 0); } else { outflags |= MMAN_PP | MMAN_nl; outflags &= ~(MMAN_sp | MMAN_br); } /* Maybe we are inside an enclosing list? */ - if (NULL != n->parent->next) + if (roff_node_next(n->parent) != NULL) mid_it(); - } static void @@ -1122,7 +1144,6 @@ pre_br(DECL_ARGS) static int pre_dl(DECL_ARGS) { - print_offs("6n", 0); return 1; } @@ -1130,11 +1151,10 @@ pre_dl(DECL_ARGS) static void post_dl(DECL_ARGS) { - print_line(".RE", MMAN_nl); /* Maybe we are inside an enclosing list? */ - if (NULL != n->parent->next) + if (roff_node_next(n->parent) != NULL) mid_it(); } @@ -1235,15 +1255,15 @@ pre_fa(DECL_ARGS) static void post_fa(DECL_ARGS) { + struct roff_node *nn; - if (NULL != n->next && MDOC_Fa == n->next->tok) + if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa) print_word(","); } static int pre_fd(DECL_ARGS) { - pre_syn(n); font_push('B'); return 1; @@ -1252,7 +1272,6 @@ pre_fd(DECL_ARGS) static void post_fd(DECL_ARGS) { - font_pop(); outflags |= MMAN_br; } @@ -1260,7 +1279,6 @@ post_fd(DECL_ARGS) static int pre_fl(DECL_ARGS) { - font_push('B'); print_word("\\-"); if (n->child != NULL) @@ -1271,12 +1289,13 @@ pre_fl(DECL_ARGS) static void post_fl(DECL_ARGS) { + struct roff_node *nn; font_pop(); - if (!(n->child != NULL || - n->next == NULL || - n->next->type == ROFFT_TEXT || - n->next->flags & NODE_LINE)) + if (n->child == NULL && + ((nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && + (nn->flags & NODE_LINE) == 0)) outflags &= ~MMAN_spc; } @@ -1419,9 +1438,9 @@ pre_it(DECL_ARGS) case ROFFT_HEAD: outflags |= MMAN_PP | MMAN_nl; bln = n->parent->parent; - if (0 == bln->norm->Bl.comp || - (NULL == n->parent->prev && - NULL == bln->parent->prev)) + if (bln->norm->Bl.comp == 0 || + (n->parent->prev == NULL && + roff_node_prev(bln->parent) == NULL)) outflags |= MMAN_sp; outflags &= ~MMAN_br; switch (bln->norm->Bl.type) { @@ -1633,17 +1652,22 @@ pre_nm(DECL_ARGS) { char *name; - if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_BLOCK: outflags |= MMAN_Bk; pre_syn(n); - } - if (n->type != ROFFT_ELEM && n->type != ROFFT_HEAD) return 1; + case ROFFT_HEAD: + case ROFFT_ELEM: + break; + default: + return 1; + } name = n->child == NULL ? NULL : n->child->string; - if (NULL == name) + if (name == NULL) return 0; if (n->type == ROFFT_HEAD) { - if (NULL == n->parent->prev) + if (roff_node_prev(n->parent) == NULL) outflags |= MMAN_sp; print_block(".HP", 0); printf(" %dn", man_strlen(name) + 1); diff --git a/mdoc_markdown.c b/mdoc_markdown.c index 88e37c0b188b..63d8e1705580 100644 --- a/mdoc_markdown.c +++ b/mdoc_markdown.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_markdown.c,v 1.31 2019/07/01 22:56:24 schwarze Exp $ */ +/* $Id: mdoc_markdown.c,v 1.37 2021/08/10 12:55:03 schwarze Exp $ */ /* - * Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,7 +13,11 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Markdown formatter for mdoc(7) used by mandoc(1). */ +#include "config.h" + #include <sys/types.h> #include <assert.h> @@ -29,16 +33,16 @@ #include "main.h" struct md_act { - int (*cond)(struct roff_node *n); - int (*pre)(struct roff_node *n); - void (*post)(struct roff_node *n); + int (*cond)(struct roff_node *); + int (*pre)(struct roff_node *); + void (*post)(struct roff_node *); const char *prefix; /* pre-node string constant */ const char *suffix; /* post-node string constant */ }; static void md_nodelist(struct roff_node *); static void md_node(struct roff_node *); -static const char *md_stack(char c); +static const char *md_stack(char); static void md_preword(void); static void md_rawword(const char *); static void md_word(const char *); @@ -226,6 +230,7 @@ static const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = { { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */ { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */ { NULL, NULL, NULL, NULL, NULL }, /* Ta */ + { NULL, md_pre_skip, NULL, NULL, NULL }, /* Tg */ }; static const struct md_act *md_act(enum roff_tok); @@ -309,7 +314,9 @@ md_node(struct roff_node *n) if (outflags & MD_nonl) outflags &= ~(MD_nl | MD_sp); - else if (outflags & MD_spc && n->flags & NODE_LINE) + else if (outflags & MD_spc && + n->flags & NODE_LINE && + !roff_node_transparent(n)) outflags |= MD_nl; act = NULL; @@ -596,16 +603,18 @@ md_word(const char *s) md_rawword("markdown"); continue; case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: nextfont = "**"; break; case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: nextfont = "*"; break; case ESCAPE_FONTBI: nextfont = "***"; break; case ESCAPE_FONT: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: case ESCAPE_FONTROMAN: nextfont = ""; break; @@ -786,14 +795,17 @@ md_post_word(struct roff_node *n) static void md_post_pc(struct roff_node *n) { + struct roff_node *nn; + md_post_raw(n); if (n->parent->tok != MDOC_Rs) return; - if (n->next != NULL) { + + if ((nn = roff_node_next(n)) != NULL) { md_word(","); - if (n->prev != NULL && - n->prev->tok == n->tok && - n->next->tok == n->tok) + if (nn->tok == n->tok && + (nn = roff_node_prev(n)) != NULL && + nn->tok == n->tok) md_word("and"); } else { md_word("."); @@ -810,10 +822,13 @@ md_pre_skip(struct roff_node *n) static void md_pre_syn(struct roff_node *n) { - if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY)) + struct roff_node *np; + + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; - if (n->prev->tok == n->tok && + if (np->tok == n->tok && n->tok != MDOC_Ft && n->tok != MDOC_Fo && n->tok != MDOC_Fn) { @@ -821,7 +836,7 @@ md_pre_syn(struct roff_node *n) return; } - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -1052,7 +1067,9 @@ md_pre_Fa(struct roff_node *n) static void md_post_Fa(struct roff_node *n) { - if (n->next != NULL && n->next->tok == MDOC_Fa) + struct roff_node *nn; + + if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa) md_word(","); } @@ -1074,9 +1091,11 @@ md_post_Fd(struct roff_node *n) static void md_post_Fl(struct roff_node *n) { + struct roff_node *nn; + md_post_raw(n); - if (n->child == NULL && n->next != NULL && - n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE)) + if (n->child == NULL && (nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && (nn->flags & NODE_LINE) == 0) outflags &= ~MD_spc; } diff --git a/mdoc_state.c b/mdoc_state.c index f9a585e73623..d696ff27e06c 100644 --- a/mdoc_state.c +++ b/mdoc_state.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_state.c,v 1.15 2019/01/01 07:42:04 schwarze Exp $ */ +/* $Id: mdoc_state.c,v 1.17 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <assert.h> @@ -157,6 +159,7 @@ static const state_handler state_handlers[MDOC_MAX - MDOC_Dd] = { NULL, /* %Q */ NULL, /* %U */ NULL, /* Ta */ + NULL, /* Tg */ }; diff --git a/mdoc_term.c b/mdoc_term.c index 18f0ff09e2c7..42392c7c0a59 100644 --- a/mdoc_term.c +++ b/mdoc_term.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_term.c,v 1.374 2019/06/27 12:20:18 schwarze Exp $ */ +/* $Id: mdoc_term.c,v 1.380 2020/04/06 10:16:17 schwarze Exp $ */ /* + * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Plain text formatter for mdoc(7), used by mandoc(1) + * for ASCII, UTF-8, PostScript, and PDF output. */ #include "config.h" @@ -33,7 +36,7 @@ #include "mdoc.h" #include "out.h" #include "term.h" -#include "tag.h" +#include "term_tag.h" #include "main.h" struct termpair { @@ -54,14 +57,12 @@ struct mdoc_term_act { static int a2width(const struct termp *, const char *); static void print_bvspace(struct termp *, - const struct roff_node *, - const struct roff_node *); + struct roff_node *, struct roff_node *); static void print_mdoc_node(DECL_ARGS); static void print_mdoc_nodelist(DECL_ARGS); static void print_mdoc_head(struct termp *, const struct roff_meta *); static void print_mdoc_foot(struct termp *, const struct roff_meta *); -static void synopsis_pre(struct termp *, - const struct roff_node *); +static void synopsis_pre(struct termp *, struct roff_node *); static void termp____post(DECL_ARGS); static void termp__t_post(DECL_ARGS); @@ -91,11 +92,8 @@ static int termp_bf_pre(DECL_ARGS); static int termp_bk_pre(DECL_ARGS); static int termp_bl_pre(DECL_ARGS); static int termp_bold_pre(DECL_ARGS); -static int termp_cd_pre(DECL_ARGS); static int termp_d1_pre(DECL_ARGS); static int termp_eo_pre(DECL_ARGS); -static int termp_em_pre(DECL_ARGS); -static int termp_er_pre(DECL_ARGS); static int termp_ex_pre(DECL_ARGS); static int termp_fa_pre(DECL_ARGS); static int termp_fd_pre(DECL_ARGS); @@ -117,8 +115,6 @@ static int termp_skip_pre(DECL_ARGS); static int termp_sm_pre(DECL_ARGS); static int termp_pp_pre(DECL_ARGS); static int termp_ss_pre(DECL_ARGS); -static int termp_sy_pre(DECL_ARGS); -static int termp_tag_pre(DECL_ARGS); static int termp_under_pre(DECL_ARGS); static int termp_vt_pre(DECL_ARGS); static int termp_xr_pre(DECL_ARGS); @@ -142,11 +138,11 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { termp_an_pre, NULL }, /* An */ { termp_ap_pre, NULL }, /* Ap */ { termp_under_pre, NULL }, /* Ar */ - { termp_cd_pre, NULL }, /* Cd */ + { termp_fd_pre, NULL }, /* Cd */ { termp_bold_pre, NULL }, /* Cm */ { termp_li_pre, NULL }, /* Dv */ - { termp_er_pre, NULL }, /* Er */ - { termp_tag_pre, NULL }, /* Ev */ + { NULL, NULL }, /* Er */ + { NULL, NULL }, /* Ev */ { termp_ex_pre, NULL }, /* Ex */ { termp_fa_pre, NULL }, /* Fa */ { termp_fd_pre, termp_fd_post }, /* Fd */ @@ -193,7 +189,7 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { termp_quote_pre, termp_quote_post }, /* Dq */ { NULL, NULL }, /* Ec */ /* FIXME: no space */ { NULL, NULL }, /* Ef */ - { termp_em_pre, NULL }, /* Em */ + { termp_under_pre, NULL }, /* Em */ { termp_eo_pre, termp_eo_post }, /* Eo */ { termp_xx_pre, termp_xx_post }, /* Fx */ { termp_bold_pre, NULL }, /* Ms */ @@ -216,7 +212,7 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { termp_quote_pre, termp_quote_post }, /* Sq */ { termp_sm_pre, NULL }, /* Sm */ { termp_under_pre, NULL }, /* Sx */ - { termp_sy_pre, NULL }, /* Sy */ + { termp_bold_pre, NULL }, /* Sy */ { NULL, NULL }, /* Tn */ { termp_xx_pre, termp_xx_post }, /* Ux */ { NULL, NULL }, /* Xc */ @@ -245,10 +241,9 @@ static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = { { NULL, termp____post }, /* %Q */ { NULL, termp____post }, /* %U */ { NULL, NULL }, /* Ta */ + { termp_skip_pre, NULL }, /* Tg */ }; -static int fn_prio; - void terminal_mdoc(void *arg, const struct roff_meta *mdoc) @@ -301,7 +296,6 @@ terminal_mdoc(void *arg, const struct roff_meta *mdoc) static void print_mdoc_nodelist(DECL_ARGS) { - while (n != NULL) { print_mdoc_node(p, pair, meta, n); n = n->next; @@ -341,6 +335,10 @@ print_mdoc_node(DECL_ARGS) memset(&npair, 0, sizeof(struct termpair)); npair.ppair = pair; + if (n->flags & NODE_ID && n->tok != MDOC_Pp && + (n->tok != MDOC_It || n->type != ROFFT_BLOCK)) + term_tag_write(n, p->line); + /* * Keeps only work until the end of a line. If a keep was * invoked in a prior line, revert it to PREKEEP. @@ -580,29 +578,20 @@ a2width(const struct termp *p, const char *v) * too. */ static void -print_bvspace(struct termp *p, - const struct roff_node *bl, - const struct roff_node *n) +print_bvspace(struct termp *p, struct roff_node *bl, struct roff_node *n) { - const struct roff_node *nn; - - assert(n); + struct roff_node *nn; term_newln(p); - if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) - return; - if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) + if ((bl->tok == MDOC_Bd && bl->norm->Bd.comp) || + (bl->tok == MDOC_Bl && bl->norm->Bl.comp)) return; /* Do not vspace directly after Ss/Sh. */ nn = n; - while (nn->prev != NULL && - (nn->prev->type == ROFFT_COMMENT || - nn->prev->flags & NODE_NOPRT)) - nn = nn->prev; - while (nn->prev == NULL) { + while (roff_node_prev(nn) == NULL) { do { nn = nn->parent; if (nn->type == ROFFT_ROOT) @@ -615,22 +604,18 @@ print_bvspace(struct termp *p, break; } - /* A `-column' does not assert vspace within the list. */ - - if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) - if (n->prev && MDOC_It == n->prev->tok) - return; - - /* A `-diag' without body does not vspace. */ - - if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) - if (n->prev && MDOC_It == n->prev->tok) { - assert(n->prev->body); - if (NULL == n->prev->body->child) - return; - } + /* + * No vertical space after: + * items in .Bl -column + * items without a body in .Bl -diag + */ - term_vspace(p); + if (bl->tok != MDOC_Bl || + n->prev == NULL || n->prev->tok != MDOC_It || + (bl->norm->Bl.type != LIST_column && + (bl->norm->Bl.type != LIST_diag || + n->prev->body->child != NULL))) + term_vspace(p); } @@ -646,6 +631,8 @@ termp_it_pre(DECL_ARGS) if (n->type == ROFFT_BLOCK) { print_bvspace(p, n->parent->parent, n); + if (n->flags & NODE_ID) + term_tag_write(n, p->line); return 1; } @@ -1018,38 +1005,44 @@ termp_nm_pre(DECL_ARGS) p->flags |= TERMP_HANG; } } - - term_fontpush(p, TERMFONT_BOLD); - return 1; + return termp_bold_pre(p, pair, meta, n); } static void termp_nm_post(DECL_ARGS) { - - if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_BLOCK: p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); - } else if (n->type == ROFFT_HEAD && - NULL != n->next && NULL != n->next->child) { + break; + case ROFFT_HEAD: + if (n->next == NULL || n->next->child == NULL) + break; term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); p->trailspace = 0; - } else if (n->type == ROFFT_BODY && n->child != NULL) - term_flushln(p); + break; + case ROFFT_BODY: + if (n->child != NULL) + term_flushln(p); + break; + default: + break; + } } static int termp_fl_pre(DECL_ARGS) { + struct roff_node *nn; - termp_tag_pre(p, pair, meta, n); term_fontpush(p, TERMFONT_BOLD); term_word(p, "\\-"); - if (!(n->child == NULL && - (n->next == NULL || - n->next->type == ROFFT_TEXT || - n->next->flags & NODE_LINE))) + if (n->child != NULL || + ((nn = roff_node_next(n)) != NULL && + nn->type != ROFFT_TEXT && + (nn->flags & NODE_LINE) == 0)) p->flags |= TERMP_NOSPACE; return 1; @@ -1058,10 +1051,11 @@ termp_fl_pre(DECL_ARGS) static int termp__a_pre(DECL_ARGS) { + struct roff_node *nn; - if (n->prev && MDOC__A == n->prev->tok) - if (NULL == n->next || MDOC__A != n->next->tok) - term_word(p, "and"); + if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A)) + term_word(p, "and"); return 1; } @@ -1102,10 +1096,9 @@ termp_ns_pre(DECL_ARGS) static int termp_rs_pre(DECL_ARGS) { - if (SEC_SEE_ALSO != n->sec) return 1; - if (n->type == ROFFT_BLOCK && n->prev != NULL) + if (n->type == ROFFT_BLOCK && roff_node_prev(n) != NULL) term_vspace(p); return 1; } @@ -1120,7 +1113,6 @@ termp_ex_pre(DECL_ARGS) static int termp_nd_pre(DECL_ARGS) { - if (n->type == ROFFT_BODY) term_word(p, "\\(en"); return 1; @@ -1129,14 +1121,20 @@ termp_nd_pre(DECL_ARGS) static int termp_bl_pre(DECL_ARGS) { - - return n->type != ROFFT_HEAD; + switch (n->type) { + case ROFFT_BLOCK: + term_newln(p); + return 1; + case ROFFT_HEAD: + return 0; + default: + return 1; + } } static void termp_bl_post(DECL_ARGS) { - if (n->type != ROFFT_BLOCK) return; term_newln(p); @@ -1150,7 +1148,6 @@ termp_bl_post(DECL_ARGS) static int termp_xr_pre(DECL_ARGS) { - if (NULL == (n = n->child)) return 0; @@ -1179,13 +1176,12 @@ termp_xr_pre(DECL_ARGS) * macro combos). */ static void -synopsis_pre(struct termp *p, const struct roff_node *n) +synopsis_pre(struct termp *p, struct roff_node *n) { - /* - * Obviously, if we're not in a SYNOPSIS or no prior macros - * exist, do nothing. - */ - if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags)) + struct roff_node *np; + + if ((n->flags & NODE_SYNPRETTY) == 0 || + (np = roff_node_prev(n)) == NULL) return; /* @@ -1193,7 +1189,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n) * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which * case we soldier on. */ - if (n->prev->tok == n->tok && + if (np->tok == n->tok && MDOC_Ft != n->tok && MDOC_Fo != n->tok && MDOC_Fn != n->tok) { @@ -1206,7 +1202,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n) * another (or Fn/Fo, which we've let slip through) then assert * vertical space, else only newline and move on. */ - switch (n->prev->tok) { + switch (np->tok) { case MDOC_Fd: case MDOC_Fn: case MDOC_Fo: @@ -1215,7 +1211,7 @@ synopsis_pre(struct termp *p, const struct roff_node *n) term_vspace(p); break; case MDOC_Ft: - if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { + if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) { term_vspace(p); break; } @@ -1229,24 +1225,22 @@ synopsis_pre(struct termp *p, const struct roff_node *n) static int termp_vt_pre(DECL_ARGS) { - - if (n->type == ROFFT_ELEM) { - synopsis_pre(p, n); - return termp_under_pre(p, pair, meta, n); - } else if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_ELEM: + return termp_ft_pre(p, pair, meta, n); + case ROFFT_BLOCK: synopsis_pre(p, n); return 1; - } else if (n->type == ROFFT_HEAD) + case ROFFT_HEAD: return 0; - - return termp_under_pre(p, pair, meta, n); + default: + return termp_under_pre(p, pair, meta, n); + } } static int termp_bold_pre(DECL_ARGS) { - - termp_tag_pre(p, pair, meta, n); term_fontpush(p, TERMFONT_BOLD); return 1; } @@ -1254,7 +1248,6 @@ termp_bold_pre(DECL_ARGS) static int termp_fd_pre(DECL_ARGS) { - synopsis_pre(p, n); return termp_bold_pre(p, pair, meta, n); } @@ -1262,13 +1255,13 @@ termp_fd_pre(DECL_ARGS) static void termp_fd_post(DECL_ARGS) { - term_newln(p); } static int termp_sh_pre(DECL_ARGS) { + struct roff_node *np; switch (n->type) { case ROFFT_BLOCK: @@ -1276,30 +1269,20 @@ termp_sh_pre(DECL_ARGS) * Vertical space before sections, except * when the previous section was empty. */ - if (n->prev == NULL || - n->prev->tok != MDOC_Sh || - (n->prev->body != NULL && - n->prev->body->child != NULL)) + if ((np = roff_node_prev(n)) == NULL || + np->tok != MDOC_Sh || + (np->body != NULL && np->body->child != NULL)) term_vspace(p); break; case ROFFT_HEAD: - term_fontpush(p, TERMFONT_BOLD); - break; + return termp_bold_pre(p, pair, meta, n); case ROFFT_BODY: p->tcol->offset = term_len(p, p->defindent); term_tab_set(p, NULL); term_tab_set(p, "T"); term_tab_set(p, ".5i"); - switch (n->sec) { - case SEC_DESCRIPTION: - fn_prio = 0; - break; - case SEC_AUTHORS: + if (n->sec == SEC_AUTHORS) p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT); - break; - default: - break; - } break; default: break; @@ -1310,7 +1293,6 @@ termp_sh_pre(DECL_ARGS) static void termp_sh_post(DECL_ARGS) { - switch (n->type) { case ROFFT_HEAD: term_newln(p); @@ -1327,15 +1309,13 @@ termp_sh_post(DECL_ARGS) static void termp_lb_post(DECL_ARGS) { - - if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags) + if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE) term_newln(p); } static int termp_d1_pre(DECL_ARGS) { - if (n->type != ROFFT_BLOCK) return 1; term_newln(p); @@ -1349,11 +1329,8 @@ termp_d1_pre(DECL_ARGS) static int termp_ft_pre(DECL_ARGS) { - - /* NB: NODE_LINE does not effect this! */ synopsis_pre(p, n); - term_fontpush(p, TERMFONT_UNDER); - return 1; + return termp_under_pre(p, pair, meta, n); } static int @@ -1362,11 +1339,9 @@ termp_fn_pre(DECL_ARGS) size_t rmargin = 0; int pretty; - pretty = NODE_SYNPRETTY & n->flags; - synopsis_pre(p, n); - - if (NULL == (n = n->child)) + pretty = n->flags & NODE_SYNPRETTY; + if ((n = n->child) == NULL) return 0; if (pretty) { @@ -1380,9 +1355,6 @@ termp_fn_pre(DECL_ARGS) term_word(p, n->string); term_fontpop(p); - if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM) - tag_put(n->string, ++fn_prio, p->line); - if (pretty) { term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); @@ -1417,7 +1389,6 @@ termp_fn_pre(DECL_ARGS) term_word(p, ";"); term_flushln(p); } - return 0; } @@ -1426,23 +1397,25 @@ termp_fa_pre(DECL_ARGS) { const struct roff_node *nn; - if (n->parent->tok != MDOC_Fo) { - term_fontpush(p, TERMFONT_UNDER); - return 1; - } + if (n->parent->tok != MDOC_Fo) + return termp_under_pre(p, pair, meta, n); - for (nn = n->child; nn; nn = nn->next) { + for (nn = n->child; nn != NULL; nn = nn->next) { term_fontpush(p, TERMFONT_UNDER); p->flags |= TERMP_NBRWORD; term_word(p, nn->string); term_fontpop(p); - - if (nn->next || (n->next && n->next->tok == MDOC_Fa)) { + if (nn->next != NULL) { p->flags |= TERMP_NOSPACE; term_word(p, ","); } } - + if (n->child != NULL && + (nn = roff_node_next(n)) != NULL && + nn->tok == MDOC_Fa) { + p->flags |= TERMP_NOSPACE; + term_word(p, ","); + } return 0; } @@ -1522,30 +1495,23 @@ termp_xx_post(DECL_ARGS) static void termp_pf_post(DECL_ARGS) { - - if ( ! (n->next == NULL || n->next->flags & NODE_LINE)) + if (n->next != NULL && (n->next->flags & NODE_LINE) == 0) p->flags |= TERMP_NOSPACE; } static int termp_ss_pre(DECL_ARGS) { - struct roff_node *nn; - switch (n->type) { case ROFFT_BLOCK: - term_newln(p); - for (nn = n->prev; nn != NULL; nn = nn->prev) - if (nn->type != ROFFT_COMMENT && - (nn->flags & NODE_NOPRT) == 0) - break; - if (nn != NULL) + if (roff_node_prev(n) == NULL) + term_newln(p); + else term_vspace(p); break; case ROFFT_HEAD: - term_fontpush(p, TERMFONT_BOLD); p->tcol->offset = term_len(p, (p->defindent+1)/2); - break; + return termp_bold_pre(p, pair, meta, n); case ROFFT_BODY: p->tcol->offset = term_len(p, p->defindent); term_tab_set(p, NULL); @@ -1555,34 +1521,21 @@ termp_ss_pre(DECL_ARGS) default: break; } - return 1; } static void termp_ss_post(DECL_ARGS) { - if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY) term_newln(p); } static int -termp_cd_pre(DECL_ARGS) -{ - - synopsis_pre(p, n); - term_fontpush(p, TERMFONT_BOLD); - return 1; -} - -static int termp_in_pre(DECL_ARGS) { - synopsis_pre(p, n); - - if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) { + if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) { term_fontpush(p, TERMFONT_BOLD); term_word(p, "#include"); term_word(p, "<"); @@ -1590,7 +1543,6 @@ termp_in_pre(DECL_ARGS) term_word(p, "<"); term_fontpush(p, TERMFONT_UNDER); } - p->flags |= TERMP_NOSPACE; return 1; } @@ -1598,36 +1550,32 @@ termp_in_pre(DECL_ARGS) static void termp_in_post(DECL_ARGS) { - - if (NODE_SYNPRETTY & n->flags) + if (n->flags & NODE_SYNPRETTY) term_fontpush(p, TERMFONT_BOLD); - p->flags |= TERMP_NOSPACE; term_word(p, ">"); - - if (NODE_SYNPRETTY & n->flags) + if (n->flags & NODE_SYNPRETTY) term_fontpop(p); } static int termp_pp_pre(DECL_ARGS) { - fn_prio = 0; term_vspace(p); + if (n->flags & NODE_ID) + term_tag_write(n, p->line); return 0; } static int termp_skip_pre(DECL_ARGS) { - return 0; } static int termp_quote_pre(DECL_ARGS) { - if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM) return 1; @@ -1784,17 +1732,15 @@ termp_eo_post(DECL_ARGS) static int termp_fo_pre(DECL_ARGS) { - size_t rmargin = 0; - int pretty; - - pretty = NODE_SYNPRETTY & n->flags; + size_t rmargin; - if (n->type == ROFFT_BLOCK) { + switch (n->type) { + case ROFFT_BLOCK: synopsis_pre(p, n); return 1; - } else if (n->type == ROFFT_BODY) { - if (pretty) { - rmargin = p->tcol->rmargin; + case ROFFT_BODY: + rmargin = p->tcol->rmargin; + if (n->flags & NODE_SYNPRETTY) { p->tcol->rmargin = p->tcol->offset + term_len(p, 4); p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; @@ -1802,7 +1748,7 @@ termp_fo_pre(DECL_ARGS) p->flags |= TERMP_NOSPACE; term_word(p, "("); p->flags |= TERMP_NOSPACE; - if (pretty) { + if (n->flags & NODE_SYNPRETTY) { term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); @@ -1811,30 +1757,21 @@ termp_fo_pre(DECL_ARGS) p->tcol->rmargin = rmargin; } return 1; + default: + return termp_bold_pre(p, pair, meta, n); } - - if (NULL == n->child) - return 0; - - /* XXX: we drop non-initial arguments as per groff. */ - - assert(n->child->string); - term_fontpush(p, TERMFONT_BOLD); - term_word(p, n->child->string); - return 0; } static void termp_fo_post(DECL_ARGS) { - if (n->type != ROFFT_BODY) return; p->flags |= TERMP_NOSPACE; term_word(p, ")"); - if (NODE_SYNPRETTY & n->flags) { + if (n->flags & NODE_SYNPRETTY) { p->flags |= TERMP_NOSPACE; term_word(p, ";"); term_flushln(p); @@ -1844,29 +1781,30 @@ termp_fo_post(DECL_ARGS) static int termp_bf_pre(DECL_ARGS) { - - if (n->type == ROFFT_HEAD) + switch (n->type) { + case ROFFT_HEAD: return 0; - else if (n->type != ROFFT_BODY) + case ROFFT_BODY: + break; + default: return 1; - - if (FONT_Em == n->norm->Bf.font) - term_fontpush(p, TERMFONT_UNDER); - else if (FONT_Sy == n->norm->Bf.font) - term_fontpush(p, TERMFONT_BOLD); - else - term_fontpush(p, TERMFONT_NONE); - - return 1; + } + switch (n->norm->Bf.font) { + case FONT_Em: + return termp_under_pre(p, pair, meta, n); + case FONT_Sy: + return termp_bold_pre(p, pair, meta, n); + default: + return termp_li_pre(p, pair, meta, n); + } } static int termp_sm_pre(DECL_ARGS) { - - if (NULL == n->child) + if (n->child == NULL) p->flags ^= TERMP_NONOSPACE; - else if (0 == strcmp("on", n->child->string)) + else if (strcmp(n->child->string, "on") == 0) p->flags &= ~TERMP_NONOSPACE; else p->flags |= TERMP_NONOSPACE; @@ -1880,7 +1818,6 @@ termp_sm_pre(DECL_ARGS) static int termp_ap_pre(DECL_ARGS) { - p->flags |= TERMP_NOSPACE; term_word(p, "'"); p->flags |= TERMP_NOSPACE; @@ -1890,24 +1827,26 @@ termp_ap_pre(DECL_ARGS) static void termp____post(DECL_ARGS) { + struct roff_node *nn; /* * Handle lists of authors. In general, print each followed by * a comma. Don't print the comma if there are only two * authors. */ - if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) - if (NULL == n->next->next || MDOC__A != n->next->next->tok) - if (NULL == n->prev || MDOC__A != n->prev->tok) - return; + if (n->tok == MDOC__A && + (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A && + ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) && + ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A)) + return; /* TODO: %U. */ - if (NULL == n->parent || MDOC_Rs != n->parent->tok) + if (n->parent == NULL || n->parent->tok != MDOC_Rs) return; p->flags |= TERMP_NOSPACE; - if (NULL == n->next) { + if (roff_node_next(n) == NULL) { term_word(p, "."); p->flags |= TERMP_SENTENCE; } else @@ -1917,8 +1856,6 @@ termp____post(DECL_ARGS) static int termp_li_pre(DECL_ARGS) { - - termp_tag_pre(p, pair, meta, n); term_fontpush(p, TERMFONT_NONE); return 1; } @@ -1968,7 +1905,6 @@ termp_lk_pre(DECL_ARGS) static int termp_bk_pre(DECL_ARGS) { - switch (n->type) { case ROFFT_BLOCK: break; @@ -1981,107 +1917,47 @@ termp_bk_pre(DECL_ARGS) default: abort(); } - return 1; } static void termp_bk_post(DECL_ARGS) { - if (n->type == ROFFT_BODY) p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); } +/* + * If we are in an `Rs' and there is a journal present, + * then quote us instead of underlining us (for disambiguation). + */ static void termp__t_post(DECL_ARGS) { - - /* - * If we're in an `Rs' and there's a journal present, then quote - * us instead of underlining us (for disambiguation). - */ - if (n->parent && MDOC_Rs == n->parent->tok && + if (n->parent != NULL && n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) termp_quote_post(p, pair, meta, n); - termp____post(p, pair, meta, n); } static int termp__t_pre(DECL_ARGS) { - - /* - * If we're in an `Rs' and there's a journal present, then quote - * us instead of underlining us (for disambiguation). - */ - if (n->parent && MDOC_Rs == n->parent->tok && + if (n->parent != NULL && n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) return termp_quote_pre(p, pair, meta, n); - - term_fontpush(p, TERMFONT_UNDER); - return 1; + else + return termp_under_pre(p, pair, meta, n); } static int termp_under_pre(DECL_ARGS) { - term_fontpush(p, TERMFONT_UNDER); return 1; } static int -termp_em_pre(DECL_ARGS) -{ - if (n->child != NULL && - n->child->type == ROFFT_TEXT) - tag_put(n->child->string, 0, p->line); - term_fontpush(p, TERMFONT_UNDER); - return 1; -} - -static int -termp_sy_pre(DECL_ARGS) -{ - if (n->child != NULL && - n->child->type == ROFFT_TEXT) - tag_put(n->child->string, 0, p->line); - term_fontpush(p, TERMFONT_BOLD); - return 1; -} - -static int -termp_er_pre(DECL_ARGS) -{ - - if (n->sec == SEC_ERRORS && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Bq && - n->parent->parent->parent->tok == MDOC_It))) - tag_put(n->child->string, 1, p->line); - return 1; -} - -static int -termp_tag_pre(DECL_ARGS) -{ - - if (n->child != NULL && - n->child->type == ROFFT_TEXT && - (n->prev == NULL || - (n->prev->type == ROFFT_TEXT && - strcmp(n->prev->string, "|") == 0)) && - (n->parent->tok == MDOC_It || - (n->parent->tok == MDOC_Xo && - n->parent->parent->prev == NULL && - n->parent->parent->parent->tok == MDOC_It))) - tag_put(n->child->string, 1, p->line); - return 1; -} - -static int termp_abort_pre(DECL_ARGS) { abort(); diff --git a/mdoc_validate.c b/mdoc_validate.c index 11cdf00b56cb..e1cd3ae1edcb 100644 --- a/mdoc_validate.c +++ b/mdoc_validate.c @@ -1,7 +1,7 @@ -/* $Id: mdoc_validate.c,v 1.374 2019/06/27 15:07:30 schwarze Exp $ */ +/* $Id: mdoc_validate.c,v 1.389 2021/07/18 11:41:23 schwarze Exp $ */ /* + * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Validation module for mdoc(7) syntax trees used by mandoc(1). */ #include "config.h" @@ -39,6 +41,7 @@ #include "libmandoc.h" #include "roff_int.h" #include "libmdoc.h" +#include "tag.h" /* FIXME: .Bl -diag can't have non-text children in HEAD. */ @@ -82,16 +85,18 @@ static void post_dd(POST_ARGS); static void post_delim(POST_ARGS); static void post_delim_nb(POST_ARGS); static void post_dt(POST_ARGS); +static void post_em(POST_ARGS); static void post_en(POST_ARGS); +static void post_er(POST_ARGS); static void post_es(POST_ARGS); static void post_eoln(POST_ARGS); static void post_ex(POST_ARGS); static void post_fa(POST_ARGS); +static void post_fl(POST_ARGS); static void post_fn(POST_ARGS); static void post_fname(POST_ARGS); static void post_fo(POST_ARGS); static void post_hyph(POST_ARGS); -static void post_ignpar(POST_ARGS); static void post_it(POST_ARGS); static void post_lb(POST_ARGS); static void post_nd(POST_ARGS); @@ -104,6 +109,7 @@ static void post_prevpar(POST_ARGS); static void post_root(POST_ARGS); static void post_rs(POST_ARGS); static void post_rv(POST_ARGS); +static void post_section(POST_ARGS); static void post_sh(POST_ARGS); static void post_sh_head(POST_ARGS); static void post_sh_name(POST_ARGS); @@ -113,6 +119,8 @@ static void post_sm(POST_ARGS); static void post_st(POST_ARGS); static void post_std(POST_ARGS); static void post_sx(POST_ARGS); +static void post_tag(POST_ARGS); +static void post_tg(POST_ARGS); static void post_useless(POST_ARGS); static void post_xr(POST_ARGS); static void post_xx(POST_ARGS); @@ -122,7 +130,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_dt, /* Dt */ post_os, /* Os */ post_sh, /* Sh */ - post_ignpar, /* Ss */ + post_section, /* Ss */ post_par, /* Pp */ post_display, /* D1 */ post_display, /* Dl */ @@ -136,19 +144,19 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* Ap */ post_defaults, /* Ar */ NULL, /* Cd */ - post_delim_nb, /* Cm */ - post_delim_nb, /* Dv */ - post_delim_nb, /* Er */ - post_delim_nb, /* Ev */ + post_tag, /* Cm */ + post_tag, /* Dv */ + post_er, /* Er */ + post_tag, /* Ev */ post_ex, /* Ex */ post_fa, /* Fa */ NULL, /* Fd */ - post_delim_nb, /* Fl */ + post_fl, /* Fl */ post_fn, /* Fn */ post_delim_nb, /* Ft */ - post_delim_nb, /* Ic */ + post_tag, /* Ic */ post_delim_nb, /* In */ - post_defaults, /* Li */ + post_tag, /* Li */ post_nd, /* Nd */ post_nm, /* Nm */ post_delim_nb, /* Op */ @@ -156,7 +164,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_defaults, /* Pa */ post_rv, /* Rv */ post_st, /* St */ - post_delim_nb, /* Va */ + post_tag, /* Va */ post_delim_nb, /* Vt */ post_xr, /* Xr */ NULL, /* %A */ @@ -186,11 +194,11 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* Dq */ NULL, /* Ec */ NULL, /* Ef */ - post_delim_nb, /* Em */ + post_em, /* Em */ NULL, /* Eo */ post_xx, /* Fx */ - post_delim_nb, /* Ms */ - NULL, /* No */ + post_tag, /* Ms */ + post_tag, /* No */ post_ns, /* Ns */ post_xx, /* Nx */ post_xx, /* Ox */ @@ -209,7 +217,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { post_delim_nb, /* Sq */ post_sm, /* Sm */ post_sx, /* Sx */ - post_delim_nb, /* Sy */ + post_em, /* Sy */ post_useless, /* Tn */ post_xx, /* Ux */ NULL, /* Xc */ @@ -238,6 +246,7 @@ static const v_post mdoc_valids[MDOC_MAX - MDOC_Dd] = { NULL, /* %Q */ NULL, /* %U */ NULL, /* Ta */ + post_tg, /* Tg */ }; #define RSORD_MAX 14 /* Number of `Rs' blocks. */ @@ -285,6 +294,8 @@ static const char * const secnames[SEC__MAX] = { NULL }; +static int fn_prio = TAG_STRONG; + /* Validate the subtree rooted at mdoc->last. */ void @@ -1090,6 +1101,125 @@ post_st(POST_ARGS) } static void +post_tg(POST_ARGS) +{ + struct roff_node *n; /* The .Tg node. */ + struct roff_node *nch; /* The first child of the .Tg node. */ + struct roff_node *nn; /* The next node after the .Tg node. */ + struct roff_node *np; /* The parent of the next node. */ + struct roff_node *nt; /* The TEXT node containing the tag. */ + size_t len; /* The number of bytes in the tag. */ + + /* Find the next node. */ + n = mdoc->last; + for (nn = n; nn != NULL; nn = nn->parent) { + if (nn->next != NULL) { + nn = nn->next; + break; + } + } + + /* Find the tag. */ + nt = nch = n->child; + if (nch == NULL && nn != NULL && nn->child != NULL && + nn->child->type == ROFFT_TEXT) + nt = nn->child; + + /* Validate the tag. */ + if (nt == NULL || *nt->string == '\0') + mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg"); + if (nt == NULL) { + roff_node_delete(mdoc, n); + return; + } + len = strcspn(nt->string, " \t\\"); + if (nt->string[len] != '\0') + mandoc_msg(MANDOCERR_TG_SPC, nt->line, + nt->pos + len, "Tg %s", nt->string); + + /* Keep only the first argument. */ + if (nch != NULL && nch->next != NULL) { + mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line, + nch->next->pos, "Tg ... %s", nch->next->string); + while (nch->next != NULL) + roff_node_delete(mdoc, nch->next); + } + + /* Drop the macro if the first argument is invalid. */ + if (len == 0 || nt->string[len] != '\0') { + roff_node_delete(mdoc, n); + return; + } + + /* By default, tag the .Tg node itself. */ + if (nn == NULL || nn->flags & NODE_ID) + nn = n; + + /* Explicit tagging of specific macros. */ + switch (nn->tok) { + case MDOC_Sh: + case MDOC_Ss: + case MDOC_Fo: + nn = nn->head->child == NULL ? n : nn->head; + break; + case MDOC_It: + np = nn->parent; + while (np->tok != MDOC_Bl) + np = np->parent; + switch (np->norm->Bl.type) { + case LIST_column: + break; + case LIST_diag: + case LIST_hang: + case LIST_inset: + case LIST_ohang: + case LIST_tag: + nn = nn->head; + break; + case LIST_bullet: + case LIST_dash: + case LIST_enum: + case LIST_hyphen: + case LIST_item: + nn = nn->body->child == NULL ? n : nn->body; + break; + default: + abort(); + } + break; + case MDOC_Bd: + case MDOC_Bl: + case MDOC_D1: + case MDOC_Dl: + nn = nn->body->child == NULL ? n : nn->body; + break; + case MDOC_Pp: + break; + case MDOC_Cm: + case MDOC_Dv: + case MDOC_Em: + case MDOC_Er: + case MDOC_Ev: + case MDOC_Fl: + case MDOC_Fn: + case MDOC_Ic: + case MDOC_Li: + case MDOC_Ms: + case MDOC_No: + case MDOC_Sy: + if (nn->child == NULL) + nn = n; + break; + default: + nn = n; + break; + } + tag_put(nt->string, TAG_MANUAL, nn); + if (nn != n) + n->flags |= NODE_NOPRT; +} + +static void post_obsolete(POST_ARGS) { struct roff_node *n; @@ -1181,22 +1311,32 @@ post_bf(POST_ARGS) static void post_fname(POST_ARGS) { - const struct roff_node *n; + struct roff_node *n, *nch; const char *cp; size_t pos; - n = mdoc->last->child; - pos = strcspn(n->string, "()"); - cp = n->string + pos; - if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*'))) - mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos, - "%s", n->string); + n = mdoc->last; + nch = n->child; + cp = nch->string; + if (*cp == '(') { + if (cp[strlen(cp + 1)] == ')') + return; + pos = 0; + } else { + pos = strcspn(cp, "()"); + if (cp[pos] == '\0') { + if (n->sec == SEC_DESCRIPTION || + n->sec == SEC_CUSTOM) + tag_put(NULL, fn_prio++, n); + return; + } + } + mandoc_msg(MANDOCERR_FN_PAREN, nch->line, nch->pos + pos, "%s", cp); } static void post_fn(POST_ARGS) { - post_fname(mdoc); post_fa(mdoc); } @@ -1360,38 +1500,29 @@ post_display(POST_ARGS) static void post_defaults(POST_ARGS) { - struct roff_node *nn; + struct roff_node *n; - if (mdoc->last->child != NULL) { + n = mdoc->last; + if (n->child != NULL) { post_delim_nb(mdoc); return; } - - /* - * The `Ar' defaults to "file ..." if no value is provided as an - * argument; the `Mt' and `Pa' macros use "~"; the `Li' just - * gets an empty string. - */ - - nn = mdoc->last; - switch (nn->tok) { + mdoc->next = ROFF_NEXT_CHILD; + switch (n->tok) { case MDOC_Ar: - mdoc->next = ROFF_NEXT_CHILD; - roff_word_alloc(mdoc, nn->line, nn->pos, "file"); - mdoc->last->flags |= NODE_NOSRC; - roff_word_alloc(mdoc, nn->line, nn->pos, "..."); + roff_word_alloc(mdoc, n->line, n->pos, "file"); mdoc->last->flags |= NODE_NOSRC; + roff_word_alloc(mdoc, n->line, n->pos, "..."); break; case MDOC_Pa: case MDOC_Mt: - mdoc->next = ROFF_NEXT_CHILD; - roff_word_alloc(mdoc, nn->line, nn->pos, "~"); - mdoc->last->flags |= NODE_NOSRC; + roff_word_alloc(mdoc, n->line, n->pos, "~"); break; default: abort(); } - mdoc->last = nn; + mdoc->last->flags |= NODE_NOSRC; + mdoc->last = n; } static void @@ -1445,23 +1576,82 @@ post_an(POST_ARGS) } static void -post_en(POST_ARGS) +post_em(POST_ARGS) { + post_tag(mdoc); + tag_put(NULL, TAG_FALLBACK, mdoc->last); +} +static void +post_en(POST_ARGS) +{ post_obsolete(mdoc); if (mdoc->last->type == ROFFT_BLOCK) mdoc->last->norm->Es = mdoc->last_es; } static void -post_es(POST_ARGS) +post_er(POST_ARGS) { + struct roff_node *n; + + n = mdoc->last; + if (n->sec == SEC_ERRORS && + (n->parent->tok == MDOC_It || + (n->parent->tok == MDOC_Bq && + n->parent->parent->parent->tok == MDOC_It))) + tag_put(NULL, TAG_STRONG, n); + post_delim_nb(mdoc); +} + +static void +post_tag(POST_ARGS) +{ + struct roff_node *n; + + n = mdoc->last; + if ((n->prev == NULL || + (n->prev->type == ROFFT_TEXT && + strcmp(n->prev->string, "|") == 0)) && + (n->parent->tok == MDOC_It || + (n->parent->tok == MDOC_Xo && + n->parent->parent->prev == NULL && + n->parent->parent->parent->tok == MDOC_It))) + tag_put(NULL, TAG_STRONG, n); + post_delim_nb(mdoc); +} +static void +post_es(POST_ARGS) +{ post_obsolete(mdoc); mdoc->last_es = mdoc->last; } static void +post_fl(POST_ARGS) +{ + struct roff_node *n; + char *cp; + + /* + * Transform ".Fl Fl long" to ".Fl \-long", + * resulting for example in better HTML output. + */ + + n = mdoc->last; + if (n->prev != NULL && n->prev->tok == MDOC_Fl && + n->prev->child == NULL && n->child != NULL && + (n->flags & NODE_LINE) == 0) { + mandoc_asprintf(&cp, "\\-%s", n->child->string); + free(n->child->string); + n->child->string = cp; + roff_node_delete(mdoc, n->prev); + } + post_tag(mdoc); +} + +static void post_xx(POST_ARGS) { struct roff_node *n; @@ -1553,8 +1743,8 @@ post_it(POST_ARGS) if ((nch = nit->head->child) != NULL) mandoc_msg(MANDOCERR_ARG_SKIP, nit->line, nit->pos, "It %s", - nch->string == NULL ? roff_name[nch->tok] : - nch->string); + nch->type == ROFFT_TEXT ? nch->string : + roff_name[nch->tok]); break; case LIST_column: cols = (int)nbl->norm->Bl.ncols; @@ -1717,8 +1907,7 @@ post_bl_head(POST_ARGS) static void post_bl(POST_ARGS) { - struct roff_node *nparent, *nprev; /* of the Bl block */ - struct roff_node *nblock, *nbody; /* of the Bl */ + struct roff_node *nbody; /* of the Bl */ struct roff_node *nchild, *nnext; /* of the Bl body */ const char *prev_Er; int order; @@ -1739,88 +1928,73 @@ post_bl(POST_ARGS) if (nbody->end != ENDBODY_NOT) return; - nchild = nbody->child; - if (nchild == NULL) { - mandoc_msg(MANDOCERR_BLK_EMPTY, - nbody->line, nbody->pos, "Bl"); - return; + /* + * Up to the first item, move nodes before the list, + * but leave transparent nodes where they are + * if they precede an item. + * The next non-transparent node is kept in nchild. + * It only needs to be updated after a non-transparent + * node was moved out, and at the very beginning + * when no node at all was moved yet. + */ + + nchild = mdoc->last; + for (;;) { + if (nchild == mdoc->last) + nchild = roff_node_child(nbody); + if (nchild == NULL) { + mdoc->last = nbody; + mandoc_msg(MANDOCERR_BLK_EMPTY, + nbody->line, nbody->pos, "Bl"); + return; + } + if (nchild->tok == MDOC_It) { + mdoc->last = nbody; + break; + } + mandoc_msg(MANDOCERR_BL_MOVE, nbody->child->line, + nbody->child->pos, "%s", roff_name[nbody->child->tok]); + if (nbody->parent->prev == NULL) { + mdoc->last = nbody->parent->parent; + mdoc->next = ROFF_NEXT_CHILD; + } else { + mdoc->last = nbody->parent->prev; + mdoc->next = ROFF_NEXT_SIBLING; + } + roff_node_relink(mdoc, nbody->child); } + + /* + * We have reached the first item, + * so moving nodes out is no longer possible. + * But in .Bl -column, the first rows may be implicit, + * that is, they may not start with .It macros. + * Such rows may be followed by nodes generated on the + * roff level, for example .TS. + * Wrap such roff nodes into an implicit row. + */ + while (nchild != NULL) { - nnext = nchild->next; - if (nchild->tok == MDOC_It || - (nchild->tok == MDOC_Sm && - nnext != NULL && nnext->tok == MDOC_It)) { - nchild = nnext; + if (nchild->tok == MDOC_It) { + nchild = roff_node_next(nchild); continue; } - - /* - * In .Bl -column, the first rows may be implicit, - * that is, they may not start with .It macros. - * Such rows may be followed by nodes generated on the - * roff level, for example .TS, which cannot be moved - * out of the list. In that case, wrap such roff nodes - * into an implicit row. - */ - - if (nchild->prev != NULL) { - mdoc->last = nchild; - mdoc->next = ROFF_NEXT_SIBLING; - roff_block_alloc(mdoc, nchild->line, - nchild->pos, MDOC_It); - roff_head_alloc(mdoc, nchild->line, - nchild->pos, MDOC_It); + nnext = nchild->next; + mdoc->last = nchild->prev; + mdoc->next = ROFF_NEXT_SIBLING; + roff_block_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); + roff_head_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); + mdoc->next = ROFF_NEXT_SIBLING; + roff_body_alloc(mdoc, nchild->line, nchild->pos, MDOC_It); + while (nchild->tok != MDOC_It) { + roff_node_relink(mdoc, nchild); + if (nnext == NULL) + break; + nchild = nnext; + nnext = nchild->next; mdoc->next = ROFF_NEXT_SIBLING; - roff_body_alloc(mdoc, nchild->line, - nchild->pos, MDOC_It); - while (nchild->tok != MDOC_It) { - roff_node_relink(mdoc, nchild); - if ((nchild = nnext) == NULL) - break; - nnext = nchild->next; - mdoc->next = ROFF_NEXT_SIBLING; - } - mdoc->last = nbody; - continue; } - - mandoc_msg(MANDOCERR_BL_MOVE, nchild->line, nchild->pos, - "%s", roff_name[nchild->tok]); - - /* - * Move the node out of the Bl block. - * First, collect all required node pointers. - */ - - nblock = nbody->parent; - nprev = nblock->prev; - nparent = nblock->parent; - - /* - * Unlink this child. - */ - - nbody->child = nnext; - if (nnext == NULL) - nbody->last = NULL; - else - nnext->prev = NULL; - - /* - * Relink this child. - */ - - nchild->parent = nparent; - nchild->prev = nprev; - nchild->next = nblock; - - nblock->prev = nchild; - if (nprev == NULL) - nparent->child = nchild; - else - nprev->next = nchild; - - nchild = nnext; + mdoc->last = nbody; } if (mdoc->meta.os_e != MANDOC_OS_NETBSD) @@ -1903,7 +2077,7 @@ post_root(POST_ARGS) /* Add missing prologue data. */ if (mdoc->meta.date == NULL) - mdoc->meta.date = mandoc_normdate(mdoc, NULL, 0, 0); + mdoc->meta.date = mandoc_normdate(NULL, NULL); if (mdoc->meta.title == NULL) { mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF"); @@ -2048,10 +2222,11 @@ post_rs(POST_ARGS) static void post_hyph(POST_ARGS) { - struct roff_node *nch; + struct roff_node *n, *nch; char *cp; - for (nch = mdoc->last->child; nch != NULL; nch = nch->next) { + n = mdoc->last; + for (nch = n->child; nch != NULL; nch = nch->next) { if (nch->type != ROFFT_TEXT) continue; cp = nch->string; @@ -2060,8 +2235,11 @@ post_hyph(POST_ARGS) while (*(++cp) != '\0') if (*cp == '-' && isalpha((unsigned char)cp[-1]) && - isalpha((unsigned char)cp[1])) + isalpha((unsigned char)cp[1])) { + if (n->tag == NULL && n->flags & NODE_ID) + n->tag = mandoc_strdup(nch->string); *cp = ASCII_HYPH; + } } } @@ -2086,8 +2264,7 @@ post_sx(POST_ARGS) static void post_sh(POST_ARGS) { - - post_ignpar(mdoc); + post_section(mdoc); switch (mdoc->last->type) { case ROFFT_HEAD: @@ -2318,6 +2495,8 @@ post_sh_head(POST_ARGS) roff_setreg(mdoc->roff, "nS", 0, '='); mdoc->flags &= ~MDOC_SYNOPSIS; } + if (sec == SEC_DESCRIPTION) + fn_prio = TAG_STRONG; /* Mark our last section. */ @@ -2418,15 +2597,31 @@ post_xr(POST_ARGS) } static void -post_ignpar(POST_ARGS) +post_section(POST_ARGS) { - struct roff_node *np; + struct roff_node *n, *nch; + char *cp, *tag; - switch (mdoc->last->type) { + n = mdoc->last; + switch (n->type) { case ROFFT_BLOCK: post_prevpar(mdoc); return; case ROFFT_HEAD: + tag = NULL; + deroff(&tag, n); + if (tag != NULL) { + for (cp = tag; *cp != '\0'; cp++) + if (*cp == ' ') + *cp = '_'; + if ((nch = n->child) != NULL && + nch->type == ROFFT_TEXT && + strcmp(nch->string, tag) == 0) + tag_put(NULL, TAG_STRONG, n); + else + tag_put(tag, TAG_FALLBACK, n); + free(tag); + } post_delim(mdoc); post_hyph(mdoc); return; @@ -2435,42 +2630,40 @@ post_ignpar(POST_ARGS) default: return; } - - if ((np = mdoc->last->child) != NULL) - if (np->tok == MDOC_Pp || - np->tok == ROFF_br || np->tok == ROFF_sp) { - mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, - "%s after %s", roff_name[np->tok], - roff_name[mdoc->last->tok]); - roff_node_delete(mdoc, np); - } - - if ((np = mdoc->last->last) != NULL) - if (np->tok == MDOC_Pp || np->tok == ROFF_br) { - mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, - "%s at the end of %s", roff_name[np->tok], - roff_name[mdoc->last->tok]); - roff_node_delete(mdoc, np); - } + if ((nch = n->child) != NULL && + (nch->tok == MDOC_Pp || nch->tok == ROFF_br || + nch->tok == ROFF_sp)) { + mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, + "%s after %s", roff_name[nch->tok], + roff_name[n->tok]); + roff_node_delete(mdoc, nch); + } + if ((nch = n->last) != NULL && + (nch->tok == MDOC_Pp || nch->tok == ROFF_br)) { + mandoc_msg(MANDOCERR_PAR_SKIP, nch->line, nch->pos, + "%s at the end of %s", roff_name[nch->tok], + roff_name[n->tok]); + roff_node_delete(mdoc, nch); + } } static void post_prevpar(POST_ARGS) { - struct roff_node *n; + struct roff_node *n, *np; n = mdoc->last; - if (NULL == n->prev) - return; if (n->type != ROFFT_ELEM && n->type != ROFFT_BLOCK) return; + if ((np = roff_node_prev(n)) == NULL) + return; /* * Don't allow `Pp' prior to a paragraph-type * block: `Pp' or non-compact `Bd' or `Bl'. */ - if (n->prev->tok != MDOC_Pp && n->prev->tok != ROFF_br) + if (np->tok != MDOC_Pp && np->tok != ROFF_br) return; if (n->tok == MDOC_Bl && n->norm->Bl.comp) return; @@ -2479,9 +2672,9 @@ post_prevpar(POST_ARGS) if (n->tok == MDOC_It && n->parent->norm->Bl.comp) return; - mandoc_msg(MANDOCERR_PAR_SKIP, n->prev->line, n->prev->pos, - "%s before %s", roff_name[n->prev->tok], roff_name[n->tok]); - roff_node_delete(mdoc, n->prev); + mandoc_msg(MANDOCERR_PAR_SKIP, np->line, np->pos, + "%s before %s", roff_name[np->tok], roff_name[n->tok]); + roff_node_delete(mdoc, np); } static void @@ -2489,6 +2682,7 @@ post_par(POST_ARGS) { struct roff_node *np; + fn_prio = TAG_STRONG; post_prevpar(mdoc); np = mdoc->last; @@ -2501,7 +2695,6 @@ static void post_dd(POST_ARGS) { struct roff_node *n; - char *datestr; n = mdoc->last; n->flags |= NODE_NOPRT; @@ -2518,10 +2711,10 @@ post_dd(POST_ARGS) mandoc_msg(MANDOCERR_PROLOG_ORDER, n->line, n->pos, "Dd after Os"); - datestr = NULL; - deroff(&datestr, n); - mdoc->meta.date = mandoc_normdate(mdoc, datestr, n->line, n->pos); - free(datestr); + if (mdoc->quick && n != NULL) + mdoc->meta.date = mandoc_strdup(""); + else + mdoc->meta.date = mandoc_normdate(n->child, n); } static void @@ -2596,8 +2789,14 @@ post_dt(POST_ARGS) mandoc_msg(MANDOCERR_MSEC_BAD, nn->line, nn->pos, "Dt ... %s", nn->string); mdoc->meta.vol = mandoc_strdup(nn->string); - } else + } else { mdoc->meta.vol = mandoc_strdup(cp); + if (mdoc->filesec != '\0' && + mdoc->filesec != *nn->string && + *nn->string >= '1' && *nn->string <= '9') + mandoc_msg(MANDOCERR_MSEC_FILE, nn->line, nn->pos, + "*.%c vs Dt ... %c", mdoc->filesec, *nn->string); + } /* Optional third argument: architecture. */ @@ -1,7 +1,8 @@ -/* $Id: out.c,v 1.78 2019/03/29 21:27:06 schwarze Exp $ */ +/* $Id: out.c,v 1.82 2021/09/07 17:07:58 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2019, 2021 + * Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,11 +23,13 @@ #include <assert.h> #include <ctype.h> #include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "mandoc_aux.h" +#include "mandoc.h" #include "tbl.h" #include "out.h" @@ -120,7 +123,6 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, const struct tbl_dat *dp; struct roffcol *col; struct tbl_colgroup *first_group, **gp, *g; - size_t *colwidth; size_t ewidth, min1, min2, wanted, width, xwidth; int done, icol, maxcol, necol, nxcol, quirkcol; @@ -209,13 +211,25 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, } /* - * Column spacings are needed for span width calculations, - * so set the default values now. + * The minimum width of columns explicitly specified + * in the layout is 1n. */ - for (icol = 0; icol <= maxcol; icol++) - if (tbl->cols[icol].spacing == SIZE_MAX || icol == maxcol) - tbl->cols[icol].spacing = 3; + if (maxcol < sp_first->opts->cols - 1) + maxcol = sp_first->opts->cols - 1; + for (icol = 0; icol <= maxcol; icol++) { + col = tbl->cols + icol; + if (col->width < 1) + col->width = 1; + + /* + * Column spacings are needed for span width + * calculations, so set the default values now. + */ + + if (col->spacing == SIZE_MAX || icol == maxcol) + col->spacing = 3; + } /* * Replace the minimum widths with the missing widths, @@ -242,33 +256,21 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, gp = &(*gp)->next; } - colwidth = mandoc_reallocarray(NULL, maxcol + 1, sizeof(*colwidth)); while (first_group != NULL) { /* - * Rebuild the array of the widths of all columns - * participating in spans that require expansion. - */ - - for (icol = 0; icol <= maxcol; icol++) - colwidth[icol] = SIZE_MAX; - for (g = first_group; g != NULL; g = g->next) - for (icol = g->startcol; icol <= g->endcol; icol++) - colwidth[icol] = tbl->cols[icol].width; - - /* * Find the smallest and second smallest column width * among the columns which may need expamsion. */ min1 = min2 = SIZE_MAX; for (icol = 0; icol <= maxcol; icol++) { - if (min1 > colwidth[icol]) { + width = tbl->cols[icol].width; + if (min1 > width) { min2 = min1; - min1 = colwidth[icol]; - } else if (min1 < colwidth[icol] && - min2 > colwidth[icol]) - min2 = colwidth[icol]; + min1 = width; + } else if (min1 < width && min2 > width) + min2 = width; } /* @@ -290,26 +292,22 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, width = min2; if (wanted > width) wanted = width; - for (icol = g->startcol; icol <= g->endcol; icol++) - if (colwidth[icol] == min1 || - (colwidth[icol] < min2 && - colwidth[icol] > width)) - colwidth[icol] = width; } - /* Record the effect of the widening on the group list. */ + /* Record the effect of the widening. */ gp = &first_group; while ((g = *gp) != NULL) { done = 0; for (icol = g->startcol; icol <= g->endcol; icol++) { - if (colwidth[icol] != wanted || - tbl->cols[icol].width == wanted) + if (tbl->cols[icol].width != min1) continue; if (g->wanted <= wanted - min1) { + tbl->cols[icol].width += g->wanted; done = 1; break; } + tbl->cols[icol].width = wanted; g->wanted -= wanted - min1; } if (done) { @@ -318,14 +316,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, } else gp = &(*gp)->next; } - - /* Record the effect of the widening on the columns. */ - - for (icol = 0; icol <= maxcol; icol++) - if (colwidth[icol] == wanted) - tbl->cols[icol].width = wanted; } - free(colwidth); /* * Align numbers with text. @@ -340,8 +331,6 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first, col = tbl->cols + icol; if (col->width > col->nwidth) col->decimal += (col->width - col->nwidth) / 2; - else - col->width = col->nwidth; if (col->flags & TBL_CELL_EQUAL) { necol++; if (ewidth < col->width) @@ -549,5 +538,7 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col, if (totsz > col->nwidth) col->nwidth = totsz; + if (col->nwidth > col->width) + col->width = col->nwidth; return totsz; } @@ -1,4 +1,4 @@ -/* $Id: out.h,v 1.33 2018/08/18 20:18:14 schwarze Exp $ */ +/* $Id: out.h,v 1.34 2020/04/03 11:35:01 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Utilities for use by multiple mandoc(1) formatters. */ enum roffscale { @@ -64,5 +66,5 @@ struct rofftbl { struct tbl_span; const char *a2roffsu(const char *, struct roffsu *, enum roffscale); -void tblcalc(struct rofftbl *tbl, +void tblcalc(struct rofftbl *, const struct tbl_span *, size_t, size_t); @@ -1,7 +1,7 @@ -/* $Id: read.c,v 1.214 2019/07/10 19:39:01 schwarze Exp $ */ +/* $Id: read.c,v 1.220 2021/06/27 17:57:54 schwarze Exp $ */ /* + * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,12 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Top-level functions of the mandoc(3) parser: + * Parser and input encoding selection, decompression, + * handling of input bytes, characters, lines, and files, + * handling of roff(7) loops and file inclusion, + * and steering of the various parsers. */ #include "config.h" @@ -41,6 +47,7 @@ #include "mandoc_parse.h" #include "libmandoc.h" #include "roff_int.h" +#include "tag.h" #define REPARSE_LIMIT 1000 @@ -147,6 +154,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) struct buf *firstln, *lastln, *thisln, *loop; char *cp; size_t pos; /* byte number in the ln buffer */ + size_t spos; /* at the start of the current line parse */ int line_result, result; int of; int lnn; /* line number in the real file */ @@ -173,6 +181,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) curp->filenc & MPARSE_LATIN1) curp->filenc = preconv_cue(&blk, i); } + spos = pos; while (i < blk.sz && (start || blk.buf[i] != '\0')) { @@ -272,7 +281,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) of = 0; rerun: - line_result = roff_parseln(curp->roff, curp->line, &ln, &of); + line_result = roff_parseln(curp->roff, curp->line, + &ln, &of, start && spos == 0 ? pos : 0); /* Process options. */ @@ -547,7 +557,7 @@ mparse_readfd(struct mparse *curp, int fd, const char *filename) struct buf blk; struct buf *save_primary; - const char *save_filename; + const char *save_filename, *cp; size_t offset; int save_filenc, save_lineno; int with_mmap; @@ -555,7 +565,13 @@ mparse_readfd(struct mparse *curp, int fd, const char *filename) if (recursion_depth > 64) { mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL); return; - } + } else if (recursion_depth == 0 && + (cp = strrchr(filename, '.')) != NULL && + cp[1] >= '1' && cp[1] <= '9') + curp->man->filesec = cp[1]; + else + curp->man->filesec = '\0'; + if (read_whole_file(curp, fd, &blk, &with_mmap) == -1) return; @@ -664,22 +680,26 @@ mparse_alloc(int options, enum mandoc_os os_e, const char *os_s) } curp->man->meta.first->tok = TOKEN_NONE; curp->man->meta.os_e = os_e; + tag_alloc(); return curp; } void mparse_reset(struct mparse *curp) { + tag_free(); roff_reset(curp->roff); roff_man_reset(curp->man); free_buf_list(curp->secondary); curp->secondary = NULL; curp->gzip = 0; + tag_alloc(); } void mparse_free(struct mparse *curp) { + tag_free(); roffhash_free(curp->man->mdocmac); roffhash_free(curp->man->manmac); roff_man_free(curp->man); @@ -697,6 +717,7 @@ mparse_result(struct mparse *curp) mdoc_validate(curp->man); else man_validate(curp->man); + tag_postprocess(curp->man, curp->man->meta.first); } return &curp->man->meta; } @@ -1,4 +1,4 @@ -.\" $Id: roff.7,v 1.114 2019/07/15 19:20:30 schwarze Exp $ +.\" $Id: roff.7,v 1.116 2021/09/18 12:23:06 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 15 2019 $ +.Dd $Mdocdate: September 18 2021 $ .Dt ROFF 7 .Os .Sh NAME @@ -624,7 +624,7 @@ Its syntax can be either .Pp or .Bd -literal -offset indent -.Pf . Ic \&de Ar macroname Ar endmacro +.Pf . Ic \&de Ar macroname endmacro .Ar definition .Pf . Ar endmacro .Ed @@ -2331,7 +2331,7 @@ for .At v2 , then ported nroff to C as troff, which Brian W. Kernighan released with .At v7 . -In 1989, James Clarke re-implemented troff in C++, naming it groff. +In 1989, James Clark re-implemented troff in C++, naming it groff. .Sh AUTHORS .An -nosplit This @@ -1,7 +1,7 @@ -/* $Id: roff.c,v 1.366 2019/07/01 22:56:24 schwarze Exp $ */ +/* $Id: roff.c,v 1.378 2021/08/10 12:55:04 schwarze Exp $ */ /* + * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Implementation of the roff(7) parser for mandoc(1). */ #include "config.h" @@ -190,13 +192,14 @@ static int roff_cc(ROFF_ARGS); static int roff_ccond(struct roff *, int, int); static int roff_char(ROFF_ARGS); static int roff_cond(ROFF_ARGS); +static int roff_cond_checkend(ROFF_ARGS); static int roff_cond_text(ROFF_ARGS); static int roff_cond_sub(ROFF_ARGS); static int roff_ds(ROFF_ARGS); static int roff_ec(ROFF_ARGS); static int roff_eo(ROFF_ARGS); static int roff_eqndelim(struct roff *, struct buf *, int); -static int roff_evalcond(struct roff *r, int, char *, int *); +static int roff_evalcond(struct roff *, int, char *, int *); static int roff_evalnum(struct roff *, int, const char *, int *, int *, int); static int roff_evalpar(struct roff *, int, @@ -355,7 +358,7 @@ const char *__roff_name[MAN_MAX + 1] = { "Lk", "Mt", "Brq", "Bro", "Brc", "%C", "Es", "En", "Dx", "%Q", "%U", "Ta", - NULL, + "Tg", NULL, "TH", "SH", "SS", "TP", "TQ", "LP", "PP", "P", "IP", @@ -771,6 +774,7 @@ void roff_reset(struct roff *r) { roff_free1(r); + r->options |= MPARSE_COMMENT; r->format = r->options & (MPARSE_MDOC | MPARSE_MAN); r->control = '\0'; r->escape = '\\'; @@ -800,7 +804,7 @@ roff_alloc(int options) r = mandoc_calloc(1, sizeof(struct roff)); r->reqtab = roffhash_alloc(0, ROFF_RENAMED); - r->options = options; + r->options = options | MPARSE_COMMENT; r->format = options & (MPARSE_MDOC | MPARSE_MAN); r->mstackpos = -1; r->rstackpos = -1; @@ -1100,6 +1104,7 @@ roff_node_free(struct roff_node *n) free(n->norm); eqn_box_free(n->eqn); free(n->string); + free(n->tag); free(n); } @@ -1113,13 +1118,72 @@ roff_node_delete(struct roff_man *man, struct roff_node *n) roff_node_free(n); } +int +roff_node_transparent(struct roff_node *n) +{ + if (n == NULL) + return 0; + if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) + return 1; + return roff_tok_transparent(n->tok); +} + +int +roff_tok_transparent(enum roff_tok tok) +{ + switch (tok) { + case ROFF_ft: + case ROFF_ll: + case ROFF_mc: + case ROFF_po: + case ROFF_ta: + case MDOC_Db: + case MDOC_Es: + case MDOC_Sm: + case MDOC_Tg: + case MAN_DT: + case MAN_UC: + case MAN_PD: + case MAN_AT: + return 1; + default: + return 0; + } +} + +struct roff_node * +roff_node_child(struct roff_node *n) +{ + for (n = n->child; roff_node_transparent(n); n = n->next) + continue; + return n; +} + +struct roff_node * +roff_node_prev(struct roff_node *n) +{ + do { + n = n->prev; + } while (roff_node_transparent(n)); + return n; +} + +struct roff_node * +roff_node_next(struct roff_node *n) +{ + do { + n = n->next; + } while (roff_node_transparent(n)); + return n; +} + void deroff(char **dest, const struct roff_node *n) { char *cp; size_t sz; - if (n->type != ROFFT_TEXT) { + if (n->string == NULL) { for (n = n->child; n != NULL; n = n->next) deroff(dest, n); return; @@ -1246,7 +1310,7 @@ roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc) * in the syntax tree. */ - if (newesc != ASCII_ESC && r->format == 0) { + if (newesc != ASCII_ESC && r->options & MPARSE_COMMENT) { while (*ep == ' ' || *ep == '\t') ep--; ep[1] = '\0'; @@ -1759,7 +1823,7 @@ roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) } int -roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) +roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs, size_t len) { enum roff_tok t; int e; @@ -1770,6 +1834,14 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) ppos = pos = *offs; + if (len > 80 && r->tbl == NULL && r->eqn == NULL && + (r->man->flags & ROFF_NOFILL) == 0 && + strchr(" .\\", buf->buf[pos]) == NULL && + buf->buf[pos] != r->control && + strcspn(buf->buf, " ") < 80) + mandoc_msg(MANDOCERR_TEXT_LONG, ln, (int)len - 1, + "%.20s...", buf->buf + pos); + /* Handle in-line equation delimiters. */ if (r->tbl == NULL && @@ -1815,8 +1887,10 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) roff_addtbl(r->man, ln, r->tbl); return e; } - if ( ! ctl) + if ( ! ctl) { + r->options &= ~MPARSE_COMMENT; return roff_parsetext(r, buf, pos, offs) | e; + } /* Skip empty request lines. */ @@ -1839,6 +1913,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) /* No scope is open. This is a new request or macro. */ + r->options &= ~MPARSE_COMMENT; spos = pos; t = roff_parse(r, buf->buf, &pos, ln, ppos); @@ -1968,14 +2043,13 @@ roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos) /* --- handling of request blocks ----------------------------------------- */ +/* + * Close a macro definition block or an "ignore" block. + */ static int roff_cblock(ROFF_ARGS) { - - /* - * A block-close `..' should only be invoked as a child of an - * ignore macro, otherwise raise a warning and just ignore it. - */ + int rr; if (r->last == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); @@ -1984,26 +2058,38 @@ roff_cblock(ROFF_ARGS) switch (r->last->tok) { case ROFF_am: - /* ROFF_am1 is remapped to ROFF_am in roff_block(). */ case ROFF_ami: case ROFF_de: - /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ case ROFF_dei: case ROFF_ig: break; + case ROFF_am1: + case ROFF_de1: + /* Remapped in roff_block(). */ + abort(); default: mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); return ROFF_IGN; } + roffnode_pop(r); + roffnode_cleanscope(r); + + /* + * If a conditional block with braces is still open, + * check for "\}" block end markers. + */ + + if (r->last != NULL && r->last->endspan < 0) { + rr = 1; /* If arguments follow "\}", warn about them. */ + roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); + } + if (buf->buf[pos] != '\0') mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, ".. %s", buf->buf + pos); - roffnode_pop(r); - roffnode_cleanscope(r); return ROFF_IGN; - } /* @@ -2016,7 +2102,7 @@ roffnode_cleanscope(struct roff *r) int inloop; inloop = 0; - while (r->last != NULL) { + while (r->last != NULL && r->last->endspan > 0) { if (--r->last->endspan != 0) break; inloop += roffnode_pop(r); @@ -2025,7 +2111,7 @@ roffnode_cleanscope(struct roff *r) } /* - * Handle the closing \} of a conditional block. + * Handle the closing "\}" of a conditional block. * Apart from generating warnings, this only pops nodes. * Return the number of loops ended. */ @@ -2245,13 +2331,20 @@ roff_block_text(ROFF_ARGS) return ROFF_IGN; } +/* + * Check for a closing "\}" and handle it. + * In this function, the final "int *offs" argument is used for + * different purposes than elsewhere: + * Input: *offs == 0: caller wants to discard arguments following \} + * *offs == 1: caller wants to preserve text following \} + * Output: *offs = 0: tell caller to discard input line + * *offs = 1: tell caller to use input line + */ static int -roff_cond_sub(ROFF_ARGS) +roff_cond_checkend(ROFF_ARGS) { - struct roffnode *bl; char *ep; int endloop, irc, rr; - enum roff_tok t; irc = ROFF_IGN; rr = r->last->rule; @@ -2261,23 +2354,28 @@ roff_cond_sub(ROFF_ARGS) irc |= endloop; /* - * If `\}' occurs on a macro line without a preceding macro, - * drop the line completely. + * If "\}" occurs on a macro line without a preceding macro or + * a text line contains nothing else, drop the line completely. */ ep = buf->buf + pos; - if (ep[0] == '\\' && ep[1] == '}') + if (ep[0] == '\\' && ep[1] == '}' && (ep[2] == '\0' || *offs == 0)) rr = 0; /* - * The closing delimiter `\}' rewinds the conditional scope + * The closing delimiter "\}" rewinds the conditional scope * but is otherwise ignored when interpreting the line. */ while ((ep = strchr(ep, '\\')) != NULL) { switch (ep[1]) { case '}': - memmove(ep, ep + 2, strlen(ep + 2) + 1); + if (ep[2] == '\0') + ep[0] = '\0'; + else if (rr) + ep[1] = '&'; + else + memmove(ep, ep + 2, strlen(ep + 2) + 1); if (roff_ccond(r, ln, ep - buf->buf)) irc |= endloop; break; @@ -2289,13 +2387,40 @@ roff_cond_sub(ROFF_ARGS) break; } } + *offs = rr; + return irc; +} + +/* + * Parse and process a request or macro line in conditional scope. + */ +static int +roff_cond_sub(ROFF_ARGS) +{ + struct roffnode *bl; + int irc, rr; + enum roff_tok t; + + rr = 0; /* If arguments follow "\}", skip them. */ + irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); + t = roff_parse(r, buf->buf, &pos, ln, ppos); + + /* For now, let high level macros abort .ce mode. */ + + if (roffce_node != NULL && + (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ || + t == ROFF_TH || t == ROFF_TS)) { + r->man->last = roffce_node; + r->man->next = ROFF_NEXT_SIBLING; + roffce_lines = 0; + roffce_node = NULL; + } /* * 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); if (t == ROFF_break) { if (irc & ROFF_LOOPMASK) irc = ROFF_IGN | ROFF_LOOPEXIT; @@ -2314,48 +2439,16 @@ roff_cond_sub(ROFF_ARGS) return irc; } +/* + * Parse and process a text line in conditional scope. + */ static int roff_cond_text(ROFF_ARGS) { - char *ep; - int endloop, irc, rr; + int irc, rr; - irc = ROFF_IGN; - rr = r->last->rule; - endloop = tok != ROFF_while ? ROFF_IGN : - rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; - if (roffnode_cleanscope(r)) - irc |= endloop; - - /* - * If `\}' occurs on a text line with neither preceding - * nor following characters, drop the line completely. - */ - - ep = buf->buf + pos; - if (strcmp(ep, "\\}") == 0) - rr = 0; - - /* - * The closing delimiter `\}' rewinds the conditional scope - * but is otherwise ignored when interpreting the line. - */ - - while ((ep = strchr(ep, '\\')) != NULL) { - switch (ep[1]) { - case '}': - memmove(ep, ep + 2, strlen(ep + 2) + 1); - if (roff_ccond(r, ln, ep - buf->buf)) - irc |= endloop; - break; - case '\0': - ++ep; - break; - default: - ep += 2; - break; - } - } + rr = 1; /* If arguments follow "\}", preserve them. */ + irc = roff_cond_checkend(r, tok, buf, ln, ppos, pos, &rr); if (rr) irc |= ROFF_CONT; return irc; @@ -3574,7 +3667,9 @@ roff_char(ROFF_ARGS) case ESCAPE_FONTITALIC: case ESCAPE_FONTBOLD: case ESCAPE_FONTBI: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: + case ESCAPE_FONTCB: + case ESCAPE_FONTCI: case ESCAPE_FONTPREV: font++; break; @@ -1,7 +1,7 @@ -/* $Id: roff.h,v 1.69 2019/03/04 13:01:57 schwarze Exp $ */ +/* $Id: roff.h,v 1.74 2020/04/08 11:56:03 schwarze Exp $ */ /* + * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -437,6 +437,7 @@ enum roff_tok { MDOC__Q, MDOC__U, MDOC_Ta, + MDOC_Tg, MDOC_MAX, MAN_TH, MAN_SH, @@ -505,6 +506,7 @@ struct roff_node { struct mdoc_arg *args; /* BLOCK/ELEM */ union mdoc_data *norm; /* Normalized arguments. */ char *string; /* TEXT */ + char *tag; /* For less(1) :t and HTML id=. */ struct tbl_span *span; /* TBL */ struct eqn_box *eqn; /* EQN */ int line; /* Input file line number. */ @@ -521,6 +523,8 @@ struct roff_node { #define NODE_NOFILL (1 << 8) /* Fill mode switched off. */ #define NODE_NOSRC (1 << 9) /* Generated node, not in input file. */ #define NODE_NOPRT (1 << 10) /* Shall not print anything. */ +#define NODE_ID (1 << 11) /* Target for deep linking. */ +#define NODE_HREF (1 << 12) /* Link to another place in this page. */ int prev_font; /* Before entering this node. */ int aux; /* Decoded node data, type-dependent. */ enum roff_tok tok; /* Request or macro ID. */ @@ -548,5 +552,10 @@ struct roff_meta { extern const char *const *roff_name; -int arch_valid(const char *, enum mandoc_os); -void deroff(char **, const struct roff_node *); +int arch_valid(const char *, enum mandoc_os); +void deroff(char **, const struct roff_node *); +struct roff_node *roff_node_child(struct roff_node *); +struct roff_node *roff_node_next(struct roff_node *); +struct roff_node *roff_node_prev(struct roff_node *); +int roff_node_transparent(struct roff_node *); +int roff_tok_transparent(enum roff_tok); diff --git a/roff_html.c b/roff_html.c index e525aa2ede68..3cc7c19a8a51 100644 --- a/roff_html.c +++ b/roff_html.c @@ -1,4 +1,4 @@ -/* $Id: roff_html.c,v 1.20 2019/04/30 15:53:01 schwarze Exp $ */ +/* $Id: roff_html.c,v 1.21 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2014, 2017, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org> @@ -15,6 +15,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <assert.h> diff --git a/roff_int.h b/roff_int.h index d033f86aceec..e0700a742910 100644 --- a/roff_int.h +++ b/roff_int.h @@ -1,7 +1,7 @@ -/* $Id: roff_int.h,v 1.16 2019/01/05 00:36:50 schwarze Exp $ */ +/* $OpenBSD: roff_int.h,v 1.16 2019/01/05 00:36:46 schwarze Exp $ */ /* + * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -59,6 +59,7 @@ struct roff_man { enum roff_sec lastsec; /* Last section seen. */ enum roff_sec lastnamed; /* Last standard section seen. */ enum roff_next next; /* Where to put the next node. */ + char filesec; /* Section digit in the file name. */ }; diff --git a/roff_term.c b/roff_term.c index f10bb61d2cdd..115d850fb261 100644 --- a/roff_term.c +++ b/roff_term.c @@ -1,6 +1,6 @@ -/* $Id: roff_term.c,v 1.19 2019/01/04 03:24:33 schwarze Exp $ */ +/* $OpenBSD: roff_term.c,v 1.20 2020/09/03 17:37:06 schwarze Exp $ */ /* - * Copyright (c) 2010,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010,2014,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <assert.h> @@ -110,9 +112,11 @@ roff_term_pre_ft(ROFF_TERM_ARGS) cp = n->child->string; switch (mandoc_font(cp, (int)strlen(cp))) { case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: term_fontrepl(p, TERMFONT_BOLD); break; case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: term_fontrepl(p, TERMFONT_UNDER); break; case ESCAPE_FONTBI: @@ -122,7 +126,7 @@ roff_term_pre_ft(ROFF_TERM_ARGS) term_fontlast(p); break; case ESCAPE_FONTROMAN: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: term_fontrepl(p, TERMFONT_NONE); break; default: @@ -155,9 +159,13 @@ static void roff_term_pre_po(ROFF_TERM_ARGS) { struct roffsu su; - static int po, polast; + static int po, pouse, polast; int ponew; + /* Revert the currently active page offset. */ + p->tcol->offset -= pouse; + + /* Determine the requested page offset. */ if (n->child != NULL && a2roffsu(n->child->string, &su, SCALE_EM) != NULL) { ponew = term_hen(p, &su); @@ -166,11 +174,15 @@ roff_term_pre_po(ROFF_TERM_ARGS) ponew += po; } else ponew = polast; + + /* Remeber both the previous and the newly requested offset. */ polast = po; po = ponew; - ponew = po - polast + (int)p->tcol->offset; - p->tcol->offset = ponew > 0 ? ponew : 0; + /* Truncate to the range [-offset, 60], remember, and apply it. */ + pouse = po >= 60 ? 60 : + po < -(int)p->tcol->offset ? -(int)p->tcol->offset : po; + p->tcol->offset += pouse; } static void @@ -208,6 +220,7 @@ roff_term_pre_ti(ROFF_TERM_ARGS) { struct roffsu su; const char *cp; + const size_t maxoff = 72; int len, sign; roff_term_pre_br(p, n); @@ -228,17 +241,26 @@ roff_term_pre_ti(ROFF_TERM_ARGS) return; len = term_hen(p, &su); - if (sign == 0) { + switch (sign) { + case 1: + if (p->tcol->offset + len <= maxoff) + p->ti = len; + else if (p->tcol->offset < maxoff) + p->ti = maxoff - p->tcol->offset; + else + p->ti = 0; + break; + case -1: + if ((size_t)len < p->tcol->offset) + p->ti = -len; + else + p->ti = -p->tcol->offset; + break; + default: + if ((size_t)len > maxoff) + len = maxoff; p->ti = len - p->tcol->offset; - p->tcol->offset = len; - } else if (sign == 1) { - p->ti = len; - p->tcol->offset += len; - } else if ((size_t)len < p->tcol->offset) { - p->ti = -len; - p->tcol->offset -= len; - } else { - p->ti = -p->tcol->offset; - p->tcol->offset = 0; + break; } + p->tcol->offset += p->ti; } diff --git a/roff_validate.c b/roff_validate.c index 9080f28797af..74eedafb9581 100644 --- a/roff_validate.c +++ b/roff_validate.c @@ -1,6 +1,6 @@ -/* $Id: roff_validate.c,v 1.18 2018/12/31 09:02:37 schwarze Exp $ */ +/* $Id: roff_validate.c,v 1.20 2020/06/22 19:20:40 schwarze Exp $ */ /* - * Copyright (c) 2010, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010, 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <assert.h> @@ -75,7 +77,7 @@ roff_valid_br(ROFF_VALID_ARGS) return; } - if ((np = n->prev) == NULL) + if ((np = roff_node_prev(n)) == NULL) return; switch (np->tok) { @@ -129,7 +131,7 @@ roff_valid_sp(ROFF_VALID_ARGS) { struct roff_node *np; - if ((np = n->prev) == NULL) + if ((np = roff_node_prev(n)) == NULL) return; switch (np->tok) { @@ -1,4 +1,4 @@ -/* $Id: soelim.c,v 1.5 2015/11/07 14:22:29 schwarze Exp $ */ +/* $Id: soelim.c,v 1.6 2021/09/19 18:14:24 schwarze Exp $ */ /* * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org> * All rights reserved. @@ -104,16 +104,16 @@ soelim_file(FILE *f, int flag) } walk = line + 3; - if (!isspace(*walk) && ((flag & C_OPTION) == 0)) { + if (!isspace((unsigned char)*walk) && (flag & C_OPTION) == 0) { printf("%s", line); continue; } - while (isspace(*walk)) + while (isspace((unsigned char)*walk)) walk++; cp = walk; - while (*cp != '\0' && !isspace(*cp)) + while (*cp != '\0' && !isspace((unsigned char)*cp)) cp++; *cp = 0; if (cp < line + linelen) @@ -1,6 +1,6 @@ -/* $Id: tag.c,v 1.24 2019/07/22 03:21:50 schwarze Exp $ */ +/* $Id: tag.c,v 1.36 2020/04/19 16:36:16 schwarze Exp $ */ /* - * Copyright (c) 2015, 2016, 2018, 2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,142 +13,109 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Functions to tag syntax tree nodes. + * For internal use by mandoc(1) validation modules only. */ #include "config.h" #include <sys/types.h> -#include <errno.h> +#include <assert.h> #include <limits.h> -#include <signal.h> #include <stddef.h> #include <stdint.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include "mandoc_aux.h" #include "mandoc_ohash.h" -#include "mandoc.h" +#include "roff.h" +#include "mdoc.h" +#include "roff_int.h" #include "tag.h" struct tag_entry { - size_t *lines; - size_t maxlines; - size_t nlines; + struct roff_node **nodes; + size_t maxnodes; + size_t nnodes; int prio; char s[]; }; -static void tag_signal(int) __attribute__((__noreturn__)); +static void tag_move_href(struct roff_man *, + struct roff_node *, const char *); +static void tag_move_id(struct roff_node *); static struct ohash tag_data; -static struct tag_files tag_files; /* - * Prepare for using a pager. - * Not all pagers are capable of using a tag file, - * but for simplicity, create it anyway. + * Set up the ohash table to collect nodes + * where various marked-up terms are documented. */ -struct tag_files * -tag_init(void) +void +tag_alloc(void) { - struct sigaction sa; - int ofd; - - ofd = -1; - tag_files.tfd = -1; - tag_files.tcpgid = -1; - - /* Clean up when dying from a signal. */ - - memset(&sa, 0, sizeof(sa)); - sigfillset(&sa.sa_mask); - sa.sa_handler = tag_signal; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - /* - * POSIX requires that a process calling tcsetpgrp(3) - * from the background gets a SIGTTOU signal. - * In that case, do not stop. - */ - - sa.sa_handler = SIG_IGN; - sigaction(SIGTTOU, &sa, NULL); - - /* Save the original standard output for use by the pager. */ - - if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) { - mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); - goto fail; - } + mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s)); +} - /* Create both temporary output files. */ +void +tag_free(void) +{ + struct tag_entry *entry; + unsigned int slot; - (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX", - sizeof(tag_files.ofn)); - (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX", - sizeof(tag_files.tfn)); - if ((ofd = mkstemp(tag_files.ofn)) == -1) { - mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, - "%s: %s", tag_files.ofn, strerror(errno)); - goto fail; - } - if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1) { - mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, - "%s: %s", tag_files.tfn, strerror(errno)); - goto fail; - } - if (dup2(ofd, STDOUT_FILENO) == -1) { - mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); - goto fail; + if (tag_data.info.free == NULL) + return; + entry = ohash_first(&tag_data, &slot); + while (entry != NULL) { + free(entry->nodes); + free(entry); + entry = ohash_next(&tag_data, &slot); } - close(ofd); - - /* - * Set up the ohash table to collect output line numbers - * where various marked-up terms are documented. - */ - - mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s)); - return &tag_files; - -fail: - tag_unlink(); - if (ofd != -1) - close(ofd); - if (tag_files.ofd != -1) - close(tag_files.ofd); - if (tag_files.tfd != -1) - close(tag_files.tfd); - *tag_files.ofn = '\0'; - *tag_files.tfn = '\0'; - tag_files.ofd = -1; - tag_files.tfd = -1; - return NULL; + ohash_delete(&tag_data); + tag_data.info.free = NULL; } /* - * Set the line number where a term is defined, + * Set a node where a term is defined, * unless it is already defined at a lower priority. */ void -tag_put(const char *s, int prio, size_t line) +tag_put(const char *s, int prio, struct roff_node *n) { struct tag_entry *entry; + struct roff_node *nold; const char *se; size_t len; unsigned int slot; - if (tag_files.tfd <= 0) - return; + assert(prio <= TAG_FALLBACK); - if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e')) - s += 2; + if (s == NULL) { + if (n->child == NULL || n->child->type != ROFFT_TEXT) + return; + s = n->child->string; + switch (s[0]) { + case '-': + s++; + break; + case '\\': + switch (s[1]) { + case '&': + case '-': + case 'e': + s += 2; + break; + default: + break; + } + break; + default: + break; + } + } /* * Skip whitespace and escapes and whatever follows, @@ -160,137 +127,201 @@ tag_put(const char *s, int prio, size_t line) return; se = s + len; - if (*se != '\0') - prio = INT_MAX; + if (*se != '\0' && prio < TAG_WEAK) + prio = TAG_WEAK; slot = ohash_qlookupi(&tag_data, s, &se); entry = ohash_find(&tag_data, slot); - if (entry == NULL) { - - /* Build a new entry. */ + /* Build a new entry. */ + if (entry == NULL) { entry = mandoc_malloc(sizeof(*entry) + len + 1); memcpy(entry->s, s, len); entry->s[len] = '\0'; - entry->lines = NULL; - entry->maxlines = entry->nlines = 0; + entry->nodes = NULL; + entry->maxnodes = entry->nnodes = 0; ohash_insert(&tag_data, slot, entry); + } - } else { - - /* - * Lower priority numbers take precedence, - * but 0 is special. - * A tag with priority 0 is only used - * if the tag occurs exactly once. - */ + /* + * Lower priority numbers take precedence. + * If a better entry is already present, ignore the new one. + */ - if (prio == 0) { - if (entry->prio == 0) - entry->prio = -1; + else if (entry->prio < prio) return; - } - /* A better entry is already present, ignore the new one. */ + /* + * If the existing entry is worse, clear it. + * In addition, a tag with priority TAG_FALLBACK + * is only used if the tag occurs exactly once. + */ - if (entry->prio > 0 && entry->prio < prio) + else if (entry->prio > prio || prio == TAG_FALLBACK) { + while (entry->nnodes > 0) { + nold = entry->nodes[--entry->nnodes]; + nold->flags &= ~NODE_ID; + free(nold->tag); + nold->tag = NULL; + } + if (prio == TAG_FALLBACK) { + entry->prio = TAG_DELETE; return; - - /* The existing entry is worse, clear it. */ - - if (entry->prio < 1 || entry->prio > prio) - entry->nlines = 0; + } } - /* Remember the new line. */ + /* Remember the new node. */ - if (entry->maxlines == entry->nlines) { - entry->maxlines += 4; - entry->lines = mandoc_reallocarray(entry->lines, - entry->maxlines, sizeof(*entry->lines)); + if (entry->maxnodes == entry->nnodes) { + entry->maxnodes += 4; + entry->nodes = mandoc_reallocarray(entry->nodes, + entry->maxnodes, sizeof(*entry->nodes)); } - entry->lines[entry->nlines++] = line; + entry->nodes[entry->nnodes++] = n; entry->prio = prio; + n->flags |= NODE_ID; + if (n->child == NULL || n->child->string != s || *se != '\0') { + assert(n->tag == NULL); + n->tag = mandoc_strndup(s, len); + } +} + +int +tag_exists(const char *tag) +{ + return ohash_find(&tag_data, ohash_qlookup(&tag_data, tag)) != NULL; } /* - * Write out the tags file using the previously collected - * information and clear the ohash table while going along. + * For in-line elements, move the link target + * to the enclosing paragraph when appropriate. */ -void -tag_write(void) +static void +tag_move_id(struct roff_node *n) { - FILE *stream; - struct tag_entry *entry; - size_t i; - unsigned int slot; - int empty; + struct roff_node *np; - if (tag_files.tfd <= 0) - return; - if (tag_files.tagname != NULL && ohash_find(&tag_data, - ohash_qlookup(&tag_data, tag_files.tagname)) == NULL) { - mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", tag_files.tagname); - tag_files.tagname = NULL; - } - if ((stream = fdopen(tag_files.tfd, "w")) == NULL) - mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); - empty = 1; - entry = ohash_first(&tag_data, &slot); - while (entry != NULL) { - if (stream != NULL && entry->prio >= 0) { - for (i = 0; i < entry->nlines; i++) { - fprintf(stream, "%s %s %zu\n", - entry->s, tag_files.ofn, entry->lines[i]); - empty = 0; + np = n; + for (;;) { + if (np->prev != NULL) + np = np->prev; + else if ((np = np->parent) == NULL) + return; + switch (np->tok) { + case MDOC_It: + switch (np->parent->parent->norm->Bl.type) { + case LIST_column: + /* Target the ROFFT_BLOCK = <tr>. */ + np = np->parent; + break; + case LIST_diag: + case LIST_hang: + case LIST_inset: + case LIST_ohang: + case LIST_tag: + /* Target the ROFFT_HEAD = <dt>. */ + np = np->parent->head; + break; + default: + /* Target the ROFF_BODY = <li>. */ + break; + } + /* FALLTHROUGH */ + case MDOC_Pp: /* Target the ROFFT_ELEM = <p>. */ + if (np->tag == NULL) { + np->tag = mandoc_strdup(n->tag == NULL ? + n->child->string : n->tag); + np->flags |= NODE_ID; + n->flags &= ~NODE_ID; } + return; + case MDOC_Sh: + case MDOC_Ss: + case MDOC_Bd: + case MDOC_Bl: + case MDOC_D1: + case MDOC_Dl: + case MDOC_Rs: + /* Do not move past major blocks. */ + return; + default: + /* + * Move past in-line content and partial + * blocks, for example .It Xo or .It Bq Er. + */ + break; } - free(entry->lines); - free(entry); - entry = ohash_next(&tag_data, &slot); - } - ohash_delete(&tag_data); - if (stream != NULL) - fclose(stream); - else - close(tag_files.tfd); - tag_files.tfd = -1; - if (empty) { - unlink(tag_files.tfn); - *tag_files.tfn = '\0'; } } -void -tag_unlink(void) +/* + * When a paragraph is tagged and starts with text, + * move the permalink to the first few words. + */ +static void +tag_move_href(struct roff_man *man, struct roff_node *n, const char *tag) { - pid_t tc_pgid; - - if (tag_files.tcpgid != -1) { - tc_pgid = tcgetpgrp(tag_files.ofd); - if (tc_pgid == tag_files.pager_pid || - tc_pgid == getpgid(0) || - getpgid(tc_pgid) == -1) - (void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid); + char *cp; + + if (n == NULL || n->type != ROFFT_TEXT || + *n->string == '\0' || *n->string == ' ') + return; + + cp = n->string; + while (cp != NULL && cp - n->string < 5) + cp = strchr(cp + 1, ' '); + + /* If the first text node is longer, split it. */ + + if (cp != NULL && cp[1] != '\0') { + man->last = n; + man->next = ROFF_NEXT_SIBLING; + roff_word_alloc(man, n->line, + n->pos + (cp - n->string), cp + 1); + man->last->flags = n->flags & ~NODE_LINE; + *cp = '\0'; } - if (*tag_files.ofn != '\0') - unlink(tag_files.ofn); - if (*tag_files.tfn != '\0') - unlink(tag_files.tfn); + + assert(n->tag == NULL); + n->tag = mandoc_strdup(tag); + n->flags |= NODE_HREF; } -static void -tag_signal(int signum) +/* + * When all tags have been set, decide where to put + * the associated permalinks, and maybe move some tags + * to the beginning of the respective paragraphs. + */ +void +tag_postprocess(struct roff_man *man, struct roff_node *n) { - struct sigaction sa; - - tag_unlink(); - memset(&sa, 0, sizeof(sa)); - sigemptyset(&sa.sa_mask); - sa.sa_handler = SIG_DFL; - sigaction(signum, &sa, NULL); - kill(getpid(), signum); - /* NOTREACHED */ - _exit(1); + if (n->flags & NODE_ID) { + switch (n->tok) { + case MDOC_Pp: + tag_move_href(man, n->next, n->tag); + break; + case MDOC_Bd: + case MDOC_D1: + case MDOC_Dl: + tag_move_href(man, n->child, n->tag); + break; + case MDOC_Bl: + /* XXX No permalink for now. */ + break; + default: + if (n->type == ROFFT_ELEM || n->tok == MDOC_Fo) + tag_move_id(n); + if (n->tok != MDOC_Tg) + n->flags |= NODE_HREF; + else if ((n->flags & NODE_ID) == 0) { + n->flags |= NODE_NOPRT; + free(n->tag); + n->tag = NULL; + } + break; + } + } + for (n = n->child; n != NULL; n = n->next) + tag_postprocess(man, n); } @@ -1,6 +1,6 @@ -/* $Id: tag.h,v 1.8 2018/11/22 11:30:23 schwarze Exp $ */ +/* $Id: tag.h,v 1.14 2020/04/18 20:40:10 schwarze Exp $ */ /* - * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -13,20 +13,23 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces to tag syntax tree nodes. + * For use by mandoc(1) validation modules only. */ -struct tag_files { - char ofn[20]; - char tfn[20]; - char *tagname; - int ofd; - int tfd; - pid_t tcpgid; - pid_t pager_pid; -}; - +/* + * Tagging priorities. + * Lower numbers indicate higher importance. + */ +#define TAG_MANUAL 1 /* Set with a .Tg macro. */ +#define TAG_STRONG 2 /* Good automatic tagging. */ +#define TAG_WEAK (INT_MAX - 2) /* Dubious automatic tagging. */ +#define TAG_FALLBACK (INT_MAX - 1) /* Tag only used if unique. */ +#define TAG_DELETE (INT_MAX) /* Tag not used at all. */ -struct tag_files *tag_init(void); -void tag_put(const char *, int, size_t); -void tag_write(void); -void tag_unlink(void); +void tag_alloc(void); +int tag_exists(const char *); +void tag_put(const char *, int, struct roff_node *); +void tag_postprocess(struct roff_man *, struct roff_node *); +void tag_free(void); @@ -1,4 +1,4 @@ -.\" $Id: tbl.7,v 1.34 2019/03/02 21:03:02 schwarze Exp $ +.\" $Id: tbl.7,v 1.37 2021/09/18 12:34:27 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2014,2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 2 2019 $ +.Dd $Mdocdate: September 18 2021 $ .Dt TBL 7 .Os .Sh NAME @@ -94,7 +94,7 @@ Allow page breaks within the table. This is a GNU extension and currently ignored. .It Cm nospaces Ignore leading and trailing spaces in data cells. -This is a GNU extension and currently ignored. +This is a GNU extension. .It Cm nowarn Suppress warnings about tables exceeding the current line length. This is a GNU extension and currently ignored. @@ -178,10 +178,11 @@ of any other column also having the .Cm e modifier. .It Cm f -The next character selects the font to use for this cell. +The next one or two characters select the font to use for this cell. +One-character font names must be followed by a blank or period. See the .Xr roff 7 -manual for supported one-character font names. +manual for supported font names. .It Cm i Use an italic font for the contents of this cell. .It Cm m @@ -416,7 +417,7 @@ equations inside tables. .Xr roff 7 .Rs .%A M. E. Lesk -.%T Tbl\(emA Program to Format Tables +.%T Tbl \(em A Program to Format Tables .%D June 11, 1976 .Re .Sh HISTORY @@ -1,7 +1,7 @@ -/* $Id: tbl.h,v 1.1 2018/12/12 21:54:35 schwarze Exp $ */ +/* $Id: tbl.h,v 1.2 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -57,14 +57,13 @@ struct tbl_cell { int vert; /* Width of subsequent vertical line. */ int col; /* Column number, starting from 0. */ int flags; -#define TBL_CELL_BOLD (1 << 0) /* b, B, fB */ -#define TBL_CELL_ITALIC (1 << 1) /* i, I, fI */ #define TBL_CELL_TALIGN (1 << 2) /* t, T */ #define TBL_CELL_UP (1 << 3) /* u, U */ #define TBL_CELL_BALIGN (1 << 4) /* d, D */ #define TBL_CELL_WIGN (1 << 5) /* z, Z */ #define TBL_CELL_EQUAL (1 << 6) /* e, E */ #define TBL_CELL_WMAX (1 << 7) /* x, X */ + enum mandoc_esc font; enum tbl_cellt pos; }; diff --git a/tbl_data.c b/tbl_data.c index 9a58d3256239..fe0259f9227a 100644 --- a/tbl_data.c +++ b/tbl_data.c @@ -1,7 +1,7 @@ -/* $Id: tbl_data.c,v 1.52 2019/02/09 16:00:39 schwarze Exp $ */ +/* $Id: tbl_data.c,v 1.59 2021/09/10 13:24:38 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011,2015,2017,2018,2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011,2015,2017-2019,2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,6 +21,7 @@ #include <assert.h> #include <ctype.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -45,16 +46,20 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, struct tbl_dat *dat, *pdat; struct tbl_cell *cp; struct tbl_span *pdp; - int sv; + const char *ccp; + int startpos, endpos; /* * Determine the length of the string in the cell * and advance the parse point to the end of the cell. */ - sv = *pos; - while (p[*pos] != '\0' && p[*pos] != tbl->opts.tab) - (*pos)++; + startpos = *pos; + ccp = p + startpos; + while (*ccp != '\0' && *ccp != tbl->opts.tab) + if (*ccp++ == '\\') + mandoc_escape(&ccp, NULL, NULL); + *pos = ccp - p; /* Advance to the next layout cell, skipping spanners. */ @@ -73,12 +78,14 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, if (dp->layout->last->col + 1 < dp->opts->cols) { cp = mandoc_calloc(1, sizeof(*cp)); cp->pos = TBL_CELL_LEFT; + cp->font = ESCAPE_FONTROMAN; + cp->spacing = SIZE_MAX; dp->layout->last->next = cp; cp->col = dp->layout->last->col + 1; dp->layout->last = cp; } else { mandoc_msg(MANDOCERR_TBLDATA_EXTRA, - ln, sv, "%s", p + sv); + ln, startpos, "%s", p + startpos); while (p[*pos] != '\0') (*pos)++; return; @@ -103,7 +110,8 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, */ if (cp->pos == TBL_CELL_DOWN || - (*pos - sv == 2 && p[sv] == '\\' && p[sv + 1] == '^')) { + (*pos - startpos == 2 && + p[startpos] == '\\' && p[startpos + 1] == '^')) { pdp = dp; while ((pdp = pdp->prev) != NULL) { pdat = pdp->first; @@ -139,18 +147,29 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, dp->last->next = dat; dp->last = dat; + /* Strip leading and trailing spaces, if requested. */ + + endpos = *pos; + if (dp->opts->opts & TBL_OPT_NOSPACE) { + while (p[startpos] == ' ') + startpos++; + while (endpos > startpos && p[endpos - 1] == ' ') + endpos--; + } + /* * Check for a continued-data scope opening. This consists of a * trailing `T{' at the end of the line. Subsequent lines, * until a standalone `T}', are included in our cell. */ - if (*pos - sv == 2 && p[sv] == 'T' && p[sv + 1] == '{') { + if (endpos - startpos == 2 && + p[startpos] == 'T' && p[startpos + 1] == '{') { tbl->part = TBL_PART_CDATA; return; } - dat->string = mandoc_strndup(p + sv, *pos - sv); + dat->string = mandoc_strndup(p + startpos, endpos - startpos); if (p[*pos] != '\0') (*pos)++; @@ -171,7 +190,7 @@ getdata(struct tbl_node *tbl, struct tbl_span *dp, dat->layout->pos == TBL_CELL_DOWN) && dat->pos == TBL_DATA_DATA && *dat->string != '\0') mandoc_msg(MANDOCERR_TBLDATA_SPAN, - ln, sv, "%s", dat->string); + ln, startpos, "%s", dat->string); } void @@ -184,6 +203,9 @@ tbl_cdata(struct tbl_node *tbl, int ln, const char *p, int pos) if (p[pos] == 'T' && p[pos + 1] == '}') { pos += 2; + if (tbl->opts.opts & TBL_OPT_NOSPACE) + while (p[pos] == ' ') + pos++; if (p[pos] == tbl->opts.tab) { tbl->part = TBL_PART_DATA; pos++; @@ -242,10 +264,11 @@ tbl_data(struct tbl_node *tbl, int ln, const char *p, int pos) struct tbl_cell *cp; struct tbl_span *sp; - rp = (sp = tbl->last_span) == NULL ? tbl->first_row : - sp->pos == TBL_SPAN_DATA && sp->layout->next != NULL ? - sp->layout->next : sp->layout; - + for (sp = tbl->last_span; sp != NULL; sp = sp->prev) + if (sp->pos == TBL_SPAN_DATA) + break; + rp = sp == NULL ? tbl->first_row : + sp->layout->next == NULL ? sp->layout : sp->layout->next; assert(rp != NULL); if (p[1] == '\0') { diff --git a/tbl_html.c b/tbl_html.c index e137757dc16c..65c8ae8f887c 100644 --- a/tbl_html.c +++ b/tbl_html.c @@ -1,7 +1,7 @@ -/* $Id: tbl_html.c,v 1.33 2019/03/17 18:21:45 schwarze Exp $ */ +/* $Id: tbl_html.c,v 1.38 2021/09/09 16:52:52 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -115,10 +115,14 @@ print_tbl(struct html *h, const struct tbl_span *sp) const struct tbl_dat *dp; const struct tbl_cell *cp; const struct tbl_span *psp; + const struct roffcol *col; struct tag *tt; const char *hspans, *vspans, *halign, *valign; const char *bborder, *lborder, *rborder; + const char *ccp; char hbuf[4], vbuf[4]; + size_t sz; + enum mandoc_esc save_font; int i; if (h->tblt == NULL) @@ -240,8 +244,40 @@ print_tbl(struct html *h, const struct tbl_span *sp) "vertical-align", valign, "text-align", halign, "border-right-style", rborder); - if (dp->string != NULL) + if (dp->layout->pos == TBL_CELL_HORIZ || + dp->layout->pos == TBL_CELL_DHORIZ || + dp->pos == TBL_DATA_HORIZ || + dp->pos == TBL_DATA_DHORIZ) + print_otag(h, TAG_HR, ""); + else if (dp->string != NULL) { + save_font = h->metac; + html_setfont(h, dp->layout->font); + if (dp->layout->pos == TBL_CELL_LONG) + print_text(h, "\\[u2003]"); /* em space */ print_text(h, dp->string); + if (dp->layout->pos == TBL_CELL_NUMBER) { + col = h->tbl.cols + dp->layout->col; + if (col->decimal < col->nwidth) { + if ((ccp = strrchr(dp->string, + sp->opts->decimal)) == NULL) { + /* Punctuation space. */ + print_text(h, "\\[u2008]"); + ccp = strchr(dp->string, '\0'); + } else + ccp++; + sz = col->nwidth - col->decimal; + while (--sz > 0) { + if (*ccp == '\0') + /* Figure space. */ + print_text(h, + "\\[u2007]"); + else + ccp++; + } + } + } + html_setfont(h, save_font); + } } print_tagq(h, tt); diff --git a/tbl_layout.c b/tbl_layout.c index 58599705c18c..171a8dbb1b49 100644 --- a/tbl_layout.c +++ b/tbl_layout.c @@ -1,7 +1,8 @@ -/* $Id: tbl_layout.c,v 1.48 2018/12/14 05:18:03 schwarze Exp $ */ +/* $Id: tbl_layout.c,v 1.50 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2012, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2012, 2014, 2015, 2017, 2020, 2021 + * Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -65,7 +66,10 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp, int ln, const char *p, int *pos) { char *endptr; + unsigned long spacing; size_t sz; + int isz; + enum mandoc_esc fontesc; mod: while (p[*pos] == ' ' || p[*pos] == '\t') @@ -93,14 +97,18 @@ mod: /* Parse numerical spacing from modifier string. */ if (isdigit((unsigned char)p[*pos])) { - cp->spacing = strtoull(p + *pos, &endptr, 10); + if ((spacing = strtoul(p + *pos, &endptr, 10)) > 9) + mandoc_msg(MANDOCERR_TBLLAYOUT_SPC, ln, *pos, + "%lu", spacing); + else + cp->spacing = spacing; *pos = endptr - p; goto mod; } switch (tolower((unsigned char)p[(*pos)++])) { case 'b': - cp->flags |= TBL_CELL_BOLD; + cp->font = ESCAPE_FONTBOLD; goto mod; case 'd': cp->flags |= TBL_CELL_BALIGN; @@ -111,7 +119,7 @@ mod: case 'f': break; case 'i': - cp->flags |= TBL_CELL_ITALIC; + cp->font = ESCAPE_FONTITALIC; goto mod; case 'm': mandoc_msg(MANDOCERR_TBLLAYOUT_MOD, ln, *pos, "m"); @@ -165,40 +173,34 @@ mod: goto mod; } + while (p[*pos] == ' ' || p[*pos] == '\t') + (*pos)++; + /* Ignore parenthised font names for now. */ if (p[*pos] == '(') goto mod; - /* Support only one-character font-names for now. */ - - if (p[*pos] == '\0' || (p[*pos + 1] != ' ' && p[*pos + 1] != '.')) { + isz = 0; + if (p[*pos] != '\0') + isz++; + if (strchr(" \t.", p[*pos + isz]) == NULL) + isz++; + + fontesc = mandoc_font(p + *pos, isz); + + switch (fontesc) { + case ESCAPE_FONTPREV: + case ESCAPE_ERROR: mandoc_msg(MANDOCERR_FT_BAD, ln, *pos, "TS %s", p + *pos - 1); - if (p[*pos] != '\0') - (*pos)++; - if (p[*pos] != '\0') - (*pos)++; - goto mod; - } - - switch (p[(*pos)++]) { - case '3': - case 'B': - cp->flags |= TBL_CELL_BOLD; - goto mod; - case '2': - case 'I': - cp->flags |= TBL_CELL_ITALIC; - goto mod; - case '1': - case 'R': - goto mod; + break; default: - mandoc_msg(MANDOCERR_FT_BAD, - ln, *pos - 1, "TS f%c", p[*pos - 1]); - goto mod; + cp->font = fontesc; + break; } + *pos += isz; + goto mod; } static void @@ -357,6 +359,7 @@ cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) p = mandoc_calloc(1, sizeof(*p)); p->spacing = SIZE_MAX; + p->font = ESCAPE_FONTROMAN; p->pos = pos; if ((pp = rp->last) != NULL) { diff --git a/tbl_term.c b/tbl_term.c index de88d61c15e1..eac125586c4d 100644 --- a/tbl_term.c +++ b/tbl_term.c @@ -1,7 +1,7 @@ -/* $Id: tbl_term.c,v 1.72 2019/07/01 22:56:24 schwarze Exp $ */ +/* $Id: tbl_term.c,v 1.75 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -190,17 +190,6 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin); - /* Tables leak .ta settings to subsequent text. */ - - term_tab_set(tp, NULL); - coloff = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) || - sp->opts->lvert; - for (ic = 0; ic < sp->opts->cols; ic++) { - coloff += tp->tbl.cols[ic].width; - term_tab_iset(coloff); - coloff += tp->tbl.cols[ic].spacing; - } - /* Center the table as a whole. */ offset = tp->tcol->offset; @@ -267,11 +256,11 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) hspans--; continue; } - if (dp == NULL) - continue; - hspans = dp->hspans; - if (ic || sp->layout->first->pos != TBL_CELL_SPAN) + if (dp != NULL && + (ic || sp->layout->first->pos != TBL_CELL_SPAN)) { + hspans = dp->hspans; dp = dp->next; + } } /* Set up a column for a right vertical frame. */ @@ -302,11 +291,11 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) tp->tcol++; tp->col = 0; tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic); - if (dp == NULL) - continue; - hspans = dp->hspans; - if (cp->pos != TBL_CELL_SPAN) + if (dp != NULL && + (ic || sp->layout->first->pos != TBL_CELL_SPAN)) { + hspans = dp->hspans; dp = dp->next; + } } break; } @@ -425,11 +414,10 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) cp = cp->next; continue; } - if (dp != NULL) { + if (dp != NULL && (ic || + sp->layout->first->pos != TBL_CELL_SPAN)) { hspans = dp->hspans; - if (ic || sp->layout->first->pos - != TBL_CELL_SPAN) - dp = dp->next; + dp = dp->next; } /* @@ -935,10 +923,24 @@ tbl_word(struct termp *tp, const struct tbl_dat *dp) int prev_font; prev_font = tp->fonti; - if (dp->layout->flags & TBL_CELL_BOLD) - term_fontpush(tp, TERMFONT_BOLD); - else if (dp->layout->flags & TBL_CELL_ITALIC) - term_fontpush(tp, TERMFONT_UNDER); + switch (dp->layout->font) { + case ESCAPE_FONTBI: + term_fontpush(tp, TERMFONT_BI); + break; + case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: + term_fontpush(tp, TERMFONT_BOLD); + break; + case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: + term_fontpush(tp, TERMFONT_UNDER); + break; + case ESCAPE_FONTROMAN: + case ESCAPE_FONTCR: + break; + default: + abort(); + } term_word(tp, dp->string); @@ -1,7 +1,7 @@ -/* $Id: term.c,v 1.281 2019/06/03 20:23:41 schwarze Exp $ */ +/* $Id: term.c,v 1.283 2021/08/10 12:55:04 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -38,8 +38,7 @@ static void bufferc(struct termp *, char); static void encode(struct termp *, const char *, size_t); static void encode1(struct termp *, int); static void endline(struct termp *); -static void term_field(struct termp *, size_t, size_t, - size_t, size_t); +static void term_field(struct termp *, size_t, size_t); static void term_fill(struct termp *, size_t *, size_t *, size_t); @@ -127,8 +126,7 @@ term_flushln(struct termp *p) * and with the BRNEVER flag, never break it at all. */ - vtarget = p->flags & TERMP_BRNEVER ? SIZE_MAX : - (p->flags & TERMP_NOBREAK) == 0 ? vfield : + vtarget = (p->flags & TERMP_NOBREAK) == 0 ? vfield : p->maxrmargin > p->viscol + vbl ? p->maxrmargin - p->viscol - vbl : 0; @@ -137,7 +135,8 @@ term_flushln(struct termp *p) * If there is whitespace only, print nothing. */ - term_fill(p, &nbr, &vbr, vtarget); + term_fill(p, &nbr, &vbr, + p->flags & TERMP_BRNEVER ? SIZE_MAX : vtarget); if (nbr == 0) break; @@ -156,7 +155,7 @@ term_flushln(struct termp *p) /* Finally, print the field content. */ - term_field(p, vbl, nbr, vbr, vtarget); + term_field(p, vbl, nbr); /* * If there is no text left in the field, exit the loop. @@ -345,12 +344,10 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget) /* * Print the contents of one field * with an indentation of vbl visual columns, - * an input string length of nbr characters, - * an output width of vbr visual columns, - * and a desired field width of vtarget visual columns. + * and an input string length of nbr characters. */ static void -term_field(struct termp *p, size_t vbl, size_t nbr, size_t vbr, size_t vtarget) +term_field(struct termp *p, size_t vbl, size_t nbr) { size_t ic; /* Character position in the input buffer. */ size_t vis; /* Visual position of the current character. */ @@ -592,16 +589,18 @@ term_word(struct termp *p, const char *word) uc = *seq; break; case ESCAPE_FONTBOLD: + case ESCAPE_FONTCB: term_fontrepl(p, TERMFONT_BOLD); continue; case ESCAPE_FONTITALIC: + case ESCAPE_FONTCI: term_fontrepl(p, TERMFONT_UNDER); continue; case ESCAPE_FONTBI: term_fontrepl(p, TERMFONT_BI); continue; case ESCAPE_FONT: - case ESCAPE_FONTCW: + case ESCAPE_FONTCR: case ESCAPE_FONTROMAN: term_fontrepl(p, TERMFONT_NONE); continue; diff --git a/term_ascii.c b/term_ascii.c index 368623cac105..bf7e9b639e04 100644 --- a/term_ascii.c +++ b/term_ascii.c @@ -1,7 +1,7 @@ -/* $Id: term_ascii.c,v 1.64 2018/11/28 14:23:06 schwarze Exp $ */ +/* $Id: term_ascii.c,v 1.66 2020/09/09 13:45:05 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -232,7 +232,10 @@ ascii_endline(struct termp *p) { p->line++; - p->tcol->offset -= p->ti; + if ((int)p->tcol->offset > p->ti) + p->tcol->offset -= p->ti; + else + p->tcol->offset = 0; p->ti = 0; putchar('\n'); } @@ -242,7 +245,14 @@ ascii_advance(struct termp *p, size_t len) { size_t i; - assert(len < UINT16_MAX); + /* + * XXX We used to have "assert(len < UINT16_MAX)" here. + * that is not quite right because the input document + * can trigger that by merely providing large input. + * For now, simply truncate. + */ + if (len > 256) + len = 256; for (i = 0; i < len; i++) putchar(' '); } @@ -380,7 +390,14 @@ locale_advance(struct termp *p, size_t len) { size_t i; - assert(len < UINT16_MAX); + /* + * XXX We used to have "assert(len < UINT16_MAX)" here. + * that is not quite right because the input document + * can trigger that by merely providing large input. + * For now, simply truncate. + */ + if (len > 256) + len = 256; for (i = 0; i < len; i++) putwchar(L' '); } @@ -390,7 +407,10 @@ locale_endline(struct termp *p) { p->line++; - p->tcol->offset -= p->ti; + if ((int)p->tcol->offset > p->ti) + p->tcol->offset -= p->ti; + else + p->tcol->offset = 0; p->ti = 0; putwchar(L'\n'); } diff --git a/term_ps.c b/term_ps.c index 2cd94c923156..374d3d9a6abd 100644 --- a/term_ps.c +++ b/term_ps.c @@ -1,7 +1,7 @@ -/* $Id: term_ps.c,v 1.91 2017/11/10 23:42:52 schwarze Exp $ */ +/* $Id: term_ps.c,v 1.92 2020/09/06 14:45:22 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2014,2015,2016,2017,2020 Ingo Schwarze <schwarze@openbsd.org> * Copyright (c) 2017 Marc Espie <espie@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -1252,7 +1252,10 @@ ps_endline(struct termp *p) ps_closepage(p); - p->tcol->offset -= p->ti; + if ((int)p->tcol->offset > p->ti) + p->tcol->offset -= p->ti; + else + p->tcol->offset = 0; p->ti = 0; } diff --git a/term_tab.c b/term_tab.c index 3343244f3c8f..84b4c00c6e65 100644 --- a/term_tab.c +++ b/term_tab.c @@ -1,4 +1,4 @@ -/* $Id: term_tab.c,v 1.5 2018/12/16 00:21:05 schwarze Exp $ */ +/* $Id: term_tab.c,v 1.6 2020/06/22 19:20:40 schwarze Exp $ */ /* * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> * @@ -14,6 +14,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "config.h" + #include <sys/types.h> #include <stddef.h> diff --git a/term_tag.c b/term_tag.c new file mode 100644 index 000000000000..c26b942531fc --- /dev/null +++ b/term_tag.c @@ -0,0 +1,227 @@ +/* $Id: term_tag.c,v 1.6 2021/03/30 17:16:55 schwarze Exp $ */ +/* + * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Functions to write a ctags(1) file. + * For use by the mandoc(1) ASCII and UTF-8 formatters only. + */ +#include "config.h" + +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mandoc.h" +#include "roff.h" +#include "roff_int.h" +#include "tag.h" +#include "term_tag.h" + +static void tag_signal(int) __attribute__((__noreturn__)); + +static struct tag_files tag_files; + + +/* + * Prepare for using a pager. + * Not all pagers are capable of using a tag file, + * but for simplicity, create it anyway. + */ +struct tag_files * +term_tag_init(const char *outfilename, const char *suffix, + const char *tagfilename) +{ + struct sigaction sa; + int ofd; /* In /tmp/, dup(2)ed to stdout. */ + int tfd; + + ofd = tfd = -1; + tag_files.tfs = NULL; + tag_files.tcpgid = -1; + + /* Clean up when dying from a signal. */ + + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = tag_signal; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + /* + * POSIX requires that a process calling tcsetpgrp(3) + * from the background gets a SIGTTOU signal. + * In that case, do not stop. + */ + + sa.sa_handler = SIG_IGN; + sigaction(SIGTTOU, &sa, NULL); + + /* Save the original standard output for use by the pager. */ + + if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) { + mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); + goto fail; + } + + /* Create both temporary output files. */ + + if (outfilename == NULL) { + (void)snprintf(tag_files.ofn, sizeof(tag_files.ofn), + "/tmp/man.XXXXXXXXXX%s", suffix); + if ((ofd = mkstemps(tag_files.ofn, strlen(suffix))) == -1) { + mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, + "%s: %s", tag_files.ofn, strerror(errno)); + goto fail; + } + } else { + (void)strlcpy(tag_files.ofn, outfilename, + sizeof(tag_files.ofn)); + unlink(outfilename); + ofd = open(outfilename, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (ofd == -1) { + mandoc_msg(MANDOCERR_OPEN, 0, 0, + "%s: %s", outfilename, strerror(errno)); + goto fail; + } + } + if (tagfilename == NULL) { + (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX", + sizeof(tag_files.tfn)); + if ((tfd = mkstemp(tag_files.tfn)) == -1) { + mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, + "%s: %s", tag_files.tfn, strerror(errno)); + goto fail; + } + } else { + (void)strlcpy(tag_files.tfn, tagfilename, + sizeof(tag_files.tfn)); + unlink(tagfilename); + tfd = open(tagfilename, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (tfd == -1) { + mandoc_msg(MANDOCERR_OPEN, 0, 0, + "%s: %s", tagfilename, strerror(errno)); + goto fail; + } + } + if ((tag_files.tfs = fdopen(tfd, "w")) == NULL) { + mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); + goto fail; + } + tfd = -1; + if (dup2(ofd, STDOUT_FILENO) == -1) { + mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); + goto fail; + } + close(ofd); + return &tag_files; + +fail: + term_tag_unlink(); + if (ofd != -1) + close(ofd); + if (tfd != -1) + close(tfd); + if (tag_files.ofd != -1) { + close(tag_files.ofd); + tag_files.ofd = -1; + } + return NULL; +} + +void +term_tag_write(struct roff_node *n, size_t line) +{ + const char *cp; + int len; + + if (tag_files.tfs == NULL) + return; + cp = n->tag == NULL ? n->child->string : n->tag; + if (cp[0] == '\\' && (cp[1] == '&' || cp[1] == 'e')) + cp += 2; + len = strcspn(cp, " \t\\"); + fprintf(tag_files.tfs, "%.*s %s %zu\n", + len, cp, tag_files.ofn, line); +} + +/* + * Close both output files and restore the original standard output + * to the terminal. In the unlikely case that the latter fails, + * trying to start a pager would be useless, so report the failure + * to the main program. + */ +int +term_tag_close(void) +{ + int irc = 0; + + if (tag_files.tfs != NULL) { + fclose(tag_files.tfs); + tag_files.tfs = NULL; + } + if (tag_files.ofd != -1) { + fflush(stdout); + if ((irc = dup2(tag_files.ofd, STDOUT_FILENO)) == -1) + mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); + close(tag_files.ofd); + tag_files.ofd = -1; + } + return irc; +} + +void +term_tag_unlink(void) +{ + pid_t tc_pgid; + + if (tag_files.tcpgid != -1) { + tc_pgid = tcgetpgrp(STDOUT_FILENO); + if (tc_pgid == tag_files.pager_pid || + tc_pgid == getpgid(0) || + getpgid(tc_pgid) == -1) + (void)tcsetpgrp(STDOUT_FILENO, tag_files.tcpgid); + } + if (strncmp(tag_files.ofn, "/tmp/man.", 9) == 0) { + unlink(tag_files.ofn); + *tag_files.ofn = '\0'; + } + if (strncmp(tag_files.tfn, "/tmp/man.", 9) == 0) { + unlink(tag_files.tfn); + *tag_files.tfn = '\0'; + } +} + +static void +tag_signal(int signum) +{ + struct sigaction sa; + + term_tag_unlink(); + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_DFL; + sigaction(signum, &sa, NULL); + kill(getpid(), signum); + /* NOTREACHED */ + _exit(1); +} diff --git a/term_tag.h b/term_tag.h new file mode 100644 index 000000000000..f82b1a6e9223 --- /dev/null +++ b/term_tag.h @@ -0,0 +1,34 @@ +/* $Id: term_tag.h,v 1.4 2021/03/30 17:16:55 schwarze Exp $ */ +/* + * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Internal interfaces to write a ctags(1) file. + * For use by the mandoc(1) ASCII and UTF-8 formatters only. + */ + +struct tag_files { + char ofn[80]; /* Output file name. */ + char tfn[80]; /* Tag file name. */ + FILE *tfs; /* Tag file object. */ + int ofd; /* Original output file descriptor. */ + pid_t tcpgid; /* Process group controlling the terminal. */ + pid_t pager_pid; /* Process ID of the pager. */ +}; + + +struct tag_files *term_tag_init(const char *, const char *, const char *); +void term_tag_write(struct roff_node *, size_t); +int term_tag_close(void); +void term_tag_unlink(void); diff --git a/test-attribute.c b/test-attribute.c new file mode 100644 index 000000000000..adbcc9268252 --- /dev/null +++ b/test-attribute.c @@ -0,0 +1,48 @@ +/* $Id: test-attribute.c,v 1.1 2020/06/22 20:00:38 schwarze Exp $ */ +/* + * Copyright (c) 2020 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +void var_arg(const char *, ...) + __attribute__((__format__ (__printf__, 1, 2))); +void no_ret(int) + __attribute__((__noreturn__)); + +void +var_arg(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +void +no_ret(int i) +{ + exit(i); +} + +int +main(void) +{ + var_arg("Test output: %d\n", 42); + no_ret(0); +} diff --git a/test-mkstemps.c b/test-mkstemps.c new file mode 100644 index 000000000000..31460dcae428 --- /dev/null +++ b/test-mkstemps.c @@ -0,0 +1,12 @@ +#include <stdlib.h> +#include <unistd.h> + +int +main(void) +{ + char filename[] = "/tmp/temp.XXXXXX.suffix"; + + if (mkstemps(filename, 7) == -1) + return 1; + return unlink(filename) == -1; +} @@ -1,7 +1,7 @@ -/* $Id: tree.c,v 1.84 2019/01/01 05:56:34 schwarze Exp $ */ +/* $Id: tree.c,v 1.91 2021/09/07 10:59:18 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> - * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2013-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,6 +14,9 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Formatting module to let mandoc(1) show + * a human readable representation of the syntax tree. */ #include "config.h" @@ -33,7 +36,9 @@ #include "eqn.h" #include "main.h" +static void print_attr(const struct roff_node *); static void print_box(const struct eqn_box *, int); +static void print_cellt(enum tbl_cellt); static void print_man(const struct roff_node *, int); static void print_meta(const struct roff_meta *); static void print_mdoc(const struct roff_node *, int); @@ -187,28 +192,8 @@ print_mdoc(const struct roff_node *n, int indent) if (argv[i].sz > 0) printf(" ]"); } - - putchar(' '); - if (n->flags & NODE_DELIMO) - putchar('('); - if (n->flags & NODE_LINE) - putchar('*'); - printf("%d:%d", n->line, n->pos + 1); - if (n->flags & NODE_DELIMC) - putchar(')'); - if (n->flags & NODE_EOS) - putchar('.'); - if (n->flags & NODE_BROKEN) - printf(" BROKEN"); - if (n->flags & NODE_NOFILL) - printf(" NOFILL"); - if (n->flags & NODE_NOSRC) - printf(" NOSRC"); - if (n->flags & NODE_NOPRT) - printf(" NOPRT"); - putchar('\n'); + print_attr(n); } - if (n->eqn) print_box(n->eqn->first, indent + 4); if (n->child) @@ -289,19 +274,9 @@ print_man(const struct roff_node *n, int indent) } else { for (i = 0; i < indent; i++) putchar(' '); - printf("%s (%s) ", p, t); - if (n->flags & NODE_LINE) - putchar('*'); - printf("%d:%d", n->line, n->pos + 1); - if (n->flags & NODE_DELIMC) - putchar(')'); - if (n->flags & NODE_EOS) - putchar('.'); - if (n->flags & NODE_NOFILL) - printf(" NOFILL"); - putchar('\n'); + printf("%s (%s)", p, t); + print_attr(n); } - if (n->eqn) print_box(n->eqn->first, indent + 4); if (n->child) @@ -312,6 +287,40 @@ print_man(const struct roff_node *n, int indent) } static void +print_attr(const struct roff_node *n) +{ + putchar(' '); + if (n->flags & NODE_DELIMO) + putchar('('); + if (n->flags & NODE_LINE) + putchar('*'); + printf("%d:%d", n->line, n->pos + 1); + if (n->flags & NODE_DELIMC) + putchar(')'); + if (n->flags & NODE_EOS) + putchar('.'); + if (n->flags & NODE_ID) { + printf(" ID"); + if (n->flags & NODE_HREF) + printf("=HREF"); + } else if (n->flags & NODE_HREF) + printf(" HREF"); + else if (n->tag != NULL) + printf(" STRAYTAG"); + if (n->tag != NULL) + printf("=%s", n->tag); + if (n->flags & NODE_BROKEN) + printf(" BROKEN"); + if (n->flags & NODE_NOFILL) + printf(" NOFILL"); + if (n->flags & NODE_NOSRC) + printf(" NOSRC"); + if (n->flags & NODE_NOPRT) + printf(" NOPRT"); + putchar('\n'); +} + +static void print_box(const struct eqn_box *ep, int indent) { int i; @@ -374,11 +383,72 @@ print_box(const struct eqn_box *ep, int indent) } static void +print_cellt(enum tbl_cellt pos) +{ + switch(pos) { + case TBL_CELL_LEFT: + putchar('L'); + break; + case TBL_CELL_LONG: + putchar('a'); + break; + case TBL_CELL_CENTRE: + putchar('c'); + break; + case TBL_CELL_RIGHT: + putchar('r'); + break; + case TBL_CELL_NUMBER: + putchar('n'); + break; + case TBL_CELL_SPAN: + putchar('s'); + break; + case TBL_CELL_DOWN: + putchar('^'); + break; + case TBL_CELL_HORIZ: + putchar('-'); + break; + case TBL_CELL_DHORIZ: + putchar('='); + break; + case TBL_CELL_MAX: + putchar('#'); + break; + } +} + +static void print_span(const struct tbl_span *sp, int indent) { const struct tbl_dat *dp; + const struct tbl_cell *cp; int i; + if (sp->prev == NULL) { + for (i = 0; i < indent; i++) + putchar(' '); + printf("%d", sp->opts->cols); + if (sp->opts->opts & TBL_OPT_CENTRE) + fputs(" center", stdout); + if (sp->opts->opts & TBL_OPT_EXPAND) + fputs(" expand", stdout); + if (sp->opts->opts & TBL_OPT_ALLBOX) + fputs(" allbox", stdout); + if (sp->opts->opts & TBL_OPT_BOX) + fputs(" box", stdout); + if (sp->opts->opts & TBL_OPT_DBOX) + fputs(" doublebox", stdout); + if (sp->opts->opts & TBL_OPT_NOKEEP) + fputs(" nokeep", stdout); + if (sp->opts->opts & TBL_OPT_NOSPACE) + fputs(" nospaces", stdout); + if (sp->opts->opts & TBL_OPT_NOWARN) + fputs(" nowarn", stdout); + printf(" (tbl options) %d:1\n", sp->line); + } + for (i = 0; i < indent; i++) putchar(' '); @@ -392,31 +462,72 @@ print_span(const struct tbl_span *sp, int indent) putchar(' '); break; default: + for (cp = sp->layout->first; cp != NULL; cp = cp->next) + print_cellt(cp->pos); + putchar(' '); for (dp = sp->first; dp; dp = dp->next) { + if ((cp = dp->layout) == NULL) + putchar('*'); + else { + printf("%d", cp->col); + print_cellt(dp->layout->pos); + switch (cp->font) { + case ESCAPE_FONTROMAN: + break; + case ESCAPE_FONTBOLD: + putchar('b'); + break; + case ESCAPE_FONTITALIC: + putchar('i'); + break; + case ESCAPE_FONTBI: + fputs("bi", stdout); + break; + case ESCAPE_FONTCR: + putchar('c'); + break; + case ESCAPE_FONTCB: + fputs("cb", stdout); + break; + case ESCAPE_FONTCI: + fputs("ci", stdout); + break; + default: + abort(); + } + if (cp->flags & TBL_CELL_TALIGN) + putchar('t'); + if (cp->flags & TBL_CELL_UP) + putchar('u'); + if (cp->flags & TBL_CELL_BALIGN) + putchar('d'); + if (cp->flags & TBL_CELL_WIGN) + putchar('z'); + if (cp->flags & TBL_CELL_EQUAL) + putchar('e'); + if (cp->flags & TBL_CELL_WMAX) + putchar('x'); + } switch (dp->pos) { case TBL_DATA_HORIZ: case TBL_DATA_NHORIZ: putchar('-'); - putchar(' '); - continue; + break; case TBL_DATA_DHORIZ: case TBL_DATA_NDHORIZ: putchar('='); - putchar(' '); - continue; + break; default: + putchar(dp->block ? '{' : '['); + if (dp->string != NULL) + fputs(dp->string, stdout); + putchar(dp->block ? '}' : ']'); break; } - printf("[\"%s\"", dp->string ? dp->string : ""); if (dp->hspans) printf(">%d", dp->hspans); if (dp->vspans) printf("v%d", dp->vspans); - if (dp->layout == NULL) - putchar('*'); - else if (dp->layout->pos == TBL_CELL_DOWN) - putchar('^'); - putchar(']'); putchar(' '); } break; |