diff --git a/contrib/mandoc/Makefile b/contrib/mandoc/Makefile index 0830c9f289a3..d4a2c794b437 100644 --- a/contrib/mandoc/Makefile +++ b/contrib/mandoc/Makefile @@ -1,618 +1,618 @@ # $Id: Makefile,v 1.543 2023/10/19 11:45:42 schwarze Exp $ # # Copyright (c) 2011, 2013-2022 Ingo Schwarze # Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -VERSION = 1.14.6s20250727 +VERSION = 1.14.6s20250926 # === LIST OF FILES ==================================================== TESTSRCS = test-attribute.c \ test-be32toh.c \ test-cmsg.c \ test-dirent-namlen.c \ test-EFTYPE.c \ test-err.c \ test-fts.c \ test-getline.c \ test-getsubopt.c \ test-isblank.c \ test-mkdtemp.c \ test-mkstemps.c \ test-nanosleep.c \ test-noop.c \ test-ntohl.c \ test-O_DIRECTORY.c \ test-ohash.c \ test-PATH_MAX.c \ test-pledge.c \ test-progname.c \ test-reallocarray.c \ test-recallocarray.c \ test-recvmsg.c \ test-rewb-bsd.c \ test-rewb-sysv.c \ test-sandbox_init.c \ test-strcasestr.c \ test-stringlist.c \ test-strlcat.c \ test-strlcpy.c \ test-strndup.c \ test-strptime.c \ test-strsep.c \ test-strtonum.c \ test-vasprintf.c \ test-wchar.c SRCS = arch.c \ att.c \ catman.c \ cgi.c \ chars.c \ compat_err.c \ compat_fts.c \ compat_getline.c \ compat_getsubopt.c \ compat_isblank.c \ compat_mkdtemp.c \ compat_mkstemps.c \ compat_ohash.c \ compat_progname.c \ compat_reallocarray.c \ compat_recallocarray.c \ compat_strcasestr.c \ compat_stringlist.c \ compat_strlcat.c \ compat_strlcpy.c \ compat_strndup.c \ compat_strsep.c \ compat_strtonum.c \ compat_vasprintf.c \ dba.c \ dba_array.c \ dba_read.c \ dba_write.c \ dbm.c \ dbm_map.c \ demandoc.c \ eqn.c \ eqn_html.c \ eqn_term.c \ html.c \ lib.c \ main.c \ man.c \ man_html.c \ man_macro.c \ man_term.c \ man_validate.c \ mandoc.c \ mandoc_aux.c \ mandoc_dbg.c \ mandoc_msg.c \ mandoc_ohash.c \ mandoc_xr.c \ mandocd.c \ mandocdb.c \ manpath.c \ mansearch.c \ mdoc.c \ mdoc_argv.c \ mdoc_html.c \ mdoc_macro.c \ mdoc_man.c \ mdoc_markdown.c \ mdoc_state.c \ mdoc_term.c \ mdoc_validate.c \ msec.c \ out.c \ preconv.c \ read.c \ roff.c \ roff_escape.c \ roff_html.c \ roff_term.c \ roff_validate.c \ soelim.c \ st.c \ tag.c \ tbl.c \ tbl_data.c \ tbl_html.c \ tbl_layout.c \ tbl_opts.c \ tbl_term.c \ term.c \ term_ascii.c \ term_ps.c \ term_tab.c \ term_tag.c \ tree.c DISTFILES = INSTALL \ LICENSE \ Makefile \ Makefile.depend \ NEWS \ TODO \ apropos.1 \ catman.8 \ cgi.h.example \ compat_fts.h \ compat_ohash.h \ compat_stringlist.h \ configure \ configure.local.example \ dba.h \ dba_array.h \ dba_write.h \ dbm.h \ dbm_map.h \ demandoc.1 \ eqn.7 \ eqn.h \ eqn_parse.h \ gmdiff \ html.h \ lib.in \ libman.h \ libmandoc.h \ libmdoc.h \ main.h \ makewhatis.8 \ man.1 \ man.7 \ man.cgi.3 \ man.cgi.8 \ man.conf.5 \ man.h \ man.options.1 \ manconf.h \ mandoc.1 \ mandoc.3 \ mandoc.css \ mandoc.db.5 \ mandoc.h \ mandoc_aux.h \ mandoc_char.7 \ mandoc_dbg.h \ mandoc_dbg_init.3 \ mandoc_escape.3 \ mandoc_headers.3 \ mandoc_html.3 \ mandoc_malloc.3 \ mandoc_ohash.h \ mandoc_parse.h \ mandoc_xr.h \ mandocd.8 \ mansearch.3 \ mansearch.h \ mchars_alloc.3 \ mdoc.7 \ mdoc.h \ msec.in \ out.h \ predefs.in \ roff.7 \ roff.h \ roff_int.h \ soelim.1 \ tag.h \ tbl.3 \ tbl.7 \ tbl.h \ tbl_int.h \ tbl_parse.h \ term.h \ term_tag.h \ $(SRCS) \ $(TESTSRCS) LIBMAN_OBJS = man.o \ man_macro.o \ man_validate.o LIBMDOC_OBJS = att.o \ lib.o \ mdoc.o \ mdoc_argv.o \ mdoc_macro.o \ mdoc_state.o \ mdoc_validate.o \ st.o LIBROFF_OBJS = eqn.o \ roff.o \ roff_escape.o \ roff_validate.o \ tbl.o \ tbl_data.o \ tbl_layout.o \ tbl_opts.o LIBMANDOC_OBJS = $(LIBMAN_OBJS) \ $(LIBMDOC_OBJS) \ $(LIBROFF_OBJS) \ $(DEBUG_OBJS) \ arch.o \ chars.o \ mandoc.o \ mandoc_aux.o \ mandoc_msg.o \ mandoc_ohash.o \ mandoc_xr.o \ msec.o \ preconv.o \ read.o \ tag.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 \ compat_strsep.o \ compat_strtonum.o \ compat_vasprintf.o MANDOC_HTML_OBJS = eqn_html.o \ html.o \ man_html.o \ mdoc_html.o \ roff_html.o \ tbl_html.o MANDOC_TERM_OBJS = eqn_term.o \ man_term.o \ mdoc_term.o \ roff_term.o \ term.o \ term_ascii.o \ term_ps.o \ term_tab.o \ term_tag.o \ tbl_term.o DBM_OBJS = dbm.o \ dbm_map.o \ mansearch.o DBA_OBJS = dba.o \ dba_array.o \ dba_read.o \ dba_write.o \ mandocdb.o MAIN_OBJS = $(MANDOC_HTML_OBJS) \ $(MANDOC_MAN_OBJS) \ $(MANDOC_TERM_OBJS) \ $(DBM_OBJS) \ $(DBA_OBJS) \ main.o \ manpath.o \ mdoc_man.o \ mdoc_markdown.o \ out.o \ tree.o CGI_OBJS = $(MANDOC_HTML_OBJS) \ $(DBM_OBJS) \ cgi.o \ out.o MANDOCD_OBJS = $(MANDOC_HTML_OBJS) \ $(MANDOC_TERM_OBJS) \ mandocd.o \ out.o DEMANDOC_OBJS = demandoc.o WWW_MANS = apropos.1.html \ demandoc.1.html \ man.1.html \ man.options.1.html \ mandoc.1.html \ soelim.1.html \ man.cgi.3.html \ mandoc.3.html \ mandoc_dbg_init.3.html \ mandoc_escape.3.html \ mandoc_headers.3.html \ mandoc_html.3.html \ mandoc_malloc.3.html \ mansearch.3.html \ mchars_alloc.3.html \ tbl.3.html \ man.conf.5.html \ mandoc.db.5.html \ eqn.7.html \ man.7.html \ mandoc_char.7.html \ mdoc.7.html \ roff.7.html \ tbl.7.html \ catman.8.html \ makewhatis.8.html \ man.cgi.8.html \ mandocd.8.html WWW_INCS = eqn.h.html \ html.h.html \ man.h.html \ manconf.h.html \ mandoc.h.html \ mandoc_aux.h.html \ mandoc_parse.h.html \ mansearch.h.html \ mdoc.h.html \ roff.h.html \ tbl.h.html \ tbl_int.h.html \ tbl_parse.h.html # === USER CONFIGURATION =============================================== include Makefile.local # === DEPENDENCY HANDLING ============================================== all: mandoc man demandoc soelim $(BUILD_TARGETS) Makefile.local install: base-install $(INSTALL_TARGETS) www: $(WWW_MANS) $(WWW_INCS) $(WWW_MANS) $(WWW_INCS): mandoc .PHONY: base-install cgi-install install www-install .PHONY: clean distclean depend include Makefile.depend # === TARGETS CONTAINING SHELL COMMANDS ================================ distclean: clean rm -f Makefile.local config.h config.h.old config.log config.log.old clean: 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.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 mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(SBINDIR) mkdir -p $(DESTDIR)$(MANDIR)/man1 mkdir -p $(DESTDIR)$(MANDIR)/man5 mkdir -p $(DESTDIR)$(MANDIR)/man7 mkdir -p $(DESTDIR)$(MANDIR)/man8 mkdir -p $(DESTDIR)$(MISCDIR) $(INSTALL_PROGRAM) mandoc demandoc $(DESTDIR)$(BINDIR) $(INSTALL_PROGRAM) soelim $(DESTDIR)$(BINDIR)/$(BINM_SOELIM) cd $(DESTDIR)$(BINDIR) && $(LN) mandoc $(BINM_MAN) cd $(DESTDIR)$(BINDIR) && $(LN) mandoc $(BINM_APROPOS) cd $(DESTDIR)$(BINDIR) && $(LN) mandoc $(BINM_WHATIS) cd $(DESTDIR)$(SBINDIR) && \ $(LN) ${BIN_FROM_SBIN}/mandoc $(BINM_MAKEWHATIS) $(INSTALL_MAN) mandoc.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1 $(INSTALL_MAN) soelim.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_SOELIM).1 $(INSTALL_MAN) man.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_MAN).1 $(INSTALL_MAN) apropos.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 cd $(DESTDIR)$(MANDIR)/man1 && $(LN) $(BINM_APROPOS).1 $(BINM_WHATIS).1 $(INSTALL_MAN) man.conf.5 $(DESTDIR)$(MANDIR)/man5/$(MANM_MANCONF).5 $(INSTALL_MAN) mandoc.db.5 $(DESTDIR)$(MANDIR)/man5 $(INSTALL_MAN) man.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_MAN).7 $(INSTALL_MAN) mdoc.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_MDOC).7 $(INSTALL_MAN) roff.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_ROFF).7 $(INSTALL_MAN) eqn.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_EQN).7 $(INSTALL_MAN) tbl.7 $(DESTDIR)$(MANDIR)/man7/$(MANM_TBL).7 $(INSTALL_MAN) mandoc_char.7 $(DESTDIR)$(MANDIR)/man7 $(INSTALL_MAN) makewhatis.8 \ $(DESTDIR)$(MANDIR)/man8/$(BINM_MAKEWHATIS).8 $(INSTALL_DATA) mandoc.css $(DESTDIR)$(MISCDIR) lib-install: libmandoc.a mkdir -p $(DESTDIR)$(LIBDIR) mkdir -p $(DESTDIR)$(INCLUDEDIR) mkdir -p $(DESTDIR)$(MANDIR)/man3 $(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR) $(INSTALL_LIB) eqn.h man.h mandoc.h mandoc_aux.h mandoc_parse.h \ mdoc.h roff.h tbl.h $(DESTDIR)$(INCLUDEDIR) $(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \ mansearch.3 mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3 cgi-install: man.cgi mkdir -p $(DESTDIR)$(CGIBINDIR) mkdir -p $(DESTDIR)$(HTDOCDIR) $(INSTALL_PROGRAM) man.cgi $(DESTDIR)$(CGIBINDIR) $(INSTALL_DATA) mandoc.css $(DESTDIR)$(HTDOCDIR) catman-install: mandocd catman mkdir -p $(DESTDIR)$(SBINDIR) mkdir -p $(DESTDIR)$(MANDIR)/man8 $(INSTALL_PROGRAM) mandocd $(DESTDIR)$(SBINDIR) $(INSTALL_PROGRAM) catman $(DESTDIR)$(SBINDIR)/$(BINM_CATMAN) $(INSTALL_MAN) mandocd.8 $(DESTDIR)$(MANDIR)/man8 $(INSTALL_MAN) catman.8 $(DESTDIR)$(MANDIR)/man8/$(BINM_CATMAN).8 uninstall: rm -f $(DESTDIR)$(BINDIR)/mandoc rm -f $(DESTDIR)$(BINDIR)/demandoc rm -f $(DESTDIR)$(BINDIR)/$(BINM_SOELIM) rm -f $(DESTDIR)$(BINDIR)/$(BINM_MAN) rm -f $(DESTDIR)$(BINDIR)/$(BINM_APROPOS) rm -f $(DESTDIR)$(BINDIR)/$(BINM_WHATIS) rm -f $(DESTDIR)$(SBINDIR)/$(BINM_MAKEWHATIS) rm -f $(DESTDIR)$(MANDIR)/man1/mandoc.1 rm -f $(DESTDIR)$(MANDIR)/man1/demandoc.1 rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_SOELIM).1 rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_MAN).1 rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 rm -f $(DESTDIR)$(MANDIR)/man1/$(BINM_WHATIS).1 rm -f $(DESTDIR)$(MANDIR)/man5/$(MANM_MANCONF).5 rm -f $(DESTDIR)$(MANDIR)/man5/mandoc.db.5 rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_MAN).7 rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_MDOC).7 rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_ROFF).7 rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_EQN).7 rm -f $(DESTDIR)$(MANDIR)/man7/$(MANM_TBL).7 rm -f $(DESTDIR)$(MANDIR)/man7/mandoc_char.7 rm -f $(DESTDIR)$(MANDIR)/man8/$(BINM_MAKEWHATIS).8 rm -f $(DESTDIR)$(CGIBINDIR)/man.cgi rm -f $(DESTDIR)$(HTDOCDIR)/mandoc.css rm -f $(DESTDIR)$(SBINDIR)/mandocd rm -f $(DESTDIR)$(SBINDIR)/$(BINM_CATMAN) rm -f $(DESTDIR)$(MANDIR)/man8/mandocd.8 rm -f $(DESTDIR)$(MANDIR)/man8/$(BINM_CATMAN).8 rm -f $(DESTDIR)$(LIBDIR)/libmandoc.a rm -f $(DESTDIR)$(MANDIR)/man3/mandoc.3 rm -f $(DESTDIR)$(MANDIR)/man3/mandoc_escape.3 rm -f $(DESTDIR)$(MANDIR)/man3/mandoc_malloc.3 rm -f $(DESTDIR)$(MANDIR)/man3/mansearch.3 rm -f $(DESTDIR)$(MANDIR)/man3/mchars_alloc.3 rm -f $(DESTDIR)$(MANDIR)/man3/tbl.3 rm -f $(DESTDIR)$(INCLUDEDIR)/eqn.h rm -f $(DESTDIR)$(INCLUDEDIR)/man.h rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc.h rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc_aux.h rm -f $(DESTDIR)$(INCLUDEDIR)/mandoc_parse.h rm -f $(DESTDIR)$(INCLUDEDIR)/mdoc.h rm -f $(DESTDIR)$(INCLUDEDIR)/roff.h rm -f $(DESTDIR)$(INCLUDEDIR)/tbl.h [ ! -e $(DESTDIR)$(INCLUDEDIR) ] || rmdir $(DESTDIR)$(INCLUDEDIR) regress: all cd regress && ./regress.pl regress-clean: cd regress && ./regress.pl . clean Makefile.local config.h: configure $(TESTSRCS) @echo "$@ is out of date; please run ./configure" @exit 1 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) mandocd: $(MANDOCD_OBJS) libmandoc.a $(CC) -o $@ $(LDFLAGS) $(MANDOCD_OBJS) libmandoc.a $(LDADD) catman: catman.o libmandoc.a $(CC) -o $@ $(LDFLAGS) catman.o libmandoc.a $(LDADD) demandoc: $(DEMANDOC_OBJS) libmandoc.a $(CC) -o $@ $(LDFLAGS) $(DEMANDOC_OBJS) libmandoc.a $(LDADD) soelim: $(SOELIM_COBJS) soelim.o $(CC) -o $@ $(LDFLAGS) $(SOELIM_COBJS) soelim.o # --- maintainer targets --- www-install: www $(INSTALL_DATA) mandoc.css $(HTDOCDIR) $(INSTALL_DATA) $(WWW_MANS) $(HTDOCDIR)/man $(INSTALL_DATA) $(WWW_INCS) $(HTDOCDIR)/includes depend: config.h ./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.tmp1 > Makefile.tmp2 rm Makefile.tmp1 mv Makefile.tmp2 Makefile.depend regress-distclean: @find regress \ -name '.#*' -o \ -name '*.orig' -o \ -name '*.rej' -o \ -name '*.core' \ -exec rm -i {} \; regress-distcheck: @find regress ! -type d ! -type f @find regress -type f \ ! -path '*/CVS/*' \ ! -name Makefile \ ! -name Makefile.inc \ ! -name '*.in' \ ! -name '*.out_ascii' \ ! -name '*.out_utf8' \ ! -name '*.out_html' \ ! -name '*.out_markdown' \ ! -name '*.out_lint' \ ! -path regress/regress.pl \ ! -path regress/regress.pl.1 dist: mandoc-$(VERSION).sha256 mandoc-$(VERSION).sha256: mandoc-$(VERSION).tar.gz sha256 mandoc-$(VERSION).tar.gz > $@ mandoc-$(VERSION).tar.gz: $(DISTFILES) ls regress/*/*/*.mandoc_* && exit 1 || true mkdir -p .dist/mandoc-$(VERSION)/ $(INSTALL) -m 0644 $(DISTFILES) .dist/mandoc-$(VERSION) cp -pR regress .dist/mandoc-$(VERSION) find .dist/mandoc-$(VERSION)/regress \ -type d -name CVS -print0 | xargs -0 rm -rf chmod 755 .dist/mandoc-$(VERSION)/configure ( cd .dist/ && tar zcf ../$@ mandoc-$(VERSION) ) rm -rf .dist/ dist-install: dist $(INSTALL_DATA) mandoc-$(VERSION).tar.gz mandoc-$(VERSION).sha256 \ $(HTDOCDIR)/snapshots # === SUFFIX RULES ===================================================== .SUFFIXES: .1 .3 .5 .7 .8 .h .SUFFIXES: .1.html .3.html .5.html .7.html .8.html .h.html .h.h.html: highlight -I $< > $@ .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/contrib/mandoc/dba.c b/contrib/mandoc/dba.c index ee43933de3bf..ab40798e8eed 100644 --- a/contrib/mandoc/dba.c +++ b/contrib/mandoc/dba.c @@ -1,508 +1,509 @@ -/* $Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp $ */ +/* $Id: dba.c,v 1.11 2025/09/24 13:13:30 schwarze Exp $ */ /* - * Copyright (c) 2016, 2017 Ingo Schwarze + * Copyright (c) 2016, 2017, 2025 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Allocation-based version of the mandoc database, for read-write access. * The interface is defined in "dba.h". */ #include "config.h" #include #if HAVE_ENDIAN #include #elif HAVE_SYS_ENDIAN #include #elif HAVE_NTOHL #include #endif #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc_ohash.h" #include "mansearch.h" #include "dba_write.h" #include "dba_array.h" #include "dba.h" struct macro_entry { struct dba_array *pages; char value[]; }; static void *prepend(const char *, char); static void dba_pages_write(struct dba_array *); static int compare_names(const void *, const void *); static int compare_strings(const void *, const void *); static struct macro_entry *get_macro_entry(struct ohash *, const char *, int32_t); static void dba_macros_write(struct dba_array *); static void dba_macro_write(struct ohash *); static int compare_entries(const void *, const void *); /*** top-level functions **********************************************/ struct dba * dba_new(int32_t npages) { struct dba *dba; struct ohash *macro; int32_t im; dba = mandoc_malloc(sizeof(*dba)); dba->pages = dba_array_new(npages, DBA_GROW); dba->macros = dba_array_new(MACRO_MAX, 0); for (im = 0; im < MACRO_MAX; im++) { macro = mandoc_malloc(sizeof(*macro)); mandoc_ohash_init(macro, 4, offsetof(struct macro_entry, value)); dba_array_set(dba->macros, im, macro); } return dba; } void dba_free(struct dba *dba) { struct dba_array *page; struct ohash *macro; struct macro_entry *entry; unsigned int slot; dba_array_FOREACH(dba->macros, macro) { for (entry = ohash_first(macro, &slot); entry != NULL; entry = ohash_next(macro, &slot)) { dba_array_free(entry->pages); free(entry); } ohash_delete(macro); free(macro); } dba_array_free(dba->macros); dba_array_undel(dba->pages); dba_array_FOREACH(dba->pages, page) { dba_array_free(dba_array_get(page, DBP_NAME)); dba_array_free(dba_array_get(page, DBP_SECT)); dba_array_free(dba_array_get(page, DBP_ARCH)); free(dba_array_get(page, DBP_DESC)); dba_array_free(dba_array_get(page, DBP_FILE)); dba_array_free(page); } dba_array_free(dba->pages); free(dba); } /* * Write the complete mandoc database to disk; the format is: * - One integer each for magic and version. * - One pointer each to the macros table and to the final magic. * - The pages table. * - The macros table. * - And at the very end, the magic integer again. */ int dba_write(const char *fname, struct dba *dba) { int save_errno; int32_t pos_end, pos_macros, pos_macros_ptr; if (dba_open(fname) == -1) return -1; dba_int_write(MANDOCDB_MAGIC); dba_int_write(MANDOCDB_VERSION); pos_macros_ptr = dba_skip(1, 2); dba_pages_write(dba->pages); pos_macros = dba_tell(); dba_macros_write(dba->macros); pos_end = dba_tell(); dba_int_write(MANDOCDB_MAGIC); dba_seek(pos_macros_ptr); dba_int_write(pos_macros); dba_int_write(pos_end); if (dba_close() == -1) { save_errno = errno; unlink(fname); errno = save_errno; return -1; } return 0; } /*** functions for handling pages *************************************/ /* * Create a new page and append it to the pages table. */ struct dba_array * dba_page_new(struct dba_array *pages, const char *arch, const char *desc, const char *file, enum form form) { struct dba_array *page, *entry; page = dba_array_new(DBP_MAX, 0); entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(page, entry); entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(page, entry); if (arch != NULL && *arch != '\0') { entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(entry, (void *)arch); } else entry = NULL; dba_array_add(page, entry); dba_array_add(page, mandoc_strdup(desc)); entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(entry, prepend(file, form)); dba_array_add(page, entry); dba_array_add(pages, page); return page; } /* * Add a section, architecture, or file name to an existing page. * Passing the NULL pointer for the architecture makes the page MI. * In that case, any earlier or later architectures are ignored. */ void dba_page_add(struct dba_array *page, int32_t ie, const char *str) { struct dba_array *entries; char *entry; entries = dba_array_get(page, ie); if (ie == DBP_ARCH) { if (entries == NULL) return; if (str == NULL || *str == '\0') { dba_array_free(entries); dba_array_set(page, DBP_ARCH, NULL); return; } } if (*str == '\0') return; dba_array_FOREACH(entries, entry) { if (ie == DBP_FILE && *entry < ' ') entry++; if (strcmp(entry, str) == 0) return; } dba_array_add(entries, (void *)str); } /* * Add an additional name to an existing page. */ void dba_page_alias(struct dba_array *page, const char *name, uint64_t mask) { struct dba_array *entries; char *entry; char maskbyte; if (*name == '\0') return; maskbyte = mask & NAME_MASK; entries = dba_array_get(page, DBP_NAME); dba_array_FOREACH(entries, entry) { if (strcmp(entry + 1, name) == 0) { *entry |= maskbyte; return; } } dba_array_add(entries, prepend(name, maskbyte)); } /* * Return a pointer to a temporary copy of instr with inbyte prepended. */ static void * prepend(const char *instr, char inbyte) { static char *outstr = NULL; static size_t outlen = 0; size_t newlen; newlen = strlen(instr) + 1; if (newlen > outlen) { outstr = mandoc_realloc(outstr, newlen + 1); outlen = newlen; } *outstr = inbyte; memcpy(outstr + 1, instr, newlen); return outstr; } /* * Write the pages table to disk; the format is: * - One integer containing the number of pages. * - For each page, five pointers to the names, sections, * architectures, description, and file names of the page. * MI pages write 0 instead of the architecture pointer. * - One list each for names, sections, architectures, descriptions and * file names. The description for each page ends with a NUL byte. * For all the other lists, each string ends with a NUL byte, * and the last string for a page ends with two NUL bytes. * - To assure alignment of following integers, * the end is padded with NUL bytes up to a multiple of four bytes. */ static void dba_pages_write(struct dba_array *pages) { struct dba_array *page, *entry; int32_t pos_pages, pos_end; pos_pages = dba_array_writelen(pages, 5); dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_NAME, dba_tell()); entry = dba_array_get(page, DBP_NAME); dba_array_sort(entry, compare_names); dba_array_writelst(entry); } dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_SECT, dba_tell()); entry = dba_array_get(page, DBP_SECT); dba_array_sort(entry, compare_strings); dba_array_writelst(entry); } dba_array_FOREACH(pages, page) { if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) { dba_array_setpos(page, DBP_ARCH, dba_tell()); dba_array_sort(entry, compare_strings); dba_array_writelst(entry); } else dba_array_setpos(page, DBP_ARCH, 0); } dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_DESC, dba_tell()); dba_str_write(dba_array_get(page, DBP_DESC)); } dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_FILE, dba_tell()); dba_array_writelst(dba_array_get(page, DBP_FILE)); } pos_end = dba_align(); dba_seek(pos_pages); dba_array_FOREACH(pages, page) dba_array_writepos(page); dba_seek(pos_end); } static int compare_names(const void *vp1, const void *vp2) { const char *cp1, *cp2; int diff; cp1 = *(const char * const *)vp1; cp2 = *(const char * const *)vp2; return (diff = *cp2 - *cp1) ? diff : - strcasecmp(cp1 + 1, cp2 + 1); + (diff = strcasecmp(cp1 + 1, cp2 + 1)) ? diff : + strcmp(cp1 + 1, cp2 + 1); } static int compare_strings(const void *vp1, const void *vp2) { const char *cp1, *cp2; cp1 = *(const char * const *)vp1; cp2 = *(const char * const *)vp2; return strcmp(cp1, cp2); } /*** functions for handling macros ************************************/ /* * In the hash table for a single macro, look up an entry by * the macro value or add an empty one if it doesn't exist yet. */ static struct macro_entry * get_macro_entry(struct ohash *macro, const char *value, int32_t np) { struct macro_entry *entry; size_t len; unsigned int slot; slot = ohash_qlookup(macro, value); if ((entry = ohash_find(macro, slot)) == NULL) { len = strlen(value) + 1; entry = mandoc_malloc(sizeof(*entry) + len); memcpy(&entry->value, value, len); entry->pages = dba_array_new(np, DBA_GROW); ohash_insert(macro, slot, entry); } return entry; } /* * In addition to get_macro_entry(), add multiple page references, * converting them from the on-disk format (byte offsets in the file) * to page pointers in memory. */ void dba_macro_new(struct dba *dba, int32_t im, const char *value, const int32_t *pp) { struct macro_entry *entry; const int32_t *ip; int32_t np; np = 0; for (ip = pp; *ip; ip++) np++; entry = get_macro_entry(dba_array_get(dba->macros, im), value, np); for (ip = pp; *ip; ip++) dba_array_add(entry->pages, dba_array_get(dba->pages, be32toh(*ip) / 5 / sizeof(*ip) - 1)); } /* * In addition to get_macro_entry(), add one page reference, * directly taking the in-memory page pointer as an argument. */ void dba_macro_add(struct dba_array *macros, int32_t im, const char *value, struct dba_array *page) { struct macro_entry *entry; if (*value == '\0') return; entry = get_macro_entry(dba_array_get(macros, im), value, 1); dba_array_add(entry->pages, page); } /* * Write the macros table to disk; the format is: * - The number of macro tables (actually, MACRO_MAX). * - That number of pointers to the individual macro tables. * - The individual macro tables. */ static void dba_macros_write(struct dba_array *macros) { struct ohash *macro; int32_t im, pos_macros, pos_end; pos_macros = dba_array_writelen(macros, 1); im = 0; dba_array_FOREACH(macros, macro) { dba_array_setpos(macros, im++, dba_tell()); dba_macro_write(macro); } pos_end = dba_tell(); dba_seek(pos_macros); dba_array_writepos(macros); dba_seek(pos_end); } /* * Write one individual macro table to disk; the format is: * - The number of entries in the table. * - For each entry, two pointers, the first one to the value * and the second one to the list of pages. * - A list of values, each ending in a NUL byte. * - To assure alignment of following integers, * padding with NUL bytes up to a multiple of four bytes. * - A list of pointers to pages, each list ending in a 0 integer. */ static void dba_macro_write(struct ohash *macro) { struct macro_entry **entries, *entry; struct dba_array *page; int32_t *kpos, *dpos; unsigned int ie, ne, slot; int use; int32_t addr, pos_macro, pos_end; /* Temporary storage for filtering and sorting. */ ne = ohash_entries(macro); entries = mandoc_reallocarray(NULL, ne, sizeof(*entries)); kpos = mandoc_reallocarray(NULL, ne, sizeof(*kpos)); dpos = mandoc_reallocarray(NULL, ne, sizeof(*dpos)); /* Build a list of non-empty entries and sort it. */ ne = 0; for (entry = ohash_first(macro, &slot); entry != NULL; entry = ohash_next(macro, &slot)) { use = 0; dba_array_FOREACH(entry->pages, page) if (dba_array_getpos(page)) use = 1; if (use) entries[ne++] = entry; } qsort(entries, ne, sizeof(*entries), compare_entries); /* Number of entries, and space for the pointer pairs. */ dba_int_write(ne); pos_macro = dba_skip(2, ne); /* String table. */ for (ie = 0; ie < ne; ie++) { kpos[ie] = dba_tell(); dba_str_write(entries[ie]->value); } dba_align(); /* Pages table. */ for (ie = 0; ie < ne; ie++) { dpos[ie] = dba_tell(); dba_array_FOREACH(entries[ie]->pages, page) if ((addr = dba_array_getpos(page))) dba_int_write(addr); dba_int_write(0); } pos_end = dba_tell(); /* Fill in the pointer pairs. */ dba_seek(pos_macro); for (ie = 0; ie < ne; ie++) { dba_int_write(kpos[ie]); dba_int_write(dpos[ie]); } dba_seek(pos_end); free(entries); free(kpos); free(dpos); } static int compare_entries(const void *vp1, const void *vp2) { const struct macro_entry *ep1, *ep2; ep1 = *(const struct macro_entry * const *)vp1; ep2 = *(const struct macro_entry * const *)vp2; return strcmp(ep1->value, ep2->value); } diff --git a/contrib/mandoc/main.c b/contrib/mandoc/main.c index 57b06c9b9e66..d70ff1ce77b4 100644 --- a/contrib/mandoc/main.c +++ b/contrib/mandoc/main.c @@ -1,1416 +1,1384 @@ -/* $Id: main.c,v 1.361 2022/04/14 16:43:43 schwarze Exp $ */ +/* $Id: main.c,v 1.364 2025/09/24 21:30:20 schwarze Exp $ */ /* - * Copyright (c) 2010-2012, 2014-2021 Ingo Schwarze + * Copyright (c) 2010-2012,2014-2021,2025 Ingo Schwarze * Copyright (c) 2008-2012 Kristaps Dzonsons * Copyright (c) 2010 Joerg Sonnenberger * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1). */ #include "config.h" #include #include #include /* MACHINE */ #include #include #include #include #if HAVE_ERR #include #endif #include #include #include #include #if HAVE_SANDBOX_INIT #include #endif #include #include #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc.h" #include "mandoc_xr.h" #include "roff.h" #include "mdoc.h" #include "man.h" #include "mandoc_parse.h" #include "tag.h" #include "term_tag.h" #include "main.h" #include "manconf.h" #include "mansearch.h" enum outmode { OUTMODE_DEF = 0, OUTMODE_FLN, OUTMODE_LST, OUTMODE_ALL, OUTMODE_ONE }; enum outt { OUTT_ASCII = 0, /* -Tascii */ OUTT_LOCALE, /* -Tlocale */ OUTT_UTF8, /* -Tutf8 */ OUTT_TREE, /* -Ttree */ OUTT_MAN, /* -Tman */ OUTT_HTML, /* -Thtml */ OUTT_MARKDOWN, /* -Tmarkdown */ OUTT_LINT, /* -Tlint */ OUTT_PS, /* -Tps */ OUTT_PDF /* -Tpdf */ }; struct outstate { struct tag_files *tag_files; /* Tagging state variables. */ void *outdata; /* data for output */ int use_pager; int wstop; /* stop after a file with a warning */ int had_output; /* Some output was generated. */ enum outt outtype; /* which output to use */ }; int mandocdb(int, 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 *, const char *, struct manpage **, size_t *); 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 void process_onefile(struct mparse *, struct manpage *, - int, struct outstate *, struct manconf *); + 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(char *, enum mandoc_os *, int *); static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static char help_arg[] = "help"; static char *help_argv[] = {help_arg, NULL}; int main(int argc, char *argv[]) { 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 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; - int startdir; int c; enum mandoc_os os_e; /* Check base system conventions. */ enum outmode outmode; /* According to command line. */ #if DEBUG_MEMORY mandoc_dbg_init(argc, argv); #endif #if HAVE_PROGNAME progname = getprogname(); #else if (argc < 1) progname = "mandoc"; else if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; setprogname(progname); #endif mandoc_msg_setoutfile(stderr); if (strncmp(progname, "mandocdb", 8) == 0 || strcmp(progname, BINM_MAKEWHATIS) == 0) return mandocdb(argc, argv); #if HAVE_PLEDGE 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(); } #endif #if HAVE_SANDBOX_INIT if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); #endif /* Search options. */ memset(&conf, 0, sizeof(conf)); conf_file = NULL; defpaths = auxpaths = NULL; memset(&search, 0, sizeof(struct mansearch)); search.outkey = "Nd"; oarg = NULL; if (strcmp(progname, BINM_MAN) == 0) search.argmode = ARG_NAME; else if (strcmp(progname, BINM_APROPOS) == 0) search.argmode = ARG_EXPR; else if (strcmp(progname, BINM_WHATIS) == 0) search.argmode = ARG_WORD; else if (strncmp(progname, "help", 4) == 0) search.argmode = ARG_NAME; else search.argmode = ARG_FILE; /* Parser options. */ 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; show_usage = 0; outmode = OUTMODE_DEF; while ((c = getopt(argc, argv, "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { if (c == 'i' && search.argmode == ARG_EXPR) { optind--; break; } switch (c) { case 'a': outmode = OUTMODE_ALL; break; case 'C': conf_file = optarg; break; case 'c': outst.use_pager = 0; break; case 'f': search.argmode = ARG_WORD; break; case 'h': conf.output.synopsisonly = 1; outst.use_pager = 0; outmode = OUTMODE_ALL; break; case 'I': if (strncmp(optarg, "os=", 3) != 0) { mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-I %s", optarg); return mandoc_msg_getrc(); } if (os_s != NULL) { mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, "-I %s", optarg); return mandoc_msg_getrc(); } os_s = optarg + 3; break; case 'K': options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); if (strcmp(optarg, "utf-8") == 0) options |= MPARSE_UTF8; else if (strcmp(optarg, "iso-8859-1") == 0) options |= MPARSE_LATIN1; else if (strcmp(optarg, "us-ascii") != 0) { mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-K %s", optarg); return mandoc_msg_getrc(); } break; case 'k': search.argmode = ARG_EXPR; break; case 'l': search.argmode = ARG_FILE; outmode = OUTMODE_ALL; break; case 'M': #ifdef __FreeBSD__ defpaths = strdup(optarg); if (defpaths == NULL) err(1, "strdup"); #else defpaths = optarg; #endif break; case 'm': auxpaths = optarg; break; case 'O': oarg = optarg; break; case 'S': search.arch = optarg; break; case 's': search.sec = optarg; break; case 'T': if (strcmp(optarg, "ascii") == 0) outst.outtype = OUTT_ASCII; else if (strcmp(optarg, "lint") == 0) { outst.outtype = OUTT_LINT; mandoc_msg_setoutfile(stdout); mandoc_msg_setmin(MANDOCERR_BASE); } else if (strcmp(optarg, "tree") == 0) outst.outtype = OUTT_TREE; else if (strcmp(optarg, "man") == 0) outst.outtype = OUTT_MAN; else if (strcmp(optarg, "html") == 0) outst.outtype = OUTT_HTML; else if (strcmp(optarg, "markdown") == 0) outst.outtype = OUTT_MARKDOWN; else if (strcmp(optarg, "utf8") == 0) outst.outtype = OUTT_UTF8; else if (strcmp(optarg, "locale") == 0) outst.outtype = OUTT_LOCALE; else if (strcmp(optarg, "ps") == 0) outst.outtype = OUTT_PS; else if (strcmp(optarg, "pdf") == 0) outst.outtype = OUTT_PDF; else { mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-T %s", optarg); return mandoc_msg_getrc(); } break; case 'W': if (woptions(optarg, &os_e, &outst.wstop) == -1) return mandoc_msg_getrc(); break; case 'w': outmode = OUTMODE_FLN; break; default: show_usage = 1; break; } } if (show_usage) usage(search.argmode); /* Postprocess options. */ switch (outmode) { case OUTMODE_DEF: switch (search.argmode) { case ARG_FILE: outmode = OUTMODE_ALL; outst.use_pager = 0; break; case ARG_NAME: outmode = OUTMODE_ONE; break; default: 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) { if (outmode == OUTMODE_LST) search.outkey = oarg; else { while (oarg != NULL) { if (manconf_output(&conf.output, strsep(&oarg, ","), 0) == -1) return mandoc_msg_getrc(); } } } if (outst.outtype != OUTT_TREE || conf.output.noval == 0) options |= MPARSE_VALIDATE; if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST || (conf.output.outfilename == NULL && conf.output.tagfilename == NULL && isatty(STDOUT_FILENO) == 0)) outst.use_pager = 0; if (outst.use_pager && (conf.output.width == 0 || conf.output.indent == 0) && ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col > 1) { if (conf.output.width == 0 && ws.ws_col < 79) conf.output.width = ws.ws_col - 1; if (conf.output.indent == 0 && ws.ws_col < 66) conf.output.indent = 3; } #if HAVE_PLEDGE 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 /* Parse arguments. */ if (argc > 0) { argc -= optind; argv += optind; } /* * Quirks for help(1) and man(1), * in particular for a section argument without -s. */ if (search.argmode == ARG_NAME) { if (*progname == 'h') { if (argc == 0) { argv = help_argv; argc = 1; } } else if (argc > 1 && ((uc = (unsigned char *)argv[0]) != NULL) && ((isdigit(uc[0]) && (uc[1] == '\0' || isalpha(uc[1]))) || (uc[0] == 'n' && uc[1] == '\0'))) { search.sec = (char *)uc; argv++; argc--; } if (search.arch == NULL) search.arch = getenv("MACHINE"); #ifdef MACHINE if (search.arch == NULL) search.arch = MACHINE; #endif if (outmode == OUTMODE_ONE) search.firstmatch = 1; } /* * Use the first argument for -O tag in addition to * using it as a search term for man(1) or apropos(1). */ if (conf.output.tag != NULL && *conf.output.tag == '\0') { tagarg = argc > 0 && search.argmode == ARG_EXPR ? strchr(*argv, '=') : NULL; conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; } if (search.argmode != ARG_FILE || mandoc_msg_getmin() == MANDOCERR_STYLE) #ifdef __FreeBSD__ { /* * Use manpath(1) to populate defpaths if -M is not specified. * Don't treat any failures as fatal. */ if (defpaths == NULL) { FILE *fp; size_t linecap = 0; ssize_t linelen; if ((fp = popen("/usr/bin/manpath -q", "r")) != NULL) { if ((linelen = getline(&defpaths, &linecap, fp)) > 0) { /* Strip trailing newline */ defpaths[linelen - 1] = '\0'; } pclose(fp); } } #endif /* Read the configuration file. */ manconf_parse(&conf, conf_file, defpaths, auxpaths); #ifdef __FreeBSD__ free(defpaths); } #endif /* man(1): Resolve each name individually. */ 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 (resnsz == 0) { 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, ressz + resnsz, sizeof(*res)); memcpy(res + ressz, resn, sizeof(*resn) * resnsz); ressz += resnsz; free(resn); resn = NULL; resnsz = 0; continue; } /* Search for the best section. */ best_prio = 40; for (ib = i = 0; i < resnsz; i++) { - sec = resn[i].file; + sec = resn[i].file + + strlen(conf.manpath.paths[resn[i].ipath]); sec += strcspn(sec, "123456789"); if (sec[0] == '\0') continue; /* No section at all. */ prio = sec_prios[sec[0] - '1']; if (search.sec != NULL) { ssz = strlen(search.sec); if (strncmp(sec, search.sec, ssz) == 0) sec += ssz; } else sec++; /* Prefer without suffix. */ if (*sec != '/') prio += 10; /* Wrong dir name. */ 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; ib = i; } res = mandoc_reallocarray(res, ressz + 1, sizeof(*res)); memcpy(res + ressz++, resn + ib, sizeof(*resn)); memset(resn + ib, 0, sizeof(*resn)); mansearch_free(resn, resnsz); resn = NULL; resnsz = 0; } /* 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 (ressz == 0) { warnx("nothing appropriate"); mandoc_msg_setrc(MANDOCLEVEL_BADARG); goto out; } /* mandoc(1): Take command line arguments as file names. */ } else { 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; } } 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) options |= MPARSE_MDOC; else if (strcmp(auxpaths, "an") == 0) options |= MPARSE_MAN; } mchars_alloc(); mp = mparse_alloc(options, os_e, os_s); - /* - * Remember the original working directory, if possible. - * This will be needed if some names on the command line - * are page names and some are relative file names. - * Do not error out if the current directory is not - * readable: Maybe it won't be needed after all. - */ - startdir = open(".", O_RDONLY | O_DIRECTORY); for (i = 0; i < ressz; i++) { - process_onefile(mp, res + i, startdir, &outst, &conf); + if (i > 0) + mparse_reset(mp); + process_onefile(mp, res + i, &outst, &conf); if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) break; } - if (startdir != -1) { - (void)fchdir(startdir); - close(startdir); - } 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(outst.outdata); break; case OUTT_UTF8: case OUTT_LOCALE: case OUTT_ASCII: ascii_free(outst.outdata); break; case OUTT_PDF: case OUTT_PS: pspdf_free(outst.outdata); break; default: break; } } mandoc_xr_free(); mparse_free(mp); mchars_free(); out: mansearch_free(res, ressz); if (search.argmode != ARG_FILE) manconf_free(&conf); 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(); #if DEBUG_MEMORY mandoc_dbg_finish(); #endif return (int)mandoc_msg_getrc(); } static void usage(enum argmode argmode) { switch (argmode) { case ARG_FILE: fputs("usage: mandoc [-ac] [-I os=name] " "[-K encoding] [-mdoc | -man] [-O options]\n" "\t [-T output] [-W level] [file ...]\n", stderr); break; case ARG_NAME: fputs("usage: man [-acfhklw] [-C file] [-M path] " "[-m path] [-S subsection]\n" "\t [[-s] section] name ...\n", stderr); break; case ARG_WORD: fputs("usage: whatis [-afk] [-C file] " "[-M path] [-m path] [-O outkey] [-S arch]\n" "\t [-s section] name ...\n", stderr); break; case ARG_EXPR: fputs("usage: apropos [-afk] [-C file] " "[-M path] [-m path] [-O outkey] [-S arch]\n" "\t [-s section] expression ...\n", stderr); break; } 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, struct manpage **res, size_t *ressz) { struct stat sb; glob_t globinfo; 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) goto found; free(file); mandoc_asprintf(&file, "%s/cat%s/%s.0", paths->paths[ipath], sec, name); if (stat(file, &sb) != -1) { form = FORM_CAT; goto found; } free(file); if (arch != NULL) { mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", paths->paths[ipath], sec, arch, name, sec); if (stat(file, &sb) != -1) goto found; free(file); } 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) goto found; globfree(&globinfo); 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; mandoc_asprintf(&file, "%s.%s", name, sec); globres = stat(file, &sb); free(file); return globres; found: warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); if (res == NULL) free(file); 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, 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; assert(cfg->argmode == ARG_NAME); if (res != NULL) *res = NULL; *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, name, res, ressz) != -1 && cfg->firstmatch) return 0; } } return -1; } static void -process_onefile(struct mparse *mp, struct manpage *resp, int startdir, +process_onefile(struct mparse *mp, struct manpage *resp, struct outstate *outst, struct manconf *conf) { 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); } 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(fd >= 0); if (file == NULL) file = ""; - if (previous) - mparse_reset(mp); - else - previous = 1; - mparse_readfd(mp, fd, file); if (fd != STDIN_FILENO) close(fd); /* * With -Wstop and warnings or errors of at least the requested * level, do not produce output. */ if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) return; 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(mp); /* Execute the out device, if it exists. */ outst->had_output = 1; if (meta->macroset == MACROSET_MDOC) { switch (outst->outtype) { case OUTT_HTML: html_mdoc(outst->outdata, meta); break; case OUTT_TREE: tree_mdoc(outst->outdata, meta); break; case OUTT_MAN: man_mdoc(outst->outdata, meta); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: terminal_mdoc(outst->outdata, meta); break; case OUTT_MARKDOWN: markdown_mdoc(outst->outdata, meta); break; default: break; } } if (meta->macroset == MACROSET_MAN) { switch (outst->outtype) { case OUTT_HTML: html_man(outst->outdata, meta); break; case OUTT_TREE: tree_man(outst->outdata, meta); break; case OUTT_MAN: mparse_copy(mp); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: terminal_man(outst->outdata, meta); break; case OUTT_MARKDOWN: mandoc_msg(MANDOCERR_MAN_TMARKDOWN, 0, 0, NULL); break; default: break; } } 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(struct manpaths *paths) { struct mansearch search; struct mandoc_xr *xr; size_t sz; for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { if (xr->line == -1) continue; search.arch = NULL; search.sec = xr->sec; search.outkey = NULL; search.argmode = ARG_NAME; search.firstmatch = 1; if (mansearch(&search, paths, 1, &xr->name, NULL, &sz)) continue; if (fs_search(&search, paths, xr->name, NULL, &sz) != -1) continue; if (xr->count == 1) mandoc_msg(MANDOCERR_XR_BAD, xr->line, xr->pos + 1, "Xr %s %s", xr->name, xr->sec); else mandoc_msg(MANDOCERR_XR_BAD, xr->line, xr->pos + 1, "Xr %s %s (%d times)", xr->name, xr->sec, xr->count); } } static void outdata_alloc(struct outstate *outst, struct manoutput *outconf) { switch (outst->outtype) { case OUTT_HTML: outst->outdata = html_alloc(outconf); break; case OUTT_UTF8: outst->outdata = utf8_alloc(outconf); break; case OUTT_LOCALE: outst->outdata = locale_alloc(outconf); break; case OUTT_ASCII: outst->outdata = ascii_alloc(outconf); break; case OUTT_PDF: outst->outdata = pdf_alloc(outconf); break; case OUTT_PS: outst->outdata = ps_alloc(outconf); break; default: break; } } static void passthrough(int fd, int synopsis_only) { const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; const char synr[] = "SYNOPSIS"; FILE *stream; char *line, *cp; size_t linesz; ssize_t len, written; int lno, print; stream = NULL; line = NULL; linesz = 0; if (fflush(stdout) == EOF) { mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno)); goto done; } if ((stream = fdopen(fd, "r")) == NULL) { close(fd); mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); goto done; } lno = print = 0; while ((len = getline(&line, &linesz, stream)) != -1) { lno++; cp = line; if (synopsis_only) { if (print) { if ( ! isspace((unsigned char)*cp)) goto done; while (isspace((unsigned char)*cp)) { cp++; len--; } } else { if (strcmp(cp, synb) == 0 || strcmp(cp, synr) == 0) print = 1; continue; } } for (; len > 0; len -= written) { if ((written = write(STDOUT_FILENO, cp, len)) == -1) { mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s", strerror(errno)); goto done; } } } if (ferror(stream)) mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno)); done: free(line); if (stream != NULL) fclose(stream); } static int woptions(char *arg, enum mandoc_os *os_e, int *wstop) { char *v, *o; const char *toks[11]; toks[0] = "stop"; toks[1] = "all"; toks[2] = "base"; toks[3] = "style"; toks[4] = "warning"; toks[5] = "error"; toks[6] = "unsupp"; toks[7] = "fatal"; toks[8] = "openbsd"; toks[9] = "netbsd"; toks[10] = NULL; while (*arg) { o = arg; switch (getsubopt(&arg, (char * const *)toks, &v)) { case 0: *wstop = 1; break; case 1: case 2: mandoc_msg_setmin(MANDOCERR_BASE); break; case 3: mandoc_msg_setmin(MANDOCERR_STYLE); break; case 4: mandoc_msg_setmin(MANDOCERR_WARNING); break; case 5: mandoc_msg_setmin(MANDOCERR_ERROR); break; case 6: mandoc_msg_setmin(MANDOCERR_UNSUPP); break; case 7: mandoc_msg_setmin(MANDOCERR_BADARG); break; case 8: mandoc_msg_setmin(MANDOCERR_BASE); *os_e = MANDOC_OS_OPENBSD; break; case 9: mandoc_msg_setmin(MANDOCERR_BASE); *os_e = MANDOC_OS_NETBSD; break; default: mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); return -1; } } 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 outstate *outst, char *tag_target) { const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ #define MAX_PAGER_ARGS 16 char *argv[MAX_PAGER_ARGS]; const char *pager; char *cp; size_t wordlen; #if HAVE_LESS_T size_t cmdlen; #endif 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 = BINM_PAGER; /* * Parse the pager command into words. * Intentionally do not do anything fancy here. */ argc = 0; while (*pager != '\0' && argc + 5 < MAX_PAGER_ARGS) { wordlen = strcspn(pager, " "); argv[argc++] = mandoc_strndup(pager, wordlen); pager += wordlen; while (*pager == ' ') pager++; } /* For less(1), use the tag file. */ use_ofn = 1; #if HAVE_LESS_T 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++] = mandoc_strdup(outst->tag_files->tfn); if (tag_target != NULL) { argv[argc++] = mandoc_strdup("-t"); argv[argc++] = mandoc_strdup(tag_target); use_ofn = 0; } } } #endif 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] = mandoc_strdup(outst->tag_files->ofn); argc++; } argv[argc] = NULL; switch (pager_pid = fork()) { case -1: mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno)); exit(mandoc_msg_getrc()); case 0: break; default: while (argc > 0) free(argv[--argc]); (void)setpgid(pager_pid, 0); (void)tcsetpgrp(STDOUT_FILENO, pager_pid); #if HAVE_PLEDGE if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); exit(mandoc_msg_getrc()); } #endif outst->tag_files->pager_pid = pager_pid; return pager_pid; } /* * The child process becomes the pager. * Do not start it before controlling the terminal. */ while (tcgetpgrp(STDOUT_FILENO) != getpid()) nanosleep(&timeout, NULL); execvp(argv[0], argv); mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); _exit(mandoc_msg_getrc()); } diff --git a/contrib/mandoc/man.c b/contrib/mandoc/man.c index f651efe3de8b..26846f1cf243 100644 --- a/contrib/mandoc/man.c +++ b/contrib/mandoc/man.c @@ -1,356 +1,357 @@ -/* $Id: man.c,v 1.189 2022/08/16 23:01:09 schwarze Exp $ */ +/* $Id: man.c,v 1.190 2025/08/22 13:17:06 schwarze Exp $ */ /* * Copyright (c) 2013-2015,2017-2019,2022 Ingo Schwarze * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2011 Joerg Sonnenberger * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc.h" #include "roff.h" #include "man.h" #include "libmandoc.h" #include "roff_int.h" #include "libman.h" static char *man_hasc(char *); static int man_ptext(struct roff_man *, int, char *, int); static int man_pmacro(struct roff_man *, int, char *, int); int man_parseln(struct roff_man *man, int ln, char *buf, int offs) { if (man->last->type != ROFFT_EQN || ln > man->last->line) man->flags |= MAN_NEWLINE; return roff_getcontrol(man->roff, buf, &offs) ? man_pmacro(man, ln, buf, offs) : man_ptext(man, ln, buf, offs); } /* * If the string ends with \c, return a pointer to the backslash. * Otherwise, return NULL. */ static char * man_hasc(char *start) { char *cp, *ep; ep = strchr(start, '\0') - 2; if (ep < start || ep[0] != '\\' || ep[1] != 'c') return NULL; for (cp = ep; cp > start; cp--) if (cp[-1] != '\\') break; return (ep - cp) % 2 ? NULL : ep; } /* * Rewind all open next-line scopes. */ void man_descope(struct roff_man *man, int line, int offs, char *start) { /* First close out all next-line element scopes, if any. */ if (man->flags & MAN_ELINE) { while (man->last->parent->type != ROFFT_ROOT && man_macro(man->last->parent->tok)->flags & MAN_ESCOPED) man_unscope(man, man->last->parent); man->flags &= ~MAN_ELINE; } /* Trailing \c keeps next-line block scope open. */ if (start != NULL && man_hasc(start) != NULL) return; /* Close out the next-line block scope, if there is one. */ if ( ! (man->flags & MAN_BLINE)) return; man_unscope(man, man->last->parent); roff_body_alloc(man, line, offs, man->last->tok); man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); } static int man_ptext(struct roff_man *man, int line, char *buf, int offs) { int i; char *ep; /* In no-fill mode, whitespace is preserved on text lines. */ if (man->flags & ROFF_NOFILL) { roff_word_alloc(man, line, offs, buf + offs); man_descope(man, line, offs, buf + offs); return 1; } for (i = offs; buf[i] == ' '; i++) /* Skip leading whitespace. */ ; /* * Blank lines are ignored in next line scope * and right after headings and cancel preceding \c, * but add a single vertical space elsewhere. */ if (buf[i] == '\0') { if (man->flags & (MAN_ELINE | MAN_BLINE)) { mandoc_msg(MANDOCERR_BLK_BLANK, line, 0, NULL); return 1; } if (man->last->tok == MAN_SH || man->last->tok == MAN_SS) return 1; if (man->last->type == ROFFT_TEXT && ((ep = man_hasc(man->last->string)) != NULL)) { *ep = '\0'; return 1; } + mandoc_msg(MANDOCERR_FI_BLANK, line, i, NULL); roff_elem_alloc(man, line, offs, ROFF_sp); man->next = ROFF_NEXT_SIBLING; return 1; } /* * Warn if the last un-escaped character is whitespace. Then * strip away the remaining spaces (tabs stay!). */ i = (int)strlen(buf); assert(i); if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { if (i > 1 && '\\' != buf[i - 2]) mandoc_msg(MANDOCERR_SPACE_EOL, line, i - 1, NULL); for (--i; i && ' ' == buf[i]; i--) /* Spin back to non-space. */ ; /* Jump ahead of escaped whitespace. */ i += '\\' == buf[i] ? 2 : 1; buf[i] = '\0'; } roff_word_alloc(man, line, offs, buf + offs); /* * End-of-sentence check. If the last character is an unescaped * EOS character, then flag the node as being the end of a * sentence. The front-end will know how to interpret this. */ assert(i); if (mandoc_eos(buf, (size_t)i)) man->last->flags |= NODE_EOS; man_descope(man, line, offs, buf + offs); return 1; } static int man_pmacro(struct roff_man *man, int ln, char *buf, int offs) { struct roff_node *n; const char *cp; size_t sz; enum roff_tok tok; int ppos; int bline; /* Determine the line macro. */ ppos = offs; tok = TOKEN_NONE; for (sz = 0; sz < 4 && strchr(" \t\\", buf[offs]) == NULL; sz++) offs++; if (sz > 0 && sz < 4) tok = roffhash_find(man->manmac, buf + ppos, sz); if (tok == TOKEN_NONE) { mandoc_msg(MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1); return 1; } /* Skip a leading escape sequence or tab. */ switch (buf[offs]) { case '\\': cp = buf + offs + 1; mandoc_escape(&cp, NULL, NULL); offs = cp - buf; break; case '\t': offs++; break; default: break; } /* Jump to the next non-whitespace word. */ while (buf[offs] == ' ') offs++; /* * Trailing whitespace. Note that tabs are allowed to be passed * into the parser as "text", so we only warn about spaces here. */ if (buf[offs] == '\0' && buf[offs - 1] == ' ') mandoc_msg(MANDOCERR_SPACE_EOL, ln, offs - 1, NULL); /* * Some macros break next-line scopes; otherwise, remember * whether we are in next-line scope for a block head. */ man_breakscope(man, tok); bline = man->flags & MAN_BLINE; /* * If the line in next-line scope ends with \c, keep the * next-line scope open for the subsequent input line. * That is not at all portable, only groff >= 1.22.4 * does it, but *if* this weird idiom occurs in a manual * page, that's very likely what the author intended. */ if (bline && man_hasc(buf + offs)) bline = 0; /* Call to handler... */ (*man_macro(tok)->fp)(man, tok, ln, ppos, &offs, buf); /* In quick mode (for mandocdb), abort after the NAME section. */ if (man->quick && tok == MAN_SH) { n = man->last; if (n->type == ROFFT_BODY && strcmp(n->prev->child->string, "NAME")) return 2; } /* * If we are in a next-line scope for a block head, * close it out now and switch to the body, * unless the next-line scope is allowed to continue. */ if (bline == 0 || (man->flags & MAN_BLINE) == 0 || man->flags & MAN_ELINE || man_macro(tok)->flags & MAN_NSCOPED) return 1; man_unscope(man, man->last->parent); roff_body_alloc(man, ln, ppos, man->last->tok); man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); return 1; } /* * Rewind open next-line scopes * unless the tok request or macro is allowed inside them. */ void man_breakscope(struct roff_man *man, int tok) { struct roff_node *n; /* * An element next line scope is open, * and the new macro is not allowed inside elements. * Delete the element that is being broken. */ if (man->flags & MAN_ELINE && (tok < MAN_TH || (man_macro(tok)->flags & MAN_NSCOPED) == 0)) { n = man->last; if (n->type == ROFFT_TEXT) n = n->parent; if (n->tok < MAN_TH || (man_macro(n->tok)->flags & (MAN_NSCOPED | MAN_ESCOPED)) == MAN_NSCOPED) n = n->parent; for (;;) { mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, "%s breaks %s", roff_name[tok], roff_name[n->tok]); if (n->parent->type != ROFFT_ELEM || (man_macro(n->parent->tok)->flags & MAN_ESCOPED) == 0) break; n = n->parent; } roff_node_delete(man, n); man->flags &= ~MAN_ELINE; } /* * Weird special case: * Switching fill mode closes section headers. */ if (man->flags & MAN_BLINE && (tok == ROFF_nf || tok == ROFF_fi) && (man->last->tok == MAN_SH || man->last->tok == MAN_SS)) { n = man->last; man_unscope(man, n); roff_body_alloc(man, n->line, n->pos, n->tok); man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); } /* * A block header next line scope is open, * and the new macro is not allowed inside block headers. * Delete the block that is being broken. */ if (man->flags & MAN_BLINE && tok != ROFF_nf && tok != ROFF_fi && (tok < MAN_TH || man_macro(tok)->flags & MAN_XSCOPE)) { n = man->last; if (n->type == ROFFT_TEXT) n = n->parent; if (n->tok < MAN_TH || (man_macro(n->tok)->flags & MAN_XSCOPE) == 0) n = n->parent; assert(n->type == ROFFT_HEAD); n = n->parent; assert(n->type == ROFFT_BLOCK); assert(man_macro(n->tok)->flags & MAN_BSCOPED); mandoc_msg(MANDOCERR_BLK_LINE, n->line, n->pos, "%s breaks %s", roff_name[tok], roff_name[n->tok]); roff_node_delete(man, n); man->flags &= ~(MAN_BLINE | ROFF_NONOFILL); } } diff --git a/contrib/mandoc/man.options.1 b/contrib/mandoc/man.options.1 index be65ad98fddc..be101d4b5b62 100644 --- a/contrib/mandoc/man.options.1 +++ b/contrib/mandoc/man.options.1 @@ -1,1333 +1,1360 @@ -.\" $Id: man.options.1,v 1.8 2025/06/30 00:11:06 schwarze Exp $ +.\" $Id: man.options.1,v 1.9 2025/08/28 13:46:57 schwarze Exp $ .\" .\" Copyright (c) 2017, 2025 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: June 30 2025 $ +.Dd $Mdocdate: August 28 2025 $ .Dt MAN.OPTIONS 1 .Os .Sh NAME .Nm man.options .Nd assignment of option letters in manual page utilities .\" .\" Sources that occur repeatedly. .\" Only use if the precise implementation time is unknown. .\" .de PWB .No PWB/UNIX 1.0 Pq July 1, 1977 \\$1 .. .de At7 .At v7 Pq January 1979 \\$1 .. .de At3 .At III Pq June 1980 \\$1 .. .de Bx4 .Bx 4 Pq November 16, 1980 \\$1 .. .de At5 .At V Pq January 1983 \\$1 .. +.de At8 +.No Version 8 At Pq February 1985 \\$1 +.. .de Bx43 .Bx 4.3 Pq June 1986 \\$1 .. +.de At9 +.No Version 9 At Pq September 1986 \\$1 +.. +.de At10 +.No Version 10 At Pq October 1989 \\$1 +.. .\" option was present in groff-1.01 as contained in 4.3BSD-Net/2 .\" and no mention of it could be found in the ChangeLog, .\" so it's probably older than groff-0.4, where the log started .de g04 .No probably before groff-0.4 Pq before July 14, 1990 \\$1 .. .de Eaton -.No Eaton Pq before July 7, 1993; 1990/91? \\$1 +.No Eaton Pq before July 7, 1993; 1990/91?\& \\$1 .. .\" man-1.5e was released on July 11, 1998. .de man15e .No man-1.5e Pq not before 1993, not after 1998 \\$1 .. .\" man-1.5g was released on April 7, 1999. .de man15g .No man-1.5g Pq not before 1993, not after 1999 \\$1 .. .\" code first seen in the initial import of man-db into CVS , .\" which was more or less debian man-db-2.3.17 .\" Colin Watson's first release was 2.3.18 on May 14, 2001 .\" no clue about it found in ChangeLog-2013, .\" so it was probably already present before man-db-2.2a4 .de dbI .No man-db probably before 2.2a4 Pq before Nov 8, 1994 \\$1 .. .\" .\" -------------------------------------------------------------------- .\" .Sh DESCRIPTION This manual page lists option letters used in many different versions of the .Nm man , .Nm apropos , .Nm whatis , .Nm mandoc , .Nm makewhatis , .Nm mandb , .Nm makemandb , .Nm catman , and .Nm manpath utilities. Option letters used by .Nm groff , .Nm nroff , .Nm troff , and .Nm roff are also included because beginning with .At v7 , many versions of .Xr man 1 pass on unrecognized options to these programs. .Pp For each option letter, information is first grouped into paragraphs, each paragraph describing similar functionality and starting with one line briefly summarizing that functionality. .Pp For each program using the letter for that functionality, one line is provided, giving the name of the program, a colon, the system where this letter first appeared for this functionality in this program, optionally a comma and a list of other system versions introducing the same, a semicolon, and a list of current systems supporting it. If a system appears before the semicolon, it is not repeated afterwards. .Pp Entries are sorted by historical precedence, except that obsolete options are moved to the end. Dates are commit dates where known, and release dates otherwise. .Bl -tag -width 3n .It Fl a display all matching manual pages .br .Nm man : .Bx 4.3 Tahoe Pq June 1988 , .Eaton ; .Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 .br .Nm apropos , whatis , mandoc : .Ox 5.7 Pq August 27, 2014 .Pp only display items that match all keywords .br .Nm apropos : .No man-db Pq Aug 29, 2007 .Pp use all directories and files for .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 5.6 Pq April 18, 2014 .Pp .Bq superseded by Fl T Cm ascii ASCII output mode .br .Nm troff : .At7 .br .Nm groff : .g04 .It Fl B use specified browser .br .Nm man : .No man-1.6 Pq June 24, 2005 .It Fl b print a backtrace with each warning or error message .br .Nm groff : .g04 .Pp .Bq obsolete hardware report whether the phototypesetter is busy .br .Nm troff : .At7 .It Fl C alternate configuration file .br .Nm apropos , whatis : .Bx 4.4 Lite1 Pq April 22, 1994 , .No man-db Pq Feb 22, 2003 ; .Ox , Nx .br .Nm man : .Nx 1.0 Pq Oct 26, 1994 , .man15e ; .Ox .br .Nm mandb , catman , manpath : .No man-db Pq Feb 22, 2003 .br .Nm makemandb : .Nx Pq Feb 7, 2012 .br .Nm makewhatis : .Ox 5.6 Pq April 18, 2014 .br .Nm mandoc : .Ox 5.7 Pq August 27, 2014 .Pp .Bq obsolete enable compatibility mode .br .Nm groff : .No before groff-0.5 Pq before August 3, 1990 .It Fl c do not use a pager .br .Nm man : .Bx 4.3 Reno Pq June 1990 ; .Ox , Nx .br .Nm apropos , whatis , mandoc : .Ox 5.7 Pq August 27, 2014 .Pp process given catpath .br .Nm makewhatis : .Pq not before 1992, not after 1995 .Pp recreate databases from scratch .br .Nm mandb : .dbI .Pp produce a catpath as opposed to a manpath .br .Nm manpath : .dbI .Pp internal option for use by .Xr catman 1 .br .Nm man : .dbI .Pp reformat source page even if cat page exists .br .Nm man : .man15e .Pp disable terminal color output in .Xr grotty 1 .br .Nm groff : .No groff-1.18.0 Pq Oct 4, 2001 .Pp recreate nroff versions from SGML sources .br .Nm catman : .No Solaris 9-11 .Pp .Bq obsolete postprocess with .Xr col 1 .br .Nm man : .At3 , .At5 .It Fl D reset whatever was set with .Ev MANOPT .br .Nm man : .dbI .Pp print debugging info in addition to manual page .br .Nm man : .man15e .Pp set default input encoding for .Xr preconv 1 .br .Nm groff : .No groff-1.20 Pq August 20, 2008 .Pp display all files added to .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 5.6 Pq April 18, 2014 .It Fl d define a user-defined string .br .Nm groff : .g04 .Pp print debugging information .br .Nm man : .Eaton ; .Fx , No man-db , man-1.6 , illumos , Solaris 9-11 .br .Nm manpath : .Eaton ; .Fx , No man-db .br .Nm apropos , whatis : .dbI ; .Fx .br .Nm mandb , catman : .dbI .Pp remove and re-add a file to .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 2.7 Pq Feb 3, 2000 .Pp .Bq superseded by Fl l interpret arguments as file names .br .Nm man : .At3 , .At5 .It Fl E inhibit all error messages .br .Nm groff : .g04 .Pp select output encoding .br .Nm man : .No man-db Pq Dec 23, 2001 .It Fl e preprocess with .Xr eqn 7 .br .Nm man : .At7 .br .Nm groff : .g04 .Pp adjust text to left and right margins .br .Nm nroff : .At7 .Pp use exact matching .br .Nm apropos , whatis : .dbI .Pp restrict search by section extension .br .Nm man : .No man-db-2.3.5 Pq April 21, 1995 .It Fl F use alternate font directory .br .Nm troff : .Bx 4.2 Pq September 1983 .br .Nm groff : .g04 .Pp preformat only, do not display .br .Nm man : .No man-1.5g Pq April 7, 1999 .Pp force searching dirs, do not use index (default) .br .Nm man : .No illumos , Solaris 9-11 .It Fl f .Xr whatis 1 mode .br .Nm man : .Bx4 , .Eaton ; .Ox , Fx , No man-db , man-1.6 .br .Nm apropos , whatis : .No man-db Pq Dec 2, 2010 , .Ox 5.7 Pq August 27, 2014 .br .Nm mandoc : .Ox 5.7 Pq August 27, 2014 .Pp set the default font family .br .Nm groff : .g04 .Pp force formatting even if cat page is newer .br .Nm catman : .Fx Pq March 15, 1995 .Pp update only the entries for the given file .br .Nm mandb : .No man-db Pq Feb 21, 2003 .Pp force rebuilding the database from scratch .br .Nm makemandb : .Nx Pq Feb 7, 2012 .Pp locate manual page related to given file name .br .Nm man : .No illumos , Solaris 9-11 .Pp .Bq obsolete hardware do not feed out paper nor stop phototypesetter .br .Nm troff : .At7 +.Pp +.Bq superseded by Fl l +interpret arguments as file names +.br +.Nm man : +.At10 .It Fl G preprocess with .Xr grap 1 .br .Nm groff : .No groff-1.16 Pq May 1, 2000 .It Fl g produce a global manpath .br .Nm manpath : .No man-db-2.2a7 Pq Nov 16, 1994 .Pp preprocess with .Xr grn 1 .br .Nm groff : .No groff-1.16 Pq Feb 20, 2000 .Pp .Bq obsolete hardware output to a GCOS phototypesetter .br .Nm troff : .At7 .Pp .Bq obsolete hardware output to a DASI 300 terminal in 12-pitch mode .br .Nm man : .PWB .It Fl H read hyphenation patterns from the given file .br .Nm groff : .g04 .Pp produce HTML output .br .Nm man : .No man-db-1.3.12 to 1.3.17 Pq not before 1996, not after 2001 .Pp use program to render HTML files as text .br .Nm man : .No man-1.6 Pq June 24, 2005 .It Fl h print a help message and exit .br .Nm groff : .g04 .br .Nm man : .Eaton ; .Fx , No man-db , man-1.6 .br .Nm manpath : .Eaton ; .Fx , No man-db .br .Nm apropos , whatis , mandb , catman : .dbI .Pp display the SYNOPSIS lines only .br .Nm man : .Bx 4.3 Net/2 Pq August 20, 1991 ; .Ox , Nx .br .Nm apropos , whatis , mandoc : .Ox 5.7 Pq Sep 3, 2014 .Pp turn on HTML formatting .br .Nm apropos : .Nx Pq Apr 2, 2013 .Pp .Bq obsolete replace spaces by tabs in the output .br .Nm roff , nroff : .At7 .It Fl I input file search path for .Xr soelim 1 .br .Nm groff : .No groff-1.12 Pq Sep 11, 1999 .Pp respect case when matching manual page names .br .Nm man , catman : .No man-db Pq Apr 21, 2002 .Pp input options, in particular default operating system name .br .Nm mandoc : .Ox 5.2 Pq May 24, 2012 .br .Nm man , apropos , whatis : .Ox 5.7 Pq August 27, 2014 .It Fl i read standard input after the input files are exhausted .br .Nm nroff , troff : .At7 .br .Nm groff : .g04 .Pp ignore case when matching manual page names .br .Nm man , catman : .No man-db Pq Apr 21, 2002 .Pp turn on terminal escape code formatting .br .Nm apropos : .Nx Pq March 29, 2013 .It Fl J preprocess with .Xr gideal 1 .br .Nm groff : .No groff-1.22.3 Pq June 17, 2014 .It Fl j preprocess with .Xr chem 1 .br .Nm groff : .No groff-1.22 Pq Jan 22, 2011 .It Fl K source code full text search .br .Nm man : .man15e , .No man-db Pq June 28, 2009 ; .No Solaris 11 .Pp input encoding .br .Nm groff : .No groff-1.20 Pq Dec 31, 2005 .br .Nm man , apropos , whatis , mandoc : .Ox 5.7 Pq Oct 30, 2014 .It Fl k .Xr apropos 1 mode .br .Nm man : .Bx4 , +.At10 , .Eaton ; .No POSIX , Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 .br .Nm apropos , whatis , mandoc : .Ox 5.7 Pq August 27, 2014 .Pp ignore formatting errors .br .Nm catman : .Nx Pq April 26, 1994 .Pp preprocess with .Xr preconv 1 .br .Nm groff : .No groff-1.20 Pq Dec 31, 2005 .Pp .Bq obsolete hardware display on a Tektronix 4014 terminal .br .Nm man : .At7 .It Fl L pass argument to the spooler .br .Nm groff : .No groff-0.6 Pq Sep 14, 1990 .Pp use alternate .Xr locale 1 .br .Nm man , apropos , whatis : .No before man-db-2.2a13 Pq before Dec 15, 1994 .Pp print list of locales .br .Nm manpath : .Fx Pq Nov 23, 1999 .Pp use .Xr locale 1 specified in the environment .br .Nm catman : .Fx Pq May 18, 2002 .It Fl l spool the output .br .Nm groff : .g04 .Pp interpret arguments as file names .br .Nm man : .No before man-2.2a7 Pq before Nov 16, 1994 , .Ox 5.7 Pq Aug 30, 2014 .br .Nm apropos , whatis , mandoc : .Ox 5.7 Pq Aug 30, 2014 .Pp do not trim output to the terminal width .br .Nm apropos , whatis : .No man-db Pq Aug 19, 2007 .Pp only parse NAME sections .br .Nm makemandb : .Nx Pq Feb 7, 2012 .Pp legacy mode: search Nm,Nd, no context or formatting .br .Nm apropos : .Nx Pq March 29, 2013 .Pp list all manual pages matching name within the search path .br .Nm man : .No illumos , Solaris 9-11 .It Fl M override manual page search path .br .Nm man : .Bx43 , .Eaton ; .Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 .br .Nm apropos , whatis : .Bx43 , .No before man-db-2.2a14 Pq before Dec 16, 1994 ; .Ox , No illumos .br .Nm catman : .dbI ; .Nx Pq July 27, 1993 , .No Solaris 9-11 .br .Nm mandoc : .Ox 5.7 Pq August 27, 2014 .Pp prepend to macro file search path .br .Nm groff : .g04 .Pp do not show the context of the match .br .Nm apropos : .Nx Pq May 22, 2016 .It Fl m specify input macro language .br .Nm nroff , troff : .At7 .br .Nm groff : .g04 .br .Nm mandoc : .Ox 4.8 Pq April 6, 2009 .Pp augment manual page search path .br .Nm man , apropos , whatis : .Bx 4.3 Reno Pq June 1990 ; .Ox , Nx .br .Nm catman : .Nx Pq Apr 4, 1999 .br .Nm mandoc : .Ox 5.7 Pq August 27, 2014 .Pp override operating system .br .Nm man : .Eaton ; .No man-db , man-1.6 .br .Nm apropos , whatis , manpath : .dbI .Pp override architecture .br .Nm man : .Fx Pq Jan 11, 2002 .Pp show the context of the match .br .Nm apropos : .Nx Pq May 22, 2016 .It Fl N do not allow newlines between .Xr eqn 7 delimiters .br .Nm groff : .No groff-1.01 Pq Feb 21, 1991 .It Fl n specify a page number for the first page .br .Nm troff : .At7 .br .Nm groff : .g04 .Pp .Xr nroff 1 output mode .br .Nm man : -.At7 +.At7 , +.At8 , +.At10 .Pp do not create the .Xr whatis 1 database .br .Nm catman : .Nx Pq July 27, 1993 .Pp print commands instead of executing them .br .Nm catman : .Fx Pq May 18, 2002 , .No Solaris 9-11 .Pp limit the number of results .br .Nm apropos : .Nx Pq Feb 7, 2012 .Pp dry run simulating .Xr mandoc.db 5 creation .br .Nm makewhatis : .Ox 5.6 Pq April 18, 2014 .It Fl O output options .br .Nm mandoc : .Ox 4.8 Pq Oct 27, 2009 .br .Nm man , apropos , whatis : .Ox 5.7 Pq August 27, 2014 .It Fl o select pages by numbers .br .Nm nroff , troff : .At7 .br .Nm groff : .g04 .Pp force use of non-localized manual pages .br .Nm man : .Fx Pq June 7, 1999 .Pp optimize index for speed and disk space .br .Nm makemandb : .Nx Pq Feb 7, 2012 .It Fl P pass argument to postprocessor .br .Nm groff : .No groff-0.6 Pq Sep 14, 1990 .Pp use specified pager .br .Nm man : .Eaton ; .Fx , No man-db , man-1.6 .Pp turn on pager formatting .br .Nm apropos : .Nx Pq Apr 2, 2013 .It Fl p preprocess with .Xr pic 1 .br .Nm groff : .g04 .Pp use the given list of preprocessors .br .Nm man : .Eaton ; .Fx , No man-db , man-1.6 .Pp dry run, display commands instead of executing them .br .Nm catman : .Nx Pq July 27, 1993 , .Fx Pq March 15, 1995 to May 18, 2002 , .No Solaris 9-11 .Pp print warnings when building .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 2.7 Pq April 23, 2000 .Pp do not look for deleted manual pages .br .Nm mandb : .No man-db Pq June 28, 2001 .Pp print the search path for manual pages .br .Nm man : .Nx Pq June 14 , 2011 .Pp turn on pager formatting and pipe through pager .br .Nm apropos : .Nx Pq Feb 7, 2012 .Pp .Bq obsolete hardware set phototypesetter point size .br .Nm troff : .At7 .It Fl Q print only fatal error messages .br .Nm makemandb : .Nx Pq Aug 29, 2012 .Pp quick mode of .Xr mandoc.db 5 creation .br .Nm makewhatis : .Ox 5.6 Pq April 18, 2014 .It Fl q invoke the simultaneous input-output mode of the .rd request .br .Nm nroff , troff : .At7 .Pp +quick mode: prefer preformatted page if available +.br +.Nm man : +.At8 , +.At10 +.Pp issue no warnings .br .Nm manpath : .Eaton ; .Fx , No man-db .br .Nm mandb : .dbI .Pp print only warnings and errors, no status updates .br .Nm makemandb : .Nx Pq Aug 29, 2012 .It Fl R postprocess with .Xr refer 1 .br .Nm groff : .No groff-1.02 Pq June 2, 1991 .Pp recode to the specified encoding .br .Nm man : .No man-db Pq Dec 31, 2007 .It Fl r set number register .br .Nm nroff , troff : .At7 .br .Nm groff : .g04 .Pp scan for and remove junk files .br .Nm catman : .Fx Pq March 31, 1995 .Pp set .Xr less 1 prompt .br .Nm man : .No man-db-2.3.5 Pq April 21, 1995 .Pp use regular expression matching .br .Nm apropos , whatis : .No man-db-2.3.5 Pq April 21, 1995 .Pp turn off formatting .br .Nm apropos : .Nx Pq Feb 10, 2013 .Pp check for formatting errors, do not display .br .Nm man : .No illumos , Solaris 9-11 .It Fl S manual section search list .br .Nm man : .Eaton ; .Fx , No man-db , man-1.6 .Pp safer mode .br .Nm groff : .No groff-1.10 Pq May 17, 1994 .Pp restrict architecture .br .Nm man : .Ox 2.3 Pq March 9, 1998 , .Nx Pq May 27, 2000 .br .Nm apropos : .Ox 4.5 Pq Dec 24, 2008 , .Nx Pq May 8, 2009 .br .Nm whatis : .Ox 5.6 Pq April 18, 2014 .br .Nm mandoc : .Ox 5.7 Pq August 27, 2014 .It Fl s preprocess with .Xr soelim 1 .br .Nm groff : .g04 .Pp silent mode, do not echo commands .br .Nm catman : .Nx Pq April 26, 1994 .Pp restrict section .br .Nm makewhatis : .man15g .br .Nm man : .Ox 2.3 Pq March 9, 1998 , .Nx Pq June 12, 2000 ; .No illumos , Solaris 9-11 .br .Nm apropos : .No man-db Pq Nov 16, 2003 , .Ox 4.5 Pq Dec 24, 2008 , .Nx Pq May 8, 2009 ; .No illumos .br .Nm whatis : .No man-db Pq Nov 16, 2003 , .Ox 5.6 Pq April 18, 2014 ; .No illumos .br .Nm mandoc : .Ox 5.7 Pq August 27, 2014 .Pp do not look for stray cats .br .Nm mandb : .dbI .Pp .Bq SysV compat, recommends Fl S manual section search list .br .Nm man : .No man-db Pq Jan 1, 2008 .Pp .Bq superseded by Fl h display the SYNOPSIS lines only .br .Nm man : .PWB .Pp .Bq obsolete hardware pause before each page for paper manipulation .br .Nm roff : .At7 .Pp .Bq obsolete hardware .Xr troff 1 output mode, small format .br .Nm man : .At3 , .At5 .It Fl T select terminal output format .br .Nm nroff : .At7 .br .Nm man : .At3 , .At5 , .dbI , .Ox 5.7 Pq August 27, 2014 .br .Nm groff : .g04 .br .Nm mandoc : .Ox 4.8 Pq April 6, 2009 .br .Nm apropos , whatis : .Ox 5.7 Pq August 27, 2014 .Pp use UTF-8 for .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 5.6 Pq April 18, 2014 .Pp .Bq superseded by Fl m use other macro package .br .Nm man , catman : .No Solaris 9-11 .It Fl t .Xr troff 1 output mode .br .Nm man : .PWB , .At7 , .Bx 2 Pq May 10, 1979 , .At3 , .At5 , +.At8 , +.At10 , .Eaton ; .Fx , No man-db , man-1.6 , illumos , Solaris 9-11 .br .Nm catman : .No Solaris 9-11 .Pp preprocess with .Xr tbl 7 .br .Nm groff : .g04 .Pp check manual pages in the hierarchy .br .Nm mandb : .No man-db-1.3.12 to 1.3.17 Pq not before 1996, not after 2001 .Pp check files for problems related to .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 2.7 Pq April 23, 2000 .It Fl U unsafe mode .br .Nm groff : .No groff-1.12 Pq Dec 13, 1999 .It Fl u update database .br .Nm makewhatis : .Pq not before 1992, not after 1995 .Pp create user databases only .br .Nm mandb : .dbI .Pp update database cache (requires suid) .br .Nm man : .No before man-db-2.2a10 Pq before Dec 6, 1994 .Pp remove files from .Xr mandoc.db 5 .br .Nm makewhatis : .Ox 3.4 Pq July 9, 2003 .It Fl V print the pipeline on stdout instead of executing it .br .Nm groff : .No groff-0.6 Pq Sep 2, 1990 .Pp print version information .br .Nm man , apropos , whatis , mandb , catman , manpath : .dbI .It Fl v print version number .br .Nm groff : .g04 .Pp verbose mode .br .Nm catman : .Fx Pq March 15, 1995 , .No mandoc Pq June 30, 2025 .br .Nm makewhatis : .man15g .br .Nm apropos , whatis : .No man-db Pq Dec 29, 2002 .Pp print the name of every parsed file .br .Nm makemandb : .Nx Pq Feb 7, 2012 .Pp .Bq obsolete hardware produce output on the Versatec printer .br .Nm man : .PWB .It Fl W disable the named warning .br .Nm groff : .No groff-0.5 Pq August 14, 1990 .Pp list pathnames without additional information .br .Nm man : .man15e .Pp list pathnames of cat files .br .Nm man : .No man-db Pq Aug 13, 2002 .Pp minimum message level to display .br .Nm mandoc : .Ox 4.8 Pq April 6, 2009 .br .Nm man , apropos , whatis : .Ox 5.7 Pq August 27, 2014 .It Fl w list pathnames .br .Nm man : .At7 , .At3 , .At5 , +.At8 , .Eaton ; .Ox , Fx , Nx , No man-db , man-1.6 .br .Nm apropos , whatis , mandoc : .Ox 5.7 Pq August 27, 2014 .Pp enable the named warning .br .Nm groff : .No groff-0.5 Pq August 14, 1990 .Pp only create the .Xr whatis 1 database .br .Nm catman : .Nx Pq July 27, 1993 , .No Solaris 9-11 .Pp use wildcard matching .br .Nm apropos , whatis : .No man-db-2.3.5 Pq April 21, 1995 .Pp use manpath obtained from man --path .br .Nm makewhatis : .man15g .Pp update the .Xr whatis 1 database .br .Nm man : .No illumos .Pp .Bq obsolete hardware wait until the phototypesetter is available .br .Nm troff : .At7 .It Fl X display with .Xr gxditview 1 .br .Nm groff : .No groff-1.06 Pq Sep 1, 1992 .br .Nm man : .dbI .It Fl y use the non-compacted version of the macros .br .Nm man : .At3 , .At5 .It Fl Z do not run preprocessors .br .Nm groff : .g04 .br .Nm man : .No man-db-2.2a5 Pq Nov 10, 1994 .It Fl z suppress formatted output from .Xr troff 1 , print only error messages .br .Nm groff : .g04 .It Fl 7 ASCII output mode .br .Nm man : .No man-db-2.3.5 Pq April 21, 1995 .It Fl \&? print a help message and exit .br .Nm groff : .g04 .br .Nm man , manpath : .Eaton ; .Fx , No man-db .br .Nm apropos , whatis , mandb , catman : .dbI .El .Pp Multi-letter options: .Bl -tag -width Ds .It Fl hp .Bq obsolete hardware output to a Hewlett Packard terminal .br .Nm man : .PWB .It Fl 12 .Bq obsolete hardware use 12-pitch for certain terminals .br .Nm man : .At3 , .At5 .It Fl 450 .Bq obsolete hardware output to a DASI 450 terminal .br .Nm man : .PWB .El .Pp In .At v3 , .Xr man 1 had no options. .br The syntax was: .Sy man Ar name Op Ar section .Pp In .At v4 , .br the syntax changed to: .Sy man Oo Ar section Oc Op Ar name ... .Sh AUTHORS This information was assembled by .An Ingo Schwarze Aq Mt schwarze@openbsd.org using .Bl -bullet -compact .It the Unix Archive of the Unix Heritage Society .It the CSRG Archive CD-ROMs .It the .Fx SVN repository .It the .Ox CVS repository .It the .Nx CVS repository .It the GNU roff (groff) git repository .It the 4.3BSD-Net/2 groff CHANGES file (Oct 1990 to March 1991) .It the 4.3BSD-Net/2 groff ChangeLog file (July 1990 to March 1991) .It the man-db CVS and git repositories (since April 2001) .It the man-db NEWS file (April 1995 to Dec 2016) .It the man-db ChangeLog-2013 file (Nov 1994 to Dec 2013) .It release tarballs man-1.5g (July 1998) to man-1.5p (Jan 2005), man-1.6 (June 2005), and man-1.6a to man-1.6g (Dec 2010) .It a makewhatis release tarball without version number from 1995 .It the illumos manual pages on the WWW .It and Solaris 11, SunOS 5.10, and SunOS 5.9 machines at opencsw.org. .El diff --git a/contrib/mandoc/mandoc.1 b/contrib/mandoc/mandoc.1 index 8b6fe7d19b1e..0f83bcd53f1b 100644 --- a/contrib/mandoc/mandoc.1 +++ b/contrib/mandoc/mandoc.1 @@ -1,2503 +1,2505 @@ -.\" $Id: mandoc.1,v 1.272 2025/07/09 13:46:05 schwarze Exp $ +.\" $Id: mandoc.1,v 1.273 2025/08/22 13:17:06 schwarze Exp $ .\" .\" Copyright (c) 2012, 2014-2023, 2025 Ingo Schwarze .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 9 2025 $ +.Dd $Mdocdate: August 22 2025 $ .Dt MANDOC 1 .Os .Sh NAME .Nm mandoc .Nd format manual pages .Sh SYNOPSIS .Nm mandoc .Op Fl ac .Op Fl I Cm os Ns = Ns Ar name .Op Fl K Ar encoding .Op Fl mdoc | man .Op Fl O Ar options .Op Fl T Ar output .Op Fl W Ar level .Op Ar .Sh DESCRIPTION The .Nm utility formats manual pages for display. .Pp By default, .Nm reads .Xr mdoc 7 or .Xr man 7 text from stdin and produces .Fl T Cm locale output. .Pp The options are as follows: .Bl -tag -width Ds .It Fl a If the standard output is a terminal device and .Fl c is not specified, use .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 less 1 to paginate them. This is the default. It can be specified to override .Fl a . .It Fl I Cm os Ns = Ns Ar name Override the default operating system .Ar name for the .Xr mdoc 7 .Ic \&Os and for the .Xr man 7 .Ic \&TH macro. .It Fl K Ar encoding Specify the input encoding. The supported .Ar encoding arguments are .Cm us-ascii , .Cm iso-8859-1 , and .Cm utf-8 . If not specified, autodetection uses the first match in the following list: .Bl -enum .It If the first three bytes of the input file are the UTF-8 byte order mark (BOM, 0xefbbbf), input is interpreted as .Cm utf-8 . .It If the first or second line of the input file matches the .Sy emacs mode line format .Pp .D1 .\e" -*- Oo ...; Oc coding: Ar encoding ; No -*- .Pp then input is interpreted according to .Ar encoding . .It If the first non-ASCII byte in the file introduces a valid UTF-8 sequence, input is interpreted as .Cm utf-8 . .It Otherwise, input is interpreted as .Cm iso-8859-1 . .El .It Fl mdoc | man With .Fl mdoc , all input files are interpreted as .Xr mdoc 7 . With .Fl man , all input files are interpreted as .Xr man 7 . By default, the input language is automatically detected for each file: if the first macro is .Ic \&Dd or .Ic \&Dt , the .Xr mdoc 7 parser is used; otherwise, the .Xr man 7 parser is used. With other arguments, .Fl m is silently ignored. .It Fl O Ar options Comma-separated output options. See the descriptions of the individual output formats for supported .Ar options . .It Fl T Ar output Select the output format. Supported values for the .Ar output argument are .Cm ascii , .Cm html , the default of .Cm locale , .Cm man , .Cm markdown , .Cm pdf , .Cm ps , .Cm tree , and .Cm utf8 . .Pp The special .Fl T Cm lint mode only parses the input and produces no output. It implies .Fl W Cm all and redirects parser messages, which usually appear on standard error output, to standard output. .It Fl W Ar level Specify the minimum message .Ar level to be reported on the standard error output and to affect the exit status. The .Ar level can be .Cm base , .Cm style , .Cm warning , .Cm error , or .Cm unsupp . The .Cm base level automatically derives the operating system from the contents of the .Ic \&Os macro, from the .Fl Ios command line option, or from the .Xr uname 3 return value. The levels .Cm openbsd and .Cm netbsd are variants of .Cm base that bypass autodetection and request validation of base system conventions for a particular operating system. The level .Cm all is an alias for .Cm base . By default, .Nm is silent. See .Sx EXIT STATUS and .Sx DIAGNOSTICS for details. .Pp The special option .Fl W Cm stop tells .Nm to exit after parsing a file that causes warnings or errors of at least the requested level. No formatted output will be produced from that file. If both a .Ar level and .Cm stop are requested, they can be joined with a comma, for example .Fl W Cm error , Ns Cm stop . .It Ar file Read from the given input file. If multiple files are specified, they are processed in the given order. If unspecified, .Nm reads from standard input. .El .Pp The options .Fl fhklw are also supported and are documented in .Xr man 1 . In .Fl f and .Fl k mode, .Nm also supports the options .Fl CMmOSs described in the .Xr apropos 1 manual. The options .Fl fkl are mutually exclusive and override each other. .Ss ASCII Output Use .Fl T Cm ascii to force text output in 7-bit ASCII character encoding documented in the .Xr ascii 7 manual page, ignoring the .Xr locale 1 set in the environment. .Pp Font styles are applied by using back-spaced encoding such that an underlined character .Sq c is rendered as .Sq _ Ns \e[bs] Ns c , where .Sq \e[bs] is the back-space character number 8. Emboldened characters are rendered as .Sq c Ns \e[bs] Ns c . This markup is typically converted to appropriate terminal sequences by the pager or .Xr ul 1 . To remove the markup, pipe the output to .Xr col 1 .Fl b instead. .Pp The special characters documented in .Xr mandoc_char 7 are rendered best-effort in an ASCII equivalent. In particular, opening and closing .Sq single quotes are represented as characters number 0x60 and 0x27, respectively, which agrees with all ASCII standards from 1965 to the latest revision (2012) and which matches the traditional way in which .Xr roff 7 formatters represent single quotes in ASCII output. This correct ASCII rendering may look strange with modern Unicode-compatible fonts because contrary to ASCII, Unicode uses the code point U+0060 for the grave accent only, never for an opening quote. .Pp The following .Fl O arguments are accepted: .Bl -tag -width Ds .It Cm indent Ns = Ns Ar indent The left margin for normal text is set to .Ar indent blank characters instead of the default of five. Increasing this is not recommended; it may result in degraded formatting, for example overfull lines or ugly line breaks. When output is to a pager on a terminal that is less than 66 columns wide, the default is reduced to three columns. .It Cm tag Ns Op = Ns Ar term If the formatted manual page is opened in a pager, go to the definition of the .Ar term rather than showing the manual page from the beginning. If no .Ar term is specified, reuse the first command line argument that is not a .Ar section number. If that argument is in .Xr apropos 1 .Ar key Ns = Ns Ar val format, only the .Ar val is used rather than the argument as a whole. This is useful for commands like .Ql man -akO tag Ic=ulimit to search for a keyword and jump right to its definition in the matching manual pages. .It Cm width Ns = Ns Ar width The output width is set to .Ar width instead of the default of 78. When output is to a pager on a terminal that is less than 79 columns wide, the default is reduced to one less than the terminal width. In any case, lines that are output in literal mode are never wrapped and may exceed the output width. .El .Ss HTML Output Output produced by .Fl T Cm html conforms to HTML5 using optional self-closing tags. Equations rendered from .Xr eqn 7 blocks use MathML. Non-ASCII characters are rendered as hexadecimal Unicode character references. .Pp The following .Fl O arguments are accepted: .Bl -tag -width Ds .It Cm fragment Omit the declaration and the , , and elements and only emit the subtree below the element. The .Cm style argument will be ignored. This is useful when embedding manual content within existing documents. .It Cm includes Ns = Ns Ar fmt The string .Ar fmt , for example, .Ar ../src/%I.html , is used as a template for linked header files (usually via the .Ic \&In macro). Instances of .Sq \&%I are replaced with the include filename. The default is not to present a hyperlink. .It Cm man Ns = Ns Ar fmt Ns Op ; Ns Ar fmt The string .Ar fmt , for example, .Ar ../html%S/%N.%S.html , is used as a template for linked manuals (usually via the .Ic \&Xr macro). Instances of .Sq \&%N and .Sq %S are replaced with the linked manual's name and section, respectively. If no section is included, section 1 is assumed. The default is not to present a hyperlink. If two formats are given and a file .Ar %N.%S exists in the current directory, the first format is used; otherwise, the second format is used. .It Cm style Ns = Ns Ar style.css The file .Ar style.css is used as an external stylesheet. This must be a valid absolute or relative URI. .Pp Using the file .Pa mandoc.css that is distributed with .Nm is recommended. It provides an appearance similar to terminal output with some additional features specific to .Nm HTML output, in particular making anchor locations that support deep linking stand out visually by putting a dotted line under them, providing tooltips showing the semantic function of elements (macro names), providing some simple aspects of responsive web design, and providing simple support for users who prefer a dark color scheme. .Pp Using a custom CSS file is possible, but writing it requires proficiency in all of the languages HTML 5, CSS 4, and .Xr mdoc 7 and familiarity with the .Nm Ns -specific classes used in .Pa mandoc.css . Besides, while the file .Pa mandoc.css is always adapted to the HTML output generated by the .Nm version it is distributed with, maintaining a custom CSS file usually requires adaptations each time .Nm is upgraded to a new version. .Pp If a stylesheet is not specified with .Fl O Cm style , .Fl T Cm html embeds a minimal stylesheet into the HTML output, mostly to select adequate font-style and font-weight attributes for various macros. The result is readable in any graphical or text-based web browser, but does not aim for looking similar to terminal output. Instead, formatting is mostly left to browser defaults and to user settings in the browser configuration. .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. .El .Ss Locale Output By default, .Nm automatically selects UTF-8 or ASCII output according to the current .Xr locale 1 . If any of the environment variables .Ev LC_ALL , .Ev LC_CTYPE , or .Ev LANG are set and the first one that is set selects the UTF-8 character encoding, it produces .Sx UTF-8 Output ; otherwise, it falls back to .Sx ASCII Output . This output mode can also be selected explicitly with .Fl T Cm locale . .Ss Man Output Use .Fl T Cm man to translate .Xr mdoc 7 input into .Xr man 7 output format. 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. The parser is also run, and as usual, the .Fl W level controls which .Sx DIAGNOSTICS are displayed before copying the input to the output. .Ss Markdown Output Use .Fl T Cm markdown to translate .Xr mdoc 7 input to the markdown format conforming to .Lk https://daringfireball.net/projects/markdown/syntax.text\ "John Gruber's 2004 specification" . The output also almost conforms to the .Lk https://commonmark.org/ CommonMark specification. .Pp The character set used for the markdown output is ASCII. Non-ASCII characters are encoded as HTML entities. Since that is not possible in literal font contexts, because these are rendered as code spans and code blocks in the markdown output, non-ASCII characters are transliterated to ASCII approximations in these contexts. .Pp Markdown is a very weak markup language, so all semantic markup is lost, and even part of the presentational markup may be lost. Do not use this as an intermediate step in converting to HTML; instead, use .Fl T Cm html directly. .Pp The .Xr man 7 , .Xr tbl 7 , and .Xr eqn 7 input languages are not supported by .Fl T Cm markdown output mode. .Ss PDF Output PDF-1.1 output may be generated by .Fl T Cm pdf . See .Sx PostScript Output for .Fl O arguments and defaults. .Ss PostScript Output PostScript .Qq Adobe-3.0 Level-2 pages may be generated by .Fl T Cm ps . Output pages default to letter sized and are rendered in the Times font family, 11-point. Margins are calculated as 1/9 the page length and width. Line-height is 1.4m. .Pp Special characters are rendered as in .Sx ASCII Output . .Pp The following .Fl O arguments are accepted: .Bl -tag -width Ds .It Cm paper Ns = Ns Ar name The paper size .Ar name may be one of .Ar a3 , .Ar a4 , .Ar a5 , .Ar legal , or .Ar letter . You may also manually specify dimensions as .Ar NNxNN , width by height in millimetres. If an unknown value is encountered, .Ar letter is used. .El .Ss UTF-8 Output Use .Fl T Cm utf8 to force text output in UTF-8 multi-byte character encoding, ignoring the .Xr locale 1 settings in the environment. See .Sx ASCII Output regarding font styles and .Fl O arguments. .Pp On operating systems lacking locale or wide character support, and on those where the internal character representation is not UCS-4, .Nm always falls back to .Sx ASCII Output . .Ss Syntax tree output Use .Fl T Cm tree to show a human readable representation of the syntax tree. It is useful for debugging the source code of manual pages. The exact format is subject to change, so don't write parsers for it. .Pp The first paragraph shows meta data found in the .Xr mdoc 7 prologue, on the .Xr man 7 .Ic \&TH line, or the fallbacks used. .Pp In the tree dump, each output line shows one syntax tree node. Child nodes are indented with respect to their parent node. The columns are: .Pp .Bl -enum -compact .It For macro nodes, the macro name; for text and .Xr tbl 7 nodes, the content. There is a special format for .Xr eqn 7 nodes. .It Node type (text, elem, block, head, body, body-end, tail, tbl, eqn). .It Flags: .Bl -dash -compact .It An opening parenthesis if the node is an opening delimiter. .It An asterisk if the node starts a new input line. .It The input line number (starting at one). .It A colon. .It The input column number (starting at one). .It A closing parenthesis if the node is a closing delimiter. .It A full stop if the node ends a sentence. .It BROKEN if the node is a block broken by another block. .It NOSRC if the node is not in the input file, but automatically generated from macros. .It NOPRT if the node is not supposed to generate output for any output format. .El .El .Pp The following .Fl O argument is accepted: .Bl -tag -width Ds .It Cm noval Skip validation and show the unvalidated syntax tree. This can help to find out whether a given behaviour is caused by the parser or by the validator. Meta data is not available in this case. .El .Sh ENVIRONMENT .Bl -tag -width MANPAGER .It Ev LC_CTYPE The character encoding .Xr locale 1 . When .Sx Locale Output is selected, it decides whether to use ASCII or UTF-8 output format. It never affects the interpretation of input files. .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER is used instead of the standard pagination program, .Xr less 1 ; see .Xr man 1 for details. Only used if .Fl a or .Fl l is specified. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, .Xr less 1 is used. Only used if .Fl a or .Fl l is specified. .El .Sh EXIT STATUS The .Nm utility exits with one of the following values, controlled by the message .Ar level associated with the .Fl W option: .Pp .Bl -tag -width Ds -compact .It 0 No base system convention violations, style suggestions, warnings, or errors occurred, or those that did were ignored because they were lower than the requested .Ar level . .It 1 At least one base system convention violation or style suggestion occurred, but no warning or error, and .Fl W Cm base or .Fl W Cm style was specified. .It 2 At least one warning occurred, but no error, and .Fl W Cm warning or a lower .Ar level was requested. .It 3 At least one parsing error occurred, but no unsupported feature was encountered, and .Fl W Cm error or a lower .Ar level was requested. .It 4 At least one unsupported feature was encountered, and .Fl W Cm unsupp or a lower .Ar level was requested. .It 5 Invalid command line arguments were specified. No input files have been read. .It 6 An operating system error occurred, for example exhaustion of memory, file descriptors, or process table entries. Such errors may cause .Nm to exit at once, possibly in the middle of parsing or formatting a file. .El .Pp Note that selecting .Fl T Cm lint output mode implies .Fl W Cm all . .Sh EXAMPLES To page manuals to the terminal: .Pp .Dl $ mandoc -a mandoc.1 man.1 apropos.1 makewhatis.8 .Pp To produce HTML manuals with .Pa /usr/share/misc/mandoc.css as the stylesheet: .Pp .Dl $ mandoc \-T html -O style=/usr/share/misc/mandoc.css mdoc.7 > mdoc.7.html .Pp To check over a large set of manuals: .Pp .Dl $ mandoc \-T lint \(gafind /usr/src -name \e*\e.[1-9]\(ga .Pp To produce a series of PostScript manuals for A4 paper: .Pp .Dl $ mandoc \-T ps \-O paper=a4 mdoc.7 man.7 > manuals.ps .Pp Convert a modern .Xr mdoc 7 manual to the older .Xr man 7 format, for use on systems lacking an .Xr mdoc 7 parser: .Pp .Dl $ mandoc \-T man foo.mdoc > foo.man .Sh DIAGNOSTICS Messages displayed by .Nm follow this format: .Bd -ragged -offset indent .Nm : .Ar file : Ns Ar line : Ns Ar column : level : message : macro argument ... .Pq Ar os .Ed .Pp The first three fields identify the .Ar file name, .Ar line number, and .Ar column number of the input file where the message was triggered. The line and column numbers start at 1. Both are omitted for messages referring to an input file as a whole. All .Ar level and .Ar message strings are explained below. The name of the .Ar macro triggering the message and its arguments are omitted where meaningless. The .Ar os operating system specifier is omitted for messages that are relevant for all operating systems. Fatal messages about invalid command line arguments or operating system errors, for example when memory is exhausted, may also omit the .Ar file and .Ar level fields. .Pp Message levels have the following meanings: .Bl -tag -width "warning" .It Cm syserr An operating system error occurred. There isn't necessarily anything wrong with the input files. Output may all the same be missing or incomplete. .It Cm badarg Invalid command line arguments were specified. No input files have been read and no output is produced. .It Cm unsupp An input file uses unsupported low-level .Xr roff 7 features. The output may be incomplete and/or misformatted, so using GNU troff instead of .Nm to process the file may be preferable. .It Cm error Indicates a risk of information loss or severe misformatting, in most cases caused by serious syntax errors. .It Cm warning Indicates a risk that the information shown or its formatting may mismatch the author's intent in minor ways. Additionally, syntax errors are classified at least as warnings, even if they do not usually cause misformatting. .It Cm style An input file uses dubious or discouraged style. This is not a complaint about the syntax, and probably neither formatting nor portability are in danger. While great care is taken to avoid false positives on the higher message levels, the .Cm style level tries to reduce the probability that issues go unnoticed, so it may occasionally issue bogus suggestions. Use your judgement to decide whether any particular .Cm style suggestion really justifies a change to the input file. .It Cm base A convention used in the base system of a specific operating system is not adhered to. These are not markup mistakes, and neither the quality of formatting nor portability are in danger. Messages of the .Cm base level are printed with the more intuitive .Cm style .Ar level tag. .El .Pp Messages of the .Cm base , .Cm style , .Cm warning , .Cm error , and .Cm unsupp levels are hidden unless their level, or a lower level, is requested using a .Fl W option or .Fl T Cm lint output mode. .Pp As indicated below, all .Cm base and some .Cm style checks are only performed if a specific operating system name occurs in the arguments of the .Fl W command line option, of the .Ic \&Os macro, of the .Fl Ios command line option, or, if neither are present, in the return value of the .Xr uname 3 function. .Ss Conventions for base system manuals .Bl -ohang .It Sy "Mdocdate found" .Pq mdoc , Nx The .Ic \&Dd macro uses CVS .Ic Mdocdate keyword substitution, which is not supported by the .Nx base system. Consider using the conventional .Dq "Month dd, yyyy" format instead. .It Sy "Mdocdate missing" .Pq mdoc , Ox The .Ic \&Dd macro does not use CVS .Ic Mdocdate keyword substitution, but using it is conventionally expected in the .Ox base system. .It Sy "unknown architecture" .Pq mdoc , Ox , Nx The third argument of the .Ic \&Dt macro does not match any of the architectures this operating system is running on. .It Sy "operating system explicitly specified" .Pq mdoc , Ox , Nx The .Ic \&Os macro has an argument. In the base system, it is conventionally left blank. .It Sy "RCS id missing" .Pq Ox , Nx The manual page lacks the comment line with the RCS identifier generated by CVS .Ic OpenBSD or .Ic NetBSD keyword substitution as conventionally used in these operating systems. .El .Ss Style suggestions .Bl -ohang .It Sy "legacy man(7) date format" .Pq mdoc The .Ic \&Dd macro uses the legacy .Xr man 7 date format .Dq yyyy-mm-dd . Consider using the conventional .Xr mdoc 7 date format .Dq "Month dd, yyyy" instead. .It Sy "normalizing date format to" : No ... .Pq mdoc , man The .Ic \&Dd or .Ic \&TH macro provides an abbreviated month name or a day number with a leading zero. In the formatted output, the month name is written out in full and the leading zero is omitted. .It Sy "lower case character in document title" .Pq mdoc , man The title is still used as given in the .Ic \&Dt or .Ic \&TH macro. .It Sy "duplicate RCS id" A single manual page contains two copies of the RCS identifier for the same operating system. Consider deleting the later instance and moving the first one up to the top of the page. .It Sy "possible typo in section name" .Pq mdoc Fuzzy string matching revealed that the argument of an .Ic \&Sh macro is similar, but not identical to a standard section name. .It Sy "unterminated quoted argument" .Pq roff Macro arguments can be enclosed in double quote characters such that space characters and macro names contained in the quoted argument need not be escaped. The closing quote of the last argument of a macro can be omitted. However, omitting it is not recommended because it makes the code harder to read. .It Sy "useless macro" .Pq mdoc A .Ic \&Bt , .Ic \&Tn , or .Ic \&Ud macro was found. Simply delete it: it serves no useful purpose. .It Sy "consider using OS macro" .Pq mdoc A string was found in plain text or in a .Ic \&Bx macro that could be represented using .Ic \&Ox , .Ic \&Nx , .Ic \&Fx , or .Ic \&Dx . .It Sy "errnos out of order" -.Pq mdoc, Nx +.Pq mdoc , Nx The .Ic \&Er items in a .Ic \&Bl list are not in alphabetical order. .It Sy "duplicate errno" -.Pq mdoc, Nx +.Pq mdoc , Nx A .Ic \&Bl list contains two consecutive .Ic \&It 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 .Ic \&Ex , \&Fo , \&Nd , \&Nm , \&Os , \&Sh , \&Ss , \&St , or .Ic \&Sx macro ends with a trailing delimiter. This is usually bad style and often indicates typos. Most likely, the delimiter can be removed. .It Sy "no blank before trailing delimiter" .Pq mdoc The last argument of a macro that supports trailing delimiter arguments is longer than one byte and ends with a trailing delimiter. Consider inserting a blank such that the delimiter becomes a separate argument, thus moving it out of the scope of the macro. .It Sy "fill mode already enabled, skipping" .Pq man A .Ic \&fi request occurs even though the document is still in fill mode, or already switched back to fill mode. It has no effect. .It Sy "fill mode already disabled, skipping" .Pq man An .Ic \&nf request occurs even though the document already switched to no-fill mode and did not switch back to fill mode yet. It has no effect. .It Sy "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 .Qq \-\- , that is not a good way to write it in an input file because it renders poorly on all other output devices. .It Sy "function name without markup" .Pq mdoc A word followed by an empty pair of parentheses occurs on a text line. Consider using an .Ic \&Fn or .Ic \&Xr macro. .It Sy "whitespace at end of input line" .Pq mdoc , man , roff Whitespace at the end of input lines is almost never semantically significant \(em but in the odd case where it might be, it is extremely confusing when reviewing and maintaining documents. .It Sy "bad comment style" .Pq roff Comment lines start with a dot, a backslash, and a double-quote character. The .Nm utility treats the line as a comment line even without the backslash, but leaving out the backslash might not be portable. .El .Ss Warnings related to the document prologue .Bl -ohang .It Sy "missing manual title, using UNTITLED" .Pq mdoc , man A .Ic \&Dt or .Ic \&TH macro has no arguments, its first argument is an empty string, or there is no .Ic \&Dt macro before the first non-prologue .Xr mdoc 7 macro. .It Sy "missing manual title, using \(dq\(dq" .Pq man An input document does not contain any .Ic \&TH macro. .It Sy "missing manual section, using \(dq\(dq" .Pq mdoc , man A .Ic \&Dt or .Ic \&TH macro lacks the mandatory section argument. .It Sy "unknown manual section" .Pq mdoc The section number in a .Ic \&Dt line is invalid, but still used. .It Sy "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 +.Pq mdoc , man The document was parsed as .Xr mdoc 7 and it has no .Ic \&Dd macro, or the .Ic \&Dd macro has no arguments or only empty arguments; or the document was parsed as .Xr man 7 and it has no .Ic \&TH macro, or the .Ic \&TH macro has less than three arguments or its third argument is empty. .It Sy "cannot parse date, using it verbatim" .Pq mdoc , man The date given in a .Ic \&Dd or .Ic \&TH macro does not follow the conventional format. .It Sy "date in the future, using it anyway" .Pq mdoc , man The date given in a .Ic \&Dd or .Ic \&TH macro is more than a day ahead of the current system .Xr time 3 . .It Sy "missing Os macro, using \(dq\(dq" .Pq mdoc The default or current system is not shown in this case. .It Sy "late prologue macro" .Pq mdoc A .Ic \&Dd or .Ic \&Os macro occurs after some non-prologue macro, but still takes effect. .It Sy "prologue macros out of order" .Pq mdoc The prologue macros are not given in the conventional order .Ic \&Dd , .Ic \&Dt , .Ic \&Os . All three macros are used even when given in another order. .El .Ss Warnings regarding document structure .Bl -ohang .It Sy ".so is fragile, better use ln(1)" .Pq roff Including files only works when the parser program runs with the correct current working directory. .It Sy "no document body" .Pq mdoc , man The document body contains neither text nor macros. An empty document is shown, consisting only of a header and a footer line. .It Sy "content before first section header" .Pq mdoc , man Some macros or text precede the first .Ic \&Sh or .Ic \&SH section header. The offending macros and text are parsed and added to the top level of the syntax tree, outside any section block. .It Sy "first section is not NAME" .Pq mdoc The argument of the first .Ic \&Sh macro is not .Sq NAME . This may confuse .Xr makewhatis 8 and .Xr apropos 1 . .It Sy "NAME section without Nm before Nd" .Pq mdoc The NAME section does not contain any .Ic \&Nm child macro before the first .Ic \&Nd macro. .It Sy "NAME section without description" .Pq mdoc The NAME section lacks the mandatory .Ic \&Nd child macro. .It Sy "description not at the end of NAME" .Pq mdoc The NAME section does contain an .Ic \&Nd child macro, but other content follows it. .It Sy "bad NAME section content" .Pq mdoc The NAME section contains plain text or macros other than .Ic \&Nm and .Ic \&Nd . .It Sy "missing comma before name" .Pq mdoc The NAME section contains an .Ic \&Nm macro that is neither the first one nor preceded by a comma. .It Sy "missing description line, using \(dq\(dq" .Pq mdoc The .Ic \&Nd macro lacks the required argument. The title line of the manual will end after the dash. .It Sy "description line outside NAME section" .Pq mdoc An .Ic \&Nd macro appears outside the NAME section. The arguments are printed anyway and the following text is used for .Xr apropos 1 , but none of that behaviour is portable. .It Sy "sections out of conventional order" .Pq mdoc A standard section occurs after another section it usually precedes. All section titles are used as given, and the order of sections is not changed. .It Sy "duplicate section title" .Pq mdoc The same standard section title occurs more than once. .It Sy "unexpected section" .Pq mdoc A standard section header occurs in a section of the manual where it normally isn't useful. .It Sy "cross reference to self" .Pq mdoc , man An .Ic \&Xr or .Ic \&MR macro refers to a name and section matching the section of the present manual page and a name mentioned in an .Ic \&Nm macro in the NAME or SYNOPSIS section, or in an .Ic \&Fn or .Ic \&Fo macro in the SYNOPSIS. Consider using .Ic \&Nm or .Ic \&Fn instead of .Ic \&Xr . .It Sy "unusual Xr order" .Pq mdoc In the SEE ALSO section, an .Ic \&Xr macro with a lower section number follows one with a higher number, or two .Ic \&Xr macros referring to the same section are out of alphabetical order. .It Sy "unusual Xr punctuation" .Pq mdoc In the SEE ALSO section, punctuation between two .Ic \&Xr macros differs from a single comma, or there is trailing punctuation after the last .Ic \&Xr macro. .It Sy "AUTHORS section without An macro" .Pq mdoc An AUTHORS sections contains no .Ic \&An macros, or only empty ones. Probably, there are author names lacking markup. .El .Ss "Warnings related to macros and nesting" .Bl -ohang .It Sy "obsolete macro" .Pq mdoc See the .Xr mdoc 7 manual for replacements. .It Sy "macro neither callable nor escaped" .Pq mdoc The name of a macro that is not callable appears on a macro line. It is printed verbatim. If the intention is to call it, move it to its own input line; otherwise, escape it by prepending .Sq \e& . .It Sy "skipping paragraph macro" In .Xr mdoc 7 documents, this happens .Bl -dash -compact .It at the beginning and end of sections and subsections .It right before non-compact lists and displays .It at the end of items in non-column, non-compact lists .It and for multiple consecutive paragraph macros. .El In .Xr man 7 documents, it happens .Bl -dash -compact .It for empty .Ic \&P , .Ic \&PP , and .Ic \&LP macros .It for .Ic \&IP macros having neither head nor body arguments .It for .Ic \&br or .Ic \&sp right after .Ic \&SH or .Ic \&SS .El .It Sy "moving paragraph macro out of list" .Pq mdoc A list item in a .Ic \&Bl list contains a trailing paragraph macro. The paragraph macro is moved after the end of the list. .It Sy "skipping no-space macro" .Pq mdoc An input line begins with an .Ic \&Ns macro, or the next argument after an .Ic \&Ns macro is an isolated closing delimiter. The macro is ignored. .It Sy "blocks badly nested" .Pq mdoc If two blocks intersect, one should completely contain the other. Otherwise, rendered output is likely to look strange in any output format, and rendering in SGML-based output formats is likely to be outright wrong because such languages do not support badly nested blocks at all. Typical examples of badly nested blocks are .Qq Ic \&Ao \&Bo \&Ac \&Bc and .Qq Ic \&Ao \&Bq \&Ac . In these examples, .Ic \&Ac breaks .Ic \&Bo and .Ic \&Bq , respectively. .It Sy "nested displays are not portable" .Pq mdoc A .Ic \&Bd , .Ic \&D1 , or .Ic \&Dl display occurs nested inside another .Ic \&Bd display. This works with .Nm , but fails with most other implementations. .It Sy "moving content out of list" .Pq mdoc A .Ic \&Bl list block contains text or macros before the first .Ic \&It macro. The offending children are moved before the beginning of the list. .It Sy "first macro on line" Inside a .Ic \&Bl Fl column list, a .Ic \&Ta macro occurs as the first macro on a line, which is not portable. .It Sy "line scope broken" .Pq man While parsing the next-line scope of the previous macro, another macro is found that prematurely terminates the previous one. The previous, interrupted macro is deleted from the parse tree. .El .Ss "Warnings related to missing arguments" .Bl -ohang .It Sy "skipping empty request" .Pq roff , eqn The macro name is missing from a macro definition request, or an .Xr eqn 7 control statement or operation keyword lacks its required argument. .It Sy "conditional request controls empty scope" .Pq roff A conditional request is only useful if any of the following follows it on the same logical input line: .Bl -dash -compact .It The .Sq \e{ keyword to open a multi-line scope. .It A request or macro or some text, resulting in a single-line scope. .It The immediate end of the logical line without any intervening whitespace, resulting in next-line scope. .El Here, a conditional request is followed by trailing whitespace only, and there is no other content on its logical input line. Note that it doesn't matter whether the logical input line is split across multiple physical input lines using .Sq \e line continuation characters. This is one of the rare cases where trailing whitespace is syntactically significant. The conditional request controls a scope containing whitespace only, so it is unlikely to have a significant effect, except that it may control a following .Ic \&el clause. .It Sy "skipping empty macro" .Pq mdoc The indicated macro has no arguments and hence no effect. .It Sy "empty block" .Pq mdoc , man A .Ic \&Bd , .Ic \&Bk , .Ic \&Bl , .Ic \&D1 , .Ic \&Dl , or .Ic \&RS block contains nothing in its body and will produce no output. .It Sy "empty argument, using 0n" .Pq mdoc The required width is missing after .Ic \&Bd or .Ic \&Bl .Fl offset or .Fl width . .It Sy "missing display type, using -ragged" .Pq mdoc The .Ic \&Bd macro is invoked without the required display type. .It Sy "list type is not the first argument" .Pq mdoc In a .Ic \&Bl macro, at least one other argument precedes the type argument. The .Nm utility copes with any argument order, but some other .Xr mdoc 7 implementations do not. .It Sy "missing -width in -tag list, using 8n" .Pq mdoc Every .Ic \&Bl macro having the .Fl tag argument requires .Fl width , too. .It Sy "missing utility name, using \(dq\(dq" .Pq mdoc The .Ic \&Ex Fl std macro is called without an argument before .Ic \&Nm has first been called with an argument. .It Sy "missing function name, using \(dq\(dq" .Pq mdoc The .Ic \&Fo macro is called without an argument. No function name is printed. .It Sy "empty head in list item" .Pq mdoc In a .Ic \&Bl .Fl diag , .Fl hang , .Fl inset , .Fl ohang , or .Fl tag list, an .Ic \&It macro lacks the required argument. The item head is left empty. .It Sy "empty list item" .Pq mdoc In a .Ic \&Bl .Fl bullet , .Fl dash , .Fl enum , or .Fl hyphen list, an .Ic \&It block is empty. An empty list item is shown. .It Sy "missing argument, using next line" .Pq mdoc An .Ic \&It macro in a .Ic \&Bd Fl column list has no arguments. While .Nm uses the text or macros of the following line, if any, for the cell, other formatters may misformat the list. .It Sy "missing font type, using \efR" .Pq mdoc A .Ic \&Bf macro has no argument. It switches to the default font. .It Sy "unknown font type, using \efR" .Pq mdoc The .Ic \&Bf argument is invalid. The default font is used instead. .It Sy "nothing follows prefix" .Pq mdoc A .Ic \&Pf macro has no argument, or only one argument and no macro follows on the same input line. This defeats its purpose; in particular, spacing is not suppressed before the text or macros following on the next input line. .It Sy "empty reference block" .Pq mdoc An .Ic \&Rs macro is immediately followed by an .Ic \&Re macro on the next input line. Such an empty block does not produce any output. .It Sy "missing section argument" .Pq mdoc , man An .Ic \&Xr or .Ic \&MR macro lacks its second, section number argument. The first argument, i.e. the name, is printed, but without a section number. In the case of .Ic \&Xr , the parentheses are also omitted. .It Sy "missing -std argument, adding it" .Pq mdoc An .Ic \&Ex or .Ic \&Rv macro lacks the required .Fl std argument. The .Nm utility assumes .Fl std even when it is not specified, but other implementations may not. .It Sy "missing option string, using \(dq\(dq" .Pq man The .Ic \&OP macro is invoked without any argument. An empty pair of square brackets is shown. .It Sy "missing resource identifier, using \(dq\(dq" .Pq man The .Ic \&MT or .Ic \&UR macro is invoked without any argument. An empty pair of angle brackets is shown. .It Sy "missing eqn box, using \(dq\(dq" .Pq eqn A diacritic mark or a binary operator is found, but there is nothing to the left of it. An empty box is inserted. .El .Ss "Warnings related to bad macro arguments" .Bl -ohang .It Sy "duplicate argument" .Pq mdoc A .Ic \&Bd or .Ic \&Bl macro has more than one .Fl compact , more than one .Fl offset , or more than one .Fl width argument. All but the last instances of these arguments are ignored. .It Sy "skipping duplicate argument" .Pq mdoc An .Ic \&An macro has more than one .Fl split or .Fl nosplit argument. All but the first of these arguments are ignored. .It Sy "skipping duplicate display type" .Pq mdoc A .Ic \&Bd macro has more than one type argument; the first one is used. .It Sy "skipping duplicate list type" .Pq mdoc A .Ic \&Bl macro has more than one type argument; the first one is used. .It Sy "skipping -width argument" .Pq mdoc A .Ic \&Bl .Fl column , .Fl diag , .Fl ohang , .Fl inset , or .Fl item list has a .Fl width argument. That has no effect. .It Sy "wrong number of cells" In a line of a .Ic \&Bl Fl column list, the number of tabs or .Ic \&Ta macros is less than the number expected from the list header line or exceeds the expected number by more than one. Missing cells remain empty, and all cells exceeding the number of columns are joined into one single cell. .It Sy "unknown AT&T UNIX version" .Pq mdoc An .Ic \&At macro has an invalid argument. It is used verbatim, with .Qq "AT&T UNIX " prefixed to it. .It Sy "comma in function argument" .Pq mdoc An argument of an .Ic \&Fa or .Ic \&Fn macro contains a comma; it should probably be split into two arguments. .It Sy "parenthesis in function name" .Pq mdoc The first argument of an .Ic \&Fc or .Ic \&Fn macro contains an opening or closing parenthesis; that's probably wrong, parentheses are added automatically. .It Sy "unknown library name" -.Pq mdoc, not on Ox +.Pq mdoc , not on Ox An .Ic \&Lb macro has an unknown name argument and will be rendered as .Qq library Dq Ar name . .It Sy "invalid content in Rs block" .Pq mdoc An .Ic \&Rs block contains plain text or non-% macros. The bogus content is left in the syntax tree. Formatting may be poor. .It Sy "invalid Boolean argument" .Pq mdoc An .Ic \&Sm macro has an argument other than .Cm on or .Cm off . The invalid argument is moved out of the macro, which leaves the macro empty, causing it to toggle the spacing mode. .It Sy "argument contains two font escapes" .Pq roff The second argument of a .Ic char request contains more than one font escape sequence. A wrong font may remain active after using the character. .It Sy "unknown font, skipping request" .Pq man , tbl A .Xr roff 7 .Ic \&ft request or a .Xr tbl 7 .Ic \&f layout modifier has an unknown .Ar font argument. .It Sy "ignoring distance argument" .Pq roff In addition to the margin character, an .Ic \&mc request has a second argument supposed to represent a distance, but the .Nm implementation of .Ic \&mc always ignores the second argument. .It Sy "odd number of characters in request" .Pq roff A .Ic \&tr request contains an odd number of characters. The last character is mapped to the blank character. .El .Ss "Warnings related to plain text" .Bl -ohang .It Sy "blank line in fill mode, using .sp" -.Pq mdoc +.Pq mdoc , man The meaning of blank input lines is only well-defined in non-fill mode: In fill mode, line breaks of text input lines are not supposed to be significant. However, for compatibility with groff, blank lines in fill mode are formatted like .Ic \&sp requests. To request a paragraph break, use .Ic \&Pp +or +.Ic \&PP instead of a blank line. .It Sy "tab in filled text" .Pq mdoc , man The meaning of tab characters is only well-defined in non-fill mode: In fill mode, whitespace is not supposed to be significant on text input lines. As an implementation dependent choice, tab characters on text lines are passed through to the formatters in any case. Given that the text before the tab character will be filled, it is hard to predict which tab stop position the tab will advance to. .It Sy "new sentence, new line" .Pq mdoc A new sentence starts in the middle of a text line. Start it on a new input line to help formatters produce correct spacing. .It Sy "invalid escape sequence argument" .Pq roff The argument of an escape sequence is of an invalid form. Invalid escape sequences are ignored. .It Sy "undefined escape, printing literally" .Pq roff In an escape sequence, the first character right after the leading backslash is invalid. That character is printed literally, which is equivalent to ignoring the backslash. .It Sy "undefined string, using \(dq\(dq" .Pq roff If a string is used without being defined before, its value is implicitly set to the empty string. However, defining strings explicitly before use keeps the code more readable. .El .Ss "Warnings related to tables" .Bl -ohang .It Sy "tbl line starts with span" .Pq tbl The first cell in a table layout line is a horizontal span .Pq Sq Cm s . Data provided for this cell is ignored, and nothing is printed in the cell. .It Sy "tbl column starts with span" .Pq tbl The first line of a table layout specification requests a vertical span .Pq Sq Cm ^ . Data provided for this cell is ignored, and nothing is printed in the cell. .It Sy "skipping vertical bar in tbl layout" .Pq tbl A table layout specification contains more than two consecutive vertical bars. A double bar is printed, all additional bars are discarded. .El .Ss "Errors related to tables" .Bl -ohang .It Sy "non-alphabetic character in tbl options" .Pq tbl The table options line contains a character other than a letter, blank, or comma where the beginning of an option name is expected. The character is ignored. .It Sy "skipping unknown tbl option" .Pq tbl The table options line contains a string of letters that does not match any known option name. The word is ignored. .It Sy "missing tbl option argument" .Pq tbl A table option that requires an argument is not followed by an opening parenthesis, or the opening parenthesis is immediately followed by a closing parenthesis. The option is ignored. .It Sy "wrong tbl option argument size" .Pq tbl A table option argument contains an invalid number of characters. Both the option and the argument are ignored. .It Sy "empty tbl layout" .Pq tbl A table layout specification is completely empty, specifying zero lines and zero columns. As a fallback, a single left-justified column is used. .It Sy "invalid character in tbl layout" .Pq tbl A table layout specification contains a character that can neither be interpreted as a layout key character nor as a layout modifier, or a modifier precedes the first key. The invalid character is discarded. .It Sy "unmatched parenthesis in tbl layout" .Pq tbl 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 invalid column width in tbl layout" .Pq tbl A column width specifier in a table layout is empty, zero, or not a valid numerical expression. The width specifier is ignored and the column is made wide enough to accommodate all its data cells. .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. It will probably produce no output. .It Sy "ignoring data in spanned tbl cell" .Pq tbl A table cell is marked as a horizontal span .Pq Sq Cm s or vertical span .Pq Sq Cm ^ in the table layout, but it contains data. The data is ignored. .It Sy "ignoring extra tbl data cells" .Pq tbl A data line contains more cells than the corresponding layout line. The data in the extra cells is ignored. .It Sy "data block open at end of tbl" .Pq tbl A data block is opened with .Cm T{ , but never closed with a matching .Cm T} . The remaining data lines of the table are all put into one cell, and any remaining cells stay empty. .El .Ss "Errors related to roff, mdoc, and man code" .Bl -ohang .It Sy "duplicate prologue macro" .Pq mdoc One of the prologue macros occurs more than once. The last instance overrides all previous ones. .It Sy "skipping late title macro" .Pq mdoc The .Ic \&Dt macro appears after the first non-prologue macro. Traditional formatters cannot handle this because they write the page header before parsing the document body. Even though this technical restriction does not apply to .Nm , traditional semantics is preserved. The late macro is discarded including its arguments. .It Sy "input stack limit exceeded, infinite loop?" .Pq roff Explicit recursion limits are implemented for the following features, in order to prevent infinite loops: .Bl -dash -compact .It expansion of nested escape sequences including expansion of strings and number registers, .It expansion of nested user-defined macros, .It and .Ic \&so file inclusion. .El When a limit is hit, the output is incorrect, typically losing some content, but the parser can continue. .It Sy "skipping bad character" .Pq mdoc , man , roff The input file contains a byte that is not a printable .Xr ascii 7 character. The message mentions the character number. The offending byte is replaced with a question mark .Pq Sq \&? . Consider editing the input file to replace the byte with an ASCII transliteration of the intended character. .It Sy "skipping unknown macro" .Pq mdoc , man , roff The first identifier on a request or macro line is neither recognized as a .Xr roff 7 request, nor as a user-defined macro, nor, respectively, as an .Xr mdoc 7 or .Xr man 7 macro. It may be mistyped or unsupported. The request or macro is discarded including its arguments. .It Sy "skipping request outside macro" .Pq roff A .Ic shift or .Ic return request occurs outside any macro definition and has no effect. .It Sy "skipping insecure request" .Pq roff An input file attempted to run a shell command or to read or write an external file. Such attempts are denied for security reasons. .It Sy "skipping item outside list" .Pq mdoc , eqn An .Ic \&It macro occurs outside any .Ic \&Bl list, or an .Xr eqn 7 .Ic above delimiter occurs outside any pile. It is discarded including its arguments. .It Sy "skipping column outside column list" .Pq mdoc A .Ic \&Ta macro occurs outside any .Ic \&Bl Fl column block. It is discarded including its arguments. .It Sy "skipping end of block that is not open" .Pq mdoc , man , eqn , tbl , roff Various syntax elements can only be used to explicitly close blocks that have previously been opened. An .Xr mdoc 7 block closing macro, a .Xr man 7 .Ic \&ME , \&RE or .Ic \&UE macro, an .Xr eqn 7 right delimiter or closing brace, or the end of an equation, table, or .Xr roff 7 conditional request is encountered but no matching block is open. The offending request or macro is discarded. .It Sy "fewer RS blocks open, skipping" .Pq man The .Ic \&RE macro is invoked with an argument, but less than the specified number of .Ic \&RS blocks is open. The .Ic \&RE macro is discarded. .It Sy "inserting missing end of block" .Pq mdoc , tbl Various .Xr mdoc 7 macros as well as tables require explicit closing by dedicated macros. A block that doesn't support bad nesting ends before all of its children are properly closed. The open child nodes are closed implicitly. .It Sy "appending missing end of block" .Pq mdoc , man , eqn , tbl , roff At the end of the document, an explicit .Xr mdoc 7 block, a .Xr man 7 next-line scope or .Ic \&MT , \&RS or .Ic \&UR block, an equation, table, or .Xr roff 7 conditional or ignore block is still open. The open block is closed implicitly. .It Sy "escaped character not allowed in a name" .Pq roff Macro, string and register identifiers consist of printable, non-whitespace ASCII characters. Escape sequences and characters and strings expressed in terms of them cannot form part of a name. The first argument of an .Ic \&am , .Ic \&as , .Ic \&de , .Ic \&ds , .Ic \&nr , or .Ic \&rr request, or any argument of an .Ic \&rm request, or the name of a request or user defined macro being called, is terminated by an escape sequence. In the cases of .Ic \&as , .Ic \&ds , and .Ic \&nr , the request has no effect at all. In the cases of .Ic \&am , .Ic \&de , .Ic \&rr , and .Ic \&rm , what was parsed up to this point is used as the arguments to the request, and the rest of the input line is discarded including the escape sequence. When parsing for a request or a user-defined macro name to be called, only the escape sequence is discarded. The characters preceding it are used as the request or macro name, the characters following it are used as the arguments to the request or macro. .It Sy "using macro argument outside macro" .Pq roff The escape sequence \e$ occurs outside any macro definition and expands to the empty string. .It Sy "argument number is not numeric" .Pq roff The argument of the escape sequence \e$ is not a digit; the escape sequence expands to the empty string. .It Sy "negative argument, using 0" .Pq roff A .Ic \&shift request has a negative argument or an argument that is negative due to integer overflow. Macro argument numbering remains unchanged. .It Sy "NOT IMPLEMENTED: Bd -file" .Pq mdoc For security reasons, the .Ic \&Bd macro does not support the .Fl file argument. By requesting the inclusion of a sensitive file, a malicious document might otherwise trick a privileged user into inadvertently displaying the file on the screen, revealing the file content to bystanders. The argument is ignored including the file name following it. .It Sy "skipping display without arguments" .Pq mdoc A .Ic \&Bd block macro does not have any arguments. The block is discarded, and the block content is displayed in whatever mode was active before the block. .It Sy "missing list type, using -item" .Pq mdoc A .Ic \&Bl macro fails to specify the list type. .It Sy "argument is not numeric, using 1" .Pq roff The argument of a .Ic \&ce request is not a number. .It Sy "argument is not a character" .Pq roff The first argument of a .Ic char request is neither a single ASCII character nor a single character escape sequence. The request is ignored including all its arguments. .It Sy "skipping unusable escape sequence" .Pq roff The first argument of an .Ic mc request is neither a single ASCII character nor a single character escape sequence. All arguments are ignored and printing of a margin character is disabled. .It Sy "missing manual name, using \(dq\(dq" .Pq mdoc , man The first call to .Ic \&Nm , or any call in the NAME section, lacks the required argument, or .Ic \&MR is called without any argument. .It Sy "uname(3) system call failed, using UNKNOWN" .Pq mdoc The .Ic \&Os macro is called without arguments, and the .Xr uname 3 system call failed. As a workaround, .Nm can be compiled with .Sm off .Fl D Cm OSNAME=\(dq\e\(dq Ar string Cm \e\(dq\(dq . .Sm on .It Sy "unknown standard specifier" .Pq mdoc An .Ic \&St macro has an unknown argument and is discarded. .It Sy "skipping request without numeric argument" .Pq roff , eqn An .Ic \&it request or an .Xr eqn 7 .Ic \&size or .Ic \&gsize statement has a non-numeric or negative argument or no argument at all. The invalid request or statement is ignored. .It Sy "excessive shift" .Pq roff The argument of a .Ic shift request is larger than the number of arguments of the macro that is currently being executed. All macro arguments are deleted and \en(.$ is set to zero. .It Sy "NOT IMPLEMENTED: .so with absolute path or \(dq..\(dq" .Pq roff For security reasons, .Nm allows .Ic \&so file inclusion requests only with relative paths and only without ascending to any parent directory. By requesting the inclusion of a sensitive file, a malicious document might otherwise trick a privileged user into inadvertently displaying the file on the screen, revealing the file content to bystanders. .Nm only shows the path as it appears behind .Ic \&so . .It Sy ".so request failed" .Pq roff Servicing a .Ic \&so request requires reading an external file, but the file could not be opened. .Nm only shows the path as it appears behind .Ic \&so . .It Sy "skipping all arguments" .Pq mdoc , man , eqn , roff An .Xr mdoc 7 .Ic \&Bt , .Ic \&Ed , .Ic \&Ef , .Ic \&Ek , .Ic \&El , .Ic \&Lp , .Ic \&Pp , .Ic \&Re , .Ic \&Rs , or .Ic \&Ud macro, an .Ic \&It macro in a list that don't support item heads, a .Xr man 7 .Ic \&LP , .Ic \&P , or .Ic \&PP macro, an .Xr eqn 7 .Ic \&EQ or .Ic \&EN macro, or a .Xr roff 7 .Ic \&br , .Ic \&fi , or .Ic \&nf request or .Sq \&.. block closing request is invoked with at least one argument. All arguments are ignored. .It Sy "skipping excess arguments" .Pq mdoc , man , roff A macro or request is invoked with too many arguments: .Bl -dash -offset 2n -width 2n -compact .It .Ic \&Fo , .Ic \&MT , .Ic \&PD , .Ic \&RS , .Ic \&UR , .Ic \&ft , or .Ic \&sp with more than one argument .It .Ic \&An with another argument after .Fl split or .Fl nosplit .It .Ic \&RE with more than one argument or with a non-integer argument .It .Ic \&OP or a request of the .Ic \&de family with more than two arguments .It .Ic \&Dt or .Ic \&MR with more than three arguments .It .Ic \&TH with more than five arguments .It .Ic \&Bd , .Ic \&Bk , or .Ic \&Bl with invalid arguments .El The excess arguments are ignored. .El .Ss "Errors related to escape sequences" .Bl -ohang .It Sy "incomplete escape sequence" .Pq roff The end of the input line is encountered while parsing the argument of an escape sequence. In this case, .Ic \e* and .Ic \en expand to an empty string, .Ic \eB to the digit .Sq 0 , and .Ic \ew to the length of the incomplete argument. All other incomplete escape sequences are ignored. .It Sy "invalid special character" .Pq roff A special character escape sequence is invalid, for example a Unicode sequence pointing to a surrogate or beyond the Unicode range, a \e[char...] escape sequence representing a control character or pointing beyond the .Vt unsigned char range, or an invalid variable-length form of a single-byte character escape sequence, for example writing .Qq \e[e] or .Qq \e[~] instead of .Qq \ee or .Qq \e~ , respectively. The escape sequence is ignored. .It Sy "unknown special character" .Pq roff The name given in a special character escape sequence is not known to .Nm . The escape sequence is ignored. .It Sy "invalid escape argument delimiter" .Pq roff An escape sequence that expects a numerical argument attempts to employ one of the characters .Qq " %&()*+-./0123456789:<=>" as an argument delimiter. The escape sequence is ignored including the invalid opening delimiter and the rest of the argument may appear as output text. While various characters can be used as argument delimiters, using the apostrophe-quote character .Pq Sq \(aq is recommended for readability and robustness. .El .Ss Unsupported features .Bl -ohang .It Sy "input too large" .Pq mdoc , man Currently, .Nm cannot handle input files larger than its arbitrary size limit of 2^31 bytes (2 Gigabytes). Since useful manuals are always small, this is not a problem in practice. Parsing is aborted as soon as the condition is detected. .It Sy "unsupported control character" .Pq roff An ASCII control character supported by other .Xr roff 7 implementations but not by .Nm was found in an input file. It is replaced by a question mark. .It Sy "unsupported escape sequence" .Pq roff An input file contains an escape sequence supported by GNU troff or Heirloom troff but not by .Nm , and it is likely that this will cause information loss or considerable misformatting. .It Sy "unsupported roff request" .Pq roff An input file contains a .Xr roff 7 request supported by GNU troff or Heirloom troff but not by .Nm , and it is likely that this will cause information loss or considerable misformatting. .It Sy "eqn delim option in tbl" .Pq eqn , tbl The options line of a table defines equation delimiters. Any equation source code contained in the table will be printed unformatted. .It Sy "unsupported table layout modifier" .Pq tbl A table layout specification contains an .Sq Cm m modifier. The modifier is discarded. .It Sy "ignoring macro in table" .Pq tbl , mdoc , man A table contains an invocation of an .Xr mdoc 7 or .Xr man 7 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 .It Sy "bad command line argument" The argument following one of the .Fl IKMmOTW command line options is invalid, or a .Ar file given as a command line argument cannot be opened. .It Sy "duplicate command line argument" The .Fl I command line option was specified twice. .It Sy "option has a superfluous value" An argument to the .Fl O option has a value but does not accept one. .It Sy "missing option value" An argument to the .Fl O option has no argument but requires one. .It Sy "bad option value" An argument to the .Fl O .Cm indent or .Cm width option has an invalid value. .It Sy "duplicate option value" The same .Fl O option is specified more than once. .It Sy "no such tag" 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 , .Xr man 1 , .Xr eqn 7 , .Xr man 7 , .Xr mandoc_char 7 , .Xr mdoc 7 , .Xr roff 7 , .Xr tbl 7 .Sh HISTORY The .Nm utility first appeared in .Ox 4.8 . The option .Fl I appeared in .Ox 5.2 , and .Fl aCcfhKklMSsw in .Ox 5.7 . .Sh AUTHORS .An -nosplit The .Nm utility was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv and is maintained by .An Ingo Schwarze Aq Mt schwarze@openbsd.org . diff --git a/contrib/mandoc/mdoc.7 b/contrib/mandoc/mdoc.7 index 55cc7fae688d..6c2d3568baa6 100644 --- a/contrib/mandoc/mdoc.7 +++ b/contrib/mandoc/mdoc.7 @@ -1,3277 +1,3288 @@ -.\" $Id: mdoc.7,v 1.299 2025/06/13 16:18:28 schwarze Exp $ +.\" $Id: mdoc.7,v 1.300 2025/08/19 14:08:59 schwarze Exp $ .\" .\" Copyright (c) 2010-2021, 2024, 2025 Ingo Schwarze .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: June 13 2025 $ +.Dd $Mdocdate: August 19 2025 $ .Dt MDOC 7 .Os .Sh NAME .Nm mdoc .Nd semantic markup language for formatting manual pages .Sh DESCRIPTION The .Nm mdoc language supports authoring of manual pages for the .Xr man 1 utility by allowing semantic annotations of words, phrases, page sections and complete manual pages. Such annotations are used by formatting tools to achieve a uniform presentation across all manuals written in .Nm , and to support hyperlinking if supported by the output medium. .Pp This reference document describes the structure of manual pages and the syntax and usage of the .Nm language. The reference implementation of a parsing and formatting tool is .Xr mandoc 1 ; the .Sx COMPATIBILITY section describes compatibility with other implementations. .Pp In an .Nm document, lines beginning with the control character .Sq \&. are called .Dq macro lines . The first word is the macro name. It consists of two or three letters. Most macro names begin with a capital letter. For a list of available macros, see .Sx MACRO OVERVIEW . The words following the macro name are arguments to the macro, optionally including the names of other, callable macros; see .Sx MACRO SYNTAX for details. .Pp Lines not beginning with the control character are called .Dq text lines . They provide free-form text to be printed; the formatting of the text depends on the respective processing context: .Bd -literal -offset indent \&.Sh Macro lines change control state. Text lines are interpreted within the current state. .Ed .Pp Many aspects of the basic syntax of the .Nm language are based on the .Xr roff 7 language; see the .Em LANGUAGE SYNTAX and .Em MACRO SYNTAX sections in the .Xr roff 7 manual for details, in particular regarding comments, escape sequences, whitespace, and quoting. However, using .Xr roff 7 requests in .Nm documents is discouraged; .Xr mandoc 1 supports some of them merely for backward compatibility. .Sh MANUAL STRUCTURE A well-formed .Nm document consists of a document prologue followed by one or more sections. .Pp The prologue, which consists of the .Ic \&Dd , .Ic \&Dt , and .Ic \&Os macros in that order, is required for every document. .Pp The first section (sections are denoted by .Ic \&Sh ) must be the NAME section, consisting of at least one .Ic \&Nm followed by .Ic \&Nd . .Pp Following that, convention dictates specifying at least the .Em SYNOPSIS and .Em DESCRIPTION sections, although this varies between manual sections. .Pp The following is a well-formed skeleton .Nm file for a utility .Qq progname : .Bd -literal -offset indent \&.Dd $\&Mdocdate$ \&.Dt PROGNAME section \&.Os \&.Sh NAME \&.Nm progname \&.Nd one line about what it does \&.\e\(dq .Sh LIBRARY \&.\e\(dq For sections 2, 3, and 9 only. \&.\e\(dq Not used in OpenBSD. \&.Sh SYNOPSIS \&.Nm progname \&.Op Fl options \&.Ar \&.Sh DESCRIPTION The \&.Nm utility processes files ... \&.\e\(dq .Sh CONTEXT \&.\e\(dq For section 9 functions only. \&.\e\(dq .Sh HARDWARE \&.\e\(dq For section 4 only. \&.\e\(dq Not used in OpenBSD. \&.\e\(dq .Sh IMPLEMENTATION NOTES \&.\e\(dq Not used in OpenBSD. \&.\e\(dq .Sh RETURN VALUES \&.\e\(dq For sections 2, 3, and 9 function return values only. \&.\e\(dq .Sh ENVIRONMENT \&.\e\(dq For sections 1, 6, 7, and 8 only. \&.\e\(dq .Sh FILES \&.\e\(dq .Sh EXIT STATUS \&.\e\(dq For sections 1, 6, and 8 only. \&.\e\(dq .Sh EXAMPLES \&.\e\(dq .Sh DIAGNOSTICS \&.\e\(dq For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only. \&.\e\(dq .Sh ERRORS \&.\e\(dq For sections 2, 3, 4, and 9 errno settings only. \&.\e\(dq .Sh SEE ALSO \&.\e\(dq .Xr foobar 1 \&.\e\(dq .Sh STANDARDS \&.\e\(dq .Sh HISTORY \&.\e\(dq .Sh AUTHORS \&.\e\(dq .Sh CAVEATS \&.\e\(dq .Sh BUGS \&.\e\(dq .Sh SECURITY CONSIDERATIONS \&.\e\(dq Not used in OpenBSD. .Ed .Pp The sections in an .Nm document are conventionally ordered as they appear above. Sections should be composed as follows: .Bl -ohang -offset Ds .It Em NAME The name(s) and a one line description of the documented material. The syntax for this as follows: .Bd -literal -offset indent \&.Nm name0 , \&.Nm name1 , \&.Nm name2 \&.Nd a one line description .Ed .Pp Multiple .Sq \&Nm names should be separated by commas. .Pp The .Ic \&Nm macro(s) must precede the .Ic \&Nd macro. .Pp See .Ic \&Nm and .Ic \&Nd . .It Em LIBRARY The name of the library containing the documented functions. Using this section is no longer recommended. If any .Ic \&Lb macro is needed, put it at the beginning of the .Em SYNOPSIS section instead. .It Em SYNOPSIS Documents the utility invocation syntax, function call syntax, or device configuration. .Pp For the first, utilities (sections 1, 6, and 8), this is generally structured as follows: .Bd -literal -offset indent \&.Nm bar \&.Op Fl v \&.Op Fl o Ar file \&.Op Ar \&.Nm foo \&.Op Fl v \&.Op Fl o Ar file \&.Op Ar .Ed .Pp Commands should be ordered alphabetically. .Pp For the second, function calls (sections 2, 3, 9): .Bd -literal -offset indent \&.Lb libname \e" unless the functions are in libc \&.In header.h \&.Vt extern const char *global; \&.Ft char * \&.Fn foo "const char *src" \&.Ft char * \&.Fn bar "const char *src" .Ed .Pp Ordering of .Ic \&In , .Ic \&Vt , .Ic \&Fn , and .Ic \&Fo macros should follow C header-file conventions. .Pp And for the third, configurations (section 4): .Bd -literal -offset indent \&.Cd \(dqit* at isa? port 0x2e\(dq \&.Cd \(dqit* at isa? port 0x4e\(dq .Ed .Pp Manuals not in these sections generally don't need a .Em SYNOPSIS . .Pp Some macros are displayed differently in the .Em SYNOPSIS section, particularly .Ic \&Nm , .Ic \&Cd , .Ic \&Fd , .Ic \&Fn , .Ic \&Fo , .Ic \&In , .Ic \&Vt , and .Ic \&Ft . All of these macros are output on their own line. If two such dissimilar macros are pairwise invoked (except for .Ic \&Ft before .Ic \&Fo or .Ic \&Fn ) , they are separated by a vertical space, unless in the case of .Ic \&Fo , .Ic \&Fn , and .Ic \&Ft , which are always separated by vertical space. .Pp When text and macros following an .Ic \&Nm macro starting an input line span multiple output lines, all output lines but the first will be indented to align with the text immediately following the .Ic \&Nm macro, up to the next .Ic \&Nm , .Ic \&Sh , or .Ic \&Ss macro or the end of an enclosing block, whichever comes first. .It Em DESCRIPTION This begins with an expansion of the brief, one line description in .Em NAME : .Bd -literal -offset indent The \&.Nm utility does this, that, and the other. .Ed .Pp It usually follows with a breakdown of the options (if documenting a command), such as: .Bd -literal -offset indent The options are as follows: \&.Bl \-tag \-width Ds \&.It Fl v Print verbose information. \&.El .Ed .Pp List the options in alphabetical order, uppercase before lowercase for each letter and with no regard to whether an option takes an argument. Put digits in ascending order before all letter options. .Pp Manuals not documenting a command won't include the above fragment. .Pp Since the .Em DESCRIPTION section usually contains most of the text of a manual, longer manuals often use the .Ic \&Ss macro to form subsections. In very long manuals, the .Em DESCRIPTION may be split into multiple sections, each started by an .Ic \&Sh macro followed by a non-standard section name, and each having several subsections, like in the present .Nm manual. .It Em CONTEXT This section lists the contexts in which functions can be called in section 9. The contexts are autoconf, process, or interrupt. .It Em HARDWARE This section lists the hardware support provided by kernel modules in section 4. FreeBSD Hardware Compatibility Notes are generated from this section. .It Em IMPLEMENTATION NOTES Implementation-specific notes should be kept here. This is useful when implementing standard functions that may have side effects or notable algorithmic implications. .It Em RETURN VALUES This section documents the return values of functions in sections 2, 3, and 9. .Pp See .Ic \&Rv . .It Em ENVIRONMENT Lists the environment variables used by the utility, and explains the syntax and semantics of their values. The .Xr environ 7 manual provides examples of typical content and formatting. .Pp See .Ic \&Ev . .It Em FILES Documents files used. It's helpful to document both the file name and a short description of how the file is used (created, modified, etc.). .Pp See .Ic \&Pa . .It Em EXIT STATUS This section documents the command exit status for section 1, 6, and 8 utilities. Historically, this information was described in .Em DIAGNOSTICS , a practise that is now discouraged. .Pp See .Ic \&Ex . .It Em EXAMPLES Example usages. This often contains snippets of well-formed, well-tested invocations. Make sure that examples work properly! .It Em DIAGNOSTICS Documents error messages. In section 4 and 9 manuals, these are usually messages printed by the kernel to the console and to the kernel log. In section 1, 6, 7, and 8, these are usually messages printed by userland programs to the standard error output. .Pp Historically, this section was used in place of .Em EXIT STATUS for manuals in sections 1, 6, and 8; however, this practise is discouraged. .Pp See .Ic \&Bl .Fl diag . .It Em ERRORS Documents .Xr errno 2 settings in sections 2, 3, 4, and 9. .Pp See .Ic \&Er . .It Em SEE ALSO References other manuals with related topics. This section should exist for most manuals. Cross-references should conventionally be ordered first by section, then alphabetically (ignoring case). .Pp References to other documentation concerning the topic of the manual page, for example authoritative books or journal articles, may also be provided in this section. .Pp See .Ic \&Rs and .Ic \&Xr . .It Em STANDARDS References any standards implemented or used. If not adhering to any standards, the .Em HISTORY section should be used instead. .Pp See .Ic \&St . .It Em HISTORY A brief history of the subject, including where it was first implemented, and when it was ported to or reimplemented for the operating system at hand. .It Em AUTHORS Credits to the person or persons who wrote the code and/or documentation. Authors should generally be noted by both name and email address. .Pp See .Ic \&An . .It Em CAVEATS Common misuses and misunderstandings should be explained in this section. .It Em BUGS Known bugs, limitations, and work-arounds should be described in this section. .It Em SECURITY CONSIDERATIONS Documents any security precautions that operators should consider. .El .Sh MACRO OVERVIEW This overview is sorted such that macros of similar purpose are listed together, to help find the best macro for any given purpose. Deprecated macros are not included in the overview, but can be found below in the alphabetical .Sx MACRO REFERENCE . .Ss Document preamble and NAME section macros .Bl -column "Brq, Bro, Brc" description .It Ic \&Dd Ta document date: Cm $\&Mdocdate$ | Ar month day , year .It Ic \&Dt Ta document title: Ar TITLE section Op Ar arch .It Ic \&Os Ta operating system footer: Op Ar footer text .It Ic \&Nm Ta document name (one argument) .It Ic \&Nd Ta document description (one line) .El .Ss Sections and cross references .Bl -column "Brq, Bro, Brc" description .It Ic \&Sh Ta section header (one line) .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 .Bl -column "Brq, Bro, Brc" description .It Ic \&Bd , \&Ed Ta display block: .Fl Ar type .Op Fl offset Ar width .Op Fl compact .It Ic \&D1 Ta indented display (one line) .It Ic \&Dl Ta indented literal display (one line) .It Ic \&Ql Ta normal in-line literal display: Ql text .It Ic \&Li Ta unquoted in-line literal display: Li text .It Ic \&Bl , \&El Ta list block: .Fl Ar type .Op Fl width Ar val .Op Fl offset Ar val .Op Fl compact .It Ic \&It Ta list item (syntax depends on Fl Ar type ) .It Ic \&Ta Ta table cell separator in Ic \&Bl Fl column No lists .It Ic \&Rs , \&%* , \&Re Ta bibliographic block (references) .El .Ss Spacing control .Bl -column "Brq, Bro, Brc" description .It Ic \&Pf Ta prefix, no following horizontal space (one argument) .It Ic \&Ns Ta roman font, no preceding horizontal space (no arguments) .It Ic \&Ap Ta apostrophe without surrounding whitespace (no arguments) .It Ic \&Sm Ta switch horizontal spacing mode: Op Cm on | off .It Ic \&Bk , \&Ek Ta keep block: Fl words .El .Ss Semantic markup for command line utilities .Bl -column "Brq, Bro, Brc" description .It Ic \&Nm Ta start a SYNOPSIS block with the name of a utility .It Ic \&Fl Ta command line options (flags) (>=0 arguments) .It Ic \&Cm Ta command modifier (>0 arguments) .It Ic \&Ar Ta command arguments (>=0 arguments) .It Ic \&Op , \&Oo , \&Oc Ta optional syntax elements (enclosure) .It Ic \&Ic Ta internal or interactive command (>0 arguments) .It Ic \&Ev Ta environmental variable (>0 arguments) .It Ic \&Pa Ta file system path (>=0 arguments) .El .Ss Semantic markup for function libraries .Bl -column "Brq, Bro, Brc" description .It Ic \&Lb Ta function library (>0 arguments) .It Ic \&In Ta include file (one argument) .It Ic \&Fd Ta other preprocessor directive (>0 arguments) .It Ic \&Ft Ta function type (>0 arguments) .It Ic \&Fo , \&Fc Ta function block: Ar funcname .It Ic \&Fn Ta function name: Ar funcname Op Ar argument ... .It Ic \&Fa Ta function argument (>0 arguments) .It Ic \&Vt Ta variable type (>0 arguments) .It Ic \&Va Ta variable name (>0 arguments) .It Ic \&Dv Ta defined variable or preprocessor constant (>0 arguments) .It Ic \&Er Ta error constant (>0 arguments) .It Ic \&Ev Ta environmental variable (>0 arguments) .El .Ss Various semantic markup .Bl -column "Brq, Bro, Brc" description .It Ic \&An Ta author name (>0 arguments) .It Ic \&Lk Ta hyperlink: Ar uri Op Ar display_name .It Ic \&Mt Ta Do mailto Dc hyperlink: Ar localpart Ns @ Ns Ar domain .It Ic \&Cd Ta kernel configuration declaration (>0 arguments) .It Ic \&Ad Ta memory address (>0 arguments) .It Ic \&Ms Ta mathematical symbol (>0 arguments) .El .Ss Physical markup .Bl -column "Brq, Bro, Brc" description .It Ic \&Em Ta italic font or underline (emphasis) (>0 arguments) .It Ic \&Sy Ta boldface font (symbolic) (>0 arguments) .It Ic \&No Ta return to roman font (normal) (>0 arguments) .It Ic \&Bf , \&Ef Ta font block: Fl Ar type | Cm \&Em | \&Li | \&Sy .El .Ss Physical enclosures .Bl -column "Brq, Bro, Brc" description .It Ic \&Dq , \&Do , \&Dc Ta enclose in typographic double quotes: Dq text .It Ic \&Qq , \&Qo , \&Qc Ta enclose in typewriter double quotes: Qq text .It Ic \&Sq , \&So , \&Sc Ta enclose in single quotes: Sq text .It Ic \&Pq , \&Po , \&Pc Ta enclose in parentheses: Pq text .It Ic \&Bq , \&Bo , \&Bc Ta enclose in square brackets: Bq text .It Ic \&Brq , \&Bro , \&Brc Ta enclose in curly braces: Brq text .It Ic \&Aq , \&Ao , \&Ac Ta enclose in angle brackets: Aq text .It Ic \&Eo , \&Ec Ta generic enclosure .El .Ss Text production .Bl -column "Brq, Bro, Brc" description .It Ic \&Ex Fl std Ta standard command exit values: Op Ar utility ... .It Ic \&Rv Fl std Ta standard function return values: Op Ar function ... .It Ic \&St Ta reference to a standards document (one argument) .It Ic \&At Ta At .It Ic \&Bx Ta Bx .It Ic \&Bsx Ta Bsx .It Ic \&Nx Ta Nx .It Ic \&Fx Ta Fx .It Ic \&Ox Ta Ox .It Ic \&Dx Ta Dx .El .Sh MACRO REFERENCE This section is a canonical reference of all macros, arranged alphabetically. For the scoping of individual macros, see .Sx MACRO SYNTAX . .Bl -tag -width 3n .It Ic \&%A Ar first_name ... last_name Author name of an .Ic \&Rs block. Multiple authors should each be accorded their own .Ic \%%A line. Author names should be ordered with full or abbreviated forename(s) first, then full surname. .It Ic \&%B Ar title Book title of an .Ic \&Rs block. This macro may also be used in a non-bibliographic context when referring to book titles. .It Ic \&%C Ar location Publication city or location of an .Ic \&Rs block. .It Ic \&%D Oo Ar month day , Oc Ar year Publication date of an .Ic \&Rs block. Provide the full English name of the .Ar month and all four digits of the .Ar year . .It Ic \&%I Ar name Publisher or issuer name of an .Ic \&Rs block. .It Ic \&%J Ar name Journal name of an .Ic \&Rs block. .It Ic \&%N Ar number Issue number (usually for journals) of an .Ic \&Rs block. .It Ic \&%O Ar line Optional information of an .Ic \&Rs block. .It Ic \&%P Ar number Book or journal page number of an .Ic \&Rs block. Conventionally, the argument starts with .Ql p.\& for a single page or .Ql pp.\& for a range of pages, for example: .Pp .Dl .%P pp. 42\e(en47 .It Ic \&%Q Ar name Institutional author (school, government, etc.) of an .Ic \&Rs block. Multiple institutional authors should each be accorded their own .Ic \&%Q line. .It Ic \&%R Ar name Technical report name of an .Ic \&Rs block. .It Ic \&%T Ar title Article title of an .Ic \&Rs block. This macro may also be used in a non-bibliographical context when referring to article titles. .It Ic \&%U Ar protocol Ns :// Ns Ar path URI of reference document. .It Ic \&%V Ar number Volume number of an .Ic \&Rs block. .It Ic \&Ac 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. .Pp 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 documented in the manual, or for the authors of the manual itself. Requires either the name of an author or one of the following arguments: .Pp .Bl -tag -width "-nosplitX" -offset indent -compact .It Fl split Start a new output line before each subsequent invocation of .Ic \&An . .It Fl nosplit The opposite of .Fl split . .El .Pp The default is .Fl nosplit . The effect of selecting either of the .Fl split modes ends at the beginning of the .Em AUTHORS section. In the .Em AUTHORS section, the default is .Fl nosplit for the first author listing and .Fl split for all other author listings. .Pp Examples: .Dl \&.An -nosplit .Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv .It Ic \&Ao Ar block Begin a block enclosed by angle brackets. Does not have any head arguments. 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 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. See .Ic \&Mt for an example. .Pp Occasionally, it is used for names of characters and keys, for example: .Bd -literal -offset indent Press the \&.Aq escape key to ... .Ed .Pp For URIs, use .Ic \&Lk instead, and .Ic \&In for .Dq #include directives. Never wrap .Ic \&Ar in .Ic \&Aq . .Pp Since .Ic \&Aq usually renders with non-ASCII characters in non-ASCII output modes, do not use it where the ASCII characters .Sq < and .Sq > are required as syntax elements. Instead, use these characters directly in such cases, combining them with the macros .Ic \&Pf , .Ic \&Ns , or .Ic \&Eo 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 .Dq file ...\& is used as a default. .Pp Examples: .Dl ".Fl o Ar file" .Dl ".Ar" .Dl ".Ar arg1 , arg2 ." .Pp The arguments to the .Ic \&Ar macro are names and placeholders for command arguments; 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 version. Accepts one optional argument: .Pp .Bl -tag -width "v[1-7] | 32vX" -offset indent -compact .It Cm v[1-7] | 32v A version of .At . .It Cm III .At III . .It Cm V | V.[1-4] A version of .At V . .El .Pp Note that these arguments do not begin with a hyphen. .Pp Examples: .Dl \&.At .Dl \&.At III .Dl \&.At V.1 .Pp See also .Ic \&Bsx , .Ic \&Bx , .Ic \&Dx , .Ic \&Fx , .Ic \&Nx , and .Ic \&Ox . .It Ic \&Bc 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 justification than the one used by the surrounding text. They may contain both macro lines and text lines. By default, a display block is preceded by a vertical space. .Pp The .Ar type must be one of the following: .Bl -tag -width 13n -offset indent .It Fl centered Produce one output line from each input line, and center-justify each line. Using this display type is not recommended; many .Nm implementations render it poorly. .It Fl filled Change the positions of line breaks to fill each line, and left- and right-justify the resulting block. .It Fl literal Produce one output line from each input line, and do not justify the block at all. Preserve white space as it appears in the input. Always use a constant-width font. Use this for displaying source code. .It Fl ragged Change the positions of line breaks to fill each line, and left-justify the resulting block. .It Fl unfilled The same as .Fl literal , but using the same font as for normal text, which is a variable width font if supported by the output device. .El .Pp The .Ar type must be provided first. Additional arguments may follow: .Bl -tag -width 13n -offset indent .It Fl offset Ar width Indent the display by the .Ar width , which may be one of the following: .Bl -item .It One of the pre-defined strings .Cm indent , the width of a standard indentation (six constant width characters); .Cm indent-two , twice .Cm indent ; .Cm left , which has no effect; .Cm right , which justifies to the right margin; or .Cm center , which aligns around an imagined center axis. .It A macro invocation, which selects a predefined width associated with that macro. The most popular is the imaginary macro .Ar \&Ds , which resolves to .Sy 6n . .It A scaling width as described in .Xr roff 7 . .It An arbitrary string, which indents by the length of this string. .El .Pp When the argument is missing, .Fl offset is ignored. .It Fl compact Do not assert vertical space before the display. .El .Pp Examples: .Bd -literal -offset indent \&.Bd \-literal \-offset indent \-compact Hello world. \&.Ed .Ed .Pp 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 .Fl emphasis and .Cm \&Em argument are equivalent, as are .Fl symbolic and .Cm \&Sy , and .Fl literal and .Cm \&Li . Without an argument, this macro does nothing. The font mode continues until broken by a new font mode in a nested scope or .Ic \&Ef is encountered. .Pp See also .Ic \&Li , .Ic \&Ef , .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, whichever comes first. Line breaks in text lines are unaffected. .Pp The .Fl words argument is required; additional arguments are ignored. .Pp The following example will not break within each .Ic \&Op macro line: .Bd -literal -offset indent \&.Bk \-words \&.Op Fl f Ar flags \&.Op Fl o Ar output \&.Ek .Ed .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 .Op Fl width Ar val .Op Fl offset Ar val .Op Fl compact .Op Ar col ... .Xc Begin a list. Lists consist of items specified using the .Ic \&It macro, containing a head or a body or both. .Pp The list .Ar type is mandatory and must be specified first. The .Fl width and .Fl offset arguments accept macro names as described for .Ic \&Bd .Fl offset , scaling widths as described in .Xr roff 7 , or use the length of the given string. The .Fl offset is a global indentation for the whole list, affecting both item heads and bodies. For those list types supporting it, the .Fl width argument requests an additional indentation of item bodies, to be added to the .Fl offset . Unless the .Fl compact argument is specified, list entries are separated by vertical space. .Pp -A list must specify one of the following list types: +The following list types are commonly used: .Bl -tag -width 12n -offset indent .It Fl bullet No item heads can be specified, but a bullet will be printed at the head of each item. Item bodies start on the same output line as the bullet and are indented according to the .Fl width argument. .It Fl column A columnated list. The .Fl width argument has no effect; instead, the string length of each argument specifies the width of one column. -If the first line of the body of a +The width specification for the last column does not affect formatting. +.Pp +For two-column lists, using +.Fl tag +often results in simpler code, identical terminal output, and better HTML +output, especially when the first column contains short identifiers. +.Pp +For compatibility with legacy documents, if the first line of the body of a .Fl column list is not an .Ic \&It macro line, .Ic \&It contexts spanning one input line each are implied until an .Ic \&It macro line is encountered, at which point items start being interpreted as described in the .Ic \&It documentation. .It Fl dash Like .Fl bullet , except that dashes are used in place of bullets. .It Fl diag Like .Fl inset , except that item heads are not parsed for macro invocations. Most often used in the .Em DIAGNOSTICS -section with error constants in the item heads. +section with error messages in the item heads. .It Fl enum A numbered list. No item heads can be specified. Formatted like .Fl bullet , except that ordinal numbers are used in place of bullets, starting at 1. +.It Fl tag +Item bodies are indented according to the +.Fl width +argument. +When an item head fits inside the indentation, the item body follows +this head on the same output line. +Otherwise, the body starts on the output line following the head. +.El +.Pp +The following list types are rarely useful: +.Bl -tag -width 12n -offset indent .It Fl hang Like .Fl tag , except that the first lines of item bodies are not indented, but follow the item heads like in .Fl inset lists. .It Fl hyphen Synonym for .Fl dash . .It Fl inset Item bodies follow items heads on the same line, using normal inter-word spacing. Bodies are not indented, and the .Fl width argument is ignored. .It Fl item No item heads can be specified, and none are printed. Bodies are not indented, and the .Fl width argument is ignored. .It Fl ohang Item bodies start on the line following item heads and are not indented. The .Fl width argument is ignored. -.It Fl tag -Item bodies are indented according to the -.Fl width -argument. -When an item head fits inside the indentation, the item body follows -this head on the same output line. -Otherwise, the body starts on the output line following the head. .El .Pp Lists may be nested within lists and displays. Nesting of .Fl column and .Fl enum lists may not be portable. .Pp See also .Ic \&El and .Ic \&It . .It Ic \&Bo Ar block Begin a block enclosed by square brackets. Does not have any head arguments. .Pp Examples: .Bd -literal -offset indent -compact \&.Bo 1 , \&.Dv BUFSIZ \&Bc .Ed .Pp See also .Ic \&Bq . .Tg Bq .It Ic \&Bq Ar line Encloses its arguments in square brackets. .Pp Examples: .Dl \&.Bq 1 , \&Dv BUFSIZ .Pp .Em Remarks : this macro is sometimes abused to emulate optional arguments for commands; the correct macros to use for this purpose are .Ic \&Op , .Ic \&Oo , and .Ic \&Oc . .Pp See also .Ic \&Bo . .It Ic \&Brc Close a .Ic \&Bro block. Does not have any tail arguments. .It Ic \&Bro Ar block Begin a block enclosed by curly braces. Does not have any head arguments. .Pp Examples: .Bd -literal -offset indent -compact \&.Bro 1 , ... , \&.Va n \&Brc .Ed .Pp See also .Ic \&Brq . .Tg Brq .It Ic \&Brq Ar line Encloses its arguments in curly braces. .Pp Examples: .Dl \&.Brq 1 , ... , \&Va n .Pp See also .Ic \&Bro . .Tg Bsx .It Ic \&Bsx Op Ar version Format the .Bsx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Bsx 1.0 .Dl \&.Bsx .Pp See also .Ic \&At , .Ic \&Bx , .Ic \&Dx , .Ic \&Fx , .Ic \&Nx , and .Ic \&Ox . .It Ic \&Bt 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 version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Bx 4.3 Tahoe .Dl \&.Bx 4.4 .Dl \&.Bx .Pp See also .Ic \&At , .Ic \&Bsx , .Ic \&Dx , .Ic \&Fx , .Ic \&Nx , and .Ic \&Ox . .Tg Cd .It Ic \&Cd Ar line Kernel configuration declaration. This denotes strings accepted by .Xr config 8 . It is most often used in section 4 manual pages. .Pp Examples: .Dl \&.Cd device le0 at scode? .Pp .Em Remarks : this macro is commonly abused by using quoted literals to retain 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 commands, to commands in interpreted scripts, or to configuration file directives, unless .Ic \&Fl is more appropriate. .Pp Examples: .Dl ".Nm mt Fl f Ar device Cm rewind" .Dl ".Nm ps Fl o Cm pid , Ns Cm command" .Dl ".Nm dd Cm if= Ns Ar file1 Cm of= Ns Ar file2" .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 statements. It is followed by a newline. .Pp Examples: .Dl \&.D1 \&Fl abcdefgh .Pp See also .Ic \&Bd and .Ic \&Dl . .It Ic \&Db This macro is obsolete. No replacement is needed. It is ignored by .Xr mandoc 1 and groff including its arguments. It was formerly used to toggle a debugging mode. .It Ic \&Dc 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, by convention the date of the last change. This is the mandatory first macro of any .Nm manual. .Pp The .Ar month is the full English month name, the .Ar day is an integer number, and the .Ar year is the full four-digit year. .Pp Other arguments are not portable; the .Xr mandoc 1 utility handles them as follows: .Bl -dash -offset 3n -compact .It To have the date automatically filled in by the .Ox version of .Xr cvs 1 , the special string .Dq $\&Mdocdate$ can be given as an argument. .It The traditional, purely numeric .Xr man 7 format .Ar year Ns \(en Ns Ar month Ns \(en Ns Ar day is accepted, too. .It If a date string cannot be parsed, it is used verbatim. .It If no date string is given, the current date is used. .El .Pp Examples: .Dl \&.Dd $\&Mdocdate$ .Dl \&.Dd $\&Mdocdate: July 2 2018$ .Dl \&.Dd July 2, 2018 .Pp See also .Ic \&Dt and .Ic \&Os . .Tg Dl .It Ic \&Dl Ar line One-line indented literal display. This is formatted using a constant-width font and is useful for commands and invocations. It is followed by a newline. .Pp Examples: .Dl \&.Dl % mandoc mdoc.7 \e(ba less .Pp See also .Ic \&Ql , .Ic \&Bd Fl literal , and .Ic \&D1 . .It Ic \&Do Ar block Begin a block enclosed by double quotes. Does not have any head arguments. .Pp Examples: .Bd -literal -offset indent -compact \&.Do April is the cruellest month \&.Dc \e(em T.S. Eliot .Ed .Pp See also .Ic \&Dq . .Tg Dq .It Ic \&Dq Ar line Encloses its arguments in .Dq typographic double-quotes. .Pp Examples: .Bd -literal -offset indent -compact \&.Dq April is the cruellest month \e(em T.S. Eliot .Ed .Pp See also .Ic \&Qq , .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 .Nm file. .Pp Its arguments are as follows: .Bl -tag -width section -offset 2n .It Ar TITLE The document's title (name), defaulting to .Dq UNTITLED if unspecified. To achieve a uniform appearance of page header lines, it should by convention be all caps. .It Ar section The manual section. This may be one of .Cm 1 .Pq General Commands , .Cm 2 .Pq System Calls , .Cm 3 .Pq Library Functions , .Cm 3p .Pq Perl Library , .Cm 4 .Pq Device Drivers , .Cm 5 .Pq File Formats , .Cm 6 .Pq Games , .Cm 7 .Pq Miscellaneous Information , .Cm 8 .Pq System Manager's Manual , or .Cm 9 .Pq Kernel Developer's Manual . It should correspond to the manual's filename suffix and defaults to the empty string if unspecified. .It Ar arch This specifies the machine architecture a manual page applies to, where relevant, for example .Cm alpha , .Cm amd64 , .Cm i386 , or .Cm sparc64 . The list of valid architectures varies by operating system. .El .Pp Examples: .Dl \&.Dt FOO 1 .Dl \&.Dt FOO 9 i386 .Pp 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. .Pp Examples: .Dl \&.Dv NULL .Dl \&.Dv BUFSIZ .Dl \&.Dv STDOUT_FILENO .Pp See also .Ic \&Er and .Ic \&Ev for special-purpose constants, .Ic \&Va 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 version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Dx 2.4.1 .Dl \&.Dx .Pp See also .Ic \&At , .Ic \&Bsx , .Ic \&Bx , .Ic \&Fx , .Ic \&Nx , and .Ic \&Ox . .It Ic \&Ec Op Ar closing_delimiter Close a scope started by .Ic \&Eo . .Pp The .Ar closing_delimiter argument is used as the enclosure tail, for example, specifying \e(rq will emulate .Ic \&Dc . .It Ic \&Ed End a display context started by .Ic \&Bd . .It Ic \&Ef End a font mode context started by .Ic \&Bf . .It Ic \&Ek End a keep context started by .Ic \&Bk . .It Ic \&El 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. .Pp This is most often used for stress emphasis (not to be confused with importance, see .Ic \&Sy ) . In the rare cases where none of the semantic markup macros fit, it can also be used for technical terms and placeholders, except that for syntax elements, .Ic \&Sy and .Ic \&Ar are preferred, respectively. .Pp Examples: .Bd -literal -compact -offset indent Selected lines are those \&.Em not matching any of the specified patterns. Some of the functions use a \&.Em hold space to save the pattern space for subsequent retrieval. .Ed .Pp See also .Ic \&No , .Ic \&Ql , and .Ic \&Sy . .It Ic \&En Ar word ... This macro is obsolete. Use .Ic \&Eo or any of the other enclosure macros. .Pp 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 .Ar opening_delimiter 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 libc global variable. This is most often used in section 2 and 3 manual pages. .Pp Examples: .Dl \&.Er EPERM .Dl \&.Er ENOENT .Pp See also .Ic \&Dv for general constants. .It Ic \&Es Ar opening_delimiter closing_delimiter This macro is obsolete. Use .Ic \&Eo or any of the other enclosure macros. .Pp 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 . .Pp Examples: .Dl \&.Ev DISPLAY .Dl \&.Ev PATH .Pp 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. This is most often used in section 1, 6, and 8 manual pages. .Pp If .Ar utility is not specified, the document's name set by .Ic \&Nm is used. Multiple .Ar utility 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 .Em SYNOPSIS section), a name alone (for function invocations), or a type alone (for function prototypes). If both a type and a name are given or if the type consists of multiple words, all words belonging to the same function argument have to be given in a single argument to the .Ic \&Fa macro. .Pp This macro is also used to specify the field name of a structure. .Pp Most often, the .Ic \&Fa macro is used in the .Em SYNOPSIS within .Ic \&Fo blocks when documenting multi-line function prototypes. If invoked with multiple arguments, the arguments are separated by a comma. Furthermore, if the following macro is another .Ic \&Fa , the last argument will also have a trailing comma. .Pp Examples: .Dl \&.Fa \(dqconst char *p\(dq .Dl \&.Fa \(dqint a\(dq \(dqint b\(dq \(dqint c\(dq .Dl \&.Fa \(dqchar *\(dq size_t .Pp See also .Ic \&Fo . .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 . Historically, it was also used to document include files. The latter usage has been deprecated in favour of .Ic \&In . .Pp Examples: .Dl \&.Fd #define sa_handler __sigaction_u.__sa_handler .Dl \&.Fd #define SIO_MAXNFDS .Dl \&.Fd #ifdef FS_DEBUG .Dl \&.Ft void .Dl \&.Fn dbg_open \(dqconst char *\(dq .Dl \&.Fd #endif .Pp See also .Sx MANUAL STRUCTURE , .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. 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 ".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-style 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 Function arguments are surrounded in parenthesis and are delimited by commas. If no arguments are specified, blank parenthesis are output. In the .Em SYNOPSIS section, this macro starts a new output line, and a blank line is automatically inserted between function definitions. .Pp Examples: .Dl \&.Fn \(dqint funcname\(dq \(dqint arg0\(dq \(dqint arg1\(dq .Dl \&.Fn funcname \(dqint arg0\(dq .Dl \&.Fn funcname arg0 .Bd -literal -offset indent \&.Ft functype \&.Fn funcname .Ed .Pp When referring to a function documented in another manual page, use .Ic \&Xr instead. See also .Sx MANUAL STRUCTURE , .Ic \&Fo , and .Ic \&Ft . .Tg Fo .It Ic \&Fo Ar funcname Begin a function block. This is a multi-line version of .Ic \&Fn . .Pp Invocations usually occur in the following context: .Bd -ragged -offset indent .Pf \. Ic \&Ft Ar functype .br .Pf \. Ic \&Fo Ar funcname .br .Pf \. Ic \&Fa Qq Ar argtype Ar argname .br \&.\.\. .br .Pf \. Ic \&Fc .Ed .Pp A .Ic \&Fo scope is closed by .Ic \&Fc . .Pp See also .Sx MANUAL STRUCTURE , .Ic \&Fa , .Ic \&Fc , and .Ic \&Ft . .It Ic \&Fr Ar number 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 In the .Em SYNOPSIS section, a new output line is started after this macro. .Pp Examples: .Dl \&.Ft int .Bd -literal -offset indent -compact \&.Ft functype \&.Fn funcname .Ed .Pp See also .Sx MANUAL STRUCTURE , .Ic \&Fn , and .Ic \&Fo . .Tg Fx .It Ic \&Fx Op Ar version Format the .Fx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Fx 7.1 .Dl \&.Fx .Pp See also .Ic \&At , .Ic \&Bsx , .Ic \&Bx , .Ic \&Dx , .Ic \&Nx , and .Ic \&Ox . .It Ic \&Hf Ar filename 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. See also .Ic \&Cm . .Pp Examples: .Dl \&.Ic :wq .Dl \&.Ic hash .Dl \&.Ic alias .Pp Note that using .Ic \&Ql , .Ic \&Dl , or .Ic \&Bd Fl literal 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. .Pp When invoked as the first macro on an input line in the .Em SYNOPSIS section, the argument is displayed in angle brackets and preceded by .Qq #include , and a blank line is inserted in front if there is a preceding function declaration. In other sections, it only encloses its argument in angle brackets and causes no line break. .Pp Examples: .Dl \&.In sys/types.h .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. .Pp Lists of type .Fl hang , .Fl ohang , .Fl inset , and .Fl diag have the following syntax: .Pp .D1 Pf \. Ic \&It Ar args .Pp Lists of type .Fl bullet , .Fl dash , .Fl enum , .Fl hyphen and .Fl item have the following syntax: .Pp .D1 Pf \. Ic \&It .Pp with subsequent lines interpreted within the scope of the .Ic \&It until either a closing .Ic \&El or another .Ic \&It . .Pp The .Fl tag list has the following syntax: .Pp .D1 Pf \. Ic \&It Op Cm args .Pp Subsequent lines are interpreted as with .Fl bullet and family. The line arguments correspond to the list's left-hand side; body arguments correspond to the list's contents. .Pp The .Fl column list is the most complicated. Its syntax is as follows: .Pp .D1 Pf \. Ic \&It Ar cell Op Ic \&Ta Ar cell ... .D1 Pf \. Ic \&It Ar cell Op Ar cell ... .Pp The arguments consist of one or more lines of text and macros representing a complete table line. Cells within the line are delimited by the special .Ic \&Ta block macro or by literal tab characters. .Pp Using literal tabs is strongly discouraged because they are very hard to use correctly and .Nm code using them is very hard to read. In particular, a blank character is syntactically significant before and after the literal tab character. If a word precedes or follows the tab without an intervening blank, that word is never interpreted as a macro call, but always output literally. .Pp The tab cell delimiter may only be used within the .Ic \&It line itself; on following lines, only the .Ic \&Ta macro can be used to delimit cells, and portability requires that .Ic \&Ta is called by other macros: some parsers do not recognize it when it appears as the first macro on a line. .Pp Note that quoted strings may span tab-delimited cells on an .Ic \&It line. For example, .Pp .Dl .It \(dqcol1 ,\& col2 ,\(dq \&; .Pp will preserve the whitespace before both commas, but not the whitespace before the semicolon. .Pp See also .Ic \&Bl . .Tg Lb .It Ic \&Lb Cm lib Ns Ar name Op Cm lib Ns Ar name ... Specify one or more libraries to link against. Putting this macro at the beginning of the .Em SYNOPSIS section is recommended, in which case it prints this comment: .D1 /* Fl l Ns Ar name Oo Fl l Ns Ar name ... Oc */ .Pp If used outside the .Em SYNOPSIS , this macro prints .D1 library Dq Cm lib Ns Ar name instead. For system libraries, some operating systems print a short library description. .Pp Example: .Bd -literal -offset indent -compact \&.Sh SYNOPSIS \&.Lb libtls libssl libcrypto \&.In tls.h \&.Ft int \&.Fn tls_init void .Ed .Tg Li .It Ic \&Li Ar word ... Unquoted in-line literal display, always set in a constant-width font. In most cases, use .Ic \&Ql instead because on terminal output devices, .Ic \&Li is usually indistinguishable from normal text. This macro is only useful when enclosing the argument in quotes is explicitly not desired, for example because it already stands out due to being wrapped in another macro, e.g. in an .Ic \&It head. .Pp For longer literal displays, use .Ic \&Dl Pq single line 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 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: hyperlink. .Pp 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 .Em NAME section and not appropriate for other sections. .Pp Examples: .Dl Pf . Ic \&Nd mdoc language reference .Dl Pf . Ic \&Nd format and display UNIX manuals .Pp The .Ic \&Nd macro technically accepts child macros and terminates with a subsequent .Ic \&Sh invocation. Do not assume this behaviour: some .Xr whatis 1 database generators are not smart enough to parse more than the line 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 the manual page. When first invoked, the .Ic \&Nm macro expects a single argument, the name of the manual page. Usually, the first invocation happens in the .Em NAME section of the page. The specified name will be remembered and used whenever the macro is called again without arguments later in the page. The .Ic \&Nm macro uses .Sx Block full-implicit semantics when invoked as the first macro on an input line in the .Em SYNOPSIS section; otherwise, it uses ordinary .Sx In-line semantics. .Pp Examples: .Bd -literal -offset indent \&.Sh SYNOPSIS \&.Nm cat \&.Op Fl benstuv \&.Op Ar .Ed .Pp In the .Em SYNOPSIS of section 2, 3 and 9 manual pages, use the .Ic \&Fn 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. When used after physical formatting macros like .Ic \&Em or .Ic \&Sy , switches back to the standard font face and weight. Can also be used to embed plain text strings in macro lines using semantic annotation macros. .Pp Examples: .Dl ".Em italic , Sy bold , No and roman" .Bd -literal -offset indent \&.Sm off \&.Cm :C No / Ar pattern No / Ar replacement No / \&.Sm on .Ed .Pp See also .Ic \&Em , .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. Following invocation, input is interpreted as normal text just like after an .Ic \&No macro. .Pp This has no effect when invoked at the start of a macro line. .Pp Examples: .Dl ".Ar name Ns = Ns Ar value" .Dl ".Cm :M Ns Ar pattern" .Dl ".Fl o Ns Ar output" .Pp See also .Ic \&No and .Ic \&Sm . .Tg Nx .It Ic \&Nx Op Ar version Format the .Nx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Nx 5.01 .Dl \&.Nx .Pp See also .Ic \&At , .Ic \&Bsx , .Ic \&Bx , .Ic \&Dx , .Ic \&Fx , and .Ic \&Ox . .It Ic \&Oc Close multi-line .Ic \&Oo context. .It Ic \&Oo Ar block Multi-line version of .Ic \&Op . .Pp Examples: .Bd -literal -offset indent -compact \&.Oo \&.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. This is most often used in the .Em SYNOPSIS section of section 1 and 8 manual pages. .Pp Examples: .Dl \&.Op \&Fl a \&Ar b .Dl \&.Op \&Ar a | b .Pp See also .Ic \&Oo . .Tg Os .It Ic \&Os Op Ar footer text The mandatory third macro of every .Nm file. Usually, do not specify any arguments, in particular not the operating system name and/or version. .Pp If no argument is given, .Xr mandoc 1 prints its .Fl Ios argument in the page footer, or .Fa sysname and .Fa release as returned by .Xr uname 3 by default. .Pp Manual pages that are part of a portable software project can override the default by giving the project name and version number as arguments, but leaving it blank is never a bad choice. .Pp See also .Ic \&Dd and .Ic \&Dt . .It Ic \&Ot Ar functype This macro is obsolete. Use .Ic \&Ft instead; with .Xr mandoc 1 , both have the same effect. .Pp Historical .Nm packages described it as .Dq "old function type (FORTRAN)" . .Tg Ox .It Ic \&Ox Op Ar version Format the .Ox version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Ox 4.5 .Dl \&.Ox .Pp See also .Ic \&At , .Ic \&Bsx , .Ic \&Bx , .Ic \&Dx , .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 .Sq \(ti is used as a default. .Pp Examples: .Dl \&.Pa /usr/bin/mandoc .Dl \&.Pa /usr/share/man/man7/mdoc.7 .Pp See also .Ic \&Lk . .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: .Pp .D1 Ic \&No Pf \e& Ar prefix Ic \&Ns Ar macro Op Ar argument ... .Pp The .Ar prefix argument is not parsed for macro names or delimiters, but used verbatim as if it were escaped. .Pp Examples: .Dl ".Pf $ Ar variable_name" .Dl ".Pf . Ar macro_name" .Dl ".Pf 0x Ar hex_digits" .Pp See also .Ic \&Ns and .Ic \&Sm . .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 and/or text. .Pp Paragraph breaks are not needed before or after .Ic \&Sh or .Ic \&Ss macros or before displays .Pq Ic \&Bd Ar line or lists .Pq Ic \&Bl unless the .Fl compact flag is given. .Tg Pq .It Ic \&Pq Ar line Parenthesised enclosure. .Pp See also .Ic \&Po . .It Ic \&Qc Close quoted context opened by .Ic \&Qo . .Tg Ql .It Ic \&Ql Ar line Normal in-line literal display, always set in constant-width font and additionally enclosed in quotes by many formatters in many cases. This can be used for complete command invocations and for multi-word code examples when an indented display is not desired. .Pp See also .Ic \&Dl , .Ic \&Bd .Fl literal , and .Ic \&Li . .It Ic \&Qo Ar block Multi-line version of .Ic \&Qq . .Tg Qq .It Ic \&Qq Ar line Encloses its arguments in .Qq typewriter double-quotes. Consider using .Ic \&Dq . .Pp See also .Ic \&Dq , .Ic \&Sq , and .Ic \&Qo . .It Ic \&Re Close an .Ic \&Rs block. Does not have any tail arguments. .Tg Rs .It Ic \&Rs Begin a bibliographic .Pq Dq reference block. Does not have any head arguments. The block macro may only contain .Ic \&%A , .Ic \&%B , .Ic \&%C , .Ic \&%D , .Ic \&%I , .Ic \&%J , .Ic \&%N , .Ic \&%O , .Ic \&%P , .Ic \&%Q , .Ic \&%R , .Ic \&%T , .Ic \&%U , and .Ic \&%V child macros (at least one must be specified). .Pp Examples: .Bd -literal -offset indent -compact \&.Rs \&.%A J. E. Hopcroft \&.%A J. D. Ullman \&.%B Introduction to Automata Theory, Languages, and Computation \&.%I Addison-Wesley \&.%C Reading, Massachusetts \&.%D 1979 \&.Re .Ed .Pp If an .Ic \&Rs 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 .Va errno libc global variable set on error. .Pp If .Ar function is not specified, the document's name set by .Ic \&Nm is used. Multiple .Ar function arguments are treated as separate functions. .Pp See also .Ic \&Ex . .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 .Sx MANUAL STRUCTURE . Use the conventional sections where applicable. For unusually long and complicated manual pages, adding custom sections is occasionally useful. .Pp Avoid using macros inside the .Ar TITLE LINE and keep that line unique within the manual page, such that it can be pointed to with .Ic \&Sx . .Pp See also .Ic \&Pp , .Ic \&Ss , and .Ic \&Sx . .Tg Sm .It Ic \&Sm Op Cm on | off Switches the spacing mode for output generated from macros. .Pp By default, spacing is .Cm on . When switched .Cm off , no white space is inserted between macro arguments and between the output generated from adjacent macros, but text lines still get normal spacing between words and sentences. .Pp When called without an argument, the .Ic \&Sm macro toggles the spacing mode. 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 single-quotes. .Pp See also .Ic \&Dq , .Ic \&Qq , and .Ic \&So . .Tg Ss .It Ic \&Ss Ar Title line Begin a new subsection. Unlike with .Ic \&Sh , there is no convention for the naming of subsections. Except .Em DESCRIPTION , the conventional sections described in .Sx MANUAL STRUCTURE rarely have subsections. .Pp Avoid using macros inside the .Ar Title line and keep that line unique within the manual page, such that it can be pointed to with .Ic \&Sx . .Pp See also .Ic \&Pp , .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. Where multiple lines are given without a blank line in between, they all refer to the same standard, and using the first form is recommended. .Bl -tag -width 1n .It C language standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-ansiC .St -ansiC .It \-ansiC-89 .St -ansiC-89 .It \-isoC .St -isoC .It \-isoC-90 .St -isoC-90 .br The original C standard. .Pp .It \-isoC-amd1 .St -isoC-amd1 .Pp .It \-isoC-tcor1 .St -isoC-tcor1 .Pp .It \-isoC-tcor2 .St -isoC-tcor2 .Pp .It \-isoC-99 .St -isoC-99 .br Edition 2 of the C language standard. .Pp .It \-isoC-2011 .St -isoC-2011 .br Edition 3 of the C language standard. .Pp .It \-isoC-2023 .St -isoC-2023 .br Edition 5 of the C language standard. .El .It POSIX.1 before XPG4.2 .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-p1003.1-88 .St -p1003.1-88 .It \-p1003.1 .St -p1003.1 .br The original POSIX standard, based on ANSI C. .Pp .It \-p1003.1-90 .St -p1003.1-90 .It \-iso9945-1-90 .St -iso9945-1-90 .br The first update of POSIX.1. .Pp .It \-p1003.1b-93 .St -p1003.1b-93 .It \-p1003.1b .St -p1003.1b .br Real-time extensions. .Pp .It \-p1003.1c-95 .St -p1003.1c-95 .br POSIX thread interfaces. .Pp .It \-p1003.1i-95 .St -p1003.1i-95 .br Technical Corrigendum. .Pp .It \-p1003.1-96 .St -p1003.1-96 .It \-iso9945-1-96 .St -iso9945-1-96 .br Includes POSIX.1-1990, 1b, 1c, and 1i. .El .It X/Open Portability Guide before XPG4.2 .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-xpg3 .St -xpg3 .br An XPG4 precursor, published in 1989. .Pp .It \-p1003.2 .St -p1003.2 .It \-p1003.2-92 .St -p1003.2-92 .It \-iso9945-2-93 .St -iso9945-2-93 .br An XCU4 precursor. .Pp .It \-p1003.2a-92 .St -p1003.2a-92 .br Updates to POSIX.2. .Pp .It \-xpg4 .St -xpg4 .br Based on POSIX.1 and POSIX.2, published in 1992. .El .It X/Open Portability Guide Issue 4 Version 2 and related standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-susv1 .St -susv1 .It \-xpg4.2 .St -xpg4.2 .br This standard was published in 1994. It was used as the basis for UNIX 95 certification. The following two refer to parts of it. .Pp .It \-xcurses4.2 .St -xcurses4.2 .Pp .It \-p1003.1g-2000 .St -p1003.1g-2000 .br Networking APIs, including sockets. .Pp .It \-svid4 .St -svid4 , .br Published in 1995. .El .It X/Open Portability Guide Issue 5 and related standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-susv2 .St -susv2 .br This Standard was published in 1997 and is also called X/Open Portability Guide Issue 5. It was used as the basis for UNIX 98 certification. The following refer to parts of it. .Pp .It \-xbd5 .St -xbd5 .Pp .It \-xsh5 .St -xsh5 .Pp .It \-xcu5 .St -xcu5 .Pp .It \-xns5 .St -xns5 .It \-xns5.2 .St -xns5.2 .El .It POSIX Issue 6 .Pp .Bl -tag -width "-p1003.1-2001" -compact .It \-p1003.1-2001 .St -p1003.1-2001 .It \-susv3 .St -susv3 .br This standard is based on C99, SUSv2, POSIX.1-1996, 1d, and 1j. It is also called X/Open Portability Guide Issue 6. It is used as the basis for UNIX 03 certification. .Pp .It \-p1003.1-2004 .St -p1003.1-2004 .br The second and last Technical Corrigendum. .El .It POSIX Issues 7 and 8 .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-p1003.1-2008 .St -p1003.1-2008 .It \-susv4 .St -susv4 .br This standard is based on C99. It is also called the Open Group Standard Base Specifications, Issue 7. .El .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-p1003.1-2024 .St -p1003.1-2024 .br This standard is based on C17. It is also called the Open Group Standard Base Specifications, Issue 8. .El .It Other standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-ieee754 .St -ieee754 .br Floating-point arithmetic. .Pp .It \-iso8601 .St -iso8601 .br Representation of dates and times, published in 1988. .Pp .It \-iso8802-3 .St -iso8802-3 .br Ethernet local area networks. .Pp .It \-ieee1275-94 .St -ieee1275-94 .El .El .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 enclosed argument, including whitespace. .Pp Examples: .Dl \&.Sx MANUAL STRUCTURE .Pp See also .Ic \&Sh and .Ic \&Ss . .Tg Sy .It Ic \&Sy Ar word ... Request a boldface font. .Pp This is most often used to indicate importance or seriousness (not to be confused with stress emphasis, see .Ic \&Em ) . When none of the semantic macros fit, it is also adequate for syntax elements that have to be given or that appear verbatim. .Pp Examples: .Bd -literal -compact -offset indent \&.Sy Warning : If \&.Sy s appears in the owner permissions, set-user-ID mode is set. This utility replaces the former \&.Sy dumpdir program. .Ed .Pp See also .Ic \&Em , .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 .Pq Dq tradename suggests a semantic function, historic usage is inconsistent, mostly using it as a presentation-level macro to request a small caps font. .It Ic \&Ud Supported only for compatibility, do not use this in new manuals. Prints out .Dq currently under development. .It Ic \&Ux 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 Examples: .Dl \&.Va foo .Dl \&.Va const char *bar ; .Pp For function arguments and parameters, use .Ic \&Fa instead. 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 This is also used for indicating global variables in the .Em SYNOPSIS section, in which case a variable name is also specified. Note that it accepts .Sx Block partial-implicit syntax when invoked as the first macro on an input line in the .Em SYNOPSIS section, else it accepts ordinary .Sx In-line syntax. In the former case, this macro starts a new output line, and a blank line is inserted in front if there is a preceding function definition or include directive. .Pp Examples: .Dl \&.Vt unsigned char .Dl \&.Vt extern const char * const sys_signame[] \&; .Pp For parameters in function prototypes, use .Ic \&Fa instead, for function return types .Ic \&Ft , and for variable names outside the .Em SYNOPSIS section .Ic \&Va , even when including a type with the name. See also .Sx MANUAL STRUCTURE . .It Ic \&Xc Close a scope opened by .Ic \&Xo . .It Ic \&Xo Ar block Extend the header of an .Ic \&It macro or the body of a partial-implicit block macro 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 . .Pp Cross reference the .Ar name and .Ar section number of another man page. .Pp Examples: .Dl \&.Xr mandoc 1 .Dl \&.Xr mandoc 1 \&; .Dl \&.Xr mandoc 1 \&Ns s behaviour .El .Sh MACRO SYNTAX The syntax of a macro depends on its classification. In this section, .Sq \-arg refers to macro arguments, which may be followed by zero or more .Sq parm parameters; .Sq \&Yo opens the scope of a macro; and if specified, .Sq \&Yc closes it out. .Pp The .Em Callable column indicates that the macro may also be called by passing its name as an argument to another macro. For example, .Sq \&.Op \&Fl O \&Ar file produces .Sq Op Fl O Ar file . To prevent a macro call and render the macro name literally, escape it by prepending a zero-width space, .Sq \e& . For example, .Sq \&Op \e&Fl O produces .Sq Op \&Fl O . If a macro is not callable but its name appears as an argument to another macro, it is interpreted as opaque text. For example, .Sq \&.Fl \&Sh produces .Sq Fl \&Sh . .Pp The .Em Parsed column indicates whether the macro may call other macros by receiving their names as arguments. If a macro is not parsed but the name of another macro appears as an argument, it is interpreted as opaque text. .Pp The .Em Scope 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 \&Bf and .Pq optionally .Ic \&Bl contain a head. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \(lBbody...\(rB \&.Yc .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Ic \&Bd Ta \&No Ta \&No Ta closed by Ic \&Ed .It Ic \&Bf Ta \&No Ta \&No Ta closed by Ic \&Ef .It Ic \&Bk Ta \&No Ta \&No Ta closed by Ic \&Ek .It Ic \&Bl Ta \&No Ta \&No Ta closed by Ic \&El .It Ic \&Ed Ta \&No Ta \&No Ta opened by Ic \&Bd .It Ic \&Ef Ta \&No Ta \&No Ta opened by Ic \&Bf .It Ic \&Ek Ta \&No Ta \&No Ta opened by Ic \&Bk .It Ic \&El Ta \&No Ta \&No Ta opened by Ic \&Bl .El .Ss Block full-implicit Multi-line scope closed by end-of-file or implicitly by another macro. All macros have bodies; some .Po .Ic \&It Fl bullet , .Fl hyphen , .Fl dash , .Fl enum , .Fl item .Pc don't have heads; only one .Po .Ic \&It in .Ic \&Bl Fl column .Pc has multiple heads. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead... \(lBTa head...\(rB\(rB \(lBbody...\(rB .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXXXXXXXXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Ic \&It Ta \&No Ta Yes Ta closed by Ic \&It , Ic \&El .It Ic \&Nd Ta \&No Ta \&No Ta closed by Ic \&Sh .It Ic \&Nm Ta \&No Ta Yes Ta closed by Ic \&Nm , Ic \&Sh , Ic \&Ss .It Ic \&Sh Ta \&No Ta Yes Ta closed by Ic \&Sh .It Ic \&Ss Ta \&No Ta Yes Ta closed by Ic \&Sh , Ic \&Ss .El .Pp Note that the .Ic \&Nm macro is a .Sx Block full-implicit macro only when invoked as the first macro in a .Em SYNOPSIS section line, else it is .Sx In-line . .Ss Block partial-explicit Like block full-explicit, but also with single-line scope. Each has at least a body and, in limited circumstances, a head .Po .Ic \&Fo , .Ic \&Eo .Pc and/or tail .Pq Ic \&Ec . .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \(lBbody...\(rB \&.Yc \(lBtail...\(rB \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \ \(lBbody...\(rB \&Yc \(lBtail...\(rB .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Ic \&Ac Ta Yes Ta Yes Ta opened by Ic \&Ao .It Ic \&Ao Ta Yes Ta Yes Ta closed by Ic \&Ac .It Ic \&Bc Ta Yes Ta Yes Ta closed by Ic \&Bo .It Ic \&Bo Ta Yes Ta Yes Ta opened by Ic \&Bc .It Ic \&Brc Ta Yes Ta Yes Ta opened by Ic \&Bro .It Ic \&Bro Ta Yes Ta Yes Ta closed by Ic \&Brc .It Ic \&Dc Ta Yes Ta Yes Ta opened by Ic \&Do .It Ic \&Do Ta Yes Ta Yes Ta closed by Ic \&Dc .It Ic \&Ec Ta Yes Ta Yes Ta opened by Ic \&Eo .It Ic \&Eo Ta Yes Ta Yes Ta closed by Ic \&Ec .It Ic \&Fc Ta Yes Ta Yes Ta opened by Ic \&Fo .It Ic \&Fo Ta \&No Ta \&No Ta closed by Ic \&Fc .It Ic \&Oc Ta Yes Ta Yes Ta closed by Ic \&Oo .It Ic \&Oo Ta Yes Ta Yes Ta opened by Ic \&Oc .It Ic \&Pc Ta Yes Ta Yes Ta closed by Ic \&Po .It Ic \&Po Ta Yes Ta Yes Ta opened by Ic \&Pc .It Ic \&Qc Ta Yes Ta Yes Ta opened by Ic \&Oo .It Ic \&Qo Ta Yes Ta Yes Ta closed by Ic \&Oc .It Ic \&Re Ta \&No Ta \&No Ta opened by Ic \&Rs .It Ic \&Rs Ta \&No Ta \&No Ta closed by Ic \&Re .It Ic \&Sc Ta Yes Ta Yes Ta opened by Ic \&So .It Ic \&So Ta Yes Ta Yes Ta closed by Ic \&Sc .It Ic \&Xc Ta Yes Ta Yes Ta opened by Ic \&Xo .It Ic \&Xo Ta Yes Ta Yes Ta closed by Ic \&Xc .El .Ss Block partial-implicit Like block full-implicit, but with single-line scope closed by the end of the line. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBbody...\(rB \(lBres...\(rB .Ed .Bl -column "MacroX" "CallableX" "ParsedX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed .It Ic \&Aq Ta Yes Ta Yes .It Ic \&Bq Ta Yes Ta Yes .It Ic \&Brq Ta Yes Ta Yes .It Ic \&D1 Ta \&No Ta \&Yes .It Ic \&Dl Ta \&No Ta Yes .It Ic \&Dq Ta Yes Ta Yes .It Ic \&En Ta Yes Ta Yes .It Ic \&Op Ta Yes Ta Yes .It Ic \&Pq Ta Yes Ta Yes .It Ic \&Ql Ta Yes Ta Yes .It Ic \&Qq Ta Yes Ta Yes .It Ic \&Sq Ta Yes Ta Yes .It Ic \&Vt Ta Yes Ta Yes .El .Pp Note that the .Ic \&Vt macro is a .Sx Block partial-implicit only when invoked as the first macro in a .Em SYNOPSIS section line, else it is .Sx In-line . .Ss Special block macro The .Ic \&Ta macro can only be used below .Ic \&It in .Ic \&Bl Fl column lists. It delimits blocks representing table cells; these blocks have bodies, but no heads. .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Ic \&Ta Ta Yes Ta Yes Ta closed by Ic \&Ta , Ic \&It .El .Ss In-line Closed by the end of the line, fixed argument lengths, and/or subsequent macros. In-line macros have only text children. If a number (or inequality) of arguments is .Pq n , then the macro accepts an arbitrary number of arguments. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB \(lBres...\(rB \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB Yc... \&.Yo \(lB\-arg \(lBval...\(rB\(rB arg0 arg1 argN .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "Arguments" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Arguments .It Ic \&%A Ta \&No Ta \&No Ta >0 .It Ic \&%B Ta \&No Ta \&No Ta >0 .It Ic \&%C Ta \&No Ta \&No Ta >0 .It Ic \&%D Ta \&No Ta \&No Ta >0 .It Ic \&%I Ta \&No Ta \&No Ta >0 .It Ic \&%J Ta \&No Ta \&No Ta >0 .It Ic \&%N Ta \&No Ta \&No Ta >0 .It Ic \&%O Ta \&No Ta \&No Ta >0 .It Ic \&%P Ta \&No Ta \&No Ta >0 .It Ic \&%Q Ta \&No Ta \&No Ta >0 .It Ic \&%R Ta \&No Ta \&No Ta >0 .It Ic \&%T Ta \&No Ta \&No Ta >0 .It Ic \&%U Ta \&No Ta \&No Ta >0 .It Ic \&%V Ta \&No Ta \&No Ta >0 .It Ic \&Ad Ta Yes Ta Yes Ta >0 .It Ic \&An Ta Yes Ta Yes Ta >0 .It Ic \&Ap Ta Yes Ta Yes Ta 0 .It Ic \&Ar Ta Yes Ta Yes Ta n .It Ic \&At Ta Yes Ta Yes Ta 1 .It Ic \&Bsx Ta Yes Ta Yes Ta n .It Ic \&Bt Ta \&No Ta \&No Ta 0 .It Ic \&Bx Ta Yes Ta Yes Ta n .It Ic \&Cd Ta Yes Ta Yes Ta >0 .It Ic \&Cm Ta Yes Ta Yes Ta >0 .It Ic \&Db Ta \&No Ta \&No Ta 1 .It Ic \&Dd Ta \&No Ta \&No Ta n .It Ic \&Dt Ta \&No Ta \&No Ta n .It Ic \&Dv Ta Yes Ta Yes Ta >0 .It Ic \&Dx Ta Yes Ta Yes Ta n .It Ic \&Em Ta Yes Ta Yes Ta >0 .It Ic \&Er Ta Yes Ta Yes Ta >0 .It Ic \&Es Ta Yes Ta Yes Ta 2 .It Ic \&Ev Ta Yes Ta Yes Ta >0 .It Ic \&Ex Ta \&No Ta \&No Ta n .It Ic \&Fa Ta Yes Ta Yes Ta >0 .It Ic \&Fd Ta \&No Ta \&No Ta >0 .It Ic \&Fl Ta Yes Ta Yes Ta n .It Ic \&Fn Ta Yes Ta Yes Ta >0 .It Ic \&Fr Ta Yes Ta Yes Ta >0 .It Ic \&Ft Ta Yes Ta Yes Ta >0 .It Ic \&Fx Ta Yes Ta Yes Ta n .It Ic \&Hf Ta \&No Ta \&No Ta n .It Ic \&Ic Ta Yes Ta Yes Ta >0 .It Ic \&In Ta Yes Ta Yes Ta 1 .It Ic \&Lb Ta \&No Ta \&No Ta >0 .It Ic \&Li Ta Yes Ta Yes Ta >0 .It Ic \&Lk Ta Yes Ta Yes Ta >0 .It Ic \&Lp Ta \&No Ta \&No Ta 0 .It Ic \&Ms Ta Yes Ta Yes Ta >0 .It Ic \&Mt Ta Yes Ta Yes Ta >0 .It Ic \&Nm Ta Yes Ta Yes Ta n .It Ic \&No Ta Yes Ta Yes Ta >0 .It Ic \&Ns Ta Yes Ta Yes Ta 0 .It Ic \&Nx Ta Yes Ta Yes Ta n .It Ic \&Os Ta \&No Ta \&No Ta n .It Ic \&Ot Ta Yes Ta Yes Ta >0 .It Ic \&Ox Ta Yes Ta Yes Ta n .It Ic \&Pa Ta Yes Ta Yes Ta n .It Ic \&Pf Ta Yes Ta Yes Ta 1 .It Ic \&Pp Ta \&No Ta \&No Ta 0 .It Ic \&Rv Ta \&No Ta \&No Ta n .It Ic \&Sm Ta \&No Ta \&No Ta <2 .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 .It Ic \&Va Ta Yes Ta Yes Ta n .It Ic \&Vt Ta Yes Ta Yes Ta >0 .It Ic \&Xr Ta Yes Ta Yes Ta 2 .El .Ss Delimiters When a macro argument consists of one single input character considered as a delimiter, the argument gets special handling. This does not apply when delimiters appear in arguments containing more than one character. Consequently, to prevent special handling and just handle it like any other argument, a delimiter can be escaped by prepending a zero-width space .Pq Sq \e& . In text lines, delimiters never need escaping, but may be used as normal punctuation. .Pp For many macros, when the leading arguments are opening delimiters, these delimiters are put before the macro scope, and when the trailing arguments are closing delimiters, these delimiters are put after the macro scope. Spacing is suppressed after opening delimiters and before closing delimiters. For example, .Pp .D1 Pf \. \&Aq "( [ word ] ) ." .Pp renders as: .Pp .D1 Aq ( [ word ] ) . .Pp Opening delimiters are: .Pp .Bl -tag -width Ds -offset indent -compact .It \&( left parenthesis .It \&[ left bracket .El .Pp Closing delimiters are: .Pp .Bl -tag -width Ds -offset indent -compact .It \&. period .It \&, comma .It \&: colon .It \&; semicolon .It \&) right parenthesis .It \&] right bracket .It \&? question mark .It \&! exclamation mark .El .Pp Note that even a period preceded by a backslash .Pq Sq \e.\& gets this special handling; use .Sq \e&.\& to prevent that. .Pp Many in-line macros interrupt their scope when they encounter delimiters, and resume their scope when more arguments follow that are not delimiters. For example, .Pp .D1 Pf \. \&Fl "a ( b | c \e*(Ba d ) e" .Pp renders as: .Pp .D1 Fl a ( b | c \*(Ba d ) e .Pp This applies to both opening and closing delimiters, and also to the middle delimiter, which does not suppress spacing: .Pp .Bl -tag -width Ds -offset indent -compact .It \&| vertical bar .El .Pp As a special case, the predefined string \e*(Ba is handled and rendered in the same way as a plain .Sq \&| character. Using this predefined string is not recommended in new manuals. .Pp Appending a zero-width space .Pq Sq \e& to the end of an input line is also useful to prevent the interpretation of a trailing period, exclamation or question mark as the end of a sentence, for example when an abbreviation happens to occur at the end of a text or macro input line. .Ss Font handling In .Nm documents, usage of semantic markup is recommended in order to have proper fonts automatically selected; only when no fitting semantic markup is available, consider falling back to .Sx Physical markup macros. Whenever any .Nm macro switches the .Xr roff 7 font mode, it will automatically restore the previous font when exiting its scope. Manually switching the font using the .Xr roff 7 .Ql \ef font escape sequences is never required. .Sh COMPATIBILITY This section provides an incomplete list of compatibility issues between mandoc and GNU troff .Pq Qq groff . .Pp The following problematic behaviour is found in groff: .Pp .Bl -dash -compact .It .Ic \&Pa does not format its arguments when used in the FILES section under certain list types. .It .Ic \&Ta can only be called by other macros, but not at the beginning of a line. .It .Sq \ef .Pq font face and .Sq \eF .Pq font family face .Sx Text Decoration escapes behave irregularly when specified within line-macro scopes. .It Negative scaling units return to prior lines. Instead, mandoc truncates them to zero. .El .Pp The following features are unimplemented in mandoc: .Pp .Bl -dash -compact .It .Ic \&Bd Fl file Ar file is unsupported for security reasons. .It .Ic \&Bd .Fl filled does not adjust the right margin, but is an alias for .Ic \&Bd .Fl ragged . .It .Ic \&Bd .Fl literal does not use a literal font, but is an alias for .Ic \&Bd .Fl unfilled . .It .Ic \&Bd .Fl offset Cm center and .Fl offset Cm right don't work. Groff does not implement centered and flush-right rendering either, but produces large indentations. .El .Sh SEE ALSO .Xr man 1 , .Xr mandoc 1 , .Xr eqn 7 , .Xr man 7 , .Xr mandoc_char 7 , .Xr roff 7 , .Xr tbl 7 .Pp The web page .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 language first appeared as a troff macro package in .Bx 4.4 . It was later significantly updated by Werner Lemberg and Ruslan Ermilov in groff-1.17. The standalone implementation that is part of the .Xr mandoc 1 utility written by Kristaps Dzonsons appeared in .Ox 4.6 . .Sh AUTHORS The .Nm reference was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . diff --git a/contrib/mandoc/roff_term.c b/contrib/mandoc/roff_term.c index 85d2caeb2749..38321c830013 100644 --- a/contrib/mandoc/roff_term.c +++ b/contrib/mandoc/roff_term.c @@ -1,276 +1,276 @@ -/* $Id: roff_term.c,v 1.26 2025/07/16 14:33:08 schwarze Exp $ */ +/* $Id: roff_term.c,v 1.27 2025/08/21 15:38:51 schwarze Exp $ */ /* * Copyright (c) 2010, 2014, 2015, 2017-2021, 2025 * Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include "mandoc.h" #include "roff.h" #include "out.h" #include "term.h" #define ROFF_TERM_ARGS struct termp *p, const struct roff_node *n typedef void (*roff_term_pre_fp)(ROFF_TERM_ARGS); static void roff_term_pre_br(ROFF_TERM_ARGS); static void roff_term_pre_ce(ROFF_TERM_ARGS); static void roff_term_pre_ft(ROFF_TERM_ARGS); static void roff_term_pre_ll(ROFF_TERM_ARGS); static void roff_term_pre_mc(ROFF_TERM_ARGS); static void roff_term_pre_po(ROFF_TERM_ARGS); static void roff_term_pre_sp(ROFF_TERM_ARGS); static void roff_term_pre_ta(ROFF_TERM_ARGS); static void roff_term_pre_ti(ROFF_TERM_ARGS); static const roff_term_pre_fp roff_term_pre_acts[ROFF_MAX] = { roff_term_pre_br, /* br */ roff_term_pre_ce, /* ce */ roff_term_pre_br, /* fi */ roff_term_pre_ft, /* ft */ roff_term_pre_ll, /* ll */ roff_term_pre_mc, /* mc */ roff_term_pre_br, /* nf */ roff_term_pre_po, /* po */ roff_term_pre_ce, /* rj */ roff_term_pre_sp, /* sp */ roff_term_pre_ta, /* ta */ roff_term_pre_ti, /* ti */ }; void roff_term_pre(struct termp *p, const struct roff_node *n) { assert(n->tok < ROFF_MAX); (*roff_term_pre_acts[n->tok])(p, n); } static void roff_term_pre_br(ROFF_TERM_ARGS) { term_newln(p); if (p->flags & TERMP_BRIND) { p->tcol->offset = p->tcol->rmargin; p->tcol->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); p->flags |= TERMP_NOSPACE; } } static void roff_term_pre_ce(ROFF_TERM_ARGS) { const struct roff_node *nc1, *nc2; roff_term_pre_br(p, n); p->flags |= n->tok == ROFF_ce ? TERMP_CENTER : TERMP_RIGHT; nc1 = n->child->next; while (nc1 != NULL) { nc2 = nc1; do { nc2 = nc2->next; } while (nc2 != NULL && (nc2->type != ROFFT_TEXT || (nc2->flags & NODE_LINE) == 0)); while (nc1 != nc2) { if (nc1->type == ROFFT_TEXT) term_word(p, nc1->string); else roff_term_pre(p, nc1); nc1 = nc1->next; } p->flags |= TERMP_NOSPACE; term_flushln(p); } p->flags &= ~(TERMP_CENTER | TERMP_RIGHT); } static void roff_term_pre_ft(ROFF_TERM_ARGS) { const char *cp; 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: term_fontrepl(p, TERMFONT_BI); break; case ESCAPE_FONTPREV: term_fontlast(p); break; case ESCAPE_FONTROMAN: case ESCAPE_FONTCR: term_fontrepl(p, TERMFONT_NONE); break; default: break; } } static void roff_term_pre_ll(ROFF_TERM_ARGS) { term_setwidth(p, n->child != NULL ? n->child->string : NULL); } static void roff_term_pre_mc(ROFF_TERM_ARGS) { if (p->col) { p->flags |= TERMP_NOBREAK; term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_NOSPACE); } if (n->child != NULL) { p->mc = n->child->string; p->flags |= TERMP_NEWMC; } else p->flags |= TERMP_ENDMC; } static void roff_term_pre_po(ROFF_TERM_ARGS) { struct roffsu su; /* Page offsets in basic units. */ static int polast; /* Previously requested. */ static int po; /* Currently requested. */ static int pouse; /* Currently used. */ int pomin; /* Minimum to be used. */ int pomax; /* Maximum to be used. */ int ponew; /* Newly requested. */ /* 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_hspan(p, &su); if (*n->child->string == '+' || *n->child->string == '-') ponew += po; } else ponew = polast; /* Remember both the previous and the newly requested offset. */ polast = po; po = ponew; /* Truncate to the range [-offset, 60], remember, and apply it. */ pomin = -p->tcol->offset; pomax = term_len(p, 60); pouse = po > pomax ? pomax : po < pomin ? pomin : po; p->tcol->offset += pouse; } static void roff_term_pre_sp(ROFF_TERM_ARGS) { struct roffsu su; int len; if (n->child != NULL) { if (a2roffsu(n->child->string, &su, SCALE_VS) == NULL) su.scale = 1.0; len = term_vspan(p, &su); } else len = 1; if (len < 0) p->skipvsp -= len; else while (len--) term_vspace(p); roff_term_pre_br(p, n); } static void roff_term_pre_ta(ROFF_TERM_ARGS) { term_tab_set(p, NULL); for (n = n->child; n != NULL; n = n->next) term_tab_set(p, n->string); } static void roff_term_pre_ti(ROFF_TERM_ARGS) { struct roffsu su; const char *cp; /* Request argument. */ size_t maxoff; /* Maximum indentation in basic units. */ int len; /* Request argument in basic units. */ int sign; roff_term_pre_br(p, n); if (n->child == NULL) return; cp = n->child->string; if (*cp == '+') { sign = 1; cp++; } else if (*cp == '-') { sign = -1; cp++; } else sign = 0; if (a2roffsu(cp, &su, SCALE_EM) == NULL) return; len = term_hspan(p, &su); maxoff = term_len(p, 72); 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; break; } p->tcol->offset += p->ti; } diff --git a/contrib/mandoc/term_ps.c b/contrib/mandoc/term_ps.c index 4c6368ca1d1f..91124152d55a 100644 --- a/contrib/mandoc/term_ps.c +++ b/contrib/mandoc/term_ps.c @@ -1,1364 +1,1364 @@ -/* $Id: term_ps.c,v 1.94 2025/07/18 15:47:18 schwarze Exp $ */ +/* $Id: term_ps.c,v 1.95 2025/09/26 12:17:12 schwarze Exp $ */ /* * Copyright (c) 2014-2017, 2020, 2025 Ingo Schwarze * Copyright (c) 2010, 2011 Kristaps Dzonsons * Copyright (c) 2017 Marc Espie * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #if HAVE_ERR #include #endif #include #include #include #include #include #include #include "mandoc_aux.h" #include "out.h" #include "term.h" #include "manconf.h" #include "main.h" /* These work the buffer used by the header and footer. */ #define PS_BUFSLOP 128 /* Convert PostScript point "x" to an AFM unit. */ #define PNT2AFM(p, x) \ (size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale)) /* Convert an AFM unit "x" to a PostScript points */ #define AFM2PNT(p, x) \ ((double)(x) / (1000.0 / (double)(p)->ps->scale)) struct glyph { unsigned short wx; /* WX in AFM */ }; struct font { const char *name; /* FontName in AFM */ #define MAXCHAR 95 /* total characters we can handle */ struct glyph gly[MAXCHAR]; /* glyph metrics */ }; struct termp_ps { int flags; #define PS_INLINE (1 << 0) /* we're in a word */ #define PS_MARGINS (1 << 1) /* we're in the margins */ #define PS_NEWPAGE (1 << 2) /* new page, no words yet */ #define PS_BACKSP (1 << 3) /* last character was backspace */ size_t pscol; /* visible column (AFM units) */ size_t pscolnext; /* used for overstrike */ size_t psrow; /* visible row (AFM units) */ size_t lastrow; /* psrow of the previous word */ char *psmarg; /* margin buf */ size_t psmargsz; /* margin buf size */ size_t psmargcur; /* cur index in margin buf */ char last; /* last non-backspace seen */ enum termfont lastf; /* last set font */ enum termfont nextf; /* building next font here */ size_t scale; /* font scaling factor */ size_t pages; /* number of pages shown */ size_t lineheight; /* line height (AFM units) */ size_t top; /* body top (AFM units) */ size_t bottom; /* body bottom (AFM units) */ const char *medianame; /* for DocumentMedia and PageSize */ size_t height; /* page height (AFM units */ size_t width; /* page width (AFM units) */ size_t lastwidth; /* page width before last ll */ size_t left; /* body left (AFM units) */ size_t header; /* header pos (AFM units) */ size_t footer; /* footer pos (AFM units) */ size_t pdfbytes; /* current output byte */ size_t pdflastpg; /* byte of last page mark */ size_t pdfbody; /* start of body object */ size_t *pdfobjs; /* table of object offsets */ size_t pdfobjsz; /* size of pdfobjs */ }; static int ps_hspan(const struct termp *, const struct roffsu *); static size_t ps_getwidth(const struct termp *, int); static void ps_advance(struct termp *, size_t); static void ps_begin(struct termp *); static void ps_closepage(struct termp *); static void ps_end(struct termp *); static void ps_endline(struct termp *); static void ps_growbuf(struct termp *, size_t); static void ps_letter(struct termp *, int); static void ps_pclose(struct termp *); static void ps_plast(struct termp *); static void ps_pletter(struct termp *, int); static void ps_printf(struct termp *, const char *, ...) __attribute__((__format__ (__printf__, 2, 3))); static void ps_putchar(struct termp *, char); static void ps_setfont(struct termp *, enum termfont); static void ps_setwidth(struct termp *, int, int); static struct termp *pspdf_alloc(const struct manoutput *, enum termtype); static void pdf_obj(struct termp *, size_t); /* * We define, for the time being, three fonts: bold, oblique/italic, and * normal (roman). The following table hard-codes the font metrics for * ASCII, i.e., 32--127. */ static const struct font fonts[TERMFONT__MAX] = { { "Times-Roman", { { 250 }, { 333 }, { 408 }, { 500 }, { 500 }, { 833 }, { 778 }, { 333 }, { 333 }, { 333 }, { 500 }, { 564 }, { 250 }, { 333 }, { 250 }, { 278 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 278 }, { 278 }, { 564 }, { 564 }, { 564 }, { 444 }, { 921 }, { 722 }, { 667 }, { 667 }, { 722 }, { 611 }, { 556 }, { 722 }, { 722 }, { 333 }, { 389 }, { 722 }, { 611 }, { 889 }, { 722 }, { 722 }, { 556 }, { 722 }, { 667 }, { 556 }, { 611 }, { 722 }, { 722 }, { 944 }, { 722 }, { 722 }, { 611 }, { 333 }, { 278 }, { 333 }, { 469 }, { 500 }, { 333 }, { 444 }, { 500 }, { 444 }, { 500}, { 444}, { 333}, { 500}, { 500}, { 278}, { 278}, { 500}, { 278}, { 778}, { 500}, { 500}, { 500}, { 500}, { 333}, { 389}, { 278}, { 500}, { 500}, { 722}, { 500}, { 500}, { 444}, { 480}, { 200}, { 480}, { 541}, } }, { "Times-Bold", { { 250 }, { 333 }, { 555 }, { 500 }, { 500 }, { 1000 }, { 833 }, { 333 }, { 333 }, { 333 }, { 500 }, { 570 }, { 250 }, { 333 }, { 250 }, { 278 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 333 }, { 333 }, { 570 }, { 570 }, { 570 }, { 500 }, { 930 }, { 722 }, { 667 }, { 722 }, { 722 }, { 667 }, { 611 }, { 778 }, { 778 }, { 389 }, { 500 }, { 778 }, { 667 }, { 944 }, { 722 }, { 778 }, { 611 }, { 778 }, { 722 }, { 556 }, { 667 }, { 722 }, { 722 }, { 1000 }, { 722 }, { 722 }, { 667 }, { 333 }, { 278 }, { 333 }, { 581 }, { 500 }, { 333 }, { 500 }, { 556 }, { 444 }, { 556 }, { 444 }, { 333 }, { 500 }, { 556 }, { 278 }, { 333 }, { 556 }, { 278 }, { 833 }, { 556 }, { 500 }, { 556 }, { 556 }, { 444 }, { 389 }, { 333 }, { 556 }, { 500 }, { 722 }, { 500 }, { 500 }, { 444 }, { 394 }, { 220 }, { 394 }, { 520 }, } }, { "Times-Italic", { { 250 }, { 333 }, { 420 }, { 500 }, { 500 }, { 833 }, { 778 }, { 333 }, { 333 }, { 333 }, { 500 }, { 675 }, { 250 }, { 333 }, { 250 }, { 278 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 333 }, { 333 }, { 675 }, { 675 }, { 675 }, { 500 }, { 920 }, { 611 }, { 611 }, { 667 }, { 722 }, { 611 }, { 611 }, { 722 }, { 722 }, { 333 }, { 444 }, { 667 }, { 556 }, { 833 }, { 667 }, { 722 }, { 611 }, { 722 }, { 611 }, { 500 }, { 556 }, { 722 }, { 611 }, { 833 }, { 611 }, { 556 }, { 556 }, { 389 }, { 278 }, { 389 }, { 422 }, { 500 }, { 333 }, { 500 }, { 500 }, { 444 }, { 500 }, { 444 }, { 278 }, { 500 }, { 500 }, { 278 }, { 278 }, { 444 }, { 278 }, { 722 }, { 500 }, { 500 }, { 500 }, { 500 }, { 389 }, { 389 }, { 278 }, { 500 }, { 444 }, { 667 }, { 444 }, { 444 }, { 389 }, { 400 }, { 275 }, { 400 }, { 541 }, } }, { "Times-BoldItalic", { { 250 }, { 389 }, { 555 }, { 500 }, { 500 }, { 833 }, { 778 }, { 333 }, { 333 }, { 333 }, { 500 }, { 570 }, { 250 }, { 333 }, { 250 }, { 278 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 500 }, { 333 }, { 333 }, { 570 }, { 570 }, { 570 }, { 500 }, { 832 }, { 667 }, { 667 }, { 667 }, { 722 }, { 667 }, { 667 }, { 722 }, { 778 }, { 389 }, { 500 }, { 667 }, { 611 }, { 889 }, { 722 }, { 722 }, { 611 }, { 722 }, { 667 }, { 556 }, { 611 }, { 722 }, { 667 }, { 889 }, { 667 }, { 611 }, { 611 }, { 333 }, { 278 }, { 333 }, { 570 }, { 500 }, { 333 }, { 500 }, { 500 }, { 444 }, { 500 }, { 444 }, { 333 }, { 500 }, { 556 }, { 278 }, { 278 }, { 500 }, { 278 }, { 778 }, { 556 }, { 500 }, { 500 }, { 500 }, { 389 }, { 389 }, { 278 }, { 556 }, { 444 }, { 667 }, { 500 }, { 444 }, { 389 }, { 348 }, { 220 }, { 348 }, { 570 }, } }, }; void * pdf_alloc(const struct manoutput *outopts) { return pspdf_alloc(outopts, TERMTYPE_PDF); } void * ps_alloc(const struct manoutput *outopts) { return pspdf_alloc(outopts, TERMTYPE_PS); } static struct termp * pspdf_alloc(const struct manoutput *outopts, enum termtype type) { struct termp *p; unsigned int pagex, pagey; size_t marginx, marginy, lineheight; const char *pp; p = mandoc_calloc(1, sizeof(*p)); p->tcol = p->tcols = mandoc_calloc(1, sizeof(*p->tcol)); p->maxtcol = 1; p->type = type; p->enc = TERMENC_ASCII; p->fontq = mandoc_reallocarray(NULL, (p->fontsz = 8), sizeof(*p->fontq)); p->fontq[0] = p->fontl = TERMFONT_NONE; p->ps = mandoc_calloc(1, sizeof(*p->ps)); p->advance = ps_advance; p->begin = ps_begin; p->end = ps_end; p->endline = ps_endline; p->hspan = ps_hspan; p->letter = ps_letter; p->setwidth = ps_setwidth; p->getwidth = ps_getwidth; /* Default to US letter (millimetres). */ p->ps->medianame = "Letter"; pagex = 216; pagey = 279; /* * The ISO-269 paper sizes can be calculated automatically, but * it would require bringing in -lm for pow() and I'd rather not * do that. So just do it the easy way for now. Since this * only happens once, I'm not terribly concerned. */ pp = outopts->paper; if (pp != NULL && strcasecmp(pp, "letter") != 0) { if (strcasecmp(pp, "a3") == 0) { p->ps->medianame = "A3"; pagex = 297; pagey = 420; } else if (strcasecmp(pp, "a4") == 0) { p->ps->medianame = "A4"; pagex = 210; pagey = 297; } else if (strcasecmp(pp, "a5") == 0) { p->ps->medianame = "A5"; pagex = 148; pagey = 210; } else if (strcasecmp(pp, "legal") == 0) { p->ps->medianame = "Legal"; pagex = 216; pagey = 356; } else if (sscanf(pp, "%ux%u", &pagex, &pagey) == 2) p->ps->medianame = "CustomSize"; else warnx("%s: Unknown paper", pp); } /* * This MUST be defined before any PNT2AFM or AFM2PNT * calculations occur. */ p->ps->scale = 11; /* Remember millimetres -> AFM units. */ pagex = PNT2AFM(p, ((double)pagex * 72.0 / 25.4)); pagey = PNT2AFM(p, ((double)pagey * 72.0 / 25.4)); /* Margins are 1/9 the page x and y. */ marginx = (size_t)((double)pagex / 9.0); marginy = (size_t)((double)pagey / 9.0); /* Line-height is 1.4em. */ lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4)); p->ps->width = p->ps->lastwidth = (size_t)pagex; p->ps->height = (size_t)pagey; p->ps->header = pagey - (marginy / 2) - (lineheight / 2); p->ps->top = pagey - marginy; p->ps->footer = (marginy / 2) - (lineheight / 2); p->ps->bottom = marginy; p->ps->left = marginx; p->ps->lineheight = lineheight; p->defrmargin = pagex - (marginx * 2); return p; } static void ps_setwidth(struct termp *p, int iop, int width) { size_t lastwidth; lastwidth = p->ps->width; if (iop > 0) p->ps->width += width; else if (iop == 0) p->ps->width = width ? (size_t)width : p->ps->lastwidth; else if (p->ps->width > (size_t)width) p->ps->width -= width; else p->ps->width = 0; p->ps->lastwidth = lastwidth; } void pspdf_free(void *arg) { struct termp *p; p = (struct termp *)arg; free(p->ps->psmarg); free(p->ps->pdfobjs); free(p->ps); term_free(p); } static void ps_printf(struct termp *p, const char *fmt, ...) { va_list ap; int pos, len; va_start(ap, fmt); /* * If we're running in regular mode, then pipe directly into * vprintf(). If we're processing margins, then push the data * into our growable margin buffer. */ if ( ! (PS_MARGINS & p->ps->flags)) { len = vprintf(fmt, ap); va_end(ap); p->ps->pdfbytes += len < 0 ? 0 : (size_t)len; return; } /* * XXX: I assume that the in-margin print won't exceed * PS_BUFSLOP (128 bytes), which is reasonable but still an * assumption that will cause pukeage if it's not the case. */ ps_growbuf(p, PS_BUFSLOP); pos = (int)p->ps->psmargcur; vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap); va_end(ap); p->ps->psmargcur = strlen(p->ps->psmarg); } static void ps_putchar(struct termp *p, char c) { int pos; /* See ps_printf(). */ if ( ! (PS_MARGINS & p->ps->flags)) { putchar(c); p->ps->pdfbytes++; return; } ps_growbuf(p, 2); pos = (int)p->ps->psmargcur++; p->ps->psmarg[pos++] = c; p->ps->psmarg[pos] = '\0'; } static void pdf_obj(struct termp *p, size_t obj) { assert(obj > 0); if ((obj - 1) >= p->ps->pdfobjsz) { p->ps->pdfobjsz = obj + 128; p->ps->pdfobjs = mandoc_reallocarray(p->ps->pdfobjs, p->ps->pdfobjsz, sizeof(size_t)); } p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes; ps_printf(p, "%zu 0 obj\n", obj); } static void ps_closepage(struct termp *p) { int i; size_t len, base; /* * Close out a page that we've already flushed to output. In * PostScript, we simply note that the page must be shown. In * PDF, we must now create the Length, Resource, and Page node * for the page contents. */ assert(p->ps->psmarg && p->ps->psmarg[0]); ps_printf(p, "%s", p->ps->psmarg); if (TERMTYPE_PS != p->type) { len = p->ps->pdfbytes - p->ps->pdflastpg; base = p->ps->pages * 4 + p->ps->pdfbody; ps_printf(p, "endstream\nendobj\n"); /* Length of content. */ pdf_obj(p, base + 1); ps_printf(p, "%zu\nendobj\n", len); /* Resource for content. */ pdf_obj(p, base + 2); ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n"); ps_printf(p, "/Font <<\n"); for (i = 0; i < (int)TERMFONT__MAX; i++) ps_printf(p, "/F%d %d 0 R\n", i, 3 + i); ps_printf(p, ">>\n>>\nendobj\n"); /* Page node. */ pdf_obj(p, base + 3); ps_printf(p, "<<\n"); ps_printf(p, "/Type /Page\n"); ps_printf(p, "/Parent 2 0 R\n"); ps_printf(p, "/Resources %zu 0 R\n", base + 2); ps_printf(p, "/Contents %zu 0 R\n", base); ps_printf(p, ">>\nendobj\n"); } else ps_printf(p, "showpage\n"); p->ps->pages++; p->ps->psrow = p->ps->top; assert( ! (PS_NEWPAGE & p->ps->flags)); p->ps->flags |= PS_NEWPAGE; } static void ps_end(struct termp *p) { size_t i, xref, base; ps_plast(p); ps_pclose(p); /* * At the end of the file, do one last showpage. This is the * same behaviour as groff(1) and works for multiple pages as * well as just one. */ if ( ! (PS_NEWPAGE & p->ps->flags)) { assert(0 == p->ps->flags); assert('\0' == p->ps->last); ps_closepage(p); } if (TERMTYPE_PS == p->type) { ps_printf(p, "%%%%Trailer\n"); ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages); ps_printf(p, "%%%%EOF\n"); return; } pdf_obj(p, 2); ps_printf(p, "<<\n/Type /Pages\n"); ps_printf(p, "/MediaBox [0 0 %zu %zu]\n", (size_t)AFM2PNT(p, p->ps->width), (size_t)AFM2PNT(p, p->ps->height)); ps_printf(p, "/Count %zu\n", p->ps->pages); ps_printf(p, "/Kids ["); for (i = 0; i < p->ps->pages; i++) ps_printf(p, " %zu 0 R", i * 4 + p->ps->pdfbody + 3); base = (p->ps->pages - 1) * 4 + p->ps->pdfbody + 4; ps_printf(p, "]\n>>\nendobj\n"); pdf_obj(p, base); ps_printf(p, "<<\n"); ps_printf(p, "/Type /Catalog\n"); ps_printf(p, "/Pages 2 0 R\n"); ps_printf(p, ">>\nendobj\n"); xref = p->ps->pdfbytes; ps_printf(p, "xref\n"); ps_printf(p, "0 %zu\n", base + 1); ps_printf(p, "0000000000 65535 f \n"); for (i = 0; i < base; i++) ps_printf(p, "%.10zu 00000 n \n", p->ps->pdfobjs[(int)i]); ps_printf(p, "trailer\n"); ps_printf(p, "<<\n"); ps_printf(p, "/Size %zu\n", base + 1); ps_printf(p, "/Root %zu 0 R\n", base); ps_printf(p, "/Info 1 0 R\n"); ps_printf(p, ">>\n"); ps_printf(p, "startxref\n"); ps_printf(p, "%zu\n", xref); ps_printf(p, "%%%%EOF\n"); } static void ps_begin(struct termp *p) { size_t width, height; int i; /* * Print margins into margin buffer. Nothing gets output to the * screen yet, so we don't need to initialise the primary state. */ if (p->ps->psmarg) { assert(p->ps->psmargsz); p->ps->psmarg[0] = '\0'; } /*p->ps->pdfbytes = 0;*/ p->ps->psmargcur = 0; p->ps->flags = PS_MARGINS; p->ps->pscol = p->ps->left; p->ps->psrow = p->ps->header; p->ps->lastrow = 0; /* impossible row */ ps_setfont(p, TERMFONT_NONE); (*p->headf)(p, p->argf); (*p->endline)(p); p->ps->pscol = p->ps->left; p->ps->psrow = p->ps->footer; (*p->footf)(p, p->argf); (*p->endline)(p); p->ps->flags &= ~PS_MARGINS; assert(0 == p->ps->flags); assert(p->ps->psmarg); assert('\0' != p->ps->psmarg[0]); /* * Print header and initialise page state. Following this, * stuff gets printed to the screen, so make sure we're sane. */ if (TERMTYPE_PS == p->type) { width = AFM2PNT(p, p->ps->width); height = AFM2PNT(p, p->ps->height); ps_printf(p, "%%!PS-Adobe-3.0\n"); ps_printf(p, "%%%%DocumentData: Clean7Bit\n"); ps_printf(p, "%%%%Orientation: Portrait\n"); ps_printf(p, "%%%%Pages: (atend)\n"); ps_printf(p, "%%%%PageOrder: Ascend\n"); ps_printf(p, "%%%%DocumentMedia: man-%s %zu %zu 0 () ()\n", p->ps->medianame, width, height); ps_printf(p, "%%%%DocumentNeededResources: font"); for (i = 0; i < (int)TERMFONT__MAX; i++) ps_printf(p, " %s", fonts[i].name); ps_printf(p, "\n%%%%DocumentSuppliedResources: " "procset MandocProcs 1.0 0\n"); ps_printf(p, "%%%%EndComments\n"); ps_printf(p, "%%%%BeginProlog\n"); ps_printf(p, "%%%%BeginResource: procset MandocProcs " "10170 10170\n"); /* The font size is effectively hard-coded for now. */ ps_printf(p, "/fs %zu def\n", p->ps->scale); for (i = 0; i < (int)TERMFONT__MAX; i++) ps_printf(p, "/f%d { /%s fs selectfont } def\n", i, fonts[i].name); ps_printf(p, "/s { 3 1 roll moveto show } bind def\n"); ps_printf(p, "/c { exch currentpoint exch pop " "moveto show } bind def\n"); ps_printf(p, "%%%%EndResource\n"); ps_printf(p, "%%%%EndProlog\n"); ps_printf(p, "%%%%BeginSetup\n"); ps_printf(p, "%%%%BeginFeature: *PageSize %s\n", p->ps->medianame); ps_printf(p, "<>setpagedevice\n", width, height); ps_printf(p, "%%%%EndFeature\n"); ps_printf(p, "%%%%EndSetup\n"); } else { ps_printf(p, "%%PDF-1.1\n"); pdf_obj(p, 1); ps_printf(p, "<<\n"); ps_printf(p, ">>\n"); ps_printf(p, "endobj\n"); for (i = 0; i < (int)TERMFONT__MAX; i++) { pdf_obj(p, (size_t)i + 3); ps_printf(p, "<<\n"); ps_printf(p, "/Type /Font\n"); ps_printf(p, "/Subtype /Type1\n"); ps_printf(p, "/Name /F%d\n", i); ps_printf(p, "/BaseFont /%s\n", fonts[i].name); ps_printf(p, ">>\nendobj\n"); } } p->ps->pdfbody = (size_t)TERMFONT__MAX + 3; p->ps->pscol = p->ps->left; p->ps->psrow = p->ps->top; p->ps->flags |= PS_NEWPAGE; ps_setfont(p, TERMFONT_NONE); } static void ps_pletter(struct termp *p, int c) { int f; /* * If we haven't opened a page context, then output that we're * in a new page and make sure the font is correctly set. */ if (PS_NEWPAGE & p->ps->flags) { if (TERMTYPE_PS == p->type) { ps_printf(p, "%%%%Page: %zu %zu\n", p->ps->pages + 1, p->ps->pages + 1); ps_printf(p, "f%d\n", (int)p->ps->lastf); } else { pdf_obj(p, p->ps->pdfbody + p->ps->pages * 4); ps_printf(p, "<<\n"); ps_printf(p, "/Length %zu 0 R\n", p->ps->pdfbody + 1 + p->ps->pages * 4); ps_printf(p, ">>\nstream\n"); } p->ps->pdflastpg = p->ps->pdfbytes; p->ps->flags &= ~PS_NEWPAGE; } /* * If we're not in a PostScript "word" context, then open one * now at the current cursor. */ if ( ! (PS_INLINE & p->ps->flags)) { if (TERMTYPE_PS != p->type) { ps_printf(p, "BT\n/F%d %zu Tf\n", (int)p->ps->lastf, p->ps->scale); ps_printf(p, "%.3f %.3f Td\n(", AFM2PNT(p, p->ps->pscol), AFM2PNT(p, p->ps->psrow)); } else { ps_printf(p, "%.3f", AFM2PNT(p, p->ps->pscol)); if (p->ps->psrow != p->ps->lastrow) ps_printf(p, " %.3f", AFM2PNT(p, p->ps->psrow)); ps_printf(p, "("); } p->ps->flags |= PS_INLINE; } assert( ! (PS_NEWPAGE & p->ps->flags)); /* * We need to escape these characters as per the PostScript * specification. We would also escape non-graphable characters * (like tabs), but none of them would get to this point and * it's superfluous to abort() on them. */ switch (c) { case '(': case ')': case '\\': ps_putchar(p, '\\'); break; default: break; } /* Write the character and adjust where we are on the page. */ f = (int)p->ps->lastf; if (c <= 32 || c - 32 >= MAXCHAR) c = 32; ps_putchar(p, (char)c); c -= 32; p->ps->pscol += (size_t)fonts[f].gly[c].wx; } static void ps_pclose(struct termp *p) { /* * Spit out that we're exiting a word context (this is a * "partial close" because we don't check the last-char buffer * or anything). */ if ( ! (PS_INLINE & p->ps->flags)) return; if (TERMTYPE_PS != p->type) ps_printf(p, ") Tj\nET\n"); else if (p->ps->psrow == p->ps->lastrow) ps_printf(p, ")c\n"); else { ps_printf(p, ")s\n"); p->ps->lastrow = p->ps->psrow; } p->ps->flags &= ~PS_INLINE; } /* If we have a `last' char that wasn't printed yet, print it now. */ static void ps_plast(struct termp *p) { size_t wx; if (p->ps->last == '\0') return; /* Check the font mode; open a new scope if it doesn't match. */ if (p->ps->nextf != p->ps->lastf) { ps_pclose(p); ps_setfont(p, p->ps->nextf); } p->ps->nextf = TERMFONT_NONE; /* * For an overstrike, if a previous character * was wider, advance to center the new one. */ if (p->ps->pscolnext) { wx = fonts[p->ps->lastf].gly[(int)p->ps->last-32].wx; if (p->ps->pscol + wx < p->ps->pscolnext) p->ps->pscol = (p->ps->pscol + p->ps->pscolnext - wx) / 2; } ps_pletter(p, p->ps->last); p->ps->last = '\0'; /* * For an overstrike, if a previous character * was wider, advance to the end of the old one. */ if (p->ps->pscol < p->ps->pscolnext) { ps_pclose(p); p->ps->pscol = p->ps->pscolnext; } } static void ps_letter(struct termp *p, int arg) { size_t savecol; char c; c = arg >= 128 || arg <= 0 ? '?' : arg; /* * When receiving a backspace, merely flag it. * We don't know yet whether it is * a font instruction or an overstrike. */ if (c == '\b') { assert(p->ps->last != '\0'); assert( ! (p->ps->flags & PS_BACKSP)); p->ps->flags |= PS_BACKSP; return; } /* * Decode font instructions. */ if (p->ps->flags & PS_BACKSP) { if (p->ps->last == '_') { switch (p->ps->nextf) { case TERMFONT_BI: break; case TERMFONT_BOLD: p->ps->nextf = TERMFONT_BI; break; default: p->ps->nextf = TERMFONT_UNDER; } p->ps->last = c; p->ps->flags &= ~PS_BACKSP; return; } if (p->ps->last == c) { switch (p->ps->nextf) { case TERMFONT_BI: break; case TERMFONT_UNDER: p->ps->nextf = TERMFONT_BI; break; default: p->ps->nextf = TERMFONT_BOLD; } p->ps->flags &= ~PS_BACKSP; return; } /* * This is not a font instruction, but rather * the next character. Prepare for overstrike. */ savecol = p->ps->pscol; } else savecol = SIZE_MAX; /* * We found the next character, so the font instructions * for the previous one are complete. * Use them and print it. */ ps_plast(p); /* * Do not print the current character yet because font * instructions might follow; only remember the character. * It will get printed later from ps_plast(). */ p->ps->last = c; /* * For an overstrike, back up to the previous position. * If the previous character is wider than any it overstrikes, * remember the current position, because it might also be * wider than all that will overstrike it. */ if (savecol != SIZE_MAX) { if (p->ps->pscolnext < p->ps->pscol) p->ps->pscolnext = p->ps->pscol; ps_pclose(p); p->ps->pscol = savecol; p->ps->flags &= ~PS_BACKSP; } else p->ps->pscolnext = 0; } static void ps_advance(struct termp *p, size_t len) { /* * Advance some spaces. This can probably be made smarter, * i.e., to have multiple space-separated words in the same * scope, but this is easier: just close out the current scope * and readjust our column settings. */ ps_plast(p); ps_pclose(p); p->ps->pscol += len; p->viscol += len; } static void ps_endline(struct termp *p) { /* Close out any scopes we have open: we're at eoln. */ ps_plast(p); ps_pclose(p); + p->viscol = 0; + p->minbl = 0; /* * If we're in the margin, don't try to recalculate our current * row. XXX: if the column tries to be fancy with multiple * lines, we'll do nasty stuff. */ if (PS_MARGINS & p->ps->flags) return; /* Left-justify. */ p->ps->pscol = p->ps->left; - p->viscol = 0; - p->minbl = 0; /* If we haven't printed anything, return. */ if (PS_NEWPAGE & p->ps->flags) return; /* * Put us down a line. If we're at the page bottom, spit out a * showpage and restart our row. */ if (p->ps->psrow >= p->ps->lineheight + p->ps->bottom) { p->ps->psrow -= p->ps->lineheight; return; } ps_closepage(p); if ((int)p->tcol->offset > p->ti) p->tcol->offset -= p->ti; else p->tcol->offset = 0; p->ti = 0; } static void ps_setfont(struct termp *p, enum termfont f) { assert(f < TERMFONT__MAX); p->ps->lastf = f; /* * If we're still at the top of the page, let the font-setting * be delayed until we actually have stuff to print. */ if (PS_NEWPAGE & p->ps->flags) return; if (TERMTYPE_PS == p->type) ps_printf(p, "f%d\n", (int)f); else ps_printf(p, "/F%d %zu Tf\n", (int)f, p->ps->scale); } static size_t ps_getwidth(const struct termp *p, int c) { if (c <= 32 || c - 32 >= MAXCHAR) c = 0; else c -= 32; return (size_t)fonts[(int)TERMFONT_NONE].gly[c].wx; } static int ps_hspan(const struct termp *p, const struct roffsu *su) { double r; /* * All of these measurements are derived by converting from the * native measurement to AFM units. */ switch (su->unit) { case SCALE_BU: /* * Traditionally, the default unit is fixed to the * output media. So this would refer to the point. In * mandoc(1), however, we stick to the default terminal * scaling unit so that output is the same regardless * the media. */ r = PNT2AFM(p, su->scale * 72.0 / 10.0); break; case SCALE_CM: r = PNT2AFM(p, su->scale * 72.0 / 2.54); break; case SCALE_EM: r = su->scale * fonts[(int)TERMFONT_NONE].gly[109 - 32].wx; break; case SCALE_EN: r = su->scale * fonts[(int)TERMFONT_NONE].gly[110 - 32].wx; break; case SCALE_IN: r = PNT2AFM(p, su->scale * 72.0); break; case SCALE_MM: r = su->scale * fonts[(int)TERMFONT_NONE].gly[109 - 32].wx / 100.0; break; case SCALE_PC: r = PNT2AFM(p, su->scale * 12.0); break; case SCALE_PT: r = PNT2AFM(p, su->scale * 1.0); break; case SCALE_VS: r = su->scale * p->ps->lineheight; break; default: r = su->scale; break; } return r; } static void ps_growbuf(struct termp *p, size_t sz) { if (p->ps->psmargcur + sz <= p->ps->psmargsz) return; if (sz < PS_BUFSLOP) sz = PS_BUFSLOP; p->ps->psmargsz += sz; p->ps->psmarg = mandoc_realloc(p->ps->psmarg, p->ps->psmargsz); }