From 36342e812a6f930924c37be1fdef501e4908bc27 Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Sun, 14 Apr 2013 10:03:58 +0200 Subject: [PATCH] Import mdocml-1.12.1 --- contrib/mdocml/Makefile | 351 ++- contrib/mdocml/TODO | 372 +++ contrib/mdocml/apropos.1 | 328 +++ contrib/mdocml/apropos.c | 239 ++ contrib/mdocml/apropos_db.c | 876 ++++++ contrib/mdocml/apropos_db.h | 73 + contrib/mdocml/arch.in | 57 +- contrib/mdocml/att.in | 3 +- contrib/mdocml/catman.8 | 111 + contrib/mdocml/catman.c | 511 ++++ contrib/mdocml/cgi.c | 1203 ++++++++ contrib/mdocml/chars.c | 94 +- contrib/mdocml/chars.in | 19 +- contrib/mdocml/compat_fgetln.c | 93 + contrib/mdocml/compat_getsubopt.c | 104 + contrib/mdocml/{compat.c => compat_strlcat.c} | 50 +- contrib/mdocml/{compat.c => compat_strlcpy.c} | 58 +- contrib/mdocml/config.h.post | 17 + contrib/mdocml/config.h.pre | 2 + contrib/mdocml/demandoc.1 | 109 + contrib/mdocml/demandoc.c | 257 ++ contrib/mdocml/eqn.7 | 250 +- contrib/mdocml/eqn.c | 906 +++++- contrib/mdocml/eqn_html.c | 81 + contrib/mdocml/eqn_term.c | 76 + contrib/mdocml/example.style.css | 242 +- contrib/mdocml/html.c | 15 +- contrib/mdocml/html.h | 13 +- contrib/mdocml/index.css | 9 +- contrib/mdocml/index.sgml | 295 +- contrib/mdocml/lib.in | 12 +- contrib/mdocml/libman.h | 7 +- contrib/mdocml/libmandoc.h | 52 +- contrib/mdocml/libmdoc.h | 7 +- contrib/mdocml/libroff.h | 28 +- contrib/mdocml/main.c | 15 +- contrib/mdocml/main.h | 7 +- contrib/mdocml/makewhatis.1 | 152 - contrib/mdocml/makewhatis.c | 920 ------ contrib/mdocml/man-cgi.css | 13 + contrib/mdocml/man.7 | 642 ++--- contrib/mdocml/man.c | 63 +- contrib/mdocml/man.cgi.7 | 123 + contrib/mdocml/man.h | 4 +- contrib/mdocml/man_hash.c | 12 +- contrib/mdocml/man_html.c | 309 +-- contrib/mdocml/man_macro.c | 43 +- contrib/mdocml/man_term.c | 302 +- contrib/mdocml/man_validate.c | 152 +- contrib/mdocml/mandoc-db.1 | 132 - contrib/mdocml/mandoc-db.c | 669 ----- contrib/mdocml/mandoc.1 | 169 +- contrib/mdocml/mandoc.3 | 120 +- contrib/mdocml/mandoc.c | 107 +- contrib/mdocml/mandoc.h | 134 +- contrib/mdocml/mandoc_char.7 | 260 +- contrib/mdocml/mandocdb.8 | 293 ++ contrib/mdocml/mandocdb.c | 1909 +++++++++++++ contrib/mdocml/mandocdb.h | 62 + contrib/mdocml/manpath.c | 225 ++ contrib/mdocml/{msec.c => manpath.h} | 38 +- contrib/mdocml/mdoc.7 | 2464 +++++++++-------- contrib/mdocml/mdoc.c | 85 +- contrib/mdocml/mdoc_argv.c | 424 ++- contrib/mdocml/mdoc_hash.c | 16 +- contrib/mdocml/mdoc_html.c | 161 +- contrib/mdocml/mdoc_macro.c | 43 +- contrib/mdocml/mdoc_man.c | 637 +++++ contrib/mdocml/mdoc_term.c | 95 +- contrib/mdocml/mdoc_validate.c | 160 +- contrib/mdocml/msec.c | 7 +- contrib/mdocml/out.c | 97 +- contrib/mdocml/out.h | 3 +- contrib/mdocml/preconv.1 | 21 +- contrib/mdocml/preconv.c | 10 +- contrib/mdocml/predefs.in | 8 +- contrib/mdocml/read.c | 181 +- contrib/mdocml/roff.7 | 393 ++- contrib/mdocml/roff.c | 563 +++- contrib/mdocml/st.in | 6 +- contrib/mdocml/style.css | 8 +- contrib/mdocml/tbl.7 | 26 +- contrib/mdocml/tbl.c | 8 +- contrib/mdocml/tbl_html.c | 4 +- contrib/mdocml/tbl_layout.c | 14 +- contrib/mdocml/tbl_opts.c | 4 +- contrib/mdocml/tbl_term.c | 199 +- contrib/mdocml/term.c | 74 +- contrib/mdocml/term.h | 8 +- contrib/mdocml/term_ascii.c | 23 +- contrib/mdocml/term_ps.c | 18 +- contrib/mdocml/test-fgetln.c | 11 + contrib/mdocml/test-getsubopt.c | 12 + contrib/mdocml/test-strptime.c | 13 + contrib/mdocml/tree.c | 91 +- contrib/mdocml/whatis.1 | 190 ++ 96 files changed, 14257 insertions(+), 5615 deletions(-) create mode 100644 contrib/mdocml/TODO create mode 100644 contrib/mdocml/apropos.1 create mode 100644 contrib/mdocml/apropos.c create mode 100644 contrib/mdocml/apropos_db.c create mode 100644 contrib/mdocml/apropos_db.h create mode 100644 contrib/mdocml/catman.8 create mode 100644 contrib/mdocml/catman.c create mode 100644 contrib/mdocml/cgi.c create mode 100644 contrib/mdocml/compat_fgetln.c create mode 100644 contrib/mdocml/compat_getsubopt.c copy contrib/mdocml/{compat.c => compat_strlcat.c} (70%) rename contrib/mdocml/{compat.c => compat_strlcpy.c} (62%) create mode 100644 contrib/mdocml/demandoc.1 create mode 100644 contrib/mdocml/demandoc.c create mode 100644 contrib/mdocml/eqn_html.c create mode 100644 contrib/mdocml/eqn_term.c delete mode 100644 contrib/mdocml/makewhatis.1 delete mode 100644 contrib/mdocml/makewhatis.c create mode 100644 contrib/mdocml/man-cgi.css create mode 100644 contrib/mdocml/man.cgi.7 delete mode 100644 contrib/mdocml/mandoc-db.1 delete mode 100644 contrib/mdocml/mandoc-db.c create mode 100644 contrib/mdocml/mandocdb.8 create mode 100644 contrib/mdocml/mandocdb.c create mode 100644 contrib/mdocml/mandocdb.h create mode 100644 contrib/mdocml/manpath.c copy contrib/mdocml/{msec.c => manpath.h} (56%) create mode 100644 contrib/mdocml/mdoc_man.c create mode 100644 contrib/mdocml/test-fgetln.c create mode 100644 contrib/mdocml/test-getsubopt.c create mode 100644 contrib/mdocml/test-strptime.c create mode 100644 contrib/mdocml/whatis.1 diff --git a/contrib/mdocml/Makefile b/contrib/mdocml/Makefile index c535f105b9..304237b478 100644 --- a/contrib/mdocml/Makefile +++ b/contrib/mdocml/Makefile @@ -1,26 +1,45 @@ .PHONY: clean install installwww .SUFFIXES: .sgml .html .md5 .h .h.html -.SUFFIXES: .1 .3 .7 -.SUFFIXES: .1.txt .3.txt .7.txt -.SUFFIXES: .1.pdf .3.pdf .7.pdf -.SUFFIXES: .1.ps .3.ps .7.ps -.SUFFIXES: .1.html .3.html .7.html -.SUFFIXES: .1.xhtml .3.xhtml .7.xhtml +.SUFFIXES: .1 .3 .7 .8 +.SUFFIXES: .1.txt .3.txt .7.txt .8.txt +.SUFFIXES: .1.pdf .3.pdf .7.pdf .8.pdf +.SUFFIXES: .1.ps .3.ps .7.ps .8.ps +.SUFFIXES: .1.html .3.html .7.html .8.html +.SUFFIXES: .1.xhtml .3.xhtml .7.xhtml .8.xhtml # Specify this if you want to hard-code the operating system to appear # in the lower-left hand corner of -mdoc manuals. +# # CFLAGS += -DOSNAME="\"OpenBSD 4.5\"" -VERSION = 1.11.3 -VDATE = 26 May 2011 +VERSION = 1.12.1 +VDATE = 23 March 2012 + # IFF your system supports multi-byte functions (setlocale(), wcwidth(), # putwchar()) AND has __STDC_ISO_10646__ (that is, wchar_t is simply a # UCS-4 value) should you define USE_WCHAR. If you define it and your # system DOESN'T support this, -Tlocale will produce garbage. # If you don't define it, -Tlocale is a synonym for -Tacsii. -CFLAGS += -g -DUSE_WCHAR -DHAVE_CONFIG_H -DVERSION="\"$(VERSION)\"" +# +CFLAGS += -DUSE_WCHAR + +# If your system has manpath(1), uncomment this. This is most any +# system that's not OpenBSD or NetBSD. If uncommented, apropos(1), +# mandocdb(8), and man.cgi will popen(3) manpath(1) to get the MANPATH +# variable. +#CFLAGS += -DUSE_MANPATH + +# If your system supports static binaries only, uncomment this. This +# appears only to be BSD UNIX systems (Mac OS X has no support and Linux +# requires -pthreads for static libdb). +STATIC = -static + +CFLAGS += -g -DHAVE_CONFIG_H -DVERSION="\"$(VERSION)\"" CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings PREFIX = /usr/local +WWWPREFIX = /var/www +HTDOCDIR = $(WWWPREFIX)/htdocs +CGIBINDIR = $(WWWPREFIX)/cgi-bin BINDIR = $(PREFIX)/bin INCLUDEDIR = $(PREFIX)/include/mandoc LIBDIR = $(PREFIX)/lib/mandoc @@ -30,22 +49,47 @@ INSTALL = install INSTALL_PROGRAM = $(INSTALL) -m 0755 INSTALL_DATA = $(INSTALL) -m 0444 INSTALL_LIB = $(INSTALL) -m 0644 +INSTALL_SOURCE = $(INSTALL) -m 0644 INSTALL_MAN = $(INSTALL_DATA) -all: mandoc preconv +# Non-BSD systems (Linux, etc.) need -ldb to compile mandocdb and +# apropos. +# However, if you don't have -ldb at all (or it's not native), then +# comment out apropos and mandocdb. +# +#DBLIB = -ldb +DBBIN = apropos mandocdb man.cgi catman whatis +DBLN = llib-lapropos.ln llib-lmandocdb.ln llib-lman.cgi.ln llib-lcatman.ln + +all: mandoc preconv demandoc $(DBBIN) SRCS = Makefile \ + TODO \ + apropos.1 \ + apropos.c \ + apropos_db.c \ + apropos_db.h \ arch.c \ arch.in \ att.c \ att.in \ + catman.8 \ + catman.c \ + cgi.c \ chars.c \ chars.in \ - compat.c \ + compat_fgetln.c \ + compat_getsubopt.c \ + compat_strlcat.c \ + compat_strlcpy.c \ config.h.post \ config.h.pre \ + demandoc.1 \ + demandoc.c \ eqn.7 \ eqn.c \ + eqn_html.c \ + eqn_term.c \ example.style.css \ external.png \ html.c \ @@ -60,9 +104,11 @@ SRCS = Makefile \ libroff.h \ main.c \ main.h \ - man.h \ man.7 \ man.c \ + man.cgi.7 \ + man-cgi.css \ + man.h \ man_hash.c \ man_html.c \ man_macro.c \ @@ -72,16 +118,20 @@ SRCS = Makefile \ mandoc.3 \ mandoc.c \ mandoc.h \ - makewhatis.1 \ - makewhatis.c \ mandoc_char.7 \ - mdoc.h \ + mandocdb.8 \ + mandocdb.c \ + mandocdb.h \ + manpath.c \ + manpath.h \ mdoc.7 \ mdoc.c \ + mdoc.h \ mdoc_argv.c \ mdoc_hash.c \ mdoc_html.c \ mdoc_macro.c \ + mdoc_man.c \ mdoc_term.c \ mdoc_validate.c \ msec.c \ @@ -108,12 +158,16 @@ SRCS = Makefile \ term.h \ term_ascii.c \ term_ps.c \ + test-fgetln.c \ + test-getsubopt.c \ test-mmap.c \ test-strlcat.c \ test-strlcpy.c \ + test-strptime.c \ tree.c \ vol.c \ - vol.in + vol.in \ + whatis.1 LIBMAN_OBJS = man.o \ man_hash.o \ @@ -132,7 +186,6 @@ LIBMDOC_OBJS = arch.o \ mdoc_hash.o \ mdoc_macro.o \ mdoc_validate.o \ - msec.o \ st.o \ vol.o LIBMDOC_LNS = arch.ln \ @@ -143,7 +196,6 @@ LIBMDOC_LNS = arch.ln \ mdoc_hash.ln \ mdoc_macro.ln \ mdoc_validate.ln \ - msec.ln \ st.ln \ vol.ln @@ -165,14 +217,25 @@ LIBMANDOC_OBJS = $(LIBMAN_OBJS) \ $(LIBROFF_OBJS) \ chars.o \ mandoc.o \ + msec.o \ read.o LIBMANDOC_LNS = $(LIBMAN_LNS) \ $(LIBMDOC_LNS) \ $(LIBROFF_LNS) \ chars.ln \ mandoc.ln \ + msec.ln \ read.ln +COMPAT_OBJS = compat_fgetln.o \ + compat_getsubopt.o \ + compat_strlcat.o \ + compat_strlcpy.o +COMPAT_LNS = compat_fgetln.ln \ + compat_getsubopt.ln \ + compat_strlcat.ln \ + compat_strlcpy.ln + arch.o arch.ln: arch.in att.o att.ln: att.in chars.o chars.ln: chars.in @@ -187,22 +250,31 @@ $(LIBMDOC_OBJS) $(LIBMDOC_LNS): libmdoc.h $(LIBROFF_OBJS) $(LIBROFF_LNS): libroff.h $(LIBMANDOC_OBJS) $(LIBMANDOC_LNS): mandoc.h mdoc.h man.h libmandoc.h config.h -MANDOC_HTML_OBJS = html.o \ +$(COMPAT_OBJS) $(COMPAT_LNS): config.h + +MANDOC_HTML_OBJS = eqn_html.o \ + html.o \ man_html.o \ mdoc_html.o \ tbl_html.o -MANDOC_HTML_LNS = html.ln \ +MANDOC_HTML_LNS = eqn_html.ln \ + html.ln \ man_html.ln \ mdoc_html.ln \ tbl_html.ln -MANDOC_TERM_OBJS = man_term.o \ +MANDOC_MAN_OBJS = mdoc_man.o +MANDOC_MAN_LNS = mdoc_man.ln + +MANDOC_TERM_OBJS = eqn_term.o \ + man_term.o \ mdoc_term.o \ term.o \ term_ascii.o \ term_ps.o \ tbl_term.o -MANDOC_TERM_LNS = man_term.ln \ +MANDOC_TERM_LNS = eqn_term.ln \ + man_term.ln \ mdoc_term.ln \ term.ln \ term_ascii.ln \ @@ -210,11 +282,13 @@ MANDOC_TERM_LNS = man_term.ln \ tbl_term.ln MANDOC_OBJS = $(MANDOC_HTML_OBJS) \ + $(MANDOC_MAN_OBJS) \ $(MANDOC_TERM_OBJS) \ main.o \ out.o \ tree.o MANDOC_LNS = $(MANDOC_HTML_LNS) \ + $(MANDOC_MAN_LNS) \ $(MANDOC_TERM_LNS) \ main.ln \ out.ln \ @@ -224,28 +298,76 @@ $(MANDOC_HTML_OBJS) $(MANDOC_HTML_LNS): html.h $(MANDOC_TERM_OBJS) $(MANDOC_TERM_LNS): term.h $(MANDOC_OBJS) $(MANDOC_LNS): main.h mandoc.h mdoc.h man.h config.h out.h -compat.o compat.ln: config.h +MANDOCDB_OBJS = mandocdb.o manpath.o +MANDOCDB_LNS = mandocdb.ln manpath.ln -MAKEWHATIS_OBJS = makewhatis.o -MAKEWHATIS_LNS = makewhatis.ln - -$(MAKEWHATIS_OBJS) $(MAKEWHATIS_LNS): mandoc.h mdoc.h man.h config.h +$(MANDOCDB_OBJS) $(MANDOCDB_LNS): mandocdb.h mandoc.h mdoc.h man.h config.h manpath.h PRECONV_OBJS = preconv.o PRECONV_LNS = preconv.ln $(PRECONV_OBJS) $(PRECONV_LNS): config.h -INDEX_MANS = makewhatis.1.html \ - makewhatis.1.xhtml \ - makewhatis.1.ps \ - makewhatis.1.pdf \ - makewhatis.1.txt \ +APROPOS_OBJS = apropos.o apropos_db.o manpath.o +APROPOS_LNS = apropos.ln apropos_db.ln manpath.ln + +$(APROPOS_OBJS) $(APROPOS_LNS): config.h mandoc.h apropos_db.h manpath.h mandocdb.h + +CGI_OBJS = $(MANDOC_HTML_OBJS) \ + $(MANDOC_MAN_OBJS) \ + $(MANDOC_TERM_OBJS) \ + cgi.o \ + apropos_db.o \ + manpath.o \ + out.o \ + tree.o + +CGI_LNS = $(MANDOC_HTML_LNS) \ + $(MANDOC_MAN_LNS) \ + $(MANDOC_TERM_LNS) \ + cgi.ln \ + apropos_db.ln \ + manpath.ln \ + out.ln \ + tree.ln + +$(CGI_OBJS) $(CGI_LNS): main.h mdoc.h man.h out.h config.h mandoc.h apropos_db.h manpath.h mandocdb.h + +CATMAN_OBJS = catman.o manpath.o +CATMAN_LNS = catman.ln manpath.ln + +$(CATMAN_OBJS) $(CATMAN_LNS): config.h mandoc.h manpath.h mandocdb.h + +DEMANDOC_OBJS = demandoc.o +DEMANDOC_LNS = demandoc.ln + +$(DEMANDOC_OBJS) $(DEMANDOC_LNS): config.h + +INDEX_MANS = apropos.1.html \ + apropos.1.xhtml \ + apropos.1.ps \ + apropos.1.pdf \ + apropos.1.txt \ + catman.8.html \ + catman.8.xhtml \ + catman.8.ps \ + catman.8.pdf \ + catman.8.txt \ + demandoc.1.html \ + demandoc.1.xhtml \ + demandoc.1.ps \ + demandoc.1.pdf \ + demandoc.1.txt \ mandoc.1.html \ mandoc.1.xhtml \ mandoc.1.ps \ mandoc.1.pdf \ mandoc.1.txt \ + whatis.1.html \ + whatis.1.xhtml \ + whatis.1.ps \ + whatis.1.pdf \ + whatis.1.txt \ mandoc.3.html \ mandoc.3.xhtml \ mandoc.3.ps \ @@ -261,6 +383,11 @@ INDEX_MANS = makewhatis.1.html \ man.7.ps \ man.7.pdf \ man.7.txt \ + man.cgi.7.html \ + man.cgi.7.xhtml \ + man.cgi.7.ps \ + man.cgi.7.pdf \ + man.cgi.7.txt \ mandoc_char.7.html \ mandoc_char.7.xhtml \ mandoc_char.7.ps \ @@ -285,7 +412,12 @@ INDEX_MANS = makewhatis.1.html \ tbl.7.xhtml \ tbl.7.ps \ tbl.7.pdf \ - tbl.7.txt + tbl.7.txt \ + mandocdb.8.html \ + mandocdb.8.xhtml \ + mandocdb.8.ps \ + mandocdb.8.pdf \ + mandocdb.8.txt $(INDEX_MANS): mandoc @@ -298,20 +430,38 @@ INDEX_OBJS = $(INDEX_MANS) \ www: index.html -lint: llib-llibmandoc.ln llib-lmandoc.ln llib-lpreconv.ln +lint: llib-lmandoc.ln llib-lpreconv.ln llib-ldemandoc.ln $(DBLN) clean: rm -f libmandoc.a $(LIBMANDOC_OBJS) rm -f llib-llibmandoc.ln $(LIBMANDOC_LNS) - rm -f makewhatis $(MAKEWHATIS_OBJS) - rm -f llib-lmakewhatis.ln $(MAKEWHATIS_LNS) + rm -f mandocdb $(MANDOCDB_OBJS) + rm -f llib-lmandocdb.ln $(MANDOCDB_LNS) rm -f preconv $(PRECONV_OBJS) rm -f llib-lpreconv.ln $(PRECONV_LNS) + rm -f apropos whatis $(APROPOS_OBJS) + rm -f llib-lapropos.ln $(APROPOS_LNS) + rm -f man.cgi $(CGI_OBJS) + rm -f llib-lman.cgi.ln $(CGI_LNS) + rm -f catman $(CATMAN_OBJS) + rm -f llib-lcatman.ln $(CATMAN_LNS) + rm -f demandoc $(DEMANDOC_OBJS) + rm -f llib-ldemandoc.ln $(DEMANDOC_LNS) rm -f mandoc $(MANDOC_OBJS) rm -f llib-lmandoc.ln $(MANDOC_LNS) - rm -f config.h config.log compat.o compat.ln - rm -f mdocml.tar.gz + rm -f config.h config.log $(COMPAT_OBJS) $(COMPAT_LNS) + rm -f mdocml.tar.gz mdocml-win32.zip mdocml-win64.zip mdocml-macosx.zip rm -f index.html $(INDEX_OBJS) + rm -rf test-fgetln.dSYM + rm -rf test-strlcpy.dSYM + rm -rf test-strlcat.dSYM + rm -rf test-strptime.dSYM + rm -rf test-mmap.dSYM + rm -rf test-getsubopt.dSYM + rm -rf apropos.dSYM + rm -rf catman.dSYM + rm -rf mandocdb.dSYM + rm -rf whatis.dSYM install: all mkdir -p $(DESTDIR)$(BINDIR) @@ -321,16 +471,24 @@ install: all mkdir -p $(DESTDIR)$(MANDIR)/man1 mkdir -p $(DESTDIR)$(MANDIR)/man3 mkdir -p $(DESTDIR)$(MANDIR)/man7 - $(INSTALL_PROGRAM) mandoc preconv $(DESTDIR)$(BINDIR) + $(INSTALL_PROGRAM) mandoc preconv demandoc $(DESTDIR)$(BINDIR) $(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR) - $(INSTALL_LIB) mandoc.h $(DESTDIR)$(INCLUDEDIR) - $(INSTALL_MAN) mandoc.1 preconv.1 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL_LIB) man.h mdoc.h mandoc.h $(DESTDIR)$(INCLUDEDIR) + $(INSTALL_MAN) mandoc.1 preconv.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1 $(INSTALL_MAN) mandoc.3 $(DESTDIR)$(MANDIR)/man3 $(INSTALL_MAN) man.7 mdoc.7 roff.7 eqn.7 tbl.7 mandoc_char.7 $(DESTDIR)$(MANDIR)/man7 $(INSTALL_DATA) example.style.css $(DESTDIR)$(EXAMPLEDIR) +installcgi: all + mkdir -p $(DESTDIR)$(CGIBINDIR) + mkdir -p $(DESTDIR)$(HTDOCDIR) + $(INSTALL_PROGRAM) man.cgi $(DESTDIR)$(CGIBINDIR) + $(INSTALL_DATA) example.style.css $(DESTDIR)$(HTDOCDIR)/man.css + $(INSTALL_DATA) man-cgi.css $(DESTDIR)$(HTDOCDIR) + installwww: www mkdir -p $(PREFIX)/snapshots + mkdir -p $(PREFIX)/binaries $(INSTALL_DATA) index.html external.png index.css $(PREFIX) $(INSTALL_DATA) $(INDEX_MANS) style.css $(PREFIX) $(INSTALL_DATA) mandoc.h.html man.h.html mdoc.h.html $(PREFIX) @@ -339,46 +497,115 @@ installwww: www $(INSTALL_DATA) mdocml.tar.gz $(PREFIX)/snapshots/mdocml-$(VERSION).tar.gz $(INSTALL_DATA) mdocml.md5 $(PREFIX)/snapshots/mdocml-$(VERSION).md5 -libmandoc.a: compat.o $(LIBMANDOC_OBJS) - $(AR) rs $@ compat.o $(LIBMANDOC_OBJS) +libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS) + $(AR) rs $@ $(COMPAT_OBJS) $(LIBMANDOC_OBJS) -llib-llibmandoc.ln: compat.ln $(LIBMANDOC_LNS) - $(LINT) $(LINTFLAGS) -Clibmandoc compat.ln $(LIBMANDOC_LNS) +llib-llibmandoc.ln: $(COMPAT_LNS) $(LIBMANDOC_LNS) + $(LINT) $(LINTFLAGS) -Clibmandoc $(COMPAT_LNS) $(LIBMANDOC_LNS) mandoc: $(MANDOC_OBJS) libmandoc.a - $(CC) -o $@ $(MANDOC_OBJS) libmandoc.a + $(CC) $(LDFLAGS) -o $@ $(MANDOC_OBJS) libmandoc.a -llib-lmandoc.ln: $(MANDOC_LNS) - $(LINT) $(LINTFLAGS) -Cmandoc $(MANDOC_LNS) +llib-lmandoc.ln: $(MANDOC_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Cmandoc $(MANDOC_LNS) llib-llibmandoc.ln -# You'll need -ldb for Linux. -makewhatis: $(MAKEWHATIS_OBJS) libmandoc.a - $(CC) -o $@ $(MAKEWHATIS_OBJS) libmandoc.a +mandocdb: $(MANDOCDB_OBJS) libmandoc.a + $(CC) $(LDFLAGS) -o $@ $(MANDOCDB_OBJS) libmandoc.a $(DBLIB) -llib-lmakewhatis.ln: $(MAKEWHATIS_LNS) - $(LINT) $(LINTFLAGS) -Cmakewhatis $(MAKEWHATIS_LNS) +llib-lmandocdb.ln: $(MANDOCDB_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Cmandocdb $(MANDOCDB_LNS) llib-llibmandoc.ln preconv: $(PRECONV_OBJS) - $(CC) -o $@ $(PRECONV_OBJS) + $(CC) $(LDFLAGS) -o $@ $(PRECONV_OBJS) + +llib-lpreconv.ln: $(PRECONV_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Cpreconv $(PRECONV_LNS) llib-llibmandoc.ln + +whatis: apropos + cp -f apropos whatis + +apropos: $(APROPOS_OBJS) libmandoc.a + $(CC) $(LDFLAGS) -o $@ $(APROPOS_OBJS) libmandoc.a $(DBLIB) -llib-lpreconv.ln: $(PRECONV_LNS) - $(LINT) $(LINTFLAGS) -Cpreconv $(PRECONV_LNS) +llib-lapropos.ln: $(APROPOS_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Capropos $(APROPOS_LNS) llib-llibmandoc.ln + +catman: $(CATMAN_OBJS) libmandoc.a + $(CC) $(LDFLAGS) -o $@ $(CATMAN_OBJS) libmandoc.a $(DBLIB) + +llib-lcatman.ln: $(CATMAN_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Ccatman $(CATMAN_LNS) llib-llibmandoc.ln + +man.cgi: $(CGI_OBJS) libmandoc.a + $(CC) $(LDFLAGS) $(STATIC) -o $@ $(CGI_OBJS) libmandoc.a $(DBLIB) + +llib-lman.cgi.ln: $(CGI_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Cman.cgi $(CGI_LNS) llib-llibmandoc.ln + +demandoc: $(DEMANDOC_OBJS) libmandoc.a + $(CC) $(LDFLAGS) -o $@ $(DEMANDOC_OBJS) libmandoc.a + +llib-ldemandoc.ln: $(DEMANDOC_LNS) llib-llibmandoc.ln + $(LINT) $(LINTFLAGS) -Cdemandoc $(DEMANDOC_LNS) llib-llibmandoc.ln mdocml.md5: mdocml.tar.gz md5 mdocml.tar.gz >$@ mdocml.tar.gz: $(SRCS) mkdir -p .dist/mdocml-$(VERSION)/ - $(INSTALL) -m 0444 $(SRCS) .dist/mdocml-$(VERSION) + $(INSTALL_SOURCE) $(SRCS) .dist/mdocml-$(VERSION) ( cd .dist/ && tar zcf ../$@ ./ ) rm -rf .dist/ +mdocml-win32.zip: $(SRCS) + mkdir -p .win32/mdocml-$(VERSION)/ + $(INSTALL_SOURCE) $(SRCS) .win32 + cp .win32/Makefile .win32/Makefile.old + egrep -v -e DUSE_WCHAR -e ^DBBIN .win32/Makefile.old >.win32/Makefile + ( cd .win32; \ + CC=i686-w64-mingw32-gcc AR=i686-w64-mingw32-ar CFLAGS='-DOSNAME=\"Windows\"' make; \ + make install PREFIX=mdocml-$(VERSION) ; \ + zip -r ../$@ mdocml-$(VERSION) ) + rm -rf .win32 + +mdocml-win64.zip: $(SRCS) + mkdir -p .win64/mdocml-$(VERSION)/ + $(INSTALL_SOURCE) $(SRCS) .win64 + cp .win64/Makefile .win64/Makefile.old + egrep -v -e DUSE_WCHAR -e ^DBBIN .win64/Makefile.old >.win64/Makefile + ( cd .win64; \ + CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar CFLAGS='-DOSNAME=\"Windows\"' make; \ + make install PREFIX=mdocml-$(VERSION) ; \ + zip -r ../$@ mdocml-$(VERSION) ) + rm -rf .win64 + +mdocml-macosx.zip: $(SRCS) + mkdir -p .macosx/mdocml-$(VERSION)/ + $(INSTALL_SOURCE) $(SRCS) .macosx + ( cd .macosx; \ + CFLAGS="-arch i386 -arch x86_64 -arch ppc" LDFLAGS="-arch i386 -arch x86_64 -arch ppc" make; \ + make install PREFIX=mdocml-$(VERSION) ; \ + zip -r ../$@ mdocml-$(VERSION) ) + rm -rf .macosx + index.html: $(INDEX_OBJS) config.h: config.h.pre config.h.post rm -f config.log ( cat config.h.pre; \ echo; \ + if $(CC) $(CFLAGS) -Werror -o test-fgetln test-fgetln.c >> config.log 2>&1; then \ + echo '#define HAVE_FGETLN'; \ + rm test-fgetln; \ + fi; \ + if $(CC) $(CFLAGS) -Werror -o test-strptime test-strptime.c >> config.log 2>&1; then \ + echo '#define HAVE_STRPTIME'; \ + rm test-strptime; \ + fi; \ + if $(CC) $(CFLAGS) -Werror -o test-getsubopt test-getsubopt.c >> config.log 2>&1; then \ + echo '#define HAVE_GETSUBOPT'; \ + rm test-getsubopt; \ + fi; \ if $(CC) $(CFLAGS) -Werror -o test-strlcat test-strlcat.c >> config.log 2>&1; then \ echo '#define HAVE_STRLCAT'; \ rm test-strlcat; \ @@ -398,19 +625,19 @@ config.h: config.h.pre config.h.post .h.h.html: highlight -I $< >$@ -.1.1.txt .3.3.txt .7.7.txt: +.1.1.txt .3.3.txt .7.7.txt .8.8.txt: ./mandoc -Tascii -Wall,stop $< | col -b >$@ -.1.1.html .3.3.html .7.7.html: +.1.1.html .3.3.html .7.7.html .8.8.html: ./mandoc -Thtml -Wall,stop -Ostyle=style.css,man=%N.%S.html,includes=%I.html $< >$@ -.1.1.ps .3.3.ps .7.7.ps: +.1.1.ps .3.3.ps .7.7.ps .8.8.ps: ./mandoc -Tps -Wall,stop $< >$@ -.1.1.xhtml .3.3.xhtml .7.7.xhtml: +.1.1.xhtml .3.3.xhtml .7.7.xhtml .8.8.xhtml: ./mandoc -Txhtml -Wall,stop -Ostyle=style.css,man=%N.%S.xhtml,includes=%I.html $< >$@ -.1.1.pdf .3.3.pdf .7.7.pdf: +.1.1.pdf .3.3.pdf .7.7.pdf .8.8.pdf: ./mandoc -Tpdf -Wall,stop $< >$@ .sgml.html: diff --git a/contrib/mdocml/TODO b/contrib/mdocml/TODO new file mode 100644 index 0000000000..a870136129 --- /dev/null +++ b/contrib/mdocml/TODO @@ -0,0 +1,372 @@ +************************************************************************ +* Official mandoc TODO. +* $Id: TODO,v 1.129 2012/03/04 23:53:37 schwarze Exp $ +************************************************************************ + +************************************************************************ +* parser bugs +************************************************************************ + +- ".\}" on its own line gets translated to bare ".\&" + which forces pset() into man(7) + and then triggers an unknown macro error + reported by naddy@ Sun, 3 Jul 2011 21:52:24 +0200 + +************************************************************************ +* formatter bugs +************************************************************************ + +- tbl(7): Horizontal and vertical lines are formatted badly: + With the box option, there is too much white space at the end of cells. + Horizontal lines from "=" lines are a bit too long. + yuri dot pankov at gmail dot com Thu, 14 Apr 2011 05:45:26 +0400 + +************************************************************************ +* missing features +************************************************************************ + +--- missing roff features ---------------------------------------------- + +- The pod2man preamble wants \h'...' with quoted numerical arguments, + see for example AUTHORS in MooseX::Getopt.3p, p5-MooseX-Getopt. + reported by Andreas Voegele + Tue, 22 Nov 2011 15:34:47 +0100 on ports@ + +- .if n \{ + .br\} + should cause an extra space to be raised. + +- .ad (adjust margins) + .ad l -- adjust left margin only (flush left) + .ad r -- adjust right margin only (flush right) + .ad c -- center text on line + .ad b -- adjust both margins (alias: .ad n) + .na -- temporarily disable adjustment without changing the mode + .ad -- re-enable adjustment without changing the mode + Adjustment mode is ignored while in no-fill mode (.nf). + +- .it (line traps) occur in mysql(1), yasm_arch(7) + generated by DocBook XSL Stylesheets v1.71.1 + reported by brad@ Sat, 15 Jan 2011 15:48:18 -0500 + +- .ns (no-space mode) occurs in xine-config(1) + reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 + +- xloadimage(1) wants .ti (temporary indent), rep by naddy@ + +- .ta (tab settings) occurs in ircbug(1) and probably gnats(1) + reported by brad@ Sat, 15 Jan 2011 15:50:51 -0500 + +- \c (interrupted text) occurs in chat(8) + +- using undefined strings or macros defines them to be empty + wl@ Mon, 14 Nov 2011 14:37:01 +0000 + +--- missing mdoc features ---------------------------------------------- + +- fix bad block nesting involving multiple identical explicit blocks + see the OpenBSD mdoc_macro.c 1.47 commit message + +- .Bl -column .Xo support is missing + ultimate goal: + restore .Xr and .Dv to + lib/libc/compat-43/sigvec.3 + lib/libc/gen/signal.3 + lib/libc/sys/sigaction.2 + +- edge case: decide how to deal with blk_full bad nesting, e.g. + .Sh .Nm .Bk .Nm .Ek .Sh found by jmc@ in ssh-keygen(1) + from jmc@ Wed, 14 Jul 2010 18:10:32 +0100 + +- \\ is now implemented correctly + * when defining strings and macros using .ds and .de + * when parsing roff(7) and man(7) macro arguments + It does not yet work in mdoc(7) macro arguments + because libmdoc does not yet use mandoc_getarg(). + Also check what happens in plain text, it must be identical to \e. + +- .Bd -filled should not be the same as .Bd -ragged, but align both + the left and right margin. In groff, it is implemented in terms + of .ad b, which we don't have either. Found in cksum(1). + +- implement blank `Bl -column', such as + .Bl -column + .It foo Ta bar + .El + +- explicitly disallow nested `Bl -column', which would clobber internal + flags defined for struct mdoc_macro + +- In .Bl -column .It, the end of the line probably has to be regarded + as an implicit .Ta, if there could be one, see the following mildly + ugly code from login.conf(5): + .Bl -column minpasswordlen program xetcxmotd + .It path Ta path Ta value of Dv _PATH_DEFPATH + .br + Default search path. + reported by Michal Mazurek + via jmc@ Thu, 7 Apr 2011 16:00:53 +0059 + +- inside `.Bl -column' phrases, punctuation is handled like normal + text, e.g. `.Bl -column .It Fl x . Ta ...' should give "-x -." + +- inside `.Bl -column' phrases, TERMP_IGNDELIM handling by `Pf' + is not safe, e.g. `.Bl -column .It Pf a b .' gives "ab." + but should give "ab ." + +- set a meaningful default if no `Bl' list type is assigned + +- have a blank `It' head for `Bl -tag' not puke + +- prohibit `Nm' from having non-text HEAD children + (e.g., NetBSD mDNSShared/dns-sd.1) + (mdoc_html.c and mdoc_term.c `Nm' handlers can be slightly simplified) + +- When there is free text in the SYNOPSIS and that free text contains + the .Nm macro, groff somehow understands to treat the .Nm as an in-line + macro, while mandoc treats it as a block macro and breaks the line. + No idea how the logic for distinguishing in-line and block instances + should be, needs investigation. + uqs@ Thu, 2 Jun 2011 11:03:51 +0200 + uqs@ Thu, 2 Jun 2011 11:33:35 +0200 + +--- missing man features ----------------------------------------------- + +- groff an-ext.tmac macros (.UR, .UE) occur in xine(5) + reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 + +- -T[x]html doesn't stipulate non-collapsing spaces in literal mode + +--- missing tbl features ----------------------------------------------- + +- implement basic non-parametric .de to support e.g. sox(1) + reported by naddy@ Sat, 16 Oct 2010 23:51:57 +0200 + *** sox(1) still doesn't work, tbl(1) errors need investigation + +- allow standalone `.' to be interpreted as an end-of-layout + delimiter instead of being thrown away as a no-op roff line + reported by Yuri Pankov, Wed 18 May 2011 11:34:59 CEST + +--- missing misc features ---------------------------------------------- + +- clean up escape sequence handling, creating three classes: + (1) fully implemented, or parsed and ignored without loss of content + (2) unimplemented, potentially causing loss of content + or serious mangling of formatting (e.g. \n) -> ERROR + see textproc/mgdiff(1) for nice examples + (3) undefined, just output the character -> perhaps WARNING + +- The \t escape sequence is the same as a literal tab, see for example + the ASCII table in hexdump(1) where + .Bl -column \&000_nu \&001_so \&002_st \&003_et \&004_eo + .It \&000\ nul\t001\ soh\t002\ stx\t003\ etx\t004\ eot\t005\ enq + produces + 000 nul 001 soh 002 stx 003 etx 004 eot 005 enq + and the example in oldrdist(1) + +- look at pages generated from reStructeredText, e.g. devel/mercurial hg(1) + These are a weird mixture of man(7) and custom autogenerated low-level + roff stuff. Figure out to what extent we can cope. + For details, see http://docutils.sourceforge.net/rst.html + noted by stsp@ Sat, 24 Apr 2010 09:17:55 +0200 + reminded by nicm@ Mon, 3 May 2010 09:52:41 +0100 + +- check compatibility with Plan9: + http://swtch.com/usr/local/plan9/tmac/tmac.an + http://swtch.com/plan9port/man/man7/man.html + "Anthony J. Bentley" 28 Dec 2010 21:58:40 -0700 + +************************************************************************ +* formatting issues: ugly output +************************************************************************ + +- a column list with blank `Ta' cells triggers a spurrious + start-with-whitespace printing of a newline + +- double quotes inside double quotes are escaped by doubling them + implement this in mdoc(7), too + so far, we only have it in roff(7) and man(7) + reminded by millert@ Thu, 09 Dec 2010 17:29:52 -0500 + +- perl(1) SYNOPSIS looks bad; reported by deraadt@ + 1) man(7) seems to need SYNOPSIS .Nm blocks, too + +- In .Bl -column, + .It Em AuthenticationKey Length + ought to render "Key Length" with emphasis, too, + see OpenBSD iked.conf(5). + reported again Nicolas Joly via wiz@ Wed, 12 Oct 2011 00:20:00 +0200 + +- empty phrases in .Bl column produce too few blanks + try e.g. .Bl -column It Ta Ta + reported by millert Fri, 02 Apr 2010 16:13:46 -0400 + +- .%T can have trailing punctuation. Currently, it puts the trailing + punctuation into a trailing MDOC_TEXT element inside its own scope. + That element should rather be outside its scope, such that the + punctuation does not get underlines. This is not trivial to + implement because .%T then needs some features of in_line_eoln() - + slurp all arguments into one single text element - and one feature + of in_line() - put trailing punctuation out of scope. + Found in mount_nfs(8) and exports(5), search for "Appendix". + +- in enclosures, mandoc sometimes fancies a bogus end of sentence + reminded by jmc@ Thu, 23 Sep 2010 18:13:39 +0059 + +************************************************************************ +* formatting issues: gratuitous differences +************************************************************************ + +- .Rv (and probably .Ex) print different text if an `Nm' has been named + or not (run a manual without `Nm blah' to see this). I'm not sure + that this exists in the wild, but it's still an error. + +- In .Bl -bullet, the groff bullet is "+\b+\bo\bo", the mandoc bullet + is just "o\bo". + see for example OpenBSD ksh(1) + +- The characters "|" and "\*(Ba" should never be bold, + not even in the middle of a word, e.g. ".Cm b\*(Bac" in + "mknod [-m mode] name b|c major minor" + in OpenBSD ksh(1) + +- A bogus .Pp between two .It must not produce a double blank line, + see between -R and -r in OpenBSD rm(1), before "update" in mount(8), + or in DIAGNOSTICS in init(8), or before "is always true" in ksh(1). + The same happens with .Pp just before .El, see bgpd.conf(5). + Also have `It' complain if `Pp' is invoked at certain times (not + -compact?). + +- .Pp between two .It in .Bl -column should produce one, + not two blank lines, see e.g. login.conf(5). + reported by jmc@ Sun, 17 Apr 2011 14:04:58 +0059 + reported again by sthen@ Wed, 18 Jan 2012 02:09:39 +0000 (UTC) + +- If the *first* line after .It is .Pp, break the line right after + the tag, do not pad with space characters before breaking. + See the description of the a, c, and i commands in sed(1). + +- If the first line after .It is .D1, do not assert a blank line + in between, see for example tmux(1). + reported by nicm@ 13 Jan 2011 00:18:57 +0000 + +- .Nx 1.0a + should be "NetBSD 1.0A", not "NetBSD 1.0a", + see OpenBSD ccdconfig(8). + +- In .Bl -tag, if a tag exceeds the right margin and must be continued + on the next line, it must be indented by -width, not width+1; + see "rule block|pass" in OpenBSD ifconfig(8). + +- When the -width string contains macros, the macros must be rendered + before measuring the width, for example + .Bl -tag -width ".Dv message" + in magic(5), located in src/usr.bin/file, is the same + as -width 7n, not -width 11n. + The same applies to .Bl -column column widths; + reported again by Nicolas Joly Thu, 1 Mar 2012 13:41:26 +0100 via wiz@ 5 Mar + +- The \& zero-width character counts as output. + That is, when it is alone on a line between two .Pp, + we want three blank lines, not two as in mandoc. + +- When .Fn arguments exceed one output line, all but the first + should be indented, see e.g. rpc(3); + reported by jmc@ on discuss@ Fri, 29 Oct 2010 13:48:33 +0100 + reported again by Nicolas Joly via wiz@ Sun, 18 Sep 2011 18:24:40 +0200 + Also, we don't want to break the line within the argument of: + .Fa "chtype tl" + +- .Ns should work when called at the end of an input line, see + the following code in vi(1): + .It Xo + .Op Ar line + .Cm a Ns Op Cm ppend Ns + .Op Cm !\& + .Xc + The input text is appended after the specified line. + +- Header lines of excessive length: + Port OpenBSD man_term.c rev. 1.25 to mdoc_term.c + and document it in mdoc(7) and man(7) COMPATIBILITY + found while talking to Chris Bennett + +- In man(7), the sequence + .HP + one line of regular text + .SH + should not produce two blank lines before the .SH, + see for example named-checkconf(8). + +- In man(7), the sequence + .SH HEADER + + .PP + regular text + should not produce any blank lines between the header and the text, + see for example rsync(1). + Reported by naddy@ Mon, 28 Mar 2011 20:45:42 +0200 + +- In man(7), the sequence + regular text + .IP + .IP "tag" + indented text + should produce one, not four blank lines between the regular text + and the tag, see for example rsync(1). + Likewise, + regular text + .IP + indented text + should produce one, not two blank lines in between, and + regular text + .IP + .RS + .IP tag + indented text + should produce one, not three blank lines. + Reported by naddy@ Mon, 28 Mar 2011 20:45:42 +0200 + +- trailing whitespace must be ignored even when followed by a font escape, + see for example + makes + \fBdig \fR + operate in batch mode + in dig(1). + +************************************************************************ +* error reporting issues +************************************************************************ + +- .TP directly followed by .RS gives an assertion. + +************************************************************************ +* performance issues +************************************************************************ + +Several areas can be cleaned up to make mandoc even faster. These are + +- improve hashing mechanism for macros (quite important: performance) + +- improve hashing mechanism for characters (not as important) + +- the PDF file is HUGE: this can be reduced by using relative offsets + +- instead of re-initialising the roff predefined-strings set before each + parse, create a read-only version the first time and copy it + +************************************************************************ +* structural issues +************************************************************************ + +- We use the input line number at several places to distinguish + same-line from different-line input. That plainly doesn't work + with user-defined macros, leading to random breakage. + +- Find better ways to prevent endless loops + in roff(7) macro and string expansion. + +- Finish cleanup of date handling. + Decide which formats should be recognized where. + Update both mdoc(7) and man(7) documentation. + Triggered by Tim van der Molen Tue, 22 Feb 2011 20:30:45 +0100 diff --git a/contrib/mdocml/apropos.1 b/contrib/mdocml/apropos.1 new file mode 100644 index 0000000000..7dea132a46 --- /dev/null +++ b/contrib/mdocml/apropos.1 @@ -0,0 +1,328 @@ +.\" $Id: apropos.1,v 1.17 2012/03/24 01:46:25 kristaps Exp $ +.\" +.\" Copyright (c) 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. +.\" +.Dd $Mdocdate: March 24 2012 $ +.Dt APROPOS 1 +.Os +.Sh NAME +.Nm apropos +.Nd search manual page databases +.Sh SYNOPSIS +.Nm +.Op Fl C Ar file +.Op Fl M Ar manpath +.Op Fl m Ar manpath +.Op Fl S Ar arch +.Op Fl s Ar section +.Ar expression ... +.Sh DESCRIPTION +The +.Nm +utility queries manual page databases generated by +.Xr mandocdb 8 , +evaluating on +.Ar expression +for each file in each database. +.Pp +By default, +.Nm +searches for +.Xr mandocdb 8 +databases in the default paths stipulated by +.Xr man 1 , +parses terms as case-sensitive regular expressions +over manual names and descriptions. +Multiple terms imply pairwise +.Fl o . +If standard output is a TTY, a result may be selected from a list and +its manual displayed with the pager. +.Pp +Its arguments are as follows: +.Bl -tag -width Ds +.It Fl C Ar file +Specify an alternative configuration +.Ar file +in +.Xr man.conf 5 +format. +.It Fl M Ar manpath +Use the colon-separated path instead of the default list of paths +searched for +.Xr mandocdb 8 +databases. +Invalid paths, or paths without manual databases, are ignored. +.It Fl m Ar manpath +Prepend the colon-separated paths to the list of paths searched +for +.Xr mandocdb 8 +databases. +Invalid paths, or paths without manual databases, are ignored. +.It Fl S Ar arch +Search only for a particular architecture. +.It Fl s Ar cat +Search only for a manual section. +See +.Xr man 1 +for a listing of manual sections. +.El +.Pp +An +.Ar expression +consists of search terms joined by logical operators +.Fl a +.Pq and +and +.Fl o +.Pq or . +The +.Fl a +operator has precedence over +.Fl o +and both are evaluated left-to-right. +.Bl -tag -width Ds +.It \&( Ar expr No \&) +True if the subexpression +.Ar expr +is true. +.It Ar expr1 Fl a Ar expr2 +True if both +.Ar expr1 +and +.Ar expr2 +are true (logical +.Qq and ) . +.It Ar expr1 Oo Fl o Oc Ar expr2 +True if +.Ar expr1 +and/or +.Ar expr2 +evaluate to true (logical +.Qq or ) . +.It Ar term +True if +.Ar term +is satisfied. +This has syntax +.Li [key[,key]*(=~)]?val , +where operand +.Cm key +is an +.Xr mdoc 7 +macro to query and +.Cm val +is its value. +See +.Sx Macro Keys +for a list of available keys. +Operator +.Li \&= +evaluates a substring, while +.Li \&~ +evaluates a regular expression. +.It Fl i Ar term +If +.Ar term +is a regular expression, it +is evaluated case-insensitively. +Has no effect on substring terms. +.El +.Pp +Results are sorted by manual title, with output formatted as +.Pp +.D1 title(sec) \- description +.Pp +Where +.Qq title +is the manual's title (note multiple manual names may exist for one +title), +.Qq sec +is the manual section, and +.Qq description +is the manual's short description. +If an architecture is specified for the manual, it is displayed as +.Pp +.D1 title(cat/arch) \- description +.Pp +If on a TTY, results are prefixed with a numeric identifier. +.Pp +.D1 [index] title(cat) \- description +.Pp +One may choose a manual be entering the index at the prompt. +Valid choices are displayed using +.Ev MANPAGER , +or failing that , +.Ev PAGER +or just +.Xr more 1 . +Source pages are formatted with +.Xr mandoc 1 ; +preformatted pages with +.Xr cat 1 . +.Ss Macro Keys +Queries evaluate over a subset of +.Xr mdoc 7 +macros indexed by +.Xr mandocdb 8 . +In addition to the macro keys listed below, the special key +.Cm any +may be used to match any available macro key. +.Pp +Names and description: +.Bl -column "xLix" description -offset indent -compact +.It Li \&Nm Ta manual name +.It Li \&Nd Ta one-line manual description +.El +.Pp +Sections and cross references: +.Bl -column "xLix" description -offset indent -compact +.It Li \&Sh Ta section header (excluding standard sections) +.It Li \&Ss Ta subsection header +.It Li \&Xr Ta cross reference to another manual page +.It Li \&Rs Ta bibliographic reference +.El +.Pp +Semantic markup for command line utilities: +.Bl -column "xLix" description -offset indent -compact +.It Li \&Fl Ta command line options (flags) +.It Li \&Cm Ta command modifier +.It Li \&Ar Ta command argument +.It Li \&Ic Ta internal or interactive command +.It Li \&Ev Ta environmental variable +.It Li \&Pa Ta file system path +.El +.Pp +Semantic markup for function libraries: +.Bl -column "xLix" description -offset indent -compact +.It Li \&Lb Ta function library name +.It Li \&In Ta include file +.It Li \&Ft Ta function return type +.It Li \&Fn Ta function name +.It Li \&Fa Ta function argument type and name +.It Li \&Vt Ta variable type +.It Li \&Va Ta variable name +.It Li \&Dv Ta defined variable or preprocessor constant +.It Li \&Er Ta error constant +.It Li \&Ev Ta environmental variable +.El +.Pp +Various semantic markup: +.Bl -column "xLix" description -offset indent -compact +.It Li \&An Ta author name +.It Li \&Lk Ta hyperlink +.It Li \&Mt Ta Do mailto Dc hyperlink +.It Li \&Cd Ta kernel configuration declaration +.It Li \&Ms Ta mathematical symbol +.It Li \&Tn Ta tradename +.El +.Pp +Physical markup: +.Bl -column "xLix" description -offset indent -compact +.It Li \&Em Ta italic font or underline +.It Li \&Sy Ta boldface font +.It Li \&Li Ta typewriter font +.El +.Pp +Text production: +.Bl -column "xLix" description -offset indent -compact +.It Li \&St Ta reference to a standards document +.It Li \&At Ta At No version reference +.It Li \&Bx Ta Bx No version reference +.It Li \&Bsx Ta Bsx No version reference +.It Li \&Nx Ta Nx No version reference +.It Li \&Fx Ta Fx No version reference +.It Li \&Ox Ta Ox No version reference +.It Li \&Dx Ta Dx No version reference +.El +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev MANPAGER +Default pager for manuals. +If this is unset, falls back to +.Ev Pager . +.It Ev PAGER +The second choice for a manual pager. +If this is unset, use +.Xr more 1 . +.It Ev MANPATH +Colon-separated paths modifying the default list of paths searched for +manual databases. +Invalid paths, or paths without manual databases, are ignored. +Overridden by +.Fl M . +If +.Ev MANPATH +begins with a +.Sq \&: , +it is appended to the default list; +else if it ends with +.Sq \&: , +it is prepended to the default list; else if it contains +.Sq \&:: , +the default list is inserted between the colons. +If none of these conditions are met, it overrides the default list. +.El +.Sh FILES +.Bl -tag -width "/etc/man.conf" -compact +.It Pa whatis.db +name of the +.Xr mandocdb 8 +keyword database +.It Pa whatis.index +name of the +.Xr mandocdb 8 +filename database +.It Pa /etc/man.conf +default +.Xr man 1 +configuration file +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Search for +.Qq mdoc +as a substring and regular expression +within each manual name and description: +.Pp +.Dl $ apropos mdoc +.Dl $ apropos ~^mdoc$ +.Pp +Include matches for +.Qq roff +and +.Qq man +for the regular expression case: +.Pp +.Dl $ apropos ~^mdoc$ roff man +.Dl $ apropos ~^mdoc$ \-o roff \-o man +.Pp +Search for +.Qq optind +and +.Qq optarg +as variable names in the library category: +.Pp +.Dl $ apropos \-s 3 Va~^optind \-a Va~^optarg$ +.Sh SEE ALSO +.Xr more 1 +.Xr re_format 7 , +.Xr mandocdb 8 +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/apropos.c b/contrib/mdocml/apropos.c new file mode 100644 index 0000000000..9c3cae96af --- /dev/null +++ b/contrib/mdocml/apropos.c @@ -0,0 +1,239 @@ +/* $Id: apropos.c,v 1.30 2012/03/24 02:18:51 kristaps Exp $ */ +/* + * Copyright (c) 2011, 2012 Kristaps Dzonsons + * Copyright (c) 2011 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include +#include +#include +#include +#include +#include + +#include "apropos_db.h" +#include "mandoc.h" +#include "manpath.h" + +#define SINGLETON(_res, _sz) \ + ((_sz) && (_res)[0].matched && \ + (1 == (_sz) || 0 == (_res)[1].matched)) +#define EMPTYSET(_res, _sz) \ + ((0 == (_sz)) || 0 == (_res)[0].matched) + +static int cmp(const void *, const void *); +static void list(struct res *, size_t, void *); +static void usage(void); + +static char *progname; + +int +main(int argc, char *argv[]) +{ + int ch, rc, whatis, usecat; + struct res *res; + struct manpaths paths; + const char *prog; + pid_t pid; + char path[PATH_MAX]; + int fds[2]; + size_t terms, ressz, sz; + struct opts opts; + struct expr *e; + char *defpaths, *auxpaths, *conf_file, *cp; + extern int optind; + extern char *optarg; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + ++progname; + + whatis = 0 == strncmp(progname, "whatis", 6); + + memset(&paths, 0, sizeof(struct manpaths)); + memset(&opts, 0, sizeof(struct opts)); + + usecat = 0; + ressz = 0; + res = NULL; + auxpaths = defpaths = NULL; + conf_file = NULL; + e = NULL; + path[0] = '\0'; + + while (-1 != (ch = getopt(argc, argv, "C:M:m:S:s:"))) + switch (ch) { + case ('C'): + conf_file = optarg; + break; + case ('M'): + defpaths = optarg; + break; + case ('m'): + auxpaths = optarg; + break; + case ('S'): + opts.arch = optarg; + break; + case ('s'): + opts.cat = optarg; + break; + default: + usage(); + return(EXIT_FAILURE); + } + + argc -= optind; + argv += optind; + + if (0 == argc) + return(EXIT_SUCCESS); + + rc = 0; + + manpath_parse(&paths, conf_file, defpaths, auxpaths); + + e = whatis ? termcomp(argc, argv, &terms) : + exprcomp(argc, argv, &terms); + + if (NULL == e) { + fprintf(stderr, "%s: Bad expression\n", progname); + goto out; + } + + rc = apropos_search + (paths.sz, paths.paths, &opts, + e, terms, NULL, &ressz, &res, list); + + terms = 1; + + if (0 == rc) { + fprintf(stderr, "%s: Bad database\n", progname); + goto out; + } else if ( ! isatty(STDOUT_FILENO) || EMPTYSET(res, ressz)) + goto out; + + if ( ! SINGLETON(res, ressz)) { + printf("Which manpage would you like [1]? "); + fflush(stdout); + if (NULL != (cp = fgetln(stdin, &sz)) && + sz > 1 && '\n' == cp[--sz]) { + if ((ch = atoi(cp)) <= 0) + goto out; + terms = (size_t)ch; + } + } + + if (--terms < ressz && res[terms].matched) { + chdir(paths.paths[res[terms].volume]); + strlcpy(path, res[terms].file, PATH_MAX); + usecat = RESTYPE_CAT == res[terms].type; + } +out: + manpath_free(&paths); + resfree(res, ressz); + exprfree(e); + + if ('\0' == path[0]) + return(rc ? EXIT_SUCCESS : EXIT_FAILURE); + + if (-1 == pipe(fds)) { + perror(NULL); + exit(EXIT_FAILURE); + } + + if (-1 == (pid = fork())) { + perror(NULL); + exit(EXIT_FAILURE); + } else if (pid > 0) { + dup2(fds[0], STDIN_FILENO); + close(fds[1]); + prog = NULL != getenv("MANPAGER") ? + getenv("MANPAGER") : + (NULL != getenv("PAGER") ? + getenv("PAGER") : "more"); + execlp(prog, prog, (char *)NULL); + perror(prog); + return(EXIT_FAILURE); + } + + dup2(fds[1], STDOUT_FILENO); + close(fds[0]); + prog = usecat ? "cat" : "mandoc"; + execlp(prog, prog, path, (char *)NULL); + perror(prog); + return(EXIT_FAILURE); +} + +/* ARGSUSED */ +static void +list(struct res *res, size_t sz, void *arg) +{ + size_t i; + + qsort(res, sz, sizeof(struct res), cmp); + + if (EMPTYSET(res, sz) || SINGLETON(res, sz)) + return; + + if ( ! isatty(STDOUT_FILENO)) + for (i = 0; i < sz && res[i].matched; i++) + printf("%s(%s%s%s) - %.70s\n", + res[i].title, res[i].cat, + *res[i].arch ? "/" : "", + *res[i].arch ? res[i].arch : "", + res[i].desc); + else + for (i = 0; i < sz && res[i].matched; i++) + printf("[%zu] %s(%s%s%s) - %.70s\n", i + 1, + res[i].title, res[i].cat, + *res[i].arch ? "/" : "", + *res[i].arch ? res[i].arch : "", + res[i].desc); +} + +static int +cmp(const void *p1, const void *p2) +{ + const struct res *r1 = p1; + const struct res *r2 = p2; + + if (0 == r1->matched) + return(1); + else if (0 == r2->matched) + return(1); + + return(strcasecmp(r1->title, r2->title)); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s " + "[-C file] " + "[-M manpath] " + "[-m manpath] " + "[-S arch] " + "[-s section] " + "expression ...\n", + progname); +} diff --git a/contrib/mdocml/apropos_db.c b/contrib/mdocml/apropos_db.c new file mode 100644 index 0000000000..8aea771da7 --- /dev/null +++ b/contrib/mdocml/apropos_db.c @@ -0,0 +1,876 @@ +/* $Id: apropos_db.c,v 1.31 2012/03/24 01:46:25 kristaps Exp $ */ +/* + * Copyright (c) 2011, 2012 Kristaps Dzonsons + * Copyright (c) 2011 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) +# include +# include +#elif defined(__APPLE__) +# include +# include +#else +# include +#endif + +#include "mandocdb.h" +#include "apropos_db.h" +#include "mandoc.h" + +#define RESFREE(_x) \ + do { \ + free((_x)->file); \ + free((_x)->cat); \ + free((_x)->title); \ + free((_x)->arch); \ + free((_x)->desc); \ + free((_x)->matches); \ + } while (/*CONSTCOND*/0) + +struct expr { + int regex; /* is regex? */ + int index; /* index in match array */ + uint64_t mask; /* type-mask */ + int and; /* is rhs of logical AND? */ + char *v; /* search value */ + regex_t re; /* compiled re, if regex */ + struct expr *next; /* next in sequence */ + struct expr *subexpr; +}; + +struct type { + uint64_t mask; + const char *name; +}; + +struct rectree { + struct res *node; /* record array for dir tree */ + int len; /* length of record array */ +}; + +static const struct type types[] = { + { TYPE_An, "An" }, + { TYPE_Ar, "Ar" }, + { TYPE_At, "At" }, + { TYPE_Bsx, "Bsx" }, + { TYPE_Bx, "Bx" }, + { TYPE_Cd, "Cd" }, + { TYPE_Cm, "Cm" }, + { TYPE_Dv, "Dv" }, + { TYPE_Dx, "Dx" }, + { TYPE_Em, "Em" }, + { TYPE_Er, "Er" }, + { TYPE_Ev, "Ev" }, + { TYPE_Fa, "Fa" }, + { TYPE_Fl, "Fl" }, + { TYPE_Fn, "Fn" }, + { TYPE_Fn, "Fo" }, + { TYPE_Ft, "Ft" }, + { TYPE_Fx, "Fx" }, + { TYPE_Ic, "Ic" }, + { TYPE_In, "In" }, + { TYPE_Lb, "Lb" }, + { TYPE_Li, "Li" }, + { TYPE_Lk, "Lk" }, + { TYPE_Ms, "Ms" }, + { TYPE_Mt, "Mt" }, + { TYPE_Nd, "Nd" }, + { TYPE_Nm, "Nm" }, + { TYPE_Nx, "Nx" }, + { TYPE_Ox, "Ox" }, + { TYPE_Pa, "Pa" }, + { TYPE_Rs, "Rs" }, + { TYPE_Sh, "Sh" }, + { TYPE_Ss, "Ss" }, + { TYPE_St, "St" }, + { TYPE_Sy, "Sy" }, + { TYPE_Tn, "Tn" }, + { TYPE_Va, "Va" }, + { TYPE_Va, "Vt" }, + { TYPE_Xr, "Xr" }, + { UINT64_MAX, "any" }, + { 0, NULL } +}; + +static DB *btree_open(void); +static int btree_read(const DBT *, const DBT *, + const struct mchars *, + uint64_t *, recno_t *, char **); +static int expreval(const struct expr *, int *); +static void exprexec(const struct expr *, + const char *, uint64_t, struct res *); +static int exprmark(const struct expr *, + const char *, uint64_t, int *); +static struct expr *exprexpr(int, char *[], int *, int *, size_t *); +static struct expr *exprterm(char *, int); +static DB *index_open(void); +static int index_read(const DBT *, const DBT *, int, + const struct mchars *, struct res *); +static void norm_string(const char *, + const struct mchars *, char **); +static size_t norm_utf8(unsigned int, char[7]); +static int single_search(struct rectree *, const struct opts *, + const struct expr *, size_t terms, + struct mchars *, int); + +/* + * Open the keyword mandoc-db database. + */ +static DB * +btree_open(void) +{ + BTREEINFO info; + DB *db; + + memset(&info, 0, sizeof(BTREEINFO)); + info.lorder = 4321; + info.flags = R_DUP; + + db = dbopen(MANDOC_DB, O_RDONLY, 0, DB_BTREE, &info); + if (NULL != db) + return(db); + + return(NULL); +} + +/* + * Read a keyword from the database and normalise it. + * Return 0 if the database is insane, else 1. + */ +static int +btree_read(const DBT *k, const DBT *v, const struct mchars *mc, + uint64_t *mask, recno_t *rec, char **buf) +{ + uint64_t vbuf[2]; + + /* Are our sizes sane? */ + if (k->size < 2 || sizeof(vbuf) != v->size) + return(0); + + /* Is our string nil-terminated? */ + if ('\0' != ((const char *)k->data)[(int)k->size - 1]) + return(0); + + norm_string((const char *)k->data, mc, buf); + memcpy(vbuf, v->data, v->size); + *mask = betoh64(vbuf[0]); + *rec = betoh64(vbuf[1]); + return(1); +} + +/* + * Take a Unicode codepoint and produce its UTF-8 encoding. + * This isn't the best way to do this, but it works. + * The magic numbers are from the UTF-8 packaging. + * They're not as scary as they seem: read the UTF-8 spec for details. + */ +static size_t +norm_utf8(unsigned int cp, char out[7]) +{ + int rc; + + rc = 0; + + if (cp <= 0x0000007F) { + rc = 1; + out[0] = (char)cp; + } else if (cp <= 0x000007FF) { + rc = 2; + out[0] = (cp >> 6 & 31) | 192; + out[1] = (cp & 63) | 128; + } else if (cp <= 0x0000FFFF) { + rc = 3; + out[0] = (cp >> 12 & 15) | 224; + out[1] = (cp >> 6 & 63) | 128; + out[2] = (cp & 63) | 128; + } else if (cp <= 0x001FFFFF) { + rc = 4; + out[0] = (cp >> 18 & 7) | 240; + out[1] = (cp >> 12 & 63) | 128; + out[2] = (cp >> 6 & 63) | 128; + out[3] = (cp & 63) | 128; + } else if (cp <= 0x03FFFFFF) { + rc = 5; + out[0] = (cp >> 24 & 3) | 248; + out[1] = (cp >> 18 & 63) | 128; + out[2] = (cp >> 12 & 63) | 128; + out[3] = (cp >> 6 & 63) | 128; + out[4] = (cp & 63) | 128; + } else if (cp <= 0x7FFFFFFF) { + rc = 6; + out[0] = (cp >> 30 & 1) | 252; + out[1] = (cp >> 24 & 63) | 128; + out[2] = (cp >> 18 & 63) | 128; + out[3] = (cp >> 12 & 63) | 128; + out[4] = (cp >> 6 & 63) | 128; + out[5] = (cp & 63) | 128; + } else + return(0); + + out[rc] = '\0'; + return((size_t)rc); +} + +/* + * Normalise strings from the index and database. + * These strings are escaped as defined by mandoc_char(7) along with + * other goop in mandoc.h (e.g., soft hyphens). + * This function normalises these into a nice UTF-8 string. + * Returns 0 if the database is fucked. + */ +static void +norm_string(const char *val, const struct mchars *mc, char **buf) +{ + size_t sz, bsz; + char utfbuf[7]; + const char *seq, *cpp; + int len, u, pos; + enum mandoc_esc esc; + static const char res[] = { '\\', '\t', + ASCII_NBRSP, ASCII_HYPH, '\0' }; + + /* Pre-allocate by the length of the input */ + + bsz = strlen(val) + 1; + *buf = mandoc_realloc(*buf, bsz); + pos = 0; + + while ('\0' != *val) { + /* + * Halt on the first escape sequence. + * This also halts on the end of string, in which case + * we just copy, fallthrough, and exit the loop. + */ + if ((sz = strcspn(val, res)) > 0) { + memcpy(&(*buf)[pos], val, sz); + pos += (int)sz; + val += (int)sz; + } + + if (ASCII_HYPH == *val) { + (*buf)[pos++] = '-'; + val++; + continue; + } else if ('\t' == *val || ASCII_NBRSP == *val) { + (*buf)[pos++] = ' '; + val++; + continue; + } else if ('\\' != *val) + break; + + /* Read past the slash. */ + + val++; + u = 0; + + /* + * Parse the escape sequence and see if it's a + * predefined character or special character. + */ + + esc = mandoc_escape(&val, &seq, &len); + if (ESCAPE_ERROR == esc) + break; + + /* + * XXX - this just does UTF-8, but we need to know + * beforehand whether we should do text substitution. + */ + + switch (esc) { + case (ESCAPE_SPECIAL): + if (0 != (u = mchars_spec2cp(mc, seq, len))) + break; + /* FALLTHROUGH */ + default: + continue; + } + + /* + * If we have a Unicode codepoint, try to convert that + * to a UTF-8 byte string. + */ + + cpp = utfbuf; + if (0 == (sz = norm_utf8(u, utfbuf))) + continue; + + /* Copy the rendered glyph into the stream. */ + + sz = strlen(cpp); + bsz += sz; + + *buf = mandoc_realloc(*buf, bsz); + + memcpy(&(*buf)[pos], cpp, sz); + pos += (int)sz; + } + + (*buf)[pos] = '\0'; +} + +/* + * Open the filename-index mandoc-db database. + * Returns NULL if opening failed. + */ +static DB * +index_open(void) +{ + DB *db; + + db = dbopen(MANDOC_IDX, O_RDONLY, 0, DB_RECNO, NULL); + if (NULL != db) + return(db); + + return(NULL); +} + +/* + * Safely unpack from an index file record into the structure. + * Returns 1 if an entry was unpacked, 0 if the database is insane. + */ +static int +index_read(const DBT *key, const DBT *val, int index, + const struct mchars *mc, struct res *rec) +{ + size_t left; + char *np, *cp; + char type; + +#define INDEX_BREAD(_dst) \ + do { \ + if (NULL == (np = memchr(cp, '\0', left))) \ + return(0); \ + norm_string(cp, mc, &(_dst)); \ + left -= (np - cp) + 1; \ + cp = np + 1; \ + } while (/* CONSTCOND */ 0) + + if (0 == (left = val->size)) + return(0); + + cp = val->data; + assert(sizeof(recno_t) == key->size); + memcpy(&rec->rec, key->data, key->size); + rec->volume = index; + + if ('d' == (type = *cp++)) + rec->type = RESTYPE_MDOC; + else if ('a' == type) + rec->type = RESTYPE_MAN; + else if ('c' == type) + rec->type = RESTYPE_CAT; + else + return(0); + + left--; + INDEX_BREAD(rec->file); + INDEX_BREAD(rec->cat); + INDEX_BREAD(rec->title); + INDEX_BREAD(rec->arch); + INDEX_BREAD(rec->desc); + return(1); +} + +/* + * Search mandocdb databases in paths for expression "expr". + * Filter out by "opts". + * Call "res" with the results, which may be zero. + * Return 0 if there was a database error, else return 1. + */ +int +apropos_search(int pathsz, char **paths, const struct opts *opts, + const struct expr *expr, size_t terms, void *arg, + size_t *sz, struct res **resp, + void (*res)(struct res *, size_t, void *)) +{ + struct rectree tree; + struct mchars *mc; + int i, rc; + + memset(&tree, 0, sizeof(struct rectree)); + + rc = 0; + mc = mchars_alloc(); + *sz = 0; + *resp = NULL; + + /* + * Main loop. Change into the directory containing manpage + * databases. Run our expession over each database in the set. + */ + + for (i = 0; i < pathsz; i++) { + if (chdir(paths[i])) + continue; + if (single_search(&tree, opts, expr, terms, mc, i)) + continue; + + resfree(tree.node, tree.len); + mchars_free(mc); + return(0); + } + + (*res)(tree.node, tree.len, arg); + *sz = tree.len; + *resp = tree.node; + mchars_free(mc); + return(1); +} + +static int +single_search(struct rectree *tree, const struct opts *opts, + const struct expr *expr, size_t terms, + struct mchars *mc, int vol) +{ + int root, leaf, ch; + DBT key, val; + DB *btree, *idx; + char *buf; + struct res *rs; + struct res r; + uint64_t mask; + recno_t rec; + + root = -1; + leaf = -1; + btree = NULL; + idx = NULL; + buf = NULL; + rs = tree->node; + + memset(&r, 0, sizeof(struct res)); + + if (NULL == (btree = btree_open())) + return(1); + + if (NULL == (idx = index_open())) { + (*btree->close)(btree); + return(1); + } + + while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { + if ( ! btree_read(&key, &val, mc, &mask, &rec, &buf)) + break; + + /* + * See if this keyword record matches any of the + * expressions we have stored. + */ + if ( ! exprmark(expr, buf, mask, NULL)) + continue; + + /* + * O(log n) scan for prior records. Since a record + * number is unbounded, this has decent performance over + * a complex hash function. + */ + + for (leaf = root; leaf >= 0; ) + if (rec > rs[leaf].rec && + rs[leaf].rhs >= 0) + leaf = rs[leaf].rhs; + else if (rec < rs[leaf].rec && + rs[leaf].lhs >= 0) + leaf = rs[leaf].lhs; + else + break; + + /* + * If we find a record, see if it has already evaluated + * to true. If it has, great, just keep going. If not, + * try to evaluate it now and continue anyway. + */ + + if (leaf >= 0 && rs[leaf].rec == rec) { + if (0 == rs[leaf].matched) + exprexec(expr, buf, mask, &rs[leaf]); + continue; + } + + /* + * We have a new file to examine. + * Extract the manpage's metadata from the index + * database, then begin partial evaluation. + */ + + key.data = &rec; + key.size = sizeof(recno_t); + + if (0 != (*idx->get)(idx, &key, &val, 0)) + break; + + r.lhs = r.rhs = -1; + if ( ! index_read(&key, &val, vol, mc, &r)) + break; + + /* XXX: this should be elsewhere, I guess? */ + + if (opts->cat && strcasecmp(opts->cat, r.cat)) + continue; + + if (opts->arch && *r.arch) + if (strcasecmp(opts->arch, r.arch)) + continue; + + tree->node = rs = mandoc_realloc + (rs, (tree->len + 1) * sizeof(struct res)); + + memcpy(&rs[tree->len], &r, sizeof(struct res)); + memset(&r, 0, sizeof(struct res)); + rs[tree->len].matches = + mandoc_calloc(terms, sizeof(int)); + + exprexec(expr, buf, mask, &rs[tree->len]); + + /* Append to our tree. */ + + if (leaf >= 0) { + if (rec > rs[leaf].rec) + rs[leaf].rhs = tree->len; + else + rs[leaf].lhs = tree->len; + } else + root = tree->len; + + tree->len++; + } + + (*btree->close)(btree); + (*idx->close)(idx); + + free(buf); + RESFREE(&r); + return(1 == ch); +} + +void +resfree(struct res *rec, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) + RESFREE(&rec[i]); + free(rec); +} + +/* + * Compile a list of straight-up terms. + * The arguments are re-written into ~[[:<:]]term[[:>:]], or "term" + * surrounded by word boundaries, then pumped through exprterm(). + * Terms are case-insensitive. + * This emulates whatis(1) behaviour. + */ +struct expr * +termcomp(int argc, char *argv[], size_t *tt) +{ + char *buf; + int pos; + struct expr *e, *next; + size_t sz; + + buf = NULL; + e = NULL; + *tt = 0; + + for (pos = argc - 1; pos >= 0; pos--) { + sz = strlen(argv[pos]) + 18; + buf = mandoc_realloc(buf, sz); + strlcpy(buf, "Nm~[[:<:]]", sz); + strlcat(buf, argv[pos], sz); + strlcat(buf, "[[:>:]]", sz); + if (NULL == (next = exprterm(buf, 0))) { + free(buf); + exprfree(e); + return(NULL); + } + next->next = e; + e = next; + (*tt)++; + } + + free(buf); + return(e); +} + +/* + * Compile a sequence of logical expressions. + * See apropos.1 for a grammar of this sequence. + */ +struct expr * +exprcomp(int argc, char *argv[], size_t *tt) +{ + int pos, lvl; + struct expr *e; + + pos = lvl = 0; + *tt = 0; + + e = exprexpr(argc, argv, &pos, &lvl, tt); + + if (0 == lvl && pos >= argc) + return(e); + + exprfree(e); + return(NULL); +} + +/* + * Compile an array of tokens into an expression. + * An informal expression grammar is defined in apropos(1). + * Return NULL if we fail doing so. All memory will be cleaned up. + * Return the root of the expression sequence if alright. + */ +static struct expr * +exprexpr(int argc, char *argv[], int *pos, int *lvl, size_t *tt) +{ + struct expr *e, *first, *next; + int log; + + first = next = NULL; + + for ( ; *pos < argc; (*pos)++) { + e = next; + + /* + * Close out a subexpression. + */ + + if (NULL != e && 0 == strcmp(")", argv[*pos])) { + if (--(*lvl) < 0) + goto err; + break; + } + + /* + * Small note: if we're just starting, don't let "-a" + * and "-o" be considered logical operators: they're + * just tokens unless pairwise joining, in which case we + * record their existence (or assume "OR"). + */ + log = 0; + + if (NULL != e && 0 == strcmp("-a", argv[*pos])) + log = 1; + else if (NULL != e && 0 == strcmp("-o", argv[*pos])) + log = 2; + + if (log > 0 && ++(*pos) >= argc) + goto err; + + /* + * Now we parse the term part. This can begin with + * "-i", in which case the expression is case + * insensitive. + */ + + if (0 == strcmp("(", argv[*pos])) { + ++(*pos); + ++(*lvl); + next = mandoc_calloc(1, sizeof(struct expr)); + next->subexpr = exprexpr(argc, argv, pos, lvl, tt); + if (NULL == next->subexpr) { + free(next); + next = NULL; + } + } else if (0 == strcmp("-i", argv[*pos])) { + if (++(*pos) >= argc) + goto err; + next = exprterm(argv[*pos], 0); + } else + next = exprterm(argv[*pos], 1); + + if (NULL == next) + goto err; + + next->and = log == 1; + next->index = (int)(*tt)++; + + /* Append to our chain of expressions. */ + + if (NULL == first) { + assert(NULL == e); + first = next; + } else { + assert(NULL != e); + e->next = next; + } + } + + return(first); +err: + exprfree(first); + return(NULL); +} + +/* + * Parse a terminal expression with the grammar as defined in + * apropos(1). + * Return NULL if we fail the parse. + */ +static struct expr * +exprterm(char *buf, int cs) +{ + struct expr e; + struct expr *p; + char *key; + int i; + + memset(&e, 0, sizeof(struct expr)); + + /* Choose regex or substring match. */ + + if (NULL == (e.v = strpbrk(buf, "=~"))) { + e.regex = 0; + e.v = buf; + } else { + e.regex = '~' == *e.v; + *e.v++ = '\0'; + } + + /* Determine the record types to search for. */ + + e.mask = 0; + if (buf < e.v) { + while (NULL != (key = strsep(&buf, ","))) { + i = 0; + while (types[i].mask && + strcmp(types[i].name, key)) + i++; + e.mask |= types[i].mask; + } + } + if (0 == e.mask) + e.mask = TYPE_Nm | TYPE_Nd; + + if (e.regex) { + i = REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE); + if (regcomp(&e.re, e.v, i)) + return(NULL); + } + + e.v = mandoc_strdup(e.v); + + p = mandoc_calloc(1, sizeof(struct expr)); + memcpy(p, &e, sizeof(struct expr)); + return(p); +} + +void +exprfree(struct expr *p) +{ + struct expr *pp; + + while (NULL != p) { + if (p->subexpr) + exprfree(p->subexpr); + if (p->regex) + regfree(&p->re); + free(p->v); + pp = p->next; + free(p); + p = pp; + } +} + +static int +exprmark(const struct expr *p, const char *cp, + uint64_t mask, int *ms) +{ + + for ( ; p; p = p->next) { + if (p->subexpr) { + if (exprmark(p->subexpr, cp, mask, ms)) + return(1); + continue; + } else if ( ! (mask & p->mask)) + continue; + + if (p->regex) { + if (regexec(&p->re, cp, 0, NULL, 0)) + continue; + } else if (NULL == strcasestr(cp, p->v)) + continue; + + if (NULL == ms) + return(1); + else + ms[p->index] = 1; + } + + return(0); +} + +static int +expreval(const struct expr *p, int *ms) +{ + int match; + + /* + * AND has precedence over OR. Analysis is left-right, though + * it doesn't matter because there are no side-effects. + * Thus, step through pairwise ANDs and accumulate their Boolean + * evaluation. If we encounter a single true AND collection or + * standalone term, the whole expression is true (by definition + * of OR). + */ + + for (match = 0; p && ! match; p = p->next) { + /* Evaluate a subexpression, if applicable. */ + if (p->subexpr && ! ms[p->index]) + ms[p->index] = expreval(p->subexpr, ms); + + match = ms[p->index]; + for ( ; p->next && p->next->and; p = p->next) { + /* Evaluate a subexpression, if applicable. */ + if (p->next->subexpr && ! ms[p->next->index]) + ms[p->next->index] = + expreval(p->next->subexpr, ms); + match = match && ms[p->next->index]; + } + } + + return(match); +} + +/* + * First, update the array of terms for which this expression evaluates + * to true. + * Second, logically evaluate all terms over the updated array of truth + * values. + * If this evaluates to true, mark the expression as satisfied. + */ +static void +exprexec(const struct expr *e, const char *cp, + uint64_t mask, struct res *r) +{ + + assert(0 == r->matched); + exprmark(e, cp, mask, r->matches); + r->matched = expreval(e, r->matches); +} diff --git a/contrib/mdocml/apropos_db.h b/contrib/mdocml/apropos_db.h new file mode 100644 index 0000000000..72d4c204a3 --- /dev/null +++ b/contrib/mdocml/apropos_db.h @@ -0,0 +1,73 @@ +/* $Id: apropos_db.h,v 1.13 2012/03/24 01:46:25 kristaps Exp $ */ +/* + * Copyright (c) 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. + */ +#ifndef APROPOS_H +#define APROPOS_H + +enum restype { + RESTYPE_MAN, /* man(7) file */ + RESTYPE_MDOC, /* mdoc(7) file */ + RESTYPE_CAT /* pre-formatted file */ +}; + +struct res { + enum restype type; /* input file type */ + char *file; /* file in file-system */ + char *cat; /* category (3p, 3, etc.) */ + char *title; /* title (FOO, etc.) */ + char *arch; /* arch (or empty string) */ + char *desc; /* description (from Nd) */ + unsigned int rec; /* record in index */ + /* + * The index volume. This indexes into the array of directories + * searched for manual page databases. + */ + unsigned int volume; + /* + * The following fields are used internally. + * + * Maintain a binary tree for checking the uniqueness of `rec' + * when adding elements to the results array. + * Since the results array is dynamic, use offset in the array + * instead of a pointer to the structure. + */ + int lhs; + int rhs; + int matched; /* expression is true */ + int *matches; /* partial truth evaluations */ +}; + +struct opts { + const char *arch; /* restrict to architecture */ + const char *cat; /* restrict to manual section */ +}; + +__BEGIN_DECLS + +struct expr; + +int apropos_search(int, char **, const struct opts *, + const struct expr *, size_t, + void *, size_t *, struct res **, + void (*)(struct res *, size_t, void *)); +struct expr *exprcomp(int, char *[], size_t *); +void exprfree(struct expr *); +void resfree(struct res *, size_t); +struct expr *termcomp(int, char *[], size_t *); + +__END_DECLS + +#endif /*!APROPOS_H*/ diff --git a/contrib/mdocml/arch.in b/contrib/mdocml/arch.in index 41543a9afc..5113446e46 100644 --- a/contrib/mdocml/arch.in +++ b/contrib/mdocml/arch.in @@ -1,4 +1,4 @@ -/* $Id: arch.in,v 1.10 2010/09/27 06:56:44 kristaps Exp $ */ +/* $Id: arch.in,v 1.12 2012/01/28 14:02:17 joerg Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -26,31 +26,86 @@ * REMEMBER TO ADD NEW ARCHITECTURES TO MDOC.7! */ +LINE("acorn26", "Acorn26") +LINE("acorn32", "Acorn32") +LINE("algor", "Algor") LINE("alpha", "Alpha") LINE("amd64", "AMD64") LINE("amiga", "Amiga") +LINE("amigappc", "AmigaPPC") LINE("arc", "ARC") LINE("arm", "ARM") +LINE("arm26", "ARM26") +LINE("arm32", "ARM32") LINE("armish", "ARMISH") LINE("aviion", "AViiON") +LINE("atari", "ATARI") +LINE("beagle", "Beagle") +LINE("bebox", "BeBox") +LINE("cats", "cats") +LINE("cesfic", "CESFIC") +LINE("cobalt", "Cobalt") +LINE("dreamcast", "Dreamcast") +LINE("emips", "EMIPS") +LINE("evbarm", "evbARM") +LINE("evbmips", "evbMIPS") +LINE("evbppc", "evbPPC") +LINE("evbsh3", "evbSH3") +LINE("ews4800mips", "EWS4800MIPS") LINE("hp300", "HP300") +LINE("hp700", "HP700") +LINE("hpcarm", "HPCARM") +LINE("hpcmips", "HPCMIPS") +LINE("hpcsh", "HPCSH") LINE("hppa", "HPPA") LINE("hppa64", "HPPA64") +LINE("ia64", "ia64") LINE("i386", "i386") +LINE("ibmnws", "IBMNWS") +LINE("iyonix", "Iyonix") LINE("landisk", "LANDISK") LINE("loongson", "Loongson") +LINE("luna68k", "Luna68k") LINE("luna88k", "Luna88k") +LINE("m68k", "m68k") LINE("mac68k", "Mac68k") LINE("macppc", "MacPPC") +LINE("mips", "MIPS") LINE("mips64", "MIPS64") +LINE("mipsco", "MIPSCo") +LINE("mmeye", "mmEye") LINE("mvme68k", "MVME68k") LINE("mvme88k", "MVME88k") LINE("mvmeppc", "MVMEPPC") +LINE("netwinder", "NetWinder") +LINE("news68k", "NeWS68k") +LINE("newsmips", "NeWSMIPS") +LINE("next68k", "NeXT68k") +LINE("ofppc", "OFPPC") +LINE("palm", "Palm") +LINE("pc532", "PC532") +LINE("playstation2", "PlayStation2") LINE("pmax", "PMAX") +LINE("pmppc", "pmPPC") +LINE("powerpc", "PowerPC") +LINE("prep", "PReP") +LINE("rs6000", "RS6000") +LINE("sandpoint", "Sandpoint") +LINE("sbmips", "SBMIPS") LINE("sgi", "SGI") +LINE("sgimips", "SGIMIPS") +LINE("sh3", "SH3") +LINE("shark", "Shark") LINE("socppc", "SOCPPC") +LINE("solbourne", "Solbourne") LINE("sparc", "SPARC") LINE("sparc64", "SPARC64") +LINE("sun2", "Sun2") LINE("sun3", "Sun3") +LINE("tahoe", "Tahoe") LINE("vax", "VAX") +LINE("x68k", "X68k") +LINE("x86", "x86") +LINE("x86_64", "x86_64") +LINE("xen", "Xen") LINE("zaurus", "Zaurus") diff --git a/contrib/mdocml/att.in b/contrib/mdocml/att.in index 95af2ef22f..b4ef822158 100644 --- a/contrib/mdocml/att.in +++ b/contrib/mdocml/att.in @@ -1,4 +1,4 @@ -/* $Id: att.in,v 1.7 2011/04/24 17:56:44 schwarze Exp $ */ +/* $Id: att.in,v 1.8 2011/07/31 17:30:33 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -32,6 +32,7 @@ LINE("v5", "Version\\~5 AT&T UNIX") LINE("v6", "Version\\~6 AT&T UNIX") LINE("v7", "Version\\~7 AT&T UNIX") LINE("32v", "Version\\~32V AT&T UNIX") +LINE("III", "AT&T System\\~III UNIX") LINE("V", "AT&T System\\~V UNIX") LINE("V.1", "AT&T System\\~V Release\\~1 UNIX") LINE("V.2", "AT&T System\\~V Release\\~2 UNIX") diff --git a/contrib/mdocml/catman.8 b/contrib/mdocml/catman.8 new file mode 100644 index 0000000000..f5246f9a6c --- /dev/null +++ b/contrib/mdocml/catman.8 @@ -0,0 +1,111 @@ +.\" $Id: catman.8,v 1.5 2011/12/25 19:35:44 kristaps Exp $ +.\" +.\" Copyright (c) 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: December 25 2011 $ +.Dt CATMAN 8 +.Os +.Sh NAME +.Nm catman +.Nd update a man.cgi manpage cache +.Sh SYNOPSIS +.Nm catman +.Op Fl fv +.Op Fl C Ar file +.Op Fl M Ar manpath +.Op Fl m Ar manpath +.Op Fl o Ar path +.Sh DESCRIPTION +The +.Nm +utility updates cached manpages for a jailed +.Xr man.cgi 7 . +.Pp +By default, +.Nm +searches for +.Xr mandocdb 8 +databases in the default paths stipulated by +.Xr man 1 +and updates the cache in +.Pa /var/www/cache/man.cgi . +.Pp +Its arguments are as follows: +.Bl -tag -width Ds +.It Fl f +Force an update to all files. +.It Fl v +Print each file being updated. +.It Fl C Ar file +Specify an alternative configuration +.Ar file +in +.Xr man.conf 5 +format. +.It Fl M Ar manpath +Use the colon-separated path instead of the default list of paths +searched for +.Xr mandocdb 8 +databases. +Invalid paths, or paths without manual databases, are ignored. +.It Fl m Ar manpath +Prepend the colon-separated paths to the list of paths searched +for +.Xr mandocdb 8 +databases. +Invalid paths, or paths without manual databases, are ignored. +.It Fl o Ar path +Update into the directory tree under +.Ar path . +.El +.Pp +Cache updates occur when a +.Xr mandocdb 8 +database is older than the cached copy unless +.Fl f +is specified, in which case files are always considered out of date. +Cached manual pages are only updated if older than the master copy. +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev MANPATH +Colon-separated paths modifying the default list of paths searched for +manual databases. +Invalid paths, or paths without manual databases, are ignored. +Overridden by +.Fl M . +If +.Ev MANPATH +begins with a +.Sq \&: , +it is appended to the default list; +else if it ends with +.Sq \&: , +it is prepended to the default list; else if it contains +.Sq \&:: , +the default list is inserted between the colons. +If none of these conditions are met, it overrides the default list. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr man.cgi 7 , +.Xr mandocdb 8 +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/catman.c b/contrib/mdocml/catman.c new file mode 100644 index 0000000000..1d313ea6e0 --- /dev/null +++ b/contrib/mdocml/catman.c @@ -0,0 +1,511 @@ +/* $Id: catman.c,v 1.10 2012/01/03 15:17:20 kristaps Exp $ */ +/* + * Copyright (c) 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# include +#else +# include +#endif + +#include "manpath.h" +#include "mandocdb.h" + +#define xstrlcpy(_dst, _src, _sz) \ + do if (strlcpy((_dst), (_src), (_sz)) >= (_sz)) { \ + fprintf(stderr, "%s: Path too long", (_dst)); \ + exit(EXIT_FAILURE); \ + } while (/* CONSTCOND */0) + +#define xstrlcat(_dst, _src, _sz) \ + do if (strlcat((_dst), (_src), (_sz)) >= (_sz)) { \ + fprintf(stderr, "%s: Path too long", (_dst)); \ + exit(EXIT_FAILURE); \ + } while (/* CONSTCOND */0) + +static int indexhtml(char *, size_t, char *, size_t); +static int manup(const struct manpaths *, char *); +static int mkpath(char *, mode_t, mode_t); +static int treecpy(char *, char *); +static int update(char *, char *); +static void usage(void); + +static const char *progname; +static int verbose; +static int force; + +int +main(int argc, char *argv[]) +{ + int ch; + char *aux, *base, *conf_file; + struct manpaths dirs; + char buf[MAXPATHLEN]; + extern char *optarg; + extern int optind; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + ++progname; + + aux = base = conf_file = NULL; + xstrlcpy(buf, "/var/www/cache/man.cgi", MAXPATHLEN); + + while (-1 != (ch = getopt(argc, argv, "C:fm:M:o:v"))) + switch (ch) { + case ('C'): + conf_file = optarg; + break; + case ('f'): + force = 1; + break; + case ('m'): + aux = optarg; + break; + case ('M'): + base = optarg; + break; + case ('o'): + xstrlcpy(buf, optarg, MAXPATHLEN); + break; + case ('v'): + verbose++; + break; + default: + usage(); + return(EXIT_FAILURE); + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + usage(); + return(EXIT_FAILURE); + } + + memset(&dirs, 0, sizeof(struct manpaths)); + manpath_parse(&dirs, conf_file, base, aux); + ch = manup(&dirs, buf); + manpath_free(&dirs); + return(ch ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s " + "[-fv] " + "[-C file] " + "[-o path] " + "[-m manpath] " + "[-M manpath]\n", + progname); +} + +/* + * If "src" file doesn't exist (errors out), return -1. Otherwise, + * return 1 if "src" is newer (which also happens "dst" doesn't exist) + * and 0 otherwise. + */ +static int +isnewer(const char *dst, const char *src) +{ + struct stat s1, s2; + + if (-1 == stat(src, &s1)) + return(-1); + if (force) + return(1); + + return(-1 == stat(dst, &s2) ? 1 : s1.st_mtime > s2.st_mtime); +} + +/* + * Copy the contents of one file into another. + * Returns 0 on failure, 1 on success. + */ +static int +filecpy(const char *dst, const char *src) +{ + char buf[BUFSIZ]; + int sfd, dfd, rc; + ssize_t rsz, wsz; + + sfd = dfd = -1; + rc = 0; + + if (-1 == (dfd = open(dst, O_CREAT|O_TRUNC|O_WRONLY, 0644))) { + perror(dst); + goto out; + } else if (-1 == (sfd = open(src, O_RDONLY, 0))) { + perror(src); + goto out; + } + + while ((rsz = read(sfd, buf, BUFSIZ)) > 0) + if (-1 == (wsz = write(dfd, buf, (size_t)rsz))) { + perror(dst); + goto out; + } else if (wsz < rsz) { + fprintf(stderr, "%s: Short write\n", dst); + goto out; + } + + if (rsz < 0) + perror(src); + else + rc = 1; +out: + if (-1 != sfd) + close(sfd); + if (-1 != dfd) + close(dfd); + + return(rc); +} + +/* + * Pass over the recno database and re-create HTML pages if they're + * found to be out of date. + * Returns -1 on fatal error, 1 on success. + */ +static int +indexhtml(char *src, size_t ssz, char *dst, size_t dsz) +{ + DB *idx; + DBT key, val; + int c, rc; + unsigned int fl; + const char *f; + char *d; + char fname[MAXPATHLEN]; + pid_t pid; + + pid = -1; + + xstrlcpy(fname, dst, MAXPATHLEN); + xstrlcat(fname, "/", MAXPATHLEN); + xstrlcat(fname, MANDOC_IDX, MAXPATHLEN); + + idx = dbopen(fname, O_RDONLY, 0, DB_RECNO, NULL); + if (NULL == idx) { + perror(fname); + return(-1); + } + + fl = R_FIRST; + while (0 == (c = (*idx->seq)(idx, &key, &val, fl))) { + fl = R_NEXT; + /* + * If the record is zero-length, then it's unassigned. + * Skip past these. + */ + if (0 == val.size) + continue; + + f = (const char *)val.data + 1; + if (NULL == memchr(f, '\0', val.size - 1)) + break; + + src[(int)ssz] = dst[(int)dsz] = '\0'; + + xstrlcat(dst, "/", MAXPATHLEN); + xstrlcat(dst, f, MAXPATHLEN); + + xstrlcat(src, "/", MAXPATHLEN); + xstrlcat(src, f, MAXPATHLEN); + + if (-1 == (rc = isnewer(dst, src))) { + fprintf(stderr, "%s: File missing\n", f); + break; + } else if (0 == rc) + continue; + + d = strrchr(dst, '/'); + assert(NULL != d); + *d = '\0'; + + if (-1 == mkpath(dst, 0755, 0755)) { + perror(dst); + break; + } + + *d = '/'; + + if ( ! filecpy(dst, src)) + break; + if (verbose) + printf("%s\n", dst); + } + + (*idx->close)(idx); + + if (c < 0) + perror(fname); + else if (0 == c) + fprintf(stderr, "%s: Corrupt index\n", fname); + + return(1 == c ? 1 : -1); +} + +/* + * Copy both recno and btree databases into the destination. + * Call in to begin recreating HTML files. + * Return -1 on fatal error and 1 if the update went well. + */ +static int +update(char *dst, char *src) +{ + size_t dsz, ssz; + + dsz = strlen(dst); + ssz = strlen(src); + + xstrlcat(src, "/", MAXPATHLEN); + xstrlcat(dst, "/", MAXPATHLEN); + + xstrlcat(src, MANDOC_DB, MAXPATHLEN); + xstrlcat(dst, MANDOC_DB, MAXPATHLEN); + + if ( ! filecpy(dst, src)) + return(-1); + if (verbose) + printf("%s\n", dst); + + dst[(int)dsz] = src[(int)ssz] = '\0'; + + xstrlcat(src, "/", MAXPATHLEN); + xstrlcat(dst, "/", MAXPATHLEN); + + xstrlcat(src, MANDOC_IDX, MAXPATHLEN); + xstrlcat(dst, MANDOC_IDX, MAXPATHLEN); + + if ( ! filecpy(dst, src)) + return(-1); + if (verbose) + printf("%s\n", dst); + + dst[(int)dsz] = src[(int)ssz] = '\0'; + + return(indexhtml(src, ssz, dst, dsz)); +} + +/* + * See if btree or recno databases in the destination are out of date + * with respect to a single manpath component. + * Return -1 on fatal error, 0 if the source is no longer valid (and + * shouldn't be listed), and 1 if the update went well. + */ +static int +treecpy(char *dst, char *src) +{ + size_t dsz, ssz; + int rc; + + dsz = strlen(dst); + ssz = strlen(src); + + xstrlcat(src, "/", MAXPATHLEN); + xstrlcat(dst, "/", MAXPATHLEN); + + xstrlcat(src, MANDOC_IDX, MAXPATHLEN); + xstrlcat(dst, MANDOC_IDX, MAXPATHLEN); + + if (-1 == (rc = isnewer(dst, src))) + return(0); + + dst[(int)dsz] = src[(int)ssz] = '\0'; + + if (1 == rc) + return(update(dst, src)); + + xstrlcat(src, "/", MAXPATHLEN); + xstrlcat(dst, "/", MAXPATHLEN); + + xstrlcat(src, MANDOC_DB, MAXPATHLEN); + xstrlcat(dst, MANDOC_DB, MAXPATHLEN); + + if (-1 == (rc = isnewer(dst, src))) + return(0); + else if (rc == 0) + return(1); + + dst[(int)dsz] = src[(int)ssz] = '\0'; + + return(update(dst, src)); +} + +/* + * Update the destination's file-tree with respect to changes in the + * source manpath components. + * "Change" is defined by an updated index or btree database. + * Returns 1 on success, 0 on failure. + */ +static int +manup(const struct manpaths *dirs, char *base) +{ + char dst[MAXPATHLEN], + src[MAXPATHLEN]; + const char *path; + int i, c; + size_t sz; + FILE *f; + + /* Create the path and file for the catman.conf file. */ + + sz = strlen(base); + xstrlcpy(dst, base, MAXPATHLEN); + xstrlcat(dst, "/etc", MAXPATHLEN); + if (-1 == mkpath(dst, 0755, 0755)) { + perror(dst); + return(0); + } + + xstrlcat(dst, "/catman.conf", MAXPATHLEN); + if (NULL == (f = fopen(dst, "w"))) { + perror(dst); + return(0); + } else if (verbose) + printf("%s\n", dst); + + for (i = 0; i < dirs->sz; i++) { + path = dirs->paths[i]; + dst[(int)sz] = '\0'; + xstrlcat(dst, path, MAXPATHLEN); + if (-1 == mkpath(dst, 0755, 0755)) { + perror(dst); + break; + } + + xstrlcpy(src, path, MAXPATHLEN); + if (-1 == (c = treecpy(dst, src))) + break; + else if (0 == c) + continue; + + /* + * We want to use a relative path here because manpath.h + * will realpath() when invoked with man.cgi, and we'll + * make sure to chdir() into the cache directory before. + * + * This allows the cache directory to be in an arbitrary + * place, working in both chroot() and non-chroot() + * "safe" modes. + */ + assert('/' == path[0]); + fprintf(f, "_whatdb %s/whatis.db\n", path + 1); + } + + fclose(f); + return(i == dirs->sz); +} + +/* + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +static int +mkpath(char *path, mode_t mode, mode_t dir_mode) +{ + struct stat sb; + char *slash; + int done, exists; + + slash = path; + + for (;;) { + /* LINTED */ + slash += strspn(slash, "/"); + /* LINTED */ + slash += strcspn(slash, "/"); + + done = (*slash == '\0'); + *slash = '\0'; + + /* skip existing path components */ + exists = !stat(path, &sb); + if (!done && exists && S_ISDIR(sb.st_mode)) { + *slash = '/'; + continue; + } + + if (mkdir(path, done ? mode : dir_mode) == 0) { + if (mode > 0777 && chmod(path, mode) < 0) + return (-1); + } else { + if (!exists) { + /* Not there */ + return (-1); + } + if (!S_ISDIR(sb.st_mode)) { + /* Is there, but isn't a directory */ + errno = ENOTDIR; + return (-1); + } + } + + if (done) + break; + + *slash = '/'; + } + + return (0); +} diff --git a/contrib/mdocml/cgi.c b/contrib/mdocml/cgi.c new file mode 100644 index 0000000000..2f5870ff8e --- /dev/null +++ b/contrib/mdocml/cgi.c @@ -0,0 +1,1203 @@ +/* $Id: cgi.c,v 1.42 2012/03/24 01:46:25 kristaps Exp $ */ +/* + * Copyright (c) 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "apropos_db.h" +#include "mandoc.h" +#include "mdoc.h" +#include "man.h" +#include "main.h" +#include "manpath.h" +#include "mandocdb.h" + +#ifdef __linux__ +# include +#else +# include +#endif + +enum page { + PAGE_INDEX, + PAGE_SEARCH, + PAGE_SHOW, + PAGE__MAX +}; + +struct paths { + char *name; + char *path; +}; + +/* + * A query as passed to the search function. + */ +struct query { + const char *arch; /* architecture */ + const char *sec; /* manual section */ + const char *expr; /* unparsed expression string */ + int manroot; /* manroot index (or -1)*/ + int legacy; /* whether legacy mode */ +}; + +struct req { + struct query q; + struct paths *p; + size_t psz; + enum page page; +}; + +static int atou(const char *, unsigned *); +static void catman(const struct req *, const char *); +static int cmp(const void *, const void *); +static void format(const struct req *, const char *); +static void html_print(const char *); +static void html_printquery(const struct req *); +static void html_putchar(char); +static int http_decode(char *); +static void http_parse(struct req *, char *); +static void http_print(const char *); +static void http_putchar(char); +static void http_printquery(const struct req *); +static int pathstop(DIR *); +static void pathgen(DIR *, char *, struct req *); +static void pg_index(const struct req *, char *); +static void pg_search(const struct req *, char *); +static void pg_show(const struct req *, char *); +static void resp_bad(void); +static void resp_baddb(void); +static void resp_error400(void); +static void resp_error404(const char *); +static void resp_begin_html(int, const char *); +static void resp_begin_http(int, const char *); +static void resp_end_html(void); +static void resp_index(const struct req *); +static void resp_search(struct res *, size_t, void *); +static void resp_searchform(const struct req *); + +static const char *progname; /* cgi script name */ +static const char *cache; /* cache directory */ +static const char *css; /* css directory */ +static const char *host; /* hostname */ + +static const char * const pages[PAGE__MAX] = { + "index", /* PAGE_INDEX */ + "search", /* PAGE_SEARCH */ + "show", /* PAGE_SHOW */ +}; + +/* + * This is just OpenBSD's strtol(3) suggestion. + * I use it instead of strtonum(3) for portability's sake. + */ +static int +atou(const char *buf, unsigned *v) +{ + char *ep; + long lval; + + errno = 0; + lval = strtol(buf, &ep, 10); + if (buf[0] == '\0' || *ep != '\0') + return(0); + if ((errno == ERANGE && (lval == LONG_MAX || + lval == LONG_MIN)) || + (lval > INT_MAX || lval < 0)) + return(0); + + *v = (unsigned int)lval; + return(1); +} + +/* + * Print a character, escaping HTML along the way. + * This will pass non-ASCII straight to output: be warned! + */ +static void +html_putchar(char c) +{ + + switch (c) { + case ('"'): + printf(""e;"); + break; + case ('&'): + printf("&"); + break; + case ('>'): + printf(">"); + break; + case ('<'): + printf("<"); + break; + default: + putchar((unsigned char)c); + break; + } +} +static void +http_printquery(const struct req *req) +{ + + printf("&expr="); + http_print(req->q.expr ? req->q.expr : ""); + printf("&sec="); + http_print(req->q.sec ? req->q.sec : ""); + printf("&arch="); + http_print(req->q.arch ? req->q.arch : ""); +} + + +static void +html_printquery(const struct req *req) +{ + + printf("&expr="); + html_print(req->q.expr ? req->q.expr : ""); + printf("&sec="); + html_print(req->q.sec ? req->q.sec : ""); + printf("&arch="); + html_print(req->q.arch ? req->q.arch : ""); +} + +static void +http_print(const char *p) +{ + + if (NULL == p) + return; + while ('\0' != *p) + http_putchar(*p++); +} + +/* + * Call through to html_putchar(). + * Accepts NULL strings. + */ +static void +html_print(const char *p) +{ + + if (NULL == p) + return; + while ('\0' != *p) + html_putchar(*p++); +} + +/* + * Parse out key-value pairs from an HTTP request variable. + * This can be either a cookie or a POST/GET string, although man.cgi + * uses only GET for simplicity. + */ +static void +http_parse(struct req *req, char *p) +{ + char *key, *val, *manroot; + int i, legacy; + + memset(&req->q, 0, sizeof(struct query)); + + legacy = -1; + manroot = NULL; + + while ('\0' != *p) { + key = p; + val = NULL; + + p += (int)strcspn(p, ";&"); + if ('\0' != *p) + *p++ = '\0'; + if (NULL != (val = strchr(key, '='))) + *val++ = '\0'; + + if ('\0' == *key || NULL == val || '\0' == *val) + continue; + + /* Just abort handling. */ + + if ( ! http_decode(key)) + break; + if (NULL != val && ! http_decode(val)) + break; + + if (0 == strcmp(key, "expr")) + req->q.expr = val; + else if (0 == strcmp(key, "query")) + req->q.expr = val; + else if (0 == strcmp(key, "sec")) + req->q.sec = val; + else if (0 == strcmp(key, "sektion")) + req->q.sec = val; + else if (0 == strcmp(key, "arch")) + req->q.arch = val; + else if (0 == strcmp(key, "manpath")) + manroot = val; + else if (0 == strcmp(key, "apropos")) + legacy = 0 == strcmp(val, "0"); + } + + /* Test for old man.cgi compatibility mode. */ + + req->q.legacy = legacy > 0; + + /* + * Section "0" means no section when in legacy mode. + * For some man.cgi scripts, "default" arch is none. + */ + + if (req->q.legacy && NULL != req->q.sec) + if (0 == strcmp(req->q.sec, "0")) + req->q.sec = NULL; + if (req->q.legacy && NULL != req->q.arch) + if (0 == strcmp(req->q.arch, "default")) + req->q.arch = NULL; + + /* Default to first manroot. */ + + if (NULL != manroot) { + for (i = 0; i < (int)req->psz; i++) + if (0 == strcmp(req->p[i].name, manroot)) + break; + req->q.manroot = i < (int)req->psz ? i : -1; + } +} + +static void +http_putchar(char c) +{ + + if (isalnum((unsigned char)c)) { + putchar((unsigned char)c); + return; + } else if (' ' == c) { + putchar('+'); + return; + } + printf("%%%.2x", c); +} + +/* + * HTTP-decode a string. The standard explanation is that this turns + * "%4e+foo" into "n foo" in the regular way. This is done in-place + * over the allocated string. + */ +static int +http_decode(char *p) +{ + char hex[3]; + int c; + + hex[2] = '\0'; + + for ( ; '\0' != *p; p++) { + if ('%' == *p) { + if ('\0' == (hex[0] = *(p + 1))) + return(0); + if ('\0' == (hex[1] = *(p + 2))) + return(0); + if (1 != sscanf(hex, "%x", &c)) + return(0); + if ('\0' == c) + return(0); + + *p = (char)c; + memmove(p + 1, p + 3, strlen(p + 3) + 1); + } else + *p = '+' == *p ? ' ' : *p; + } + + *p = '\0'; + return(1); +} + +static void +resp_begin_http(int code, const char *msg) +{ + + if (200 != code) + printf("Status: %d %s\n", code, msg); + + puts("Content-Type: text/html; charset=utf-8\n" + "Cache-Control: no-cache\n" + "Pragma: no-cache\n" + ""); + + fflush(stdout); +} + +static void +resp_begin_html(int code, const char *msg) +{ + + resp_begin_http(code, msg); + + printf("\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "System Manpage Reference\n" + "\n" + "\n" + "\n", css, css); +} + +static void +resp_end_html(void) +{ + + puts("\n" + ""); +} + +static void +resp_searchform(const struct req *req) +{ + int i; + + puts(""); + printf("
\n" + "
\n" + "
\n" + "Search Parameters\n" + " for manuals satisfying \n" + "q.expr ? req->q.expr : ""); + printf("\">, section " + "q.sec ? req->q.sec : ""); + printf("\">, arch " + "q.arch ? req->q.arch : ""); + printf("\">"); + if (req->psz > 1) { + puts(", "); + } + puts(".\n" + "\n" + "
\n" + "
\n" + "
"); + puts(""); +} + +static void +resp_index(const struct req *req) +{ + + resp_begin_html(200, NULL); + resp_searchform(req); + resp_end_html(); +} + +static void +resp_error400(void) +{ + + resp_begin_html(400, "Query Malformed"); + printf("

Malformed Query

\n" + "

\n" + "The query your entered was malformed.\n" + "Try again from the\n" + "main page.\n" + "

", progname); + resp_end_html(); +} + +static void +resp_error404(const char *page) +{ + + resp_begin_html(404, "Not Found"); + puts("

Page Not Found

\n" + "

\n" + "The page you're looking for, "); + printf(""); + html_print(page); + printf(",\n" + "could not be found.\n" + "Try searching from the\n" + "main page.\n" + "

", progname); + resp_end_html(); +} + +static void +resp_bad(void) +{ + resp_begin_html(500, "Internal Server Error"); + puts("

Generic badness happened.

"); + resp_end_html(); +} + +static void +resp_baddb(void) +{ + + resp_begin_html(500, "Internal Server Error"); + puts("

Your database is broken.

"); + resp_end_html(); +} + +static void +resp_search(struct res *r, size_t sz, void *arg) +{ + size_t i, matched; + const struct req *req; + + req = (const struct req *)arg; + + if (sz > 0) + assert(req->q.manroot >= 0); + + for (matched = i = 0; i < sz; i++) + if (r[i].matched) + matched++; + + if (1 == matched) { + for (i = 0; i < sz; i++) + if (r[i].matched) + break; + /* + * If we have just one result, then jump there now + * without any delay. + */ + puts("Status: 303 See Other"); + printf("Location: http://%s%s/show/%d/%u/%u.html?", + host, progname, req->q.manroot, + r[i].volume, r[i].rec); + http_printquery(req); + puts("\n" + "Content-Type: text/html; charset=utf-8\n"); + return; + } + + resp_begin_html(200, NULL); + resp_searchform(req); + + puts("
"); + + if (0 == matched) { + puts("

\n" + "No results found.\n" + "

\n" + "
"); + resp_end_html(); + return; + } + + qsort(r, sz, sizeof(struct res), cmp); + + puts(""); + + for (i = 0; i < sz; i++) { + if ( ! r[i].matched) + continue; + printf("\n" + "\n" + "\n" + ""); + } + + puts("
\n" + "q.manroot, + r[i].volume, r[i].rec); + html_printquery(req); + printf("\">"); + html_print(r[i].title); + putchar('('); + html_print(r[i].cat); + if (r[i].arch && '\0' != *r[i].arch) { + putchar('/'); + html_print(r[i].arch); + } + printf(")\n" + ""); + html_print(r[i].desc); + puts("
\n" + ""); + resp_end_html(); +} + +/* ARGSUSED */ +static void +pg_index(const struct req *req, char *path) +{ + + resp_index(req); +} + +static void +catman(const struct req *req, const char *file) +{ + FILE *f; + size_t len; + int i; + char *p; + int italic, bold; + + if (NULL == (f = fopen(file, "r"))) { + resp_baddb(); + return; + } + + resp_begin_html(200, NULL); + resp_searchform(req); + puts("
\n" + "
");
+
+	while (NULL != (p = fgetln(f, &len))) {
+		bold = italic = 0;
+		for (i = 0; i < (int)len - 1; i++) {
+			/* 
+			 * This means that the catpage is out of state.
+			 * Ignore it and keep going (although the
+			 * catpage is bogus).
+			 */
+
+			if ('\b' == p[i] || '\n' == p[i])
+				continue;
+
+			/*
+			 * Print a regular character.
+			 * Close out any bold/italic scopes.
+			 * If we're in back-space mode, make sure we'll
+			 * have something to enter when we backspace.
+			 */
+
+			if ('\b' != p[i + 1]) {
+				if (italic)
+					printf("");
+				if (bold)
+					printf("");
+				italic = bold = 0;
+				html_putchar(p[i]);
+				continue;
+			} else if (i + 2 >= (int)len)
+				continue;
+
+			/* Italic mode. */
+
+			if ('_' == p[i]) {
+				if (bold)
+					printf("");
+				if ( ! italic)
+					printf("");
+				bold = 0;
+				italic = 1;
+				i += 2;
+				html_putchar(p[i]);
+				continue;
+			}
+
+			/* 
+			 * Handle funny behaviour troff-isms.
+			 * These grok'd from the original man2html.c.
+			 */
+
+			if (('+' == p[i] && 'o' == p[i + 2]) ||
+					('o' == p[i] && '+' == p[i + 2]) ||
+					('|' == p[i] && '=' == p[i + 2]) ||
+					('=' == p[i] && '|' == p[i + 2]) ||
+					('*' == p[i] && '=' == p[i + 2]) ||
+					('=' == p[i] && '*' == p[i + 2]) ||
+					('*' == p[i] && '|' == p[i + 2]) ||
+					('|' == p[i] && '*' == p[i + 2]))  {
+				if (italic)
+					printf("");
+				if (bold)
+					printf("");
+				italic = bold = 0;
+				putchar('*');
+				i += 2;
+				continue;
+			} else if (('|' == p[i] && '-' == p[i + 2]) ||
+					('-' == p[i] && '|' == p[i + 1]) ||
+					('+' == p[i] && '-' == p[i + 1]) ||
+					('-' == p[i] && '+' == p[i + 1]) ||
+					('+' == p[i] && '|' == p[i + 1]) ||
+					('|' == p[i] && '+' == p[i + 1]))  {
+				if (italic)
+					printf("");
+				if (bold)
+					printf("");
+				italic = bold = 0;
+				putchar('+');
+				i += 2;
+				continue;
+			}
+
+			/* Bold mode. */
+			
+			if (italic)
+				printf("");
+			if ( ! bold)
+				printf("");
+			bold = 1;
+			italic = 0;
+			i += 2;
+			html_putchar(p[i]);
+		}
+
+		/* 
+		 * Clean up the last character.
+		 * We can get to a newline; don't print that. 
+		 */
+
+		if (italic)
+			printf("");
+		if (bold)
+			printf("");
+
+		if (i == (int)len - 1 && '\n' != p[i])
+			html_putchar(p[i]);
+
+		putchar('\n');
+	}
+
+	puts("
\n" + "
\n" + "\n" + ""); + + fclose(f); +} + +static void +format(const struct req *req, const char *file) +{ + struct mparse *mp; + int fd; + struct mdoc *mdoc; + struct man *man; + void *vp; + enum mandoclevel rc; + char opts[MAXPATHLEN + 128]; + + if (-1 == (fd = open(file, O_RDONLY, 0))) { + resp_baddb(); + return; + } + + mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); + rc = mparse_readfd(mp, fd, file); + close(fd); + + if (rc >= MANDOCLEVEL_FATAL) { + resp_baddb(); + return; + } + + snprintf(opts, sizeof(opts), "fragment," + "man=%s/search.html?sec=%%S&expr=%%N," + /*"includes=/cgi-bin/man.cgi/usr/include/%%I"*/, + progname); + + mparse_result(mp, &mdoc, &man); + if (NULL == man && NULL == mdoc) { + resp_baddb(); + mparse_free(mp); + return; + } + + resp_begin_html(200, NULL); + resp_searchform(req); + + vp = html_alloc(opts); + + if (NULL != mdoc) + html_mdoc(vp, mdoc); + else + html_man(vp, man); + + puts("\n" + ""); + + html_free(vp); + mparse_free(mp); +} + +static void +pg_show(const struct req *req, char *path) +{ + struct manpaths ps; + size_t sz; + char *sub; + char file[MAXPATHLEN]; + const char *cp; + int rc, catm; + unsigned int vol, rec, mr; + DB *idx; + DBT key, val; + + idx = NULL; + + /* Parse out mroot, volume, and record from the path. */ + + if (NULL == path || NULL == (sub = strchr(path, '/'))) { + resp_error400(); + return; + } + *sub++ = '\0'; + if ( ! atou(path, &mr)) { + resp_error400(); + return; + } + path = sub; + if (NULL == (sub = strchr(path, '/'))) { + resp_error400(); + return; + } + *sub++ = '\0'; + if ( ! atou(path, &vol) || ! atou(sub, &rec)) { + resp_error400(); + return; + } else if (mr >= (unsigned int)req->psz) { + resp_error400(); + return; + } + + /* + * Begin by chdir()ing into the manroot. + * This way we can pick up the database files, which are + * relative to the manpath root. + */ + + if (-1 == chdir(req->p[(int)mr].path)) { + perror(req->p[(int)mr].path); + resp_baddb(); + return; + } + + memset(&ps, 0, sizeof(struct manpaths)); + manpath_manconf(&ps, "etc/catman.conf"); + + if (vol >= (unsigned int)ps.sz) { + resp_error400(); + goto out; + } + + sz = strlcpy(file, ps.paths[vol], MAXPATHLEN); + assert(sz < MAXPATHLEN); + strlcat(file, "/", MAXPATHLEN); + strlcat(file, MANDOC_IDX, MAXPATHLEN); + + /* Open the index recno(3) database. */ + + idx = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL); + if (NULL == idx) { + perror(file); + resp_baddb(); + goto out; + } + + key.data = &rec; + key.size = 4; + + if (0 != (rc = (*idx->get)(idx, &key, &val, 0))) { + rc < 0 ? resp_baddb() : resp_error400(); + goto out; + } else if (0 == val.size) { + resp_baddb(); + goto out; + } + + cp = (char *)val.data; + catm = 'c' == *cp++; + + if (NULL == memchr(cp, '\0', val.size - 1)) + resp_baddb(); + else { + file[(int)sz] = '\0'; + strlcat(file, "/", MAXPATHLEN); + strlcat(file, cp, MAXPATHLEN); + if (catm) + catman(req, file); + else + format(req, file); + } +out: + if (idx) + (*idx->close)(idx); + manpath_free(&ps); +} + +static void +pg_search(const struct req *req, char *path) +{ + size_t tt, ressz; + struct manpaths ps; + int i, sz, rc; + const char *ep, *start; + struct res *res; + char **cp; + struct opts opt; + struct expr *expr; + + if (req->q.manroot < 0 || 0 == req->psz) { + resp_search(NULL, 0, (void *)req); + return; + } + + memset(&opt, 0, sizeof(struct opts)); + + ep = req->q.expr; + opt.arch = req->q.arch; + opt.cat = req->q.sec; + rc = -1; + sz = 0; + cp = NULL; + ressz = 0; + res = NULL; + + /* + * Begin by chdir()ing into the root of the manpath. + * This way we can pick up the database files, which are + * relative to the manpath root. + */ + + assert(req->q.manroot < (int)req->psz); + if (-1 == (chdir(req->p[req->q.manroot].path))) { + perror(req->p[req->q.manroot].path); + resp_search(NULL, 0, (void *)req); + return; + } + + memset(&ps, 0, sizeof(struct manpaths)); + manpath_manconf(&ps, "etc/catman.conf"); + + /* + * Poor man's tokenisation: just break apart by spaces. + * Yes, this is half-ass. But it works for now. + */ + + while (ep && isspace((unsigned char)*ep)) + ep++; + + while (ep && '\0' != *ep) { + cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *)); + start = ep; + while ('\0' != *ep && ! isspace((unsigned char)*ep)) + ep++; + cp[sz] = mandoc_malloc((ep - start) + 1); + memcpy(cp[sz], start, ep - start); + cp[sz++][ep - start] = '\0'; + while (isspace((unsigned char)*ep)) + ep++; + } + + /* + * Pump down into apropos backend. + * The resp_search() function is called with the results. + */ + + expr = req->q.legacy ? + termcomp(sz, cp, &tt) : exprcomp(sz, cp, &tt); + + if (NULL != expr) + rc = apropos_search + (ps.sz, ps.paths, &opt, expr, tt, + (void *)req, &ressz, &res, resp_search); + + /* ...unless errors occured. */ + + if (0 == rc) + resp_baddb(); + else if (-1 == rc) + resp_search(NULL, 0, NULL); + + for (i = 0; i < sz; i++) + free(cp[i]); + + free(cp); + resfree(res, ressz); + exprfree(expr); + manpath_free(&ps); +} + +int +main(void) +{ + int i; + char buf[MAXPATHLEN]; + DIR *cwd; + struct req req; + char *p, *path, *subpath; + + /* Scan our run-time environment. */ + + if (NULL == (cache = getenv("CACHE_DIR"))) + cache = "/cache/man.cgi"; + + if (NULL == (progname = getenv("SCRIPT_NAME"))) + progname = ""; + + if (NULL == (css = getenv("CSS_DIR"))) + css = ""; + + if (NULL == (host = getenv("HTTP_HOST"))) + host = "localhost"; + + /* + * First we change directory into the cache directory so that + * subsequent scanning for manpath directories is rooted + * relative to the same position. + */ + + if (-1 == chdir(cache)) { + perror(cache); + resp_bad(); + return(EXIT_FAILURE); + } else if (NULL == (cwd = opendir(cache))) { + perror(cache); + resp_bad(); + return(EXIT_FAILURE); + } + + memset(&req, 0, sizeof(struct req)); + + strlcpy(buf, ".", MAXPATHLEN); + pathgen(cwd, buf, &req); + closedir(cwd); + + /* Next parse out the query string. */ + + if (NULL != (p = getenv("QUERY_STRING"))) + http_parse(&req, p); + + /* + * Now juggle paths to extract information. + * We want to extract our filetype (the file suffix), the + * initial path component, then the trailing component(s). + * Start with leading subpath component. + */ + + subpath = path = NULL; + req.page = PAGE__MAX; + + if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path) + req.page = PAGE_INDEX; + + if (NULL != path && '/' == *path && '\0' == *++path) + req.page = PAGE_INDEX; + + /* Strip file suffix. */ + + if (NULL != path && NULL != (p = strrchr(path, '.'))) + if (NULL != p && NULL == strchr(p, '/')) + *p++ = '\0'; + + /* Resolve subpath component. */ + + if (NULL != path && NULL != (subpath = strchr(path, '/'))) + *subpath++ = '\0'; + + /* Map path into one we recognise. */ + + if (NULL != path && '\0' != *path) + for (i = 0; i < (int)PAGE__MAX; i++) + if (0 == strcmp(pages[i], path)) { + req.page = (enum page)i; + break; + } + + /* Route pages. */ + + switch (req.page) { + case (PAGE_INDEX): + pg_index(&req, subpath); + break; + case (PAGE_SEARCH): + pg_search(&req, subpath); + break; + case (PAGE_SHOW): + pg_show(&req, subpath); + break; + default: + resp_error404(path); + break; + } + + for (i = 0; i < (int)req.psz; i++) { + free(req.p[i].path); + free(req.p[i].name); + } + + free(req.p); + return(EXIT_SUCCESS); +} + +static int +cmp(const void *p1, const void *p2) +{ + + return(strcasecmp(((const struct res *)p1)->title, + ((const struct res *)p2)->title)); +} + +/* + * Check to see if an "etc" path consists of a catman.conf file. If it + * does, that means that the path contains a tree created by catman(8) + * and should be used for indexing. + */ +static int +pathstop(DIR *dir) +{ + struct dirent *d; + + while (NULL != (d = readdir(dir))) + if (DT_REG == d->d_type) + if (0 == strcmp(d->d_name, "catman.conf")) + return(1); + + return(0); +} + +/* + * Scan for indexable paths. + * This adds all paths with "etc/catman.conf" to the buffer. + */ +static void +pathgen(DIR *dir, char *path, struct req *req) +{ + struct dirent *d; + char *cp; + DIR *cd; + int rc; + size_t sz, ssz; + + sz = strlcat(path, "/", MAXPATHLEN); + if (sz >= MAXPATHLEN) { + fprintf(stderr, "%s: Path too long", path); + return; + } + + /* + * First, scan for the "etc" directory. + * If it's found, then see if it should cause us to stop. This + * happens when a catman.conf is found in the directory. + */ + + rc = 0; + while (0 == rc && NULL != (d = readdir(dir))) { + if (DT_DIR != d->d_type || strcmp(d->d_name, "etc")) + continue; + + path[(int)sz] = '\0'; + ssz = strlcat(path, d->d_name, MAXPATHLEN); + + if (ssz >= MAXPATHLEN) { + fprintf(stderr, "%s: Path too long", path); + return; + } else if (NULL == (cd = opendir(path))) { + perror(path); + return; + } + + rc = pathstop(cd); + closedir(cd); + } + + if (rc > 0) { + /* This also strips the trailing slash. */ + path[(int)--sz] = '\0'; + req->p = mandoc_realloc + (req->p, + (req->psz + 1) * sizeof(struct paths)); + /* + * Strip out the leading "./" unless we're just a ".", + * in which case use an empty string as our name. + */ + req->p[(int)req->psz].path = mandoc_strdup(path); + req->p[(int)req->psz].name = + cp = mandoc_strdup(path + (1 == sz ? 1 : 2)); + req->psz++; + /* + * The name is just the path with all the slashes taken + * out of it. Simple but effective. + */ + for ( ; '\0' != *cp; cp++) + if ('/' == *cp) + *cp = ' '; + return; + } + + /* + * If no etc/catman.conf was found, recursively enter child + * directory and continue scanning. + */ + + rewinddir(dir); + while (NULL != (d = readdir(dir))) { + if (DT_DIR != d->d_type || '.' == d->d_name[0]) + continue; + + path[(int)sz] = '\0'; + ssz = strlcat(path, d->d_name, MAXPATHLEN); + + if (ssz >= MAXPATHLEN) { + fprintf(stderr, "%s: Path too long", path); + return; + } else if (NULL == (cd = opendir(path))) { + perror(path); + return; + } + + pathgen(cd, path, req); + closedir(cd); + } +} diff --git a/contrib/mdocml/chars.c b/contrib/mdocml/chars.c index 5158612a32..ce03347b5d 100644 --- a/contrib/mdocml/chars.c +++ b/contrib/mdocml/chars.c @@ -1,6 +1,6 @@ -/* $Id: chars.c,v 1.46 2011/05/24 21:31:23 kristaps Exp $ */ +/* $Id: chars.c,v 1.52 2011/11/08 00:15:23 kristaps Exp $ */ /* - * Copyright (c) 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2011 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -38,7 +37,7 @@ struct ln { int unicode; }; -#define LINES_MAX 325 +#define LINES_MAX 328 #define CHAR(in, ch, code) \ { NULL, (in), (ch), (code) }, @@ -52,8 +51,8 @@ struct mchars { struct ln **htab; }; -static inline int match(const struct ln *, const char *, size_t); -static const struct ln *find(struct mchars *, const char *, size_t); +static const struct ln *find(const struct mchars *, + const char *, size_t); void mchars_free(struct mchars *arg) @@ -74,8 +73,7 @@ mchars_alloc(void) /* * Constructs a very basic chaining hashtable. The hash routine * is simply the integral value of the first character. - * Subsequent entries are chained in the order they're processed - * (they're in-line re-ordered during lookup). + * Subsequent entries are chained in the order they're processed. */ tab = mandoc_malloc(sizeof(struct mchars)); @@ -98,12 +96,8 @@ mchars_alloc(void) return(tab); } - -/* - * Special character to Unicode codepoint. - */ int -mchars_spec2cp(struct mchars *arg, const char *p, size_t sz) +mchars_spec2cp(const struct mchars *arg, const char *p, size_t sz) { const struct ln *ln; @@ -113,103 +107,61 @@ mchars_spec2cp(struct mchars *arg, const char *p, size_t sz) return(ln->unicode); } -/* - * Numbered character string to ASCII codepoint. - * This can only be a printable character (i.e., alnum, punct, space) so - * prevent the character from ruining our state (backspace, newline, and - * so on). - * If the character is illegal, returns '\0'. - */ char mchars_num2char(const char *p, size_t sz) { int i; - if ((i = mandoc_strntou(p, sz, 10)) < 0) + if ((i = mandoc_strntoi(p, sz, 10)) < 0) return('\0'); - return(isprint(i) ? i : '\0'); + return(i > 0 && i < 256 && isprint(i) ? + /* LINTED */ i : '\0'); } -/* - * Hex character string to Unicode codepoint. - * If the character is illegal, returns '\0'. - */ int mchars_num2uc(const char *p, size_t sz) { int i; - if ((i = mandoc_strntou(p, sz, 16)) < 0) + if ((i = mandoc_strntoi(p, sz, 16)) < 0) return('\0'); /* FIXME: make sure we're not in a bogus range. */ return(i > 0x80 && i <= 0x10FFFF ? i : '\0'); } -/* - * Special character to string array. - */ const char * -mchars_spec2str(struct mchars *arg, const char *p, size_t sz, size_t *rsz) +mchars_spec2str(const struct mchars *arg, + const char *p, size_t sz, size_t *rsz) { const struct ln *ln; ln = find(arg, p, sz); - if (NULL == ln) + if (NULL == ln) { + *rsz = 1; return(NULL); + } *rsz = strlen(ln->ascii); return(ln->ascii); } static const struct ln * -find(struct mchars *tab, const char *p, size_t sz) +find(const struct mchars *tab, const char *p, size_t sz) { - struct ln *pp, *prev; - struct ln **htab; + const struct ln *pp; int hash; assert(p); - if (0 == sz) - return(NULL); - if (p[0] < PRINT_LO || p[0] > PRINT_HI) + if (0 == sz || p[0] < PRINT_LO || p[0] > PRINT_HI) return(NULL); - /* - * Lookup the symbol in the symbol hash. See ascii2htab for the - * hashtable specs. This dynamically re-orders the hash chain - * to optimise for repeat hits. - */ - hash = (int)p[0] - PRINT_LO; - htab = tab->htab; - - if (NULL == (pp = htab[hash])) - return(NULL); - for (prev = NULL; pp; pp = pp->next) { - if ( ! match(pp, p, sz)) { - prev = pp; - continue; - } - - if (prev) { - prev->next = pp->next; - pp->next = htab[hash]; - htab[hash] = pp; - } - - return(pp); - } + for (pp = tab->htab[hash]; pp; pp = pp->next) + if (0 == strncmp(pp->code, p, sz) && + '\0' == pp->code[(int)sz]) + return(pp); return(NULL); } - -static inline int -match(const struct ln *ln, const char *p, size_t sz) -{ - - if (strncmp(ln->code, p, sz)) - return(0); - return('\0' == ln->code[(int)sz]); -} diff --git a/contrib/mdocml/chars.in b/contrib/mdocml/chars.in index 483a2bb828..a4c45b3c43 100644 --- a/contrib/mdocml/chars.in +++ b/contrib/mdocml/chars.in @@ -1,6 +1,6 @@ -/* $Id: chars.in,v 1.39 2011/05/24 21:40:14 kristaps Exp $ */ +/* $Id: chars.in,v 1.42 2011/10/02 10:02:26 kristaps Exp $ */ /* - * Copyright (c) 2009, 2010 Kristaps Dzonsons + * 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 @@ -33,15 +33,15 @@ static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' }; CHAR_TBL_START /* Spacing. */ -CHAR("c", "", 8203) +CHAR("c", "", 0) CHAR("0", " ", 8194) CHAR(" ", ascii_nbrsp, 160) CHAR("~", ascii_nbrsp, 160) -CHAR("%", "", 8203) -CHAR("&", "", 8203) -CHAR("^", "", 8203) -CHAR("|", "", 8203) -CHAR("}", "", 8203) +CHAR("%", "", 0) +CHAR("&", "", 0) +CHAR("^", "", 0) +CHAR("|", "", 0) +CHAR("}", "", 0) /* Accents. */ CHAR("a\"", "\"", 779) @@ -312,6 +312,9 @@ CHAR("Im", "I", 8465) CHAR("Re", "R", 8476) CHAR("pd", "a", 8706) CHAR("-h", "/h", 8463) +CHAR("12", "1/2", 189) +CHAR("14", "1/4", 188) +CHAR("34", "3/4", 190) /* Ligatures. */ CHAR("ff", "ff", 64256) diff --git a/contrib/mdocml/compat_fgetln.c b/contrib/mdocml/compat_fgetln.c new file mode 100644 index 0000000000..49c9985b6e --- /dev/null +++ b/contrib/mdocml/compat_fgetln.c @@ -0,0 +1,93 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_FGETLN + +int dummy; + +#else + +/* $NetBSD: fgetln.c,v 1.3 2006/09/25 07:18:17 lukem Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include + +char * +fgetln(fp, len) + FILE *fp; + size_t *len; +{ + static char *buf = NULL; + static size_t bufsiz = 0; + char *ptr; + + + if (buf == NULL) { + bufsiz = BUFSIZ; + if ((buf = malloc(bufsiz)) == NULL) + return NULL; + } + + if (fgets(buf, bufsiz, fp) == NULL) + return NULL; + + *len = 0; + while ((ptr = strchr(&buf[*len], '\n')) == NULL) { + size_t nbufsiz = bufsiz + BUFSIZ; + char *nbuf = realloc(buf, nbufsiz); + + if (nbuf == NULL) { + int oerrno = errno; + free(buf); + errno = oerrno; + buf = NULL; + return NULL; + } else + buf = nbuf; + + *len = bufsiz; + if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) + return buf; + + bufsiz = nbufsiz; + } + + *len = (ptr - buf) + 1; + return buf; +} + +#endif diff --git a/contrib/mdocml/compat_getsubopt.c b/contrib/mdocml/compat_getsubopt.c new file mode 100644 index 0000000000..9cd415367d --- /dev/null +++ b/contrib/mdocml/compat_getsubopt.c @@ -0,0 +1,104 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_GETSUBOPT + +int dummy; + +#else + +/* $OpenBSD: getsubopt.c,v 1.4 2005/08/08 08:05:36 espie Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +/* + * The SVID interface to getsubopt provides no way of figuring out which + * part of the suboptions list wasn't matched. This makes error messages + * tricky... The extern variable suboptarg is a pointer to the token + * which didn't match. + */ +char *suboptarg; + +int +getsubopt(char **optionp, char * const *tokens, char **valuep) +{ + int cnt; + char *p; + + suboptarg = *valuep = NULL; + + if (!optionp || !*optionp) + return(-1); + + /* skip leading white-space, commas */ + for (p = *optionp; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p); + + if (!*p) { + *optionp = p; + return(-1); + } + + /* save the start of the token, and skip the rest of the token. */ + for (suboptarg = p; + *++p && *p != ',' && *p != '=' && *p != ' ' && *p != '\t';); + + if (*p) { + /* + * If there's an equals sign, set the value pointer, and + * skip over the value part of the token. Terminate the + * token. + */ + if (*p == '=') { + *p = '\0'; + for (*valuep = ++p; + *p && *p != ',' && *p != ' ' && *p != '\t'; ++p); + if (*p) + *p++ = '\0'; + } else + *p++ = '\0'; + /* Skip any whitespace or commas after this token. */ + for (; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p); + } + + /* set optionp for next round. */ + *optionp = p; + + for (cnt = 0; *tokens; ++tokens, ++cnt) + if (!strcmp(suboptarg, *tokens)) + return(cnt); + return(-1); +} + +#endif diff --git a/contrib/mdocml/compat.c b/contrib/mdocml/compat_strlcat.c similarity index 70% copy from contrib/mdocml/compat.c copy to contrib/mdocml/compat_strlcat.c index f00cc5c6b4..543d40b38b 100644 --- a/contrib/mdocml/compat.c +++ b/contrib/mdocml/compat_strlcat.c @@ -1,3 +1,13 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRLCAT + +int dummy; + +#else + /* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* @@ -15,16 +25,10 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include #include -int dummy; /* To prevent an empty object file */ - -#ifndef HAVE_STRLCAT /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters @@ -59,37 +63,5 @@ strlcat(char *dst, const char *src, size_t siz) return(dlen + (s - src)); /* count does not include NUL */ } -#endif -#ifndef HAVE_STRLCPY -/* - * Copy src to string dst of size siz. At most siz-1 characters - * will be copied. Always NUL terminates (unless siz == 0). - * Returns strlen(src); if retval >= siz, truncation occurred. - */ -size_t -strlcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL and traverse rest of src */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - while (*s++) - ; - } - - return(s - src - 1); /* count does not include NUL */ -} -#endif +#endif diff --git a/contrib/mdocml/compat.c b/contrib/mdocml/compat_strlcpy.c similarity index 62% rename from contrib/mdocml/compat.c rename to contrib/mdocml/compat_strlcpy.c index f00cc5c6b4..a7c64ff999 100644 --- a/contrib/mdocml/compat.c +++ b/contrib/mdocml/compat_strlcpy.c @@ -1,4 +1,14 @@ -/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STRLCPY + +int dummy; + +#else + +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller @@ -15,53 +25,10 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif #include #include -int dummy; /* To prevent an empty object file */ - -#ifndef HAVE_STRLCAT -/* - * Appends src to string dst of size siz (unlike strncat, siz is the - * full size of dst, not space left). At most siz-1 characters - * will be copied. Always NUL terminates (unless siz <= strlen(dst)). - * Returns strlen(src) + MIN(siz, strlen(initial dst)). - * If retval >= siz, truncation occurred. - */ -size_t -strlcat(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - size_t dlen; - - /* Find the end of dst and adjust bytes left but don't go past end */ - while (n-- != 0 && *d != '\0') - d++; - dlen = d - dst; - n = siz - dlen; - - if (n == 0) - return(dlen + strlen(s)); - while (*s != '\0') { - if (n != 1) { - *d++ = *s; - n--; - } - s++; - } - *d = '\0'; - - return(dlen + (s - src)); /* count does not include NUL */ -} -#endif - -#ifndef HAVE_STRLCPY /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). @@ -92,4 +59,5 @@ strlcpy(char *dst, const char *src, size_t siz) return(s - src - 1); /* count does not include NUL */ } -#endif + +#endif diff --git a/contrib/mdocml/config.h.post b/contrib/mdocml/config.h.post index 81c01b9316..39da2b2f06 100644 --- a/contrib/mdocml/config.h.post +++ b/contrib/mdocml/config.h.post @@ -15,11 +15,28 @@ # endif #endif +#if defined(__APPLE__) +# define htobe32(x) OSSwapHostToBigInt32(x) +# define betoh32(x) OSSwapBigToHostInt32(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# define betoh64(x) OSSwapBigToHostInt64(x) +#elif defined(__linux__) +# define betoh32(x) be32toh(x) +# define betoh64(x) be64toh(x) +#endif + #ifndef HAVE_STRLCAT extern size_t strlcat(char *, const char *, size_t); #endif #ifndef HAVE_STRLCPY extern size_t strlcpy(char *, const char *, size_t); #endif +#ifndef HAVE_GETSUBOPT +extern int getsubopt(char **, char * const *, char **); +extern char *suboptarg; +#endif +#ifndef HAVE_FGETLN +extern char *fgetln(FILE *, size_t *); +#endif #endif /* MANDOC_CONFIG_H */ diff --git a/contrib/mdocml/config.h.pre b/contrib/mdocml/config.h.pre index a309ed9569..bc59478485 100644 --- a/contrib/mdocml/config.h.pre +++ b/contrib/mdocml/config.h.pre @@ -4,3 +4,5 @@ #if defined(__linux__) || defined(__MINT__) # define _GNU_SOURCE /* strptime(), getsubopt() */ #endif + +#include diff --git a/contrib/mdocml/demandoc.1 b/contrib/mdocml/demandoc.1 new file mode 100644 index 0000000000..845b9c14b5 --- /dev/null +++ b/contrib/mdocml/demandoc.1 @@ -0,0 +1,109 @@ +.\" $Id: demandoc.1,v 1.6 2011/12/25 19:35:44 kristaps Exp $ +.\" +.\" Copyright (c) 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: December 25 2011 $ +.Dt DEMANDOC 1 +.Os +.Sh NAME +.Nm demandoc +.Nd emit only text of UNIX manuals +.Sh SYNOPSIS +.Nm demandoc +.Op Fl w +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility emits only the text portions of well-formed +.Xr mdoc 7 +and +.Xr man 7 +.Ux +manual files. +.Pp +By default, +.Nm +parses standard input and outputs only text nodes, preserving line +and column position. +Escape sequences are omitted from the output. +.Pp +Its arguments are as follows: +.Bl -tag -width Ds +.It Fl w +Output a word list. +This outputs each word of text on its own line. +A +.Qq word , +in this case, refers to whitespace-delimited terms beginning with at +least two letters and not consisting of any escape sequences. +Words have their leading and trailing punctuation +.Pq double-quotes, sentence punctuation, etc. +stripped. +.It Ar +The input files. +.El +.Pp +If a document is not well-formed, it is skipped. +.Pp +The +.Fl i , +.Fl k , +.Fl m , +and +.Fl p +flags are silently discarded for calling compatibility with the +historical deroff. +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Pp +.Bl -tag -width Ds -compact +.It 0 +No errors occurred. +.It 6 +An operating system error occurred, for example memory exhaustion or an +error accessing input files. +Such errors cause +.Nm +to exit at once, possibly in the middle of parsing or formatting a file. +The output databases are corrupt and should be removed . +.El +.Sh EXAMPLES +The traditional usage of +.Nm +is for spell-checking manuals on +.Bx . +This is accomplished as follows (assuming British spelling): +.Pp +.Dl $ demandoc -w file.1 | spell -b +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr man 7 +.Xr mdoc 7 +.Sh HISTORY +.Nm +replaces the historical deroff utility for handling modern +.Xr man 7 +and +.Xr mdoc 7 +documents. +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/demandoc.c b/contrib/mdocml/demandoc.c new file mode 100644 index 0000000000..2474a358e2 --- /dev/null +++ b/contrib/mdocml/demandoc.c @@ -0,0 +1,257 @@ +/* $Id: demandoc.c,v 1.6 2011/09/01 22:25:53 kristaps Exp $ */ +/* + * Copyright (c) 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "man.h" +#include "mdoc.h" +#include "mandoc.h" + +static void pline(int, int *, int *, int); +static void pman(const struct man_node *, int *, int *, int); +static void pmandoc(struct mparse *, int, const char *, int); +static void pmdoc(const struct mdoc_node *, int *, int *, int); +static void pstring(const char *, int, int *, int); +static void usage(void); + +static const char *progname; + +int +main(int argc, char *argv[]) +{ + struct mparse *mp; + int ch, i, list; + extern int optind; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + ++progname; + + mp = NULL; + list = 0; + + while (-1 != (ch = getopt(argc, argv, "ikm:pw"))) + switch (ch) { + case ('i'): + /* FALLTHROUGH */ + case ('k'): + /* FALLTHROUGH */ + case ('m'): + /* FALLTHROUGH */ + case ('p'): + break; + case ('w'): + list = 1; + break; + default: + usage(); + return((int)MANDOCLEVEL_BADARG); + } + + argc -= optind; + argv += optind; + + mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); + assert(mp); + + if (0 == argc) + pmandoc(mp, STDIN_FILENO, "", list); + + for (i = 0; i < argc; i++) { + mparse_reset(mp); + pmandoc(mp, -1, argv[i], list); + } + + mparse_free(mp); + return((int)MANDOCLEVEL_OK); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s [-w] [files...]\n", progname); +} + +static void +pmandoc(struct mparse *mp, int fd, const char *fn, int list) +{ + struct mdoc *mdoc; + struct man *man; + int line, col; + + if (mparse_readfd(mp, fd, fn) >= MANDOCLEVEL_FATAL) { + fprintf(stderr, "%s: Parse failure\n", fn); + return; + } + + mparse_result(mp, &mdoc, &man); + line = 1; + col = 0; + + if (mdoc) + pmdoc(mdoc_node(mdoc), &line, &col, list); + else if (man) + pman(man_node(man), &line, &col, list); + else + return; + + if ( ! list) + putchar('\n'); +} + +/* + * Strip the escapes out of a string, emitting the results. + */ +static void +pstring(const char *p, int col, int *colp, int list) +{ + enum mandoc_esc esc; + const char *start, *end; + int emit; + + /* + * Print as many column spaces til we achieve parity with the + * input document. + */ + +again: + if (list && '\0' != *p) { + while (isspace((unsigned char)*p)) + p++; + + while ('\'' == *p || '(' == *p || '"' == *p) + p++; + + emit = isalpha((unsigned char)p[0]) && + isalpha((unsigned char)p[1]); + + for (start = p; '\0' != *p; p++) + if ('\\' == *p) { + p++; + esc = mandoc_escape(&p, NULL, NULL); + if (ESCAPE_ERROR == esc) + return; + emit = 0; + } else if (isspace((unsigned char)*p)) + break; + + end = p - 1; + + while (end > start) + if ('.' == *end || ',' == *end || + '\'' == *end || '"' == *end || + ')' == *end || '!' == *end || + '?' == *end || ':' == *end || + ';' == *end) + end--; + else + break; + + if (emit && end - start >= 1) { + for ( ; start <= end; start++) + if (ASCII_HYPH == *start) + putchar('-'); + else + putchar((unsigned char)*start); + putchar('\n'); + } + + if (isspace((unsigned char)*p)) + goto again; + + return; + } + + while (*colp < col) { + putchar(' '); + (*colp)++; + } + + /* + * Print the input word, skipping any special characters. + */ + while ('\0' != *p) + if ('\\' == *p) { + p++; + esc = mandoc_escape(&p, NULL, NULL); + if (ESCAPE_ERROR == esc) + break; + } else { + putchar((unsigned char )*p++); + (*colp)++; + } +} + +static void +pline(int line, int *linep, int *col, int list) +{ + + if (list) + return; + + /* + * Print out as many lines as needed to reach parity with the + * original input. + */ + + while (*linep < line) { + putchar('\n'); + (*linep)++; + } + + *col = 0; +} + +static void +pmdoc(const struct mdoc_node *p, int *line, int *col, int list) +{ + + for ( ; p; p = p->next) { + if (MDOC_LINE & p->flags) + pline(p->line, line, col, list); + if (MDOC_TEXT == p->type) + pstring(p->string, p->pos, col, list); + if (p->child) + pmdoc(p->child, line, col, list); + } +} + +static void +pman(const struct man_node *p, int *line, int *col, int list) +{ + + for ( ; p; p = p->next) { + if (MAN_LINE & p->flags) + pline(p->line, line, col, list); + if (MAN_TEXT == p->type) + pstring(p->string, p->pos, col, list); + if (p->child) + pman(p->child, line, col, list); + } +} diff --git a/contrib/mdocml/eqn.7 b/contrib/mdocml/eqn.7 index b2b66f424e..f86b9c496b 100644 --- a/contrib/mdocml/eqn.7 +++ b/contrib/mdocml/eqn.7 @@ -1,4 +1,4 @@ -.\" $Id: eqn.7,v 1.2 2011/03/17 15:12:42 kristaps Exp $ +.\" $Id: eqn.7,v 1.28 2011/09/25 18:37:09 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 17 2011 $ +.Dd $Mdocdate: September 25 2011 $ .Dt EQN 7 .Os .Sh NAME @@ -23,43 +23,220 @@ .Sh DESCRIPTION The .Nm eqn -language is a equation-formatting language. +language is an equation-formatting language. It is used within .Xr mdoc 7 and .Xr man 7 .Ux manual pages. -This manual describes the subset of the +It describes the +.Em structure +of an equation, not its mathematical meaning. +This manual describes the .Nm language accepted by the .Xr mandoc 1 -utility. +utility, which corresponds to the Second Edition eqn specification (see +.Sx SEE ALSO +for references). .Pp Equations within .Xr mdoc 7 or .Xr man 7 -are enclosed by the -.Sq EQ +documents are enclosed by the standalone +.Sq \&.EQ and -.Sq EN -macro tags, whose precise syntax is documented in -.Xr roff 7 . -Equations consist of multi-line equation data. +.Sq \&.EN +tags. +Equations are multi-line blocks consisting of formulas and control +statements. +.Sh EQUATION STRUCTURE +Each equation is bracketed by +.Sq \&.EQ +and +.Sq \&.EN +strings. +.Em Note : +these are not the same as +.Xr roff 7 +macros, and may only be invoked as +.Sq \&.EQ . .Pp -For the time being, -.Xr mandoc 1 -reproduces the contents of +The equation grammar is as follows, where quoted strings are +case-sensitive literals in the input: +.Bd -literal -offset indent +eqn : box | eqn box +box : text + | \*q{\*q eqn \*q}\*q + | \*qdefine\*q text text + | \*qndefine\*q text text + | \*qtdefine\*q text text + | \*qgfont\*q text + | \*qgsize\*q text + | \*qset\*q text text + | \*qundef\*q text + | box pos box + | box mark + | \*qmatrix\*q \*q{\*q [col \*q{\*q list \*q}\*q ]* + | pile \*q{\*q list \*q}\*q + | font box + | \*qsize\*q text box + | \*qleft\*q text eqn [\*qright\*q text] +col : \*qlcol\*q | \*qrcol\*q | \*qccol\*q | \*qcol\*q +text : [^space\e\*q]+ | \e\*q.*\e\*q +pile : \*qlpile\*q | \*qcpile\*q | \*qrpile\*q | \*qpile\*q +pos : \*qover\*q | \*qsup\*q | \*qsub\*q | \*qto\*q | \*qfrom\*q +mark : \*qdot\*q | \*qdotdot\*q | \*qhat\*q | \*qtilde\*q | \*qvec\*q + | \*qdyad\*q | \*qbar\*q | \*qunder\*q +font : \*qroman\*q | \*qitalic\*q | \*qbold\*q | \*qfat\*q +list : eqn + | list \*qabove\*q eqn +space : [\e^~ \et] +.Ed +.Pp +White-space consists of the space, tab, circumflex, and tilde +characters. +If within a quoted string, these space characters are retained. +Quoted strings are also not scanned for replacement definitions. +.Pp +The following text terms are translated into a rendered glyph, if +available: alpha, beta, chi, delta, epsilon, eta, gamma, iota, kappa, +lambda, mu, nu, omega, omicron, phi, pi, psi, rho, sigma, tau, theta, +upsilon, xi, zeta, DELTA, GAMMA, LAMBDA, OMEGA, PHI, PI, PSI, SIGMA, +THETA, UPSILON, XI, inter (intersection), union (union), prod (product), +int (integral), sum (summation), grad (gradient), del (vector +differential), times (multiply), cdot (centre-dot), nothing (zero-width +space), approx (approximately equals), prime (prime), half (one-half), +partial (partial differential), inf (infinity), >> (much greater), << +(much less), \-> (left arrow), <\- (right arrow), += (plus-minus), != +(not equal), == (equivalence), <= (less-than-equal), and >= +(more-than-equal). +.Pp +The following control statements are available: +.Bl -tag -width Ds +.It Cm define +Replace all occurrences of a key with a value. +Its syntax is as follows: +.Pp +.D1 define Ar key cvalc +.Pp +The first character of the value string, +.Ar c , +is used as the delimiter for the value +.Ar val . +This allows for arbitrary enclosure of terms (not just quotes), such as +.Pp +.D1 define Ar foo 'bar baz' +.D1 define Ar foo cbar bazc +.Pp +It is an error to have an empty +.Ar key +or +.Ar val . +Note that a quoted +.Ar key +causes errors in some .Nm -equations verbatim in its output. +implementations and should not be considered portable. +It is not expanded for replacements. +Definitions may refer to other definitions; these are evaluated +recursively when text replacement occurs and not when the definition is +created. +.Pp +Definitions can create arbitrary strings, for example, the following is +a legal construction. +.Bd -literal -offset indent +define foo 'define' +foo bar 'baz' +.Ed +.Pp +Self-referencing definitions will raise an error. +The +.Cm ndefine +statement is a synonym for +.Cm define , +while +.Cm tdefine +is discarded. +.It Cm gfont +Set the default font of subsequent output. +Its syntax is as follows: +.Pp +.D1 gfont Ar font +.Pp +In mandoc, this value is discarded. +.It Cm gsize +Set the default size of subsequent output. +Its syntax is as follows: +.Pp +.D1 gsize Ar size +.Pp +The +.Ar size +value should be an integer. +.It Cm set +Set an equation mode. +In mandoc, both arguments are thrown away. +Its syntax is as follows: +.Pp +.D1 set Ar key val .Pp The +.Ar key +and +.Ar val +are not expanded for replacements. +This statement is a GNU extension. +.It Cm undef +Unset a previously-defined key. +Its syntax is as follows: +.Pp +.D1 define Ar key +.Pp +Once invoked, the definition for +.Ar key +is discarded. +The +.Ar key +is not expanded for replacements. +This statement is a GNU extension. +.El +.Sh COMPATIBILITY +This section documents the compatibility of mandoc .Nm -implementation in -.Xr mandoc 1 -is -.Ud +and the troff +.Nm +implementation (including GNU troff). +.Pp +.Bl -dash -compact +.It +The text string +.Sq \e\*q +is interpreted as a literal quote in troff. +In mandoc, this is interpreted as a comment. +.It +In troff, The circumflex and tilde white-space symbols map to +fixed-width spaces. +In mandoc, these characters are synonyms for the space character. +.It +The troff implementation of +.Nm +allows for equation alignment with the +.Cm mark +and +.Cm lineup +tokens. +mandoc discards these tokens. +The +.Cm back Ar n , +.Cm fwd Ar n , +.Cm up Ar n , +and +.Cm down Ar n +commands are also ignored. +.El .Sh SEE ALSO .Xr mandoc 1 , .Xr man 7 , @@ -75,18 +252,29 @@ is .%P 151\(en157 .%D March, 1975 .Re -.\" .Sh HISTORY -.\" The tbl utility, a preprocessor for troff, was originally written by M. -.\" E. Lesk at Bell Labs in 1975. -.\" The GNU reimplementation of tbl, part of the groff package, was released -.\" in 1990 by James Clark. -.\" A standalone tbl implementation was written by Kristaps Dzonsons in -.\" 2010. -.\" This formed the basis of the implementation that is part of the -.\" .Xr mandoc 1 -.\" utility. +.Rs +.%A Brian W. Kernighan +.%A Lorinda L. Cherry +.%T Typesetting Mathematics, User's Guide +.%D 1976 +.Re +.Rs +.%A Brian W. Kernighan +.%A Lorinda L. Cherry +.%T Typesetting Mathematics, User's Guide (Second Edition) +.%D 1978 +.Re +.Sh HISTORY +The eqn utility, a preprocessor for troff, was originally written by +Brian W. Kernighan and Lorinda L. Cherry in 1975. +The GNU reimplementation of eqn, part of the GNU troff package, was +released in 1989 by James Clark. +The eqn component of +.Xr mandoc 1 +was added in 2011. .Sh AUTHORS -This partial +This .Nm reference was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/eqn.c b/contrib/mdocml/eqn.c index 220f3f88bf..37f01bcb5b 100644 --- a/contrib/mdocml/eqn.c +++ b/contrib/mdocml/eqn.c @@ -1,4 +1,4 @@ -/* $Id: eqn.c,v 1.4 2011/03/22 09:48:13 kristaps Exp $ */ +/* $Id: eqn.c,v 1.38 2011/07/25 15:37:00 kristaps Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons * @@ -19,6 +19,7 @@ #endif #include +#include #include #include #include @@ -28,54 +29,921 @@ #include "libmandoc.h" #include "libroff.h" +#define EQN_NEST_MAX 128 /* maximum nesting of defines */ +#define EQN_MSG(t, x) mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL) + +enum eqn_rest { + EQN_DESCOPE, + EQN_ERR, + EQN_OK, + EQN_EOF +}; + +enum eqn_symt { + EQNSYM_alpha, + EQNSYM_beta, + EQNSYM_chi, + EQNSYM_delta, + EQNSYM_epsilon, + EQNSYM_eta, + EQNSYM_gamma, + EQNSYM_iota, + EQNSYM_kappa, + EQNSYM_lambda, + EQNSYM_mu, + EQNSYM_nu, + EQNSYM_omega, + EQNSYM_omicron, + EQNSYM_phi, + EQNSYM_pi, + EQNSYM_ps, + EQNSYM_rho, + EQNSYM_sigma, + EQNSYM_tau, + EQNSYM_theta, + EQNSYM_upsilon, + EQNSYM_xi, + EQNSYM_zeta, + EQNSYM_DELTA, + EQNSYM_GAMMA, + EQNSYM_LAMBDA, + EQNSYM_OMEGA, + EQNSYM_PHI, + EQNSYM_PI, + EQNSYM_PSI, + EQNSYM_SIGMA, + EQNSYM_THETA, + EQNSYM_UPSILON, + EQNSYM_XI, + EQNSYM_inter, + EQNSYM_union, + EQNSYM_prod, + EQNSYM_int, + EQNSYM_sum, + EQNSYM_grad, + EQNSYM_del, + EQNSYM_times, + EQNSYM_cdot, + EQNSYM_nothing, + EQNSYM_approx, + EQNSYM_prime, + EQNSYM_half, + EQNSYM_partial, + EQNSYM_inf, + EQNSYM_muchgreat, + EQNSYM_muchless, + EQNSYM_larrow, + EQNSYM_rarrow, + EQNSYM_pm, + EQNSYM_nequal, + EQNSYM_equiv, + EQNSYM_lessequal, + EQNSYM_moreequal, + EQNSYM__MAX +}; + +enum eqnpartt { + EQN_DEFINE = 0, + EQN_NDEFINE, + EQN_TDEFINE, + EQN_SET, + EQN_UNDEF, + EQN_GFONT, + EQN_GSIZE, + EQN_BACK, + EQN_FWD, + EQN_UP, + EQN_DOWN, + EQN__MAX +}; + +struct eqnstr { + const char *name; + size_t sz; +}; + +#define STRNEQ(p1, sz1, p2, sz2) \ + ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) +#define EQNSTREQ(x, p, sz) \ + STRNEQ((x)->name, (x)->sz, (p), (sz)) + +struct eqnpart { + struct eqnstr str; + int (*fp)(struct eqn_node *); +}; + +struct eqnsym { + struct eqnstr str; + const char *sym; +}; + + +static enum eqn_rest eqn_box(struct eqn_node *, struct eqn_box *); +static struct eqn_box *eqn_box_alloc(struct eqn_node *, + struct eqn_box *); +static void eqn_box_free(struct eqn_box *); +static struct eqn_def *eqn_def_find(struct eqn_node *, + const char *, size_t); +static int eqn_do_gfont(struct eqn_node *); +static int eqn_do_gsize(struct eqn_node *); +static int eqn_do_define(struct eqn_node *); +static int eqn_do_ign1(struct eqn_node *); +static int eqn_do_ign2(struct eqn_node *); +static int eqn_do_tdefine(struct eqn_node *); +static int eqn_do_undef(struct eqn_node *); +static enum eqn_rest eqn_eqn(struct eqn_node *, struct eqn_box *); +static enum eqn_rest eqn_list(struct eqn_node *, struct eqn_box *); +static enum eqn_rest eqn_matrix(struct eqn_node *, struct eqn_box *); +static const char *eqn_nexttok(struct eqn_node *, size_t *); +static const char *eqn_nextrawtok(struct eqn_node *, size_t *); +static const char *eqn_next(struct eqn_node *, + char, size_t *, int); +static void eqn_rewind(struct eqn_node *); + +static const struct eqnpart eqnparts[EQN__MAX] = { + { { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */ + { { "ndefine", 7 }, eqn_do_define }, /* EQN_NDEFINE */ + { { "tdefine", 7 }, eqn_do_tdefine }, /* EQN_TDEFINE */ + { { "set", 3 }, eqn_do_ign2 }, /* EQN_SET */ + { { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */ + { { "gfont", 5 }, eqn_do_gfont }, /* EQN_GFONT */ + { { "gsize", 5 }, eqn_do_gsize }, /* EQN_GSIZE */ + { { "back", 4 }, eqn_do_ign1 }, /* EQN_BACK */ + { { "fwd", 3 }, eqn_do_ign1 }, /* EQN_FWD */ + { { "up", 2 }, eqn_do_ign1 }, /* EQN_UP */ + { { "down", 4 }, eqn_do_ign1 }, /* EQN_DOWN */ +}; + +static const struct eqnstr eqnmarks[EQNMARK__MAX] = { + { "", 0 }, /* EQNMARK_NONE */ + { "dot", 3 }, /* EQNMARK_DOT */ + { "dotdot", 6 }, /* EQNMARK_DOTDOT */ + { "hat", 3 }, /* EQNMARK_HAT */ + { "tilde", 5 }, /* EQNMARK_TILDE */ + { "vec", 3 }, /* EQNMARK_VEC */ + { "dyad", 4 }, /* EQNMARK_DYAD */ + { "bar", 3 }, /* EQNMARK_BAR */ + { "under", 5 }, /* EQNMARK_UNDER */ +}; + +static const struct eqnstr eqnfonts[EQNFONT__MAX] = { + { "", 0 }, /* EQNFONT_NONE */ + { "roman", 5 }, /* EQNFONT_ROMAN */ + { "bold", 4 }, /* EQNFONT_BOLD */ + { "fat", 3 }, /* EQNFONT_FAT */ + { "italic", 6 }, /* EQNFONT_ITALIC */ +}; + +static const struct eqnstr eqnposs[EQNPOS__MAX] = { + { "", 0 }, /* EQNPOS_NONE */ + { "over", 4 }, /* EQNPOS_OVER */ + { "sup", 3 }, /* EQNPOS_SUP */ + { "sub", 3 }, /* EQNPOS_SUB */ + { "to", 2 }, /* EQNPOS_TO */ + { "from", 4 }, /* EQNPOS_FROM */ +}; + +static const struct eqnstr eqnpiles[EQNPILE__MAX] = { + { "", 0 }, /* EQNPILE_NONE */ + { "pile", 4 }, /* EQNPILE_PILE */ + { "cpile", 5 }, /* EQNPILE_CPILE */ + { "rpile", 5 }, /* EQNPILE_RPILE */ + { "lpile", 5 }, /* EQNPILE_LPILE */ + { "col", 3 }, /* EQNPILE_COL */ + { "ccol", 4 }, /* EQNPILE_CCOL */ + { "rcol", 4 }, /* EQNPILE_RCOL */ + { "lcol", 4 }, /* EQNPILE_LCOL */ +}; + +static const struct eqnsym eqnsyms[EQNSYM__MAX] = { + { { "alpha", 5 }, "*a" }, /* EQNSYM_alpha */ + { { "beta", 4 }, "*b" }, /* EQNSYM_beta */ + { { "chi", 3 }, "*x" }, /* EQNSYM_chi */ + { { "delta", 5 }, "*d" }, /* EQNSYM_delta */ + { { "epsilon", 7 }, "*e" }, /* EQNSYM_epsilon */ + { { "eta", 3 }, "*y" }, /* EQNSYM_eta */ + { { "gamma", 5 }, "*g" }, /* EQNSYM_gamma */ + { { "iota", 4 }, "*i" }, /* EQNSYM_iota */ + { { "kappa", 5 }, "*k" }, /* EQNSYM_kappa */ + { { "lambda", 6 }, "*l" }, /* EQNSYM_lambda */ + { { "mu", 2 }, "*m" }, /* EQNSYM_mu */ + { { "nu", 2 }, "*n" }, /* EQNSYM_nu */ + { { "omega", 5 }, "*w" }, /* EQNSYM_omega */ + { { "omicron", 7 }, "*o" }, /* EQNSYM_omicron */ + { { "phi", 3 }, "*f" }, /* EQNSYM_phi */ + { { "pi", 2 }, "*p" }, /* EQNSYM_pi */ + { { "psi", 2 }, "*q" }, /* EQNSYM_psi */ + { { "rho", 3 }, "*r" }, /* EQNSYM_rho */ + { { "sigma", 5 }, "*s" }, /* EQNSYM_sigma */ + { { "tau", 3 }, "*t" }, /* EQNSYM_tau */ + { { "theta", 5 }, "*h" }, /* EQNSYM_theta */ + { { "upsilon", 7 }, "*u" }, /* EQNSYM_upsilon */ + { { "xi", 2 }, "*c" }, /* EQNSYM_xi */ + { { "zeta", 4 }, "*z" }, /* EQNSYM_zeta */ + { { "DELTA", 5 }, "*D" }, /* EQNSYM_DELTA */ + { { "GAMMA", 5 }, "*G" }, /* EQNSYM_GAMMA */ + { { "LAMBDA", 6 }, "*L" }, /* EQNSYM_LAMBDA */ + { { "OMEGA", 5 }, "*W" }, /* EQNSYM_OMEGA */ + { { "PHI", 3 }, "*F" }, /* EQNSYM_PHI */ + { { "PI", 2 }, "*P" }, /* EQNSYM_PI */ + { { "PSI", 3 }, "*Q" }, /* EQNSYM_PSI */ + { { "SIGMA", 5 }, "*S" }, /* EQNSYM_SIGMA */ + { { "THETA", 5 }, "*H" }, /* EQNSYM_THETA */ + { { "UPSILON", 7 }, "*U" }, /* EQNSYM_UPSILON */ + { { "XI", 2 }, "*C" }, /* EQNSYM_XI */ + { { "inter", 5 }, "ca" }, /* EQNSYM_inter */ + { { "union", 5 }, "cu" }, /* EQNSYM_union */ + { { "prod", 4 }, "product" }, /* EQNSYM_prod */ + { { "int", 3 }, "integral" }, /* EQNSYM_int */ + { { "sum", 3 }, "sum" }, /* EQNSYM_sum */ + { { "grad", 4 }, "gr" }, /* EQNSYM_grad */ + { { "del", 3 }, "gr" }, /* EQNSYM_del */ + { { "times", 5 }, "mu" }, /* EQNSYM_times */ + { { "cdot", 4 }, "pc" }, /* EQNSYM_cdot */ + { { "nothing", 7 }, "&" }, /* EQNSYM_nothing */ + { { "approx", 6 }, "~~" }, /* EQNSYM_approx */ + { { "prime", 5 }, "aq" }, /* EQNSYM_prime */ + { { "half", 4 }, "12" }, /* EQNSYM_half */ + { { "partial", 7 }, "pd" }, /* EQNSYM_partial */ + { { "inf", 3 }, "if" }, /* EQNSYM_inf */ + { { ">>", 2 }, ">>" }, /* EQNSYM_muchgreat */ + { { "<<", 2 }, "<<" }, /* EQNSYM_muchless */ + { { "<-", 2 }, "<-" }, /* EQNSYM_larrow */ + { { "->", 2 }, "->" }, /* EQNSYM_rarrow */ + { { "+-", 2 }, "+-" }, /* EQNSYM_pm */ + { { "!=", 2 }, "!=" }, /* EQNSYM_nequal */ + { { "==", 2 }, "==" }, /* EQNSYM_equiv */ + { { "<=", 2 }, "<=" }, /* EQNSYM_lessequal */ + { { ">=", 2 }, ">=" }, /* EQNSYM_moreequal */ +}; + /* ARGSUSED */ enum rofferr -eqn_read(struct eqn_node **epp, int ln, const char *p, int offs) +eqn_read(struct eqn_node **epp, int ln, + const char *p, int pos, int *offs) { size_t sz; struct eqn_node *ep; + enum rofferr er; + + ep = *epp; - if (0 == strcmp(p, ".EN")) { - *epp = NULL; - return(ROFF_EQN); + /* + * If we're the terminating mark, unset our equation status and + * validate the full equation. + */ + + if (0 == strncmp(p, ".EN", 3)) { + er = eqn_end(epp); + p += 3; + while (' ' == *p || '\t' == *p) + p++; + if ('\0' == *p) + return(er); + mandoc_msg(MANDOCERR_ARGSLOST, ep->parse, ln, pos, NULL); + return(er); } - ep = *epp; + /* + * Build up the full string, replacing all newlines with regular + * whitespace. + */ + + sz = strlen(p + pos) + 1; + ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1); - sz = strlen(&p[offs]); - ep->eqn.data = mandoc_realloc(ep->eqn.data, ep->eqn.sz + sz + 1); - if (0 == ep->eqn.sz) - *ep->eqn.data = '\0'; + /* First invocation: nil terminate the string. */ - ep->eqn.sz += sz; - strlcat(ep->eqn.data, &p[offs], ep->eqn.sz + 1); + if (0 == ep->sz) + *ep->data = '\0'; + + ep->sz += sz; + strlcat(ep->data, p + pos, ep->sz + 1); + strlcat(ep->data, " ", ep->sz + 1); return(ROFF_IGN); } struct eqn_node * -eqn_alloc(int pos, int line) +eqn_alloc(const char *name, int pos, int line, struct mparse *parse) { struct eqn_node *p; + size_t sz; + const char *end; p = mandoc_calloc(1, sizeof(struct eqn_node)); - p->eqn.line = line; + + if (name && '\0' != *name) { + sz = strlen(name); + assert(sz); + do { + sz--; + end = name + (int)sz; + } while (' ' == *end || '\t' == *end); + p->eqn.name = mandoc_strndup(name, sz + 1); + } + + p->parse = parse; + p->eqn.ln = line; p->eqn.pos = pos; + p->gsize = EQN_DEFSIZE; return(p); } -/* ARGSUSED */ -void -eqn_end(struct eqn_node *e) +enum rofferr +eqn_end(struct eqn_node **epp) +{ + struct eqn_node *ep; + struct eqn_box *root; + enum eqn_rest c; + + ep = *epp; + *epp = NULL; + + ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); + + root = ep->eqn.root; + root->type = EQN_ROOT; + + if (0 == ep->sz) + return(ROFF_IGN); + + if (EQN_DESCOPE == (c = eqn_eqn(ep, root))) { + EQN_MSG(MANDOCERR_EQNNSCOPE, ep); + c = EQN_ERR; + } + + return(EQN_EOF == c ? ROFF_EQN : ROFF_IGN); +} + +static enum eqn_rest +eqn_eqn(struct eqn_node *ep, struct eqn_box *last) { + struct eqn_box *bp; + enum eqn_rest c; + + bp = eqn_box_alloc(ep, last); + bp->type = EQN_SUBEXPR; - /* Nothing to do. */ + while (EQN_OK == (c = eqn_box(ep, bp))) + /* Spin! */ ; + + return(c); +} + +static enum eqn_rest +eqn_matrix(struct eqn_node *ep, struct eqn_box *last) +{ + struct eqn_box *bp; + const char *start; + size_t sz; + enum eqn_rest c; + + bp = eqn_box_alloc(ep, last); + bp->type = EQN_MATRIX; + + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + if ( ! STRNEQ(start, sz, "{", 1)) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } + + while (EQN_OK == (c = eqn_box(ep, bp))) + switch (bp->last->pile) { + case (EQNPILE_LCOL): + /* FALLTHROUGH */ + case (EQNPILE_CCOL): + /* FALLTHROUGH */ + case (EQNPILE_RCOL): + continue; + default: + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + }; + + if (EQN_DESCOPE != c) { + if (EQN_EOF == c) + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (STRNEQ(start, sz, "}", 1)) + return(EQN_OK); + + EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); + return(EQN_ERR); +} + +static enum eqn_rest +eqn_list(struct eqn_node *ep, struct eqn_box *last) +{ + struct eqn_box *bp; + const char *start; + size_t sz; + enum eqn_rest c; + + bp = eqn_box_alloc(ep, last); + bp->type = EQN_LIST; + + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + if ( ! STRNEQ(start, sz, "{", 1)) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } + + while (EQN_DESCOPE == (c = eqn_eqn(ep, bp))) { + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if ( ! STRNEQ(start, sz, "above", 5)) + break; + } + + if (EQN_DESCOPE != c) { + if (EQN_ERR != c) + EQN_MSG(MANDOCERR_EQNSCOPE, ep); + return(EQN_ERR); + } + + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (STRNEQ(start, sz, "}", 1)) + return(EQN_OK); + + EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); + return(EQN_ERR); +} + +static enum eqn_rest +eqn_box(struct eqn_node *ep, struct eqn_box *last) +{ + size_t sz; + const char *start; + char *left; + char sym[64]; + enum eqn_rest c; + int i, size; + struct eqn_box *bp; + + if (NULL == (start = eqn_nexttok(ep, &sz))) + return(EQN_EOF); + + if (STRNEQ(start, sz, "}", 1)) + return(EQN_DESCOPE); + else if (STRNEQ(start, sz, "right", 5)) + return(EQN_DESCOPE); + else if (STRNEQ(start, sz, "above", 5)) + return(EQN_DESCOPE); + else if (STRNEQ(start, sz, "mark", 4)) + return(EQN_OK); + else if (STRNEQ(start, sz, "lineup", 6)) + return(EQN_OK); + + for (i = 0; i < (int)EQN__MAX; i++) { + if ( ! EQNSTREQ(&eqnparts[i].str, start, sz)) + continue; + return((*eqnparts[i].fp)(ep) ? + EQN_OK : EQN_ERR); + } + + if (STRNEQ(start, sz, "{", 1)) { + if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) { + if (EQN_ERR != c) + EQN_MSG(MANDOCERR_EQNSCOPE, ep); + return(EQN_ERR); + } + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if (STRNEQ(start, sz, "}", 1)) + return(EQN_OK); + EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); + return(EQN_ERR); + } + + for (i = 0; i < (int)EQNPILE__MAX; i++) { + if ( ! EQNSTREQ(&eqnpiles[i], start, sz)) + continue; + if (EQN_OK == (c = eqn_list(ep, last))) + last->last->pile = (enum eqn_pilet)i; + return(c); + } + + if (STRNEQ(start, sz, "matrix", 6)) + return(eqn_matrix(ep, last)); + + if (STRNEQ(start, sz, "left", 4)) { + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + left = mandoc_strndup(start, sz); + c = eqn_eqn(ep, last); + if (last->last) + last->last->left = left; + else + free(left); + if (EQN_DESCOPE != c) + return(c); + assert(last->last); + eqn_rewind(ep); + start = eqn_nexttok(ep, &sz); + assert(start); + if ( ! STRNEQ(start, sz, "right", 5)) + return(EQN_DESCOPE); + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + last->last->right = mandoc_strndup(start, sz); + return(EQN_OK); + } + + for (i = 0; i < (int)EQNPOS__MAX; i++) { + if ( ! EQNSTREQ(&eqnposs[i], start, sz)) + continue; + if (NULL == last->last) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } + last->last->pos = (enum eqn_post)i; + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + return(c); + } + + for (i = 0; i < (int)EQNMARK__MAX; i++) { + if ( ! EQNSTREQ(&eqnmarks[i], start, sz)) + continue; + if (NULL == last->last) { + EQN_MSG(MANDOCERR_EQNSYNT, ep); + return(EQN_ERR); + } + last->last->mark = (enum eqn_markt)i; + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + return(c); + } + + for (i = 0; i < (int)EQNFONT__MAX; i++) { + if ( ! EQNSTREQ(&eqnfonts[i], start, sz)) + continue; + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } else if (EQN_OK == c) + last->last->font = (enum eqn_fontt)i; + return(c); + } + + if (STRNEQ(start, sz, "size", 4)) { + if (NULL == (start = eqn_nexttok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } + size = mandoc_strntoi(start, sz, 10); + if (EQN_EOF == (c = eqn_box(ep, last))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(EQN_ERR); + } else if (EQN_OK != c) + return(c); + last->last->size = size; + } + + bp = eqn_box_alloc(ep, last); + bp->type = EQN_TEXT; + for (i = 0; i < (int)EQNSYM__MAX; i++) + if (EQNSTREQ(&eqnsyms[i].str, start, sz)) { + sym[63] = '\0'; + snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym); + bp->text = mandoc_strdup(sym); + return(EQN_OK); + } + + bp->text = mandoc_strndup(start, sz); + return(EQN_OK); } void eqn_free(struct eqn_node *p) { + int i; + + eqn_box_free(p->eqn.root); + + for (i = 0; i < (int)p->defsz; i++) { + free(p->defs[i].key); + free(p->defs[i].val); + } - free(p->eqn.data); + free(p->eqn.name); + free(p->data); + free(p->defs); free(p); } + +static struct eqn_box * +eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) +{ + struct eqn_box *bp; + + bp = mandoc_calloc(1, sizeof(struct eqn_box)); + bp->parent = parent; + bp->size = ep->gsize; + + if (NULL == parent->first) + parent->first = bp; + else + parent->last->next = bp; + + parent->last = bp; + return(bp); +} + +static void +eqn_box_free(struct eqn_box *bp) +{ + + if (bp->first) + eqn_box_free(bp->first); + if (bp->next) + eqn_box_free(bp->next); + + free(bp->text); + free(bp->left); + free(bp->right); + free(bp); +} + +static const char * +eqn_nextrawtok(struct eqn_node *ep, size_t *sz) +{ + + return(eqn_next(ep, '"', sz, 0)); +} + +static const char * +eqn_nexttok(struct eqn_node *ep, size_t *sz) +{ + + return(eqn_next(ep, '"', sz, 1)); +} + +static void +eqn_rewind(struct eqn_node *ep) +{ + + ep->cur = ep->rew; +} + +static const char * +eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl) +{ + char *start, *next; + int q, diff, lim; + size_t ssz, dummy; + struct eqn_def *def; + + if (NULL == sz) + sz = &dummy; + + lim = 0; + ep->rew = ep->cur; +again: + /* Prevent self-definitions. */ + + if (lim >= EQN_NEST_MAX) { + EQN_MSG(MANDOCERR_ROFFLOOP, ep); + return(NULL); + } + + ep->cur = ep->rew; + start = &ep->data[(int)ep->cur]; + q = 0; + + if ('\0' == *start) + return(NULL); + + if (quote == *start) { + ep->cur++; + q = 1; + } + + start = &ep->data[(int)ep->cur]; + + if ( ! q) { + if ('{' == *start || '}' == *start) + ssz = 1; + else + ssz = strcspn(start + 1, " ^~\"{}\t") + 1; + next = start + (int)ssz; + if ('\0' == *next) + next = NULL; + } else + next = strchr(start, quote); + + if (NULL != next) { + *sz = (size_t)(next - start); + ep->cur += *sz; + if (q) + ep->cur++; + while (' ' == ep->data[(int)ep->cur] || + '\t' == ep->data[(int)ep->cur] || + '^' == ep->data[(int)ep->cur] || + '~' == ep->data[(int)ep->cur]) + ep->cur++; + } else { + if (q) + EQN_MSG(MANDOCERR_BADQUOTE, ep); + next = strchr(start, '\0'); + *sz = (size_t)(next - start); + ep->cur += *sz; + } + + /* Quotes aren't expanded for values. */ + + if (q || ! repl) + return(start); + + if (NULL != (def = eqn_def_find(ep, start, *sz))) { + diff = def->valsz - *sz; + + if (def->valsz > *sz) { + ep->sz += diff; + ep->data = mandoc_realloc(ep->data, ep->sz + 1); + ep->data[ep->sz] = '\0'; + start = &ep->data[(int)ep->rew]; + } + + diff = def->valsz - *sz; + memmove(start + *sz + diff, start + *sz, + (strlen(start) - *sz) + 1); + memcpy(start, def->val, def->valsz); + goto again; + } + + return(start); +} + +static int +eqn_do_ign1(struct eqn_node *ep) +{ + + if (NULL == eqn_nextrawtok(ep, NULL)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + else + return(1); + + return(0); +} + +static int +eqn_do_ign2(struct eqn_node *ep) +{ + + if (NULL == eqn_nextrawtok(ep, NULL)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + else if (NULL == eqn_nextrawtok(ep, NULL)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + else + return(1); + + return(0); +} + +static int +eqn_do_tdefine(struct eqn_node *ep) +{ + + if (NULL == eqn_nextrawtok(ep, NULL)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + else if (NULL == eqn_next(ep, ep->data[(int)ep->cur], NULL, 0)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + else + return(1); + + return(0); +} + +static int +eqn_do_define(struct eqn_node *ep) +{ + const char *start; + size_t sz; + struct eqn_def *def; + int i; + + if (NULL == (start = eqn_nextrawtok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(0); + } + + /* + * Search for a key that already exists. + * Create a new key if none is found. + */ + + if (NULL == (def = eqn_def_find(ep, start, sz))) { + /* Find holes in string array. */ + for (i = 0; i < (int)ep->defsz; i++) + if (0 == ep->defs[i].keysz) + break; + + if (i == (int)ep->defsz) { + ep->defsz++; + ep->defs = mandoc_realloc + (ep->defs, ep->defsz * + sizeof(struct eqn_def)); + ep->defs[i].key = ep->defs[i].val = NULL; + } + + ep->defs[i].keysz = sz; + ep->defs[i].key = mandoc_realloc + (ep->defs[i].key, sz + 1); + + memcpy(ep->defs[i].key, start, sz); + ep->defs[i].key[(int)sz] = '\0'; + def = &ep->defs[i]; + } + + start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0); + + if (NULL == start) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(0); + } + + def->valsz = sz; + def->val = mandoc_realloc(def->val, sz + 1); + memcpy(def->val, start, sz); + def->val[(int)sz] = '\0'; + return(1); +} + +static int +eqn_do_gfont(struct eqn_node *ep) +{ + + if (NULL == eqn_nextrawtok(ep, NULL)) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(0); + } + return(1); +} + +static int +eqn_do_gsize(struct eqn_node *ep) +{ + const char *start; + size_t sz; + + if (NULL == (start = eqn_nextrawtok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(0); + } + ep->gsize = mandoc_strntoi(start, sz, 10); + return(1); +} + +static int +eqn_do_undef(struct eqn_node *ep) +{ + const char *start; + struct eqn_def *def; + size_t sz; + + if (NULL == (start = eqn_nextrawtok(ep, &sz))) { + EQN_MSG(MANDOCERR_EQNEOF, ep); + return(0); + } else if (NULL != (def = eqn_def_find(ep, start, sz))) + def->keysz = 0; + + return(1); +} + +static struct eqn_def * +eqn_def_find(struct eqn_node *ep, const char *key, size_t sz) +{ + int i; + + for (i = 0; i < (int)ep->defsz; i++) + if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, + ep->defs[i].keysz, key, sz)) + return(&ep->defs[i]); + + return(NULL); +} diff --git a/contrib/mdocml/eqn_html.c b/contrib/mdocml/eqn_html.c new file mode 100644 index 0000000000..80c82f1de5 --- /dev/null +++ b/contrib/mdocml/eqn_html.c @@ -0,0 +1,81 @@ +/* $Id: eqn_html.c,v 1.2 2011/07/24 10:09:03 kristaps Exp $ */ +/* + * Copyright (c) 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "mandoc.h" +#include "out.h" +#include "html.h" + +static const enum htmltag fontmap[EQNFONT__MAX] = { + TAG_SPAN, /* EQNFONT_NONE */ + TAG_SPAN, /* EQNFONT_ROMAN */ + TAG_B, /* EQNFONT_BOLD */ + TAG_B, /* EQNFONT_FAT */ + TAG_I /* EQNFONT_ITALIC */ +}; + + +static void eqn_box(struct html *, const struct eqn_box *); + +void +print_eqn(struct html *p, const struct eqn *ep) +{ + struct htmlpair tag; + struct tag *t; + + PAIR_CLASS_INIT(&tag, "eqn"); + t = print_otag(p, TAG_SPAN, 1, &tag); + + p->flags |= HTML_NONOSPACE; + eqn_box(p, ep->root); + p->flags &= ~HTML_NONOSPACE; + + print_tagq(p, t); +} + +static void +eqn_box(struct html *p, const struct eqn_box *bp) +{ + struct tag *t; + + t = EQNFONT_NONE == bp->font ? NULL : + print_otag(p, fontmap[(int)bp->font], 0, NULL); + + if (bp->left) + print_text(p, bp->left); + + if (bp->text) + print_text(p, bp->text); + + if (bp->first) + eqn_box(p, bp->first); + + if (NULL != t) + print_tagq(p, t); + if (bp->right) + print_text(p, bp->right); + + if (bp->next) + eqn_box(p, bp->next); +} diff --git a/contrib/mdocml/eqn_term.c b/contrib/mdocml/eqn_term.c new file mode 100644 index 0000000000..cfbd8d48f8 --- /dev/null +++ b/contrib/mdocml/eqn_term.c @@ -0,0 +1,76 @@ +/* $Id: eqn_term.c,v 1.4 2011/07/24 10:09:03 kristaps Exp $ */ +/* + * Copyright (c) 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "mandoc.h" +#include "out.h" +#include "term.h" + +static const enum termfont fontmap[EQNFONT__MAX] = { + TERMFONT_NONE, /* EQNFONT_NONE */ + TERMFONT_NONE, /* EQNFONT_ROMAN */ + TERMFONT_BOLD, /* EQNFONT_BOLD */ + TERMFONT_BOLD, /* EQNFONT_FAT */ + TERMFONT_UNDER /* EQNFONT_ITALIC */ +}; + +static void eqn_box(struct termp *, const struct eqn_box *); + +void +term_eqn(struct termp *p, const struct eqn *ep) +{ + + p->flags |= TERMP_NONOSPACE; + eqn_box(p, ep->root); + term_word(p, " "); + p->flags &= ~TERMP_NONOSPACE; +} + +static void +eqn_box(struct termp *p, const struct eqn_box *bp) +{ + + if (EQNFONT_NONE != bp->font) + term_fontpush(p, fontmap[(int)bp->font]); + if (bp->left) + term_word(p, bp->left); + if (EQN_SUBEXPR == bp->type) + term_word(p, "("); + + if (bp->text) + term_word(p, bp->text); + + if (bp->first) + eqn_box(p, bp->first); + + if (EQN_SUBEXPR == bp->type) + term_word(p, ")"); + if (bp->right) + term_word(p, bp->right); + if (EQNFONT_NONE != bp->font) + term_fontpop(p); + + if (bp->next) + eqn_box(p, bp->next); +} diff --git a/contrib/mdocml/example.style.css b/contrib/mdocml/example.style.css index 39075460a4..660f4d1320 100644 --- a/contrib/mdocml/example.style.css +++ b/contrib/mdocml/example.style.css @@ -1,144 +1,110 @@ -/* $Id: example.style.css,v 1.43 2011/04/11 22:58:28 kristaps Exp $ */ - +/* $Id: example.style.css,v 1.49 2011/12/15 12:18:57 kristaps Exp $ */ /* * This is an example style-sheet provided for mandoc(1) and the -Thtml * or -Txhtml output mode. - * - * It mimics the appearance of the traditional cvsweb output. - * + * It mimics the appearance of the legacy man.cgi output. * See mdoc(7) and man(7) for macro explanations. */ -html { min-width: 580px; width: 580px; } -body { font-family: monospace; } -h1 { margin-bottom: 0ex; font-size: inherit; margin-left: -4ex; } /* Section header (Sh, SH). */ -h2 { margin-bottom: 0ex; font-size: inherit; margin-left: -2ex; } /* Sub-section header (Ss, SS). */ -table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */ -td { vertical-align: top; } /* All table cells. */ -p { } /* Paragraph: Pp, Lp. */ -blockquote { margin-top: 0ex; margin-bottom: 0ex; } /* D1. */ -div.section { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */ -div.subsection { } /* Sub-sections (Ss, SS). */ -table.synopsis { } /* SYNOPSIS section table. */ - -/* Preamble structure. */ - -table.foot { } /* Document footer. */ -td.foot-date { width: 50%; } /* Document footer: date. */ -td.foot-os { width: 50%; text-align: right; } /* Document footer: OS/source. */ -table.head { } /* Document header. */ -td.head-ltitle { width: 10%; } /* Document header: left-title. */ -td.head-vol { width: 80%; text-align: center; } /* Document header: volume. */ -td.head-rtitle { width: 10%; text-align: right; } /* Document header: right-title. */ - -/* General font modes. */ - -i { } /* Italic: BI, IB, I, (implicit). */ -.emph { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */ -b { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */ -.symb { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */ -small { } /* Small: SB, SM. */ - -/* Block modes. */ - -.display { } /* Top of all Bd, D1, Dl. */ -.list { } /* Top of all Bl. */ - -/* Context-specific modes. */ - -i.addr { font-weight: normal; } /* Address (Ad). */ -i.arg { font-weight: normal; } /* Command argument (Ar). */ -span.author { } /* Author name (An). */ -b.cmd { font-style: normal; } /* Command (Cm). */ -b.config { font-style: normal; } /* Config statement (Cd). */ -span.define { } /* Defines (Dv). */ -span.desc { } /* Nd. After em-dash. */ -b.diag { font-style: normal; } /* Diagnostic (Bl -diag). */ -span.env { } /* Environment variables (Ev). */ -span.errno { } /* Error string (Er). */ -i.farg { font-weight: normal; } /* Function argument (Fa, Fn). */ -i.file { font-weight: normal; } /* File (Pa). */ -b.flag { font-style: normal; } /* Flag (Fl, Cm). */ -b.fname { font-style: normal; } /* Function name (Fa, Fn, Rv). */ -i.ftype { font-weight: normal; } /* Function types (Ft, Fn). */ -b.includes { font-style: normal; } /* Header includes (In). */ -span.lib { } /* Library (Lb). */ -i.link-sec { font-weight: normal; } /* Section links (Sx). */ -code.lit { font-style: normal; font-weight: normal; } /* Literal: Dl, Li, Bf -literal, Bl -literal, Bl -unfilled. */ -b.macro { font-style: normal; } /* Macro-ish thing (Fd). */ -b.name { font-style: normal; } /* Name of utility (Nm). */ -span.opt { } /* Options (Op, Oo/Oc). */ -span.ref { } /* Citations (Rs). */ -span.ref-auth { } /* Reference author (%A). */ -i.ref-book { font-weight: normal; } /* Reference book (%B). */ -span.ref-city { } /* Reference city (%C). */ -span.ref-date { } /* Reference date (%D). */ -i.ref-issue { font-weight: normal; } /* Reference issuer/publisher (%I). */ -i.ref-jrnl { font-weight: normal; } /* Reference journal (%J). */ -span.ref-num { } /* Reference number (%N). */ -span.ref-opt { } /* Reference optionals (%O). */ -span.ref-page { } /* Reference page (%P). */ -span.ref-corp { } /* Reference corporate/foreign author (%Q). */ -span.ref-rep { } /* Reference report (%R). */ -span.ref-title { text-decoration: underline; } /* Reference title (%T). */ -span.ref-vol { } /* Reference volume (%V). */ -span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */ -span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */ -b.utility { font-style: normal; } /* Name of utility (Ex). */ -b.var { font-style: normal; } /* Variables (Rv). */ - -a.link-ext { } /* Off-site link (Lk). */ -a.link-includes { } /* Include-file link (In). */ -a.link-mail { } /* Mailto links (Mt). */ -a.link-man { } /* Manual links (Xr). */ -a.link-ref { } /* Reference section links (%Q). */ -a.link-sec { } /* Section links (Sx). */ - -/* Formatting for lists. See mdoc(7). */ - -dl.list-diag { } -dt.list-diag { } -dd.list-diag { } - -dl.list-hang { } -dt.list-hang { } -dd.list-hang { } - -dl.list-inset { } -dt.list-inset { } -dd.list-inset { } - -dl.list-ohang { } -dt.list-ohang { } -dd.list-ohang { margin-left: 0ex; } - -dl.list-tag { } -dt.list-tag { } -dd.list-tag { } - -table.list-col { } -tr.list-col { } -td.list-col { } - -ul.list-bul { list-style-type: disc; padding-left: 1em; } -li.list-bul { } - -ul.list-dash { list-style-type: none; padding-left: 0em; } -li.list-dash:before { content: "\2014 "; } - -ul.list-hyph { list-style-type: none; padding-left: 0em; } -li.list-hyph:before { content: "\2013 "; } - -ul.list-item { list-style-type: none; padding-left: 0em; } -li.list-item { } - -ol.list-enum { padding-left: 2em; } -li.list-enum { } - -/* Equation modes. See eqn(7). */ - -span.eqn { } - -/* Table modes. See tbl(7). */ - -table.tbl { } +div.mandoc { min-width: 102ex; + width: 102ex; + font-family: monospace; } /* This is the outer node of all mandoc -T[x]html documents. */ +div.mandoc h1 { margin-bottom: 0ex; font-size: inherit; margin-left: -4ex; } /* Section header (Sh, SH). */ +div.mandoc h2 { margin-bottom: 0ex; font-size: inherit; margin-left: -2ex; } /* Sub-section header (Ss, SS). */ +div.mandoc table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */ +div.mandoc td { vertical-align: top; } /* All table cells. */ +div.mandoc p { } /* Paragraph: Pp, Lp. */ +div.mandoc blockquote { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1, Dl. */ +div.mandoc div.section { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */ +div.mandoc div.subsection { } /* Sub-sections (Ss, SS). */ +div.mandoc table.synopsis { } /* SYNOPSIS section table. */ +div.mandoc table.foot { } /* Document footer. */ +div.mandoc td.foot-date { width: 50%; } /* Document footer: date. */ +div.mandoc td.foot-os { width: 50%; text-align: right; } /* Document footer: OS/source. */ +div.mandoc table.head { } /* Document header. */ +div.mandoc td.head-ltitle { width: 10%; } /* Document header: left-title. */ +div.mandoc td.head-vol { width: 80%; text-align: center; } /* Document header: volume. */ +div.mandoc td.head-rtitle { width: 10%; text-align: right; } /* Document header: right-title. */ +div.mandoc .display { } /* All Bd, D1, Dl. */ +div.mandoc .list { } /* All Bl. */ +div.mandoc i { } /* Italic: BI, IB, I, (implicit). */ +div.mandoc b { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */ +div.mandoc small { } /* Small: SB, SM. */ +div.mandoc .emph { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */ +div.mandoc .symb { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */ +div.mandoc .lit { font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */ +div.mandoc i.addr { font-weight: normal; } /* Address (Ad). */ +div.mandoc i.arg { font-weight: normal; } /* Command argument (Ar). */ +div.mandoc span.author { } /* Author name (An). */ +div.mandoc b.cmd { font-style: normal; } /* Command (Cm). */ +div.mandoc b.config { font-style: normal; } /* Config statement (Cd). */ +div.mandoc span.define { } /* Defines (Dv). */ +div.mandoc span.desc { } /* Nd. After em-dash. */ +div.mandoc b.diag { font-style: normal; } /* Diagnostic (Bl -diag). */ +div.mandoc span.env { } /* Environment variables (Ev). */ +div.mandoc span.errno { } /* Error string (Er). */ +div.mandoc i.farg { font-weight: normal; } /* Function argument (Fa, Fn). */ +div.mandoc i.file { font-weight: normal; } /* File (Pa). */ +div.mandoc b.flag { font-style: normal; } /* Flag (Fl, Cm). */ +div.mandoc b.fname { font-style: normal; } /* Function name (Fa, Fn, Rv). */ +div.mandoc i.ftype { font-weight: normal; } /* Function types (Ft, Fn). */ +div.mandoc b.includes { font-style: normal; } /* Header includes (In). */ +div.mandoc span.lib { } /* Library (Lb). */ +div.mandoc i.link-sec { font-weight: normal; } /* Section links (Sx). */ +div.mandoc b.macro { font-style: normal; } /* Macro-ish thing (Fd). */ +div.mandoc b.name { font-style: normal; } /* Name of utility (Nm). */ +div.mandoc span.opt { } /* Options (Op, Oo/Oc). */ +div.mandoc span.ref { } /* Citations (Rs). */ +div.mandoc span.ref-auth { } /* Reference author (%A). */ +div.mandoc i.ref-book { font-weight: normal; } /* Reference book (%B). */ +div.mandoc span.ref-city { } /* Reference city (%C). */ +div.mandoc span.ref-date { } /* Reference date (%D). */ +div.mandoc i.ref-issue { font-weight: normal; } /* Reference issuer/publisher (%I). */ +div.mandoc i.ref-jrnl { font-weight: normal; } /* Reference journal (%J). */ +div.mandoc span.ref-num { } /* Reference number (%N). */ +div.mandoc span.ref-opt { } /* Reference optionals (%O). */ +div.mandoc span.ref-page { } /* Reference page (%P). */ +div.mandoc span.ref-corp { } /* Reference corporate/foreign author (%Q). */ +div.mandoc span.ref-rep { } /* Reference report (%R). */ +div.mandoc span.ref-title { text-decoration: underline; } /* Reference title (%T). */ +div.mandoc span.ref-vol { } /* Reference volume (%V). */ +div.mandoc span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */ +div.mandoc span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */ +div.mandoc b.utility { font-style: normal; } /* Name of utility (Ex). */ +div.mandoc b.var { font-style: normal; } /* Variables (Rv). */ +div.mandoc a.link-ext { } /* Off-site link (Lk). */ +div.mandoc a.link-includes { } /* Include-file link (In). */ +div.mandoc a.link-mail { } /* Mailto links (Mt). */ +div.mandoc a.link-man { } /* Manual links (Xr). */ +div.mandoc a.link-ref { } /* Reference section links (%Q). */ +div.mandoc a.link-sec { } /* Section links (Sx). */ +div.mandoc dl.list-diag { } /* Formatting for lists. See mdoc(7). */ +div.mandoc dt.list-diag { } +div.mandoc dd.list-diag { } +div.mandoc dl.list-hang { } +div.mandoc dt.list-hang { } +div.mandoc dd.list-hang { } +div.mandoc dl.list-inset { } +div.mandoc dt.list-inset { } +div.mandoc dd.list-inset { } +div.mandoc dl.list-ohang { } +div.mandoc dt.list-ohang { } +div.mandoc dd.list-ohang { margin-left: 0ex; } +div.mandoc dl.list-tag { } +div.mandoc dt.list-tag { } +div.mandoc dd.list-tag { } +div.mandoc table.list-col { } +div.mandoc tr.list-col { } +div.mandoc td.list-col { } +div.mandoc ul.list-bul { list-style-type: disc; padding-left: 1em; } +div.mandoc li.list-bul { } +div.mandoc ul.list-dash { list-style-type: none; padding-left: 0em; } +div.mandoc li.list-dash:before { content: "\2014 "; } +div.mandoc ul.list-hyph { list-style-type: none; padding-left: 0em; } +div.mandoc li.list-hyph:before { content: "\2013 "; } +div.mandoc ul.list-item { list-style-type: none; padding-left: 0em; } +div.mandoc li.list-item { } +div.mandoc ol.list-enum { padding-left: 2em; } +div.mandoc li.list-enum { } +div.mandoc span.eqn { } /* Equation modes. See eqn(7). */ +div.mandoc table.tbl { } /* Table modes. See tbl(7). */ diff --git a/contrib/mdocml/html.c b/contrib/mdocml/html.c index 45471fe3b0..326df035fc 100644 --- a/contrib/mdocml/html.c +++ b/contrib/mdocml/html.c @@ -1,4 +1,4 @@ -/* $Id: html.c,v 1.147 2011/05/24 21:40:14 kristaps Exp $ */ +/* $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2011 Ingo Schwarze @@ -118,13 +118,14 @@ static void * ml_alloc(char *outopts, enum htmltype type) { struct html *h; - const char *toks[4]; + const char *toks[5]; char *v; toks[0] = "style"; toks[1] = "man"; toks[2] = "includes"; - toks[3] = NULL; + toks[3] = "fragment"; + toks[4] = NULL; h = mandoc_calloc(1, sizeof(struct html)); @@ -143,6 +144,9 @@ ml_alloc(char *outopts, enum htmltype type) case (2): h->base_includes = v; break; + case (3): + h->oflags |= HTML_FRAGMENT; + break; default: break; } @@ -513,9 +517,11 @@ print_text(struct html *h, const char *word) print_otag(h, TAG_I, 0, NULL); assert(word); - if ( ! print_encode(h, word, 0)) + if ( ! print_encode(h, word, 0)) { if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; + } else + h->flags |= HTML_NOSPACE; if (h->metaf) { print_tagq(h, h->metaf); @@ -595,7 +601,6 @@ bufcat(struct html *h, const char *p) h->buflen = strlcat(h->buf, p, BUFSIZ); assert(h->buflen < BUFSIZ); - h->buflen--; } void diff --git a/contrib/mdocml/html.h b/contrib/mdocml/html.h index aba635f144..60960702f1 100644 --- a/contrib/mdocml/html.h +++ b/contrib/mdocml/html.h @@ -1,6 +1,6 @@ -/* $Id: html.h,v 1.44 2011/05/17 11:34:31 kristaps Exp $ */ +/* $Id: html.h,v 1.47 2011/10/05 21:35:17 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2008, 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 @@ -104,7 +104,7 @@ struct htmlpair { #define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf) #define PAIR_SUMMARY_INIT(p, v) PAIR_INIT(p, ATTR_SUMMARY, v) -enum htmltype { +enum htmltype { HTML_HTML_4_01_STRICT, HTML_XHTML_1_0_STRICT }; @@ -125,11 +125,13 @@ struct html { char *base_includes; /* base for include href */ char *style; /* style-sheet URI */ char buf[BUFSIZ]; /* see bufcat and friends */ - size_t buflen; + size_t buflen; struct tag *metaf; /* current open font scope */ enum htmlfont metal; /* last used font */ enum htmlfont metac; /* current font mode */ - enum htmltype type; + enum htmltype type; /* output media type */ + int oflags; /* output options */ +#define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */ }; void print_gen_decls(struct html *); @@ -141,6 +143,7 @@ void print_stagq(struct html *, const struct tag *); void print_text(struct html *, const char *); void print_tblclose(struct html *); void print_tbl(struct html *, const struct tbl_span *); +void print_eqn(struct html *, const struct eqn *); void bufcat_fmt(struct html *, const char *, ...); void bufcat(struct html *, const char *); diff --git a/contrib/mdocml/index.css b/contrib/mdocml/index.css index ce0898d0d3..d98316eaf3 100644 --- a/contrib/mdocml/index.css +++ b/contrib/mdocml/index.css @@ -11,7 +11,9 @@ body { text-align: justify; p,ul,table { margin-left: 3em; } -p.head, p.foot { margin-left: 0.0em; margin-right: 0.0em; } +p.head, +p.subhead, +p.foot { margin-left: 0.0em; margin-right: 0.0em; } p.news { margin-left: 2.0em; } @@ -20,10 +22,13 @@ li { margin: 0.25em; } h1 { font-size: 110%; } h2 { font-size: 105%; margin-left: 1.5em } -p.head { margin-bottom: 1.75em; +p.head { margin-bottom: 0.5em; border-bottom: 1px solid #dddddd; padding-bottom: 0.2em; } +p.subhead { margin-top: 0em; + margin-bottom: 1.75em; } + p.foot { border-top: 1px solid #dddddd; color: #666666; padding-top: 0.2em; diff --git a/contrib/mdocml/index.sgml b/contrib/mdocml/index.sgml index fa7d8b431d..4386a9ebd9 100644 --- a/contrib/mdocml/index.sgml +++ b/contrib/mdocml/index.sgml @@ -3,11 +3,16 @@ - mdocml | mdoc macro compiler + mdocml | UNIX manpage compiler

- mdocml – mdoc macro compiler + mdocml – UNIX manpage compiler, current version @VERSION@ (@VDATE@) +

+

+ Sources: current, + cvsweb + (archives)

Description @@ -28,61 +33,46 @@ mdocml consists of the libmandoc validating compiler and mandoc, which interfaces with the compiler library to format output for UNIX terminals (with support for wide-character locales), XHTML, HTML, PostScript, and PDF. - It also includes preconv, for recoding multibyte manuals; and makewhatis, for indexing manuals. + It also includes preconv, for recoding multibyte manuals; + demandoc, for emitting only text parts of manuals; + mandocdb, for indexing manuals; and + apropos, whatis, and + man.cgi (via catman) for semantic search of manual content. It is a BSD.lv project.

Disambiguation: mdocml is often referred to by its installed binary, mandoc.

-

+

Sources -

-

- mdocml is in plain-old ANSI C and should build and run on any UNIX system, although makewhatis requires Berkeley Database (this is - installed by default on all BSD operating systems). - To compile mdocml, run make, then make install to install into - /usr/local. - Be aware: if you have an existing groff installation, - this may overwrite its preconv binary. - The makewhatis utility is not yet linked to the build. You must run make - makewhatis to build it (it does not install). -

+

- The most current version of mdocml is @VERSION@, dated @VDATE@. + mdocml is in plain-old ANSI C and should build and run on any modern system; however, you'll + need libdb to build apropos, whatis, man.cgi, catman, and mandocdb (this is installed by default on BSD UNIX + systems — see the Makefile if you're running Linux). To build and install into /usr/local/, just + run make install. Be careful: the preconv, apropos, and whatis binary names are + usually taken by existing utilities.

-

- Current + Binaries

- - - - - - - - - - - - - - -
Source archive - /snapshots/mdocml.tar.gz - (md5) -
Online source - cvsweb -
- +

+ Binary archives consist of pre-compiled binaries, manuals, and other necessary files. + Universal (Mac OS X) binaries are compiled for the PCC, i386, and x86_64 architectures. + Windows binaries are compiled with MingW for the 32-bit (i686) and + 64-bit (x86_64) architectures. +

Downstream

- +

+ Several systems come bundled with mdocml utilities. + If your system does not appear below, the maintainers have not contacted me and it should not be considered + official. + Please contact us if you plan on maintaining a downstream version! +

@@ -90,198 +80,215 @@ - -
DragonFly BSD - usr.bin/mandoc + usr.bin/mandoc
FreeBSD - ports/textproc/mdocml + ports/textproc/mdocml
NetBSD - src/external/bsd/mdocml + src/external/bsd/mdocml
OpenBSD - src/usr.bin/mandoc + src/usr.bin/mandoc
- -

- Historical -

- - - - - - + + + + +
Source archivepkgsrc - /snapshots/ + textproc/mdocml +
Minix3 + external/bsd/mdocml
-

Documentation

-

- These manuals are generated automatically and refer to the current snapshot. + These manuals are generated automatically and refer to the current release. + They are the authoritative documentation for the mdocml system.

+ + + + + + + + - - - - + + + + + + + + + + + + + + + +
apropos(1) + search the manual page database + (text | + xhtml | + pdf | + ps) +
demandoc(1) + emit only text of UNIX manuals + (text | + xhtml | + pdf | + ps) +
mandoc(1) format and display UNIX manuals - (text | xhtml | pdf | - postscript) - -
makewhatis(1) - index UNIX manuals - - (text | - xhtml | - pdf | - postscript) - + ps)
preconv(1) recode multibyte UNIX manuals - (text | xhtml | pdf | - postscript) - + ps) +
whatis(1) + search the manual page database + (text | + xhtml | + pdf | + ps)
mandoc(3) mandoc macro compiler library - (text | xhtml | pdf | - postscript) - + ps)
man(7) man language reference - (text | xhtml | pdf | - postscript) - + ps) +
man.cgi(7) + cgi for manpage query and display + (text | + xhtml | + pdf | + ps)
eqn(7) eqn-mandoc language reference - (text | xhtml | pdf | - postscript) - + ps)
mandoc_char(7) mandoc special characters - (text | xhtml | pdf | - postscript) - + ps)
mdoc(7) mdoc language reference - (text | xhtml | pdf | - postscript) - + ps)
roff(7) roff-mandoc language reference - (text | xhtml | pdf | - postscript) - + ps)
tbl(7) tbl-mandoc language reference - (text | xhtml | pdf | - postscript) - + ps) +
catman(8) + update a man.cgi manpage cache + (text | + xhtml | + pdf | + ps) +
mandocdb(8) + index UNIX manuals + (text | + xhtml | + pdf | + ps)
-

Contact

-

- Use the mailing lists for bug-reports, patches, questions, etc. (these require subscription). Please check the + Use the mailing lists for bug-reports, patches, questions, etc. Please check the TODO for known issues - before posting. Beyond that, contact Kristaps at kris...@bsd.lv. + before posting. All lists are subscription-only: send a blank e-mail to the listed address to subscribe. Beyond that, + contact Kristaps at kris...@bsd.lv. Archives are available at Gmane.

- @@ -293,7 +300,6 @@ @@ -303,7 +309,6 @@ @@ -313,74 +318,46 @@
bug-reports, general questions, and announcements - (archive)
patches and system discussions - (archive)
source commit messages - (archive)
-

News

- -

- 26-05-2011: version 1.11.3 -

-

- Introduce locale-encoding of output with the -Tlocale output option and Unicode escaped-character input. - See mandoc and mandoc_char, respectively, for details. - This allows for non-ASCII characters (e.g., \[u5000]) to be rendered in the locale's encoding, if said - environment supports wide-character encoding (if it does not, -Tascii is used instead). - Locale support can be turned off at compile time by removing -DUSE_WCHAR in the Makefile, in which case - -Tlocale is always a synonym for -Tascii. -

-

- Furthermore, multibyte-encoded documents, such as those in UTF-8, may be on-the-fly recoded into mandoc input by using the newly-added preconv utility. - Note: in the future, this feature may be integrated into mandoc. -

-

- 12-05-2011: version 1.11.2 + 23-03-2011: version 1.12.1

- Corrected some installation issues in version 1.11.1. - Further migration to libmandoc. - Initial public release (this utility is very much under development) of makewhatis, - initially named mandoc-db. - This utility produces keyword databases of manual content - mandoc-cgi, which features semantic querying of manual content. -

- -

- 04-04-2011: version 1.11.1 + Significant work on apropos and mandocdb. These tools are + now much more robust. + A whatis implementation is now handled as an apropos mode. + These tools are also able to minimally handle pre-formatted pages, that is, those already formatted by another utility + such as GNU troff.

- The earlier libroff, libmdoc, and libman soup have been merged into - a single library, libmandoc, which manages all aspects of - parsing real manuals (from line-handling to tbl parsing). + The man.cgi script is also now available for wider testing. It interfaces with mandocdb manuals cached by catman. HTML output is generated + on-the-fly by libmandoc or internal methods to convert pre-formatted pages.

- Beyond this structural change, initial eqn functionality is in - place. For the time being, this is limited to the recognition of equation blocks; - future version of mdocml will expand upon this framework. + The mailing list archive for the discuss and tech lists are being hosted by Gmane at gmane.comp.tools.mdocml.user and gmane.comp.tools.mdocml.devel, respectively.

- As usual, many general fixes and improvements have also occurred. In particular, a great - deal of redundancy and superfluous code has been removed with the merging of the backend - libraries. + Lastly, I'm no longer providing binaries, as nobody has asked for them.

-

See cvsweb for historical notes.

-

Copyright © 2008–2011 Kristaps Dzonsons, - $Date: 2011/05/26 21:23:50 $ + $Date: 2012/03/24 02:07:32 $

diff --git a/contrib/mdocml/lib.in b/contrib/mdocml/lib.in index 18ee711c1b..230a465ad3 100644 --- a/contrib/mdocml/lib.in +++ b/contrib/mdocml/lib.in @@ -1,4 +1,4 @@ -/* $Id: lib.in,v 1.9 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Id: lib.in,v 1.13 2012/01/28 23:46:28 joerg Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -40,8 +40,9 @@ LINE("libcurses", "Curses Library (libcurses, \\-lcurses)") LINE("libdevinfo", "Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)") LINE("libdevstat", "Device Statistics Library (libdevstat, \\-ldevstat)") LINE("libdisk", "Interface to Slice and Partition Labels Library (libdisk, \\-ldisk)") +LINE("libdwarf", "DWARF Access Library (libdwarf, \\-ldwarf)") LINE("libedit", "Command Line Editor Library (libedit, \\-ledit)") -LINE("libelf", "ELF Parsing Library (libelf, \\-lelf)") +LINE("libelf", "ELF Access Library (libelf, \\-lelf)") LINE("libevent", "Event Notification Library (libevent, \\-levent)") LINE("libfetch", "File Transfer Library for URLs (libfetch, \\-lfetch)") LINE("libform", "Curses Form Library (libform, \\-lform)") @@ -52,6 +53,7 @@ LINE("libintl", "Internationalized Message Handling Library (libintl, \\-lintl) LINE("libipsec", "IPsec Policy Control Library (libipsec, \\-lipsec)") LINE("libipx", "IPX Address Conversion Support Library (libipx, \\-lipx)") LINE("libiscsi", "iSCSI protocol library (libiscsi, \\-liscsi)") +LINE("libisns", "Internet Storage Name Service Library (libisns, \\-lisns)") LINE("libjail", "Jail Library (libjail, \\-ljail)") LINE("libkiconv", "Kernel side iconv library (libkiconv, \\-lkiconv)") LINE("libkse", "N:M Threading Library (libkse, \\-lkse)") @@ -70,16 +72,20 @@ LINE("libpcap", "Capture Library (libpcap, \\-lpcap)") LINE("libpci", "PCI Bus Access Library (libpci, \\-lpci)") LINE("libpmc", "Performance Counters Library (libpmc, \\-lpmc)") LINE("libposix", "POSIX Compatibility Library (libposix, \\-lposix)") +LINE("libppath", "Property-List Paths Library (libppath, \\-lppath)") LINE("libprop", "Property Container Object Library (libprop, \\-lprop)") LINE("libpthread", "POSIX Threads Library (libpthread, \\-lpthread)") LINE("libpuffs", "puffs Convenience Library (libpuffs, \\-lpuffs)") +LINE("libquota", "Disk Quota Access and Control Library (libquota, \\-lquota)") LINE("librefuse", "File System in Userspace Convenience Library (librefuse, \\-lrefuse)") LINE("libresolv", "DNS Resolver Library (libresolv, \\-lresolv)") LINE("librpcsec_gss", "RPC GSS-API Authentication Library (librpcsec_gss, \\-lrpcsec_gss)") LINE("librpcsvc", "RPC Service Library (librpcsvc, \\-lrpcsvc)") -LINE("librt", "POSIX Real\\-time Library (librt, -lrt)") +LINE("librt", "POSIX Real\\-time Library (librt, \\-lrt)") +LINE("libsaslc", "Simple Authentication and Security Layer client library (libsaslc, \\-lsaslc)") LINE("libsdp", "Bluetooth Service Discovery Protocol User Library (libsdp, \\-lsdp)") LINE("libssp", "Buffer Overflow Protection Library (libssp, \\-lssp)") +LINE("libSystem", "System Library (libSystem, \\-lSystem)") LINE("libtermcap", "Termcap Access Library (libtermcap, \\-ltermcap)") LINE("libterminfo", "Terminal Information Library (libterminfo, \\-lterminfo)") LINE("libthr", "1:1 Threading Library (libthr, \\-lthr)") diff --git a/contrib/mdocml/libman.h b/contrib/mdocml/libman.h index 30c159f7da..4bc5128204 100644 --- a/contrib/mdocml/libman.h +++ b/contrib/mdocml/libman.h @@ -1,6 +1,6 @@ -/* $Id: libman.h,v 1.51 2011/03/23 15:33:57 kristaps Exp $ */ +/* $Id: libman.h,v 1.55 2011/11/07 01:24:40 schwarze Exp $ */ /* - * Copyright (c) 2009, 2010 Kristaps Dzonsons + * 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 @@ -36,7 +36,7 @@ struct man { struct man_node *last; /* the last parsed node */ struct man_node *first; /* the first parsed node */ struct man_meta meta; /* document meta-data */ - struct regset *regs; /* registers */ + struct roff *roff; }; #define MACRO_PROT_ARGS struct man *m, \ @@ -54,6 +54,7 @@ struct man_macro { #define MAN_FSCOPED (1 << 2) /* See blk_imp(). */ #define MAN_NSCOPED (1 << 3) /* See in_line_eoln(). */ #define MAN_NOCLOSE (1 << 4) /* See blk_exp(). */ +#define MAN_BSCOPE (1 << 5) /* Break BLINE scope. */ }; extern const struct man_macro *const man_macros; diff --git a/contrib/mdocml/libmandoc.h b/contrib/mdocml/libmandoc.h index 5f8379aff2..de422884a2 100644 --- a/contrib/mdocml/libmandoc.h +++ b/contrib/mdocml/libmandoc.h @@ -1,4 +1,4 @@ -/* $Id: libmandoc.h,v 1.21 2011/05/14 16:06:09 kristaps Exp $ */ +/* $Id: libmandoc.h,v 1.29 2011/12/02 01:37:14 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * @@ -34,35 +34,6 @@ enum regs { REG__MAX }; -/* - * A register (struct reg) can consist of many types: this consists of - * normalised types from the original string form. For the time being, - * there's only an unsigned integer type. - */ -union regval { - unsigned u; /* unsigned integer */ -}; - -/* - * A single register entity. If "set" is zero, the value of the - * register should be the default one, which is per-register. It's - * assumed that callers know which type in "v" corresponds to which - * register value. - */ -struct reg { - int set; /* whether set or not */ - union regval v; /* parsed data */ -}; - -/* - * The primary interface to setting register values is in libroff, - * although libmdoc and libman from time to time will manipulate - * registers (such as `.Sh SYNOPSIS' enabling REG_nS). - */ -struct regset { - struct reg regs[REG__MAX]; -}; - __BEGIN_DECLS struct roff; @@ -73,16 +44,15 @@ void mandoc_msg(enum mandocerr, struct mparse *, int, int, const char *); void mandoc_vmsg(enum mandocerr, struct mparse *, int, int, const char *, ...); -char *mandoc_strdup(const char *); char *mandoc_getarg(struct mparse *, char **, int, int *); char *mandoc_normdate(struct mparse *, char *, int, int); int mandoc_eos(const char *, size_t, int); -int mandoc_hyph(const char *, const char *); int mandoc_getcontrol(const char *, int *); -int mandoc_strntou(const char *, size_t, int); +int mandoc_strntoi(const char *, size_t, int); +const char *mandoc_a2msec(const char*); void mdoc_free(struct mdoc *); -struct mdoc *mdoc_alloc(struct regset *, struct mparse *); +struct mdoc *mdoc_alloc(struct roff *, struct mparse *); void mdoc_reset(struct mdoc *); int mdoc_parseln(struct mdoc *, int, char *, int); int mdoc_endparse(struct mdoc *); @@ -90,7 +60,7 @@ int mdoc_addspan(struct mdoc *, const struct tbl_span *); int mdoc_addeqn(struct mdoc *, const struct eqn *); void man_free(struct man *); -struct man *man_alloc(struct regset *, struct mparse *); +struct man *man_alloc(struct roff *, struct mparse *); void man_reset(struct man *); int man_parseln(struct man *, int, char *, int); int man_endparse(struct man *); @@ -98,11 +68,21 @@ int man_addspan(struct man *, const struct tbl_span *); int man_addeqn(struct man *, const struct eqn *); void roff_free(struct roff *); -struct roff *roff_alloc(struct regset *, struct mparse *); +struct roff *roff_alloc(struct mparse *); void roff_reset(struct roff *); enum rofferr roff_parseln(struct roff *, int, char **, size_t *, int, int *); void roff_endparse(struct roff *); +int roff_regisset(const struct roff *, enum regs); +unsigned int roff_regget(const struct roff *, enum regs); +void roff_regunset(struct roff *, enum regs); +char *roff_strdup(const struct roff *, const char *); +#if 0 +char roff_eqndelim(const struct roff *); +void roff_openeqn(struct roff *, const char *, + int, int, const char *); +int roff_closeeqn(struct roff *); +#endif const struct tbl_span *roff_span(const struct roff *); const struct eqn *roff_eqn(const struct roff *); diff --git a/contrib/mdocml/libmdoc.h b/contrib/mdocml/libmdoc.h index 0e4b125f42..af1729268a 100644 --- a/contrib/mdocml/libmdoc.h +++ b/contrib/mdocml/libmdoc.h @@ -1,6 +1,6 @@ -/* $Id: libmdoc.h,v 1.74 2011/04/19 16:38:48 kristaps Exp $ */ +/* $Id: libmdoc.h,v 1.78 2011/12/02 01:37:14 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2008, 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 @@ -39,7 +39,7 @@ struct mdoc { struct mdoc_meta meta; /* document meta-data */ enum mdoc_sec lastnamed; enum mdoc_sec lastsec; - struct regset *regs; /* registers */ + struct roff *roff; }; #define MACRO_PROT_ARGS struct mdoc *m, \ @@ -124,7 +124,6 @@ const char *mdoc_a2lib(const char *); const char *mdoc_a2st(const char *); const char *mdoc_a2arch(const char *); const char *mdoc_a2vol(const char *); -const char *mdoc_a2msec(const char *); int mdoc_valid_pre(struct mdoc *, struct mdoc_node *); int mdoc_valid_post(struct mdoc *); enum margverr mdoc_argv(struct mdoc *, int, enum mdoct, diff --git a/contrib/mdocml/libroff.h b/contrib/mdocml/libroff.h index d013688778..0bdd5a3604 100644 --- a/contrib/mdocml/libroff.h +++ b/contrib/mdocml/libroff.h @@ -1,4 +1,4 @@ -/* $Id: libroff.h,v 1.20 2011/03/20 16:02:05 kristaps Exp $ */ +/* $Id: libroff.h,v 1.27 2011/07/25 15:37:00 kristaps Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * @@ -43,8 +43,23 @@ struct tbl_node { }; struct eqn_node { + struct eqn_def *defs; + size_t defsz; + char *data; + size_t rew; + size_t cur; + size_t sz; + int gsize; struct eqn eqn; - struct eqn_node *next; + struct mparse *parse; + struct eqn_node *next; +}; + +struct eqn_def { + char *key; + size_t keysz; + char *val; + size_t valsz; }; struct tbl_node *tbl_alloc(int, int, struct mparse *); @@ -57,11 +72,12 @@ int tbl_layout(struct tbl_node *, int, const char *); int tbl_data(struct tbl_node *, int, const char *); int tbl_cdata(struct tbl_node *, int, const char *); const struct tbl_span *tbl_span(struct tbl_node *); -void tbl_end(struct tbl_node *); -struct eqn_node *eqn_alloc(int, int); -void eqn_end(struct eqn_node *); +void tbl_end(struct tbl_node **); +struct eqn_node *eqn_alloc(const char *, int, int, struct mparse *); +enum rofferr eqn_end(struct eqn_node **); void eqn_free(struct eqn_node *); -enum rofferr eqn_read(struct eqn_node **, int, const char *, int); +enum rofferr eqn_read(struct eqn_node **, int, + const char *, int, int *); __END_DECLS diff --git a/contrib/mdocml/main.c b/contrib/mdocml/main.c index 4c2e7e790c..fec83fba51 100644 --- a/contrib/mdocml/main.c +++ b/contrib/mdocml/main.c @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.163 2011/05/20 15:51:18 kristaps Exp $ */ +/* $Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010, 2011 Ingo Schwarze @@ -46,6 +46,7 @@ enum outt { OUTT_LOCALE, /* -Tlocale */ OUTT_UTF8, /* -Tutf8 */ OUTT_TREE, /* -Ttree */ + OUTT_MAN, /* -Tman */ OUTT_HTML, /* -Thtml */ OUTT_XHTML, /* -Txhtml */ OUTT_LINT, /* -Tlint */ @@ -126,6 +127,12 @@ main(int argc, char *argv[]) curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp); + /* + * Conditionally start up the lookaside buffer before parsing. + */ + if (OUTT_MAN == curp.outtype) + mparse_keep(curp.mp); + argc -= optind; argv += optind; @@ -249,6 +256,10 @@ parse(struct curparse *curp, int fd, curp->outman = tree_man; curp->outmdoc = tree_mdoc; break; + case (OUTT_MAN): + curp->outmdoc = man_mdoc; + curp->outman = man_man; + break; case (OUTT_PDF): /* FALLTHROUGH */ case (OUTT_ASCII): @@ -312,6 +323,8 @@ toptions(struct curparse *curp, char *arg) curp->wlevel = MANDOCLEVEL_WARNING; } else if (0 == strcmp(arg, "tree")) curp->outtype = OUTT_TREE; + else if (0 == strcmp(arg, "man")) + curp->outtype = OUTT_MAN; else if (0 == strcmp(arg, "html")) curp->outtype = OUTT_HTML; else if (0 == strcmp(arg, "utf8")) diff --git a/contrib/mdocml/main.h b/contrib/mdocml/main.h index 07b9e879fa..79dcf489ae 100644 --- a/contrib/mdocml/main.h +++ b/contrib/mdocml/main.h @@ -1,6 +1,6 @@ -/* $Id: main.h,v 1.12 2011/05/20 15:48:22 kristaps Exp $ */ +/* $Id: main.h,v 1.15 2011/10/06 22:29:12 kristaps Exp $ */ /* - * Copyright (c) 2009, 2010 Kristaps Dzonsons + * 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 @@ -41,6 +41,9 @@ void html_free(void *); void tree_mdoc(void *, const struct mdoc *); void tree_man(void *, const struct man *); +void man_mdoc(void *, const struct mdoc *); +void man_man(void *, const struct man *); + void *locale_alloc(char *); void *utf8_alloc(char *); void *ascii_alloc(char *); diff --git a/contrib/mdocml/makewhatis.1 b/contrib/mdocml/makewhatis.1 deleted file mode 100644 index 2eb385cd00..0000000000 --- a/contrib/mdocml/makewhatis.1 +++ /dev/null @@ -1,152 +0,0 @@ -.\" $Id: makewhatis.1,v 1.2 2011/05/14 23:43:03 kristaps Exp $ -.\" -.\" Copyright (c) 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: May 14 2011 $ -.Dt MAKEWHATIS 1 -.Os -.Sh NAME -.Nm makewhatis -.Nd index UNIX manuals -.Sh SYNOPSIS -.Nm -.Op Fl d Ar dir -.Ar -.Sh DESCRIPTION -The -.Nm -utility extracts keywords from -.Ux -manuals and indexes them for fast retrieval. -The arguments are as follows: -.Bl -tag -width Ds -.It Fl d Ar dir -The directory into which to write the keyword and index databases. -.It Ar -Read input from zero or more files in -.Xr mdoc 7 -or -.Xr man 7 -.Ux -manual format. -.El -.Pp -By default, -.Nm -constructs the -.Sx Index Database -and -.Sx Keyword Database -in the current working directory. -.Pp -If fatal parse errors are encountered, the offending file is printed to -stderr, omitted from the index, and the parse continues with the next -input file. -.Ss Index Database -The index database, -.Pa mandoc.index , -is a -.Xr recno 3 -database with record values consisting of -.Pp -.Bl -enum -compact -.It -a nil-terminated filename, -.It -a nil-terminated manual section, -.It -a nil-terminated manual title, -.It -a nil-terminated architecture -.Pq this is not often available -.It -and a nil-terminated description. -.El -.Pp -Both the manual section and description may be zero-length. -Entries are sequentially-numbered, but the filenames are unordered. -.Ss Keyword Database -The keyword database, -.Pa mandoc.db , -is a -.Xr btree 3 -database of nil-terminated keywords (record length is non-zero string -length plus one) mapping to a 8-byte binary field consisting of the -keyword type and source -.Sx Index Database -record number. -The type, an unsigned 32-bit integer in host order, is one of the -following: -.Pp -.Bl -tag -width Ds -offset indent -compact -.It Li 0x01 -The name of a manual page as given in the NAME section. -.It Li 0x02 -A function prototype name as given in the SYNOPSIS section. -.It Li 0x03 -A utility name as given in the SYNOPSIS section. -.It Li 0x04 -An include file as given in the SYNOPSIS section. -.It Li 0x05 -A variable name as given in the SYNOPSIS section. -.It Li 0x06 -A standard as given in the STANDARDS section. -.It Li 0x07 -An author as given in the AUTHORS section. -.It Li 0x08 -A configuration as given in the SYNOPSIS section. -.El -.Pp -If a value is encountered outside of this range, the database is -corrupt. -.Pp -The latter four bytes are a host-ordered record number within the -.Sx Index Database . -.Pp -The -.Nm -utility is -.Ud -.Sh FILES -.Bl -tag -width Ds -.It Pa mandoc.db -A -.Xr btree 3 -keyword database mapping keywords to a type and file reference in -.Pa mandoc.index . -.It Pa mandoc.db~ -Working copy of -.Pa mandoc.db . -.It Pa mandoc.index -A -.Xr recno 3 -database of indexed file-names. -.It Pa mandoc.index~ -Working copy of -.Pa mandoc.index . -.El -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr mandoc 1 -.Sh AUTHORS -The -.Nm -utility was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . -.Sh CAVEATS -Only -.Xr mdoc 7 -manuals are processed. diff --git a/contrib/mdocml/makewhatis.c b/contrib/mdocml/makewhatis.c deleted file mode 100644 index 01d0f6a0bd..0000000000 --- a/contrib/mdocml/makewhatis.c +++ /dev/null @@ -1,920 +0,0 @@ -/* $Id: makewhatis.c,v 1.2 2011/05/15 02:47:17 kristaps Exp $ */ -/* - * Copyright (c) 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. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include -#ifdef __linux__ -# include -#else -# include -#endif -#include -#include -#include -#include -#include -#include - -#include "man.h" -#include "mdoc.h" -#include "mandoc.h" - -#define MANDOC_DB "mandoc.db" -#define MANDOC_IDX "mandoc.index" -#define MANDOC_BUFSZ BUFSIZ -#define MANDOC_FLAGS O_CREAT|O_TRUNC|O_RDWR - -enum type { - MANDOC_NONE = 0, - MANDOC_NAME, - MANDOC_FUNCTION, - MANDOC_UTILITY, - MANDOC_INCLUDES, - MANDOC_VARIABLE, - MANDOC_STANDARD, - MANDOC_AUTHOR, - MANDOC_CONFIG -}; - -#define MAN_ARGS DB *db, \ - const char *dbn, \ - DBT *key, size_t *ksz, \ - DBT *val, \ - DBT *rval, size_t *rsz, \ - const struct man_node *n -#define MDOC_ARGS DB *db, \ - const char *dbn, \ - DBT *key, size_t *ksz, \ - DBT *val, \ - DBT *rval, size_t *rsz, \ - const struct mdoc_node *n - -static void dbt_append(DBT *, size_t *, const char *); -static void dbt_appendb(DBT *, size_t *, - const void *, size_t); -static void dbt_init(DBT *, size_t *); -static void dbt_put(DB *, const char *, DBT *, DBT *); -static void usage(void); -static void pman(DB *, const char *, DBT *, size_t *, - DBT *, DBT *, size_t *, struct man *); -static int pman_node(MAN_ARGS); -static void pmdoc(DB *, const char *, DBT *, size_t *, - DBT *, DBT *, size_t *, struct mdoc *); -static void pmdoc_node(MDOC_ARGS); -static void pmdoc_An(MDOC_ARGS); -static void pmdoc_Cd(MDOC_ARGS); -static void pmdoc_Fd(MDOC_ARGS); -static void pmdoc_In(MDOC_ARGS); -static void pmdoc_Fn(MDOC_ARGS); -static void pmdoc_Fo(MDOC_ARGS); -static void pmdoc_Nd(MDOC_ARGS); -static void pmdoc_Nm(MDOC_ARGS); -static void pmdoc_St(MDOC_ARGS); -static void pmdoc_Vt(MDOC_ARGS); - -typedef void (*pmdoc_nf)(MDOC_ARGS); - -static const char *progname; - -static const pmdoc_nf mdocs[MDOC_MAX] = { - NULL, /* Ap */ - NULL, /* Dd */ - NULL, /* Dt */ - NULL, /* Os */ - NULL, /* Sh */ - NULL, /* Ss */ - NULL, /* Pp */ - NULL, /* D1 */ - NULL, /* Dl */ - NULL, /* Bd */ - NULL, /* Ed */ - NULL, /* Bl */ - NULL, /* El */ - NULL, /* It */ - NULL, /* Ad */ - pmdoc_An, /* An */ - NULL, /* Ar */ - pmdoc_Cd, /* Cd */ - NULL, /* Cm */ - NULL, /* Dv */ - NULL, /* Er */ - NULL, /* Ev */ - NULL, /* Ex */ - NULL, /* Fa */ - pmdoc_Fd, /* Fd */ - NULL, /* Fl */ - pmdoc_Fn, /* Fn */ - NULL, /* Ft */ - NULL, /* Ic */ - pmdoc_In, /* In */ - NULL, /* Li */ - pmdoc_Nd, /* Nd */ - pmdoc_Nm, /* Nm */ - NULL, /* Op */ - NULL, /* Ot */ - NULL, /* Pa */ - NULL, /* Rv */ - pmdoc_St, /* St */ - pmdoc_Vt, /* Va */ - pmdoc_Vt, /* Vt */ - NULL, /* Xr */ - NULL, /* %A */ - NULL, /* %B */ - NULL, /* %D */ - NULL, /* %I */ - NULL, /* %J */ - NULL, /* %N */ - NULL, /* %O */ - NULL, /* %P */ - NULL, /* %R */ - NULL, /* %T */ - NULL, /* %V */ - NULL, /* Ac */ - NULL, /* Ao */ - NULL, /* Aq */ - NULL, /* At */ - NULL, /* Bc */ - NULL, /* Bf */ - NULL, /* Bo */ - NULL, /* Bq */ - NULL, /* Bsx */ - NULL, /* Bx */ - NULL, /* Db */ - NULL, /* Dc */ - NULL, /* Do */ - NULL, /* Dq */ - NULL, /* Ec */ - NULL, /* Ef */ - NULL, /* Em */ - NULL, /* Eo */ - NULL, /* Fx */ - NULL, /* Ms */ - NULL, /* No */ - NULL, /* Ns */ - NULL, /* Nx */ - NULL, /* Ox */ - NULL, /* Pc */ - NULL, /* Pf */ - NULL, /* Po */ - NULL, /* Pq */ - NULL, /* Qc */ - NULL, /* Ql */ - NULL, /* Qo */ - NULL, /* Qq */ - NULL, /* Re */ - NULL, /* Rs */ - NULL, /* Sc */ - NULL, /* So */ - NULL, /* Sq */ - NULL, /* Sm */ - NULL, /* Sx */ - NULL, /* Sy */ - NULL, /* Tn */ - NULL, /* Ux */ - NULL, /* Xc */ - NULL, /* Xo */ - pmdoc_Fo, /* Fo */ - NULL, /* Fc */ - NULL, /* Oo */ - NULL, /* Oc */ - NULL, /* Bk */ - NULL, /* Ek */ - NULL, /* Bt */ - NULL, /* Hf */ - NULL, /* Fr */ - NULL, /* Ud */ - NULL, /* Lb */ - NULL, /* Lp */ - NULL, /* Lk */ - NULL, /* Mt */ - NULL, /* Brq */ - NULL, /* Bro */ - NULL, /* Brc */ - NULL, /* %C */ - NULL, /* Es */ - NULL, /* En */ - NULL, /* Dx */ - NULL, /* %Q */ - NULL, /* br */ - NULL, /* sp */ - NULL, /* %U */ - NULL, /* Ta */ -}; - -int -main(int argc, char *argv[]) -{ - struct mparse *mp; /* parse sequence */ - struct mdoc *mdoc; /* resulting mdoc */ - struct man *man; /* resulting man */ - char *fn; /* current file being parsed */ - const char *msec, /* manual section */ - *mtitle, /* manual title */ - *arch, /* manual architecture */ - *dir; /* result dir (default: cwd) */ - char ibuf[MAXPATHLEN], /* index fname */ - ibbuf[MAXPATHLEN], /* index backup fname */ - fbuf[MAXPATHLEN], /* btree fname */ - fbbuf[MAXPATHLEN]; /* btree backup fname */ - int ch; - DB *idx, /* index database */ - *db; /* keyword database */ - DBT rkey, rval, /* recno entries */ - key, val; /* persistent keyword entries */ - size_t sv, - ksz, rsz; /* entry buffer size */ - char vbuf[8]; /* stringified record number */ - BTREEINFO info; /* btree configuration */ - recno_t rec; /* current record number */ - extern int optind; - extern char *optarg; - - progname = strrchr(argv[0], '/'); - if (progname == NULL) - progname = argv[0]; - else - ++progname; - - dir = ""; - - while (-1 != (ch = getopt(argc, argv, "d:"))) - switch (ch) { - case ('d'): - dir = optarg; - break; - default: - usage(); - return((int)MANDOCLEVEL_BADARG); - } - - argc -= optind; - argv += optind; - - /* - * Set up temporary file-names into which we're going to write - * all of our data (both for the index and database). These - * will be securely renamed to the real file-names after we've - * written all of our data. - */ - - ibuf[0] = ibuf[MAXPATHLEN - 2] = - ibbuf[0] = ibbuf[MAXPATHLEN - 2] = - fbuf[0] = fbuf[MAXPATHLEN - 2] = - fbbuf[0] = fbbuf[MAXPATHLEN - 2] = '\0'; - - strlcat(fbuf, dir, MAXPATHLEN); - strlcat(fbuf, MANDOC_DB, MAXPATHLEN); - - strlcat(fbbuf, fbuf, MAXPATHLEN); - strlcat(fbbuf, "~", MAXPATHLEN); - - strlcat(ibuf, dir, MAXPATHLEN); - strlcat(ibuf, MANDOC_IDX, MAXPATHLEN); - - strlcat(ibbuf, ibuf, MAXPATHLEN); - strlcat(ibbuf, "~", MAXPATHLEN); - - if ('\0' != fbuf[MAXPATHLEN - 2] || - '\0' != fbbuf[MAXPATHLEN - 2] || - '\0' != ibuf[MAXPATHLEN - 2] || - '\0' != ibbuf[MAXPATHLEN - 2]) { - fprintf(stderr, "%s: Path too long\n", progname); - exit((int)MANDOCLEVEL_SYSERR); - } - - /* - * For the keyword database, open a BTREE database that allows - * duplicates. For the index database, use a standard RECNO - * database type. - */ - - memset(&info, 0, sizeof(BTREEINFO)); - info.flags = R_DUP; - db = dbopen(fbbuf, MANDOC_FLAGS, 0644, DB_BTREE, &info); - - if (NULL == db) { - perror(fbbuf); - exit((int)MANDOCLEVEL_SYSERR); - } - - idx = dbopen(ibbuf, MANDOC_FLAGS, 0644, DB_RECNO, NULL); - - if (NULL == db) { - perror(ibbuf); - (*db->close)(db); - exit((int)MANDOCLEVEL_SYSERR); - } - - /* - * Try parsing the manuals given on the command line. If we - * totally fail, then just keep on going. Take resulting trees - * and push them down into the database code. - * Use the auto-parser and don't report any errors. - */ - - mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); - - memset(&key, 0, sizeof(DBT)); - memset(&val, 0, sizeof(DBT)); - memset(&rkey, 0, sizeof(DBT)); - memset(&rval, 0, sizeof(DBT)); - - val.size = sizeof(vbuf); - val.data = vbuf; - rkey.size = sizeof(recno_t); - - rec = 1; - ksz = rsz = 0; - - while (NULL != (fn = *argv++)) { - mparse_reset(mp); - - /* Parse and get (non-empty) AST. */ - - if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL) { - fprintf(stderr, "%s: Parse failure\n", fn); - continue; - } - mparse_result(mp, &mdoc, &man); - if (NULL == mdoc && NULL == man) - continue; - - /* Manual section: can be empty string. */ - - msec = NULL != mdoc ? - mdoc_meta(mdoc)->msec : - man_meta(man)->msec; - mtitle = NULL != mdoc ? - mdoc_meta(mdoc)->title : - man_meta(man)->title; - arch = NULL != mdoc ? mdoc_meta(mdoc)->arch : NULL; - - assert(msec); - assert(mtitle); - - /* - * The index record value consists of a nil-terminated - * filename, a nil-terminated manual section, and a - * nil-terminated description. Since the description - * may not be set, we set a sentinel to see if we're - * going to write a nil byte in its place. - */ - - dbt_init(&rval, &rsz); - dbt_appendb(&rval, &rsz, fn, strlen(fn) + 1); - dbt_appendb(&rval, &rsz, msec, strlen(msec) + 1); - dbt_appendb(&rval, &rsz, mtitle, strlen(mtitle) + 1); - dbt_appendb(&rval, &rsz, arch ? arch : "", - arch ? strlen(arch) + 1 : 1); - - sv = rval.size; - - /* Fix the record number in the btree value. */ - - memset(val.data, 0, sizeof(uint32_t)); - memcpy(val.data + 4, &rec, sizeof(uint32_t)); - - if (mdoc) - pmdoc(db, fbbuf, &key, &ksz, - &val, &rval, &rsz, mdoc); - else - pman(db, fbbuf, &key, &ksz, - &val, &rval, &rsz, man); - - /* - * Apply this to the index. If we haven't had a - * description set, put an empty one in now. - */ - - if (rval.size == sv) - dbt_appendb(&rval, &rsz, "", 1); - - rkey.data = &rec; - dbt_put(idx, ibbuf, &rkey, &rval); - - printf("Indexed: %s\n", fn); - rec++; - } - - (*db->close)(db); - (*idx->close)(idx); - - mparse_free(mp); - - free(key.data); - free(rval.data); - - /* Atomically replace the file with our temporary one. */ - - if (-1 == rename(fbbuf, fbuf)) - perror(fbuf); - if (-1 == rename(ibbuf, ibuf)) - perror(fbuf); - - return((int)MANDOCLEVEL_OK); -} - -/* - * Initialise the stored database key whose data buffer is shared - * between uses (as the key must sometimes be constructed from an array - * of - */ -static void -dbt_init(DBT *key, size_t *ksz) -{ - - if (0 == *ksz) { - assert(0 == key->size); - assert(NULL == key->data); - key->data = mandoc_malloc(MANDOC_BUFSZ); - *ksz = MANDOC_BUFSZ; - } - - key->size = 0; -} - -/* - * Append a binary value to a database entry. This can be invoked - * multiple times; the buffer is automatically resized. - */ -static void -dbt_appendb(DBT *key, size_t *ksz, const void *cp, size_t sz) -{ - - assert(key->data); - - /* Overshoot by MANDOC_BUFSZ. */ - - while (key->size + sz >= *ksz) { - *ksz = key->size + sz + MANDOC_BUFSZ; - key->data = mandoc_realloc(key->data, *ksz); - } - - memcpy(key->data + (int)key->size, cp, sz); - key->size += sz; -} - -/* - * Append a nil-terminated string to the database entry. This can be - * invoked multiple times. The database entry will be nil-terminated as - * well; if invoked multiple times, a space is put between strings. - */ -static void -dbt_append(DBT *key, size_t *ksz, const char *cp) -{ - size_t sz; - - if (0 == (sz = strlen(cp))) - return; - - assert(key->data); - - if (key->size) - ((char *)key->data)[(int)key->size - 1] = ' '; - - dbt_appendb(key, ksz, cp, sz + 1); -} - -/* ARGSUSED */ -static void -pmdoc_An(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_AUTHORS != n->sec) - return; - - for (n = n->child; n; n = n->next) - if (MDOC_TEXT == n->type) - dbt_append(key, ksz, n->string); - - fl = (uint32_t)MANDOC_AUTHOR; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Fd(MDOC_ARGS) -{ - uint32_t fl; - const char *start, *end; - size_t sz; - - if (SEC_SYNOPSIS != n->sec) - return; - if (NULL == (n = n->child) || MDOC_TEXT != n->type) - return; - - /* - * Only consider those `Fd' macro fields that begin with an - * "inclusion" token (versus, e.g., #define). - */ - if (strcmp("#include", n->string)) - return; - - if (NULL == (n = n->next) || MDOC_TEXT != n->type) - return; - - /* - * Strip away the enclosing angle brackets and make sure we're - * not zero-length. - */ - - start = n->string; - if ('<' == *start || '"' == *start) - start++; - - if (0 == (sz = strlen(start))) - return; - - end = &start[(int)sz - 1]; - if ('>' == *end || '"' == *end) - end--; - - assert(end >= start); - dbt_appendb(key, ksz, start, (size_t)(end - start + 1)); - dbt_appendb(key, ksz, "", 1); - - fl = (uint32_t)MANDOC_INCLUDES; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Cd(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_SYNOPSIS != n->sec) - return; - - for (n = n->child; n; n = n->next) - if (MDOC_TEXT == n->type) - dbt_append(key, ksz, n->string); - - fl = (uint32_t)MANDOC_CONFIG; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_In(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_SYNOPSIS != n->sec) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - dbt_append(key, ksz, n->child->string); - fl = (uint32_t)MANDOC_INCLUDES; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Fn(MDOC_ARGS) -{ - uint32_t fl; - const char *cp; - - if (SEC_SYNOPSIS != n->sec) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - /* .Fn "struct type *arg" "foo" */ - - cp = strrchr(n->child->string, ' '); - if (NULL == cp) - cp = n->child->string; - - /* Strip away pointer symbol. */ - - while ('*' == *cp) - cp++; - - dbt_append(key, ksz, cp); - fl = (uint32_t)MANDOC_FUNCTION; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_St(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_STANDARDS != n->sec) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - dbt_append(key, ksz, n->child->string); - fl = (uint32_t)MANDOC_STANDARD; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Vt(MDOC_ARGS) -{ - uint32_t fl; - const char *start; - size_t sz; - - if (SEC_SYNOPSIS != n->sec) - return; - if (MDOC_Vt == n->tok && MDOC_BODY != n->type) - return; - if (NULL == n->last || MDOC_TEXT != n->last->type) - return; - - /* - * Strip away leading pointer symbol '*' and trailing ';'. - */ - - start = n->last->string; - - while ('*' == *start) - start++; - - if (0 == (sz = strlen(start))) - return; - - if (';' == start[(int)sz - 1]) - sz--; - - if (0 == sz) - return; - - dbt_appendb(key, ksz, start, sz); - dbt_appendb(key, ksz, "", 1); - - fl = (uint32_t)MANDOC_VARIABLE; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Fo(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - dbt_append(key, ksz, n->child->string); - fl = (uint32_t)MANDOC_FUNCTION; - memcpy(val->data, &fl, 4); -} - - -/* ARGSUSED */ -static void -pmdoc_Nd(MDOC_ARGS) -{ - int first; - - for (first = 1, n = n->child; n; n = n->next) { - if (MDOC_TEXT != n->type) - continue; - if (first) - dbt_appendb(rval, rsz, n->string, strlen(n->string) + 1); - else - dbt_append(rval, rsz, n->string); - first = 0; - } -} - -/* ARGSUSED */ -static void -pmdoc_Nm(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_NAME == n->sec) { - for (n = n->child; n; n = n->next) { - if (MDOC_TEXT != n->type) - continue; - dbt_append(key, ksz, n->string); - } - fl = (uint32_t)MANDOC_NAME; - memcpy(val->data, &fl, 4); - return; - } else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type) - return; - - for (n = n->child; n; n = n->next) { - if (MDOC_TEXT != n->type) - continue; - dbt_append(key, ksz, n->string); - } - - fl = (uint32_t)MANDOC_UTILITY; - memcpy(val->data, &fl, 4); -} - -static void -dbt_put(DB *db, const char *dbn, DBT *key, DBT *val) -{ - - if (0 == key->size) - return; - - assert(key->data); - assert(val->size); - assert(val->data); - - if (0 == (*db->put)(db, key, val, 0)) - return; - - perror(dbn); - exit((int)MANDOCLEVEL_SYSERR); - /* NOTREACHED */ -} - -/* - * Call out to per-macro handlers after clearing the persistent database - * key. If the macro sets the database key, flush it to the database. - */ -static void -pmdoc_node(MDOC_ARGS) -{ - - if (NULL == n) - return; - - switch (n->type) { - case (MDOC_HEAD): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_TAIL): - /* FALLTHROUGH */ - case (MDOC_BLOCK): - /* FALLTHROUGH */ - case (MDOC_ELEM): - if (NULL == mdocs[n->tok]) - break; - - dbt_init(key, ksz); - - (*mdocs[n->tok])(db, dbn, key, ksz, val, rval, rsz, n); - dbt_put(db, dbn, key, val); - break; - default: - break; - } - - pmdoc_node(db, dbn, key, ksz, val, rval, rsz, n->child); - pmdoc_node(db, dbn, key, ksz, val, rval, rsz, n->next); -} - -static int -pman_node(MAN_ARGS) -{ - const struct man_node *head, *body; - const char *start, *sv; - size_t sz; - uint32_t fl; - - if (NULL == n) - return(0); - - /* - * We're only searching for one thing: the first text child in - * the BODY of a NAME section. Since we don't keep track of - * sections in -man, run some hoops to find out whether we're in - * the correct section or not. - */ - - if (MAN_BODY == n->type && MAN_SH == n->tok) { - body = n; - assert(body->parent); - if (NULL != (head = body->parent->head) && - 1 == head->nchild && - NULL != (head = (head->child)) && - MAN_TEXT == head->type && - 0 == strcmp(head->string, "NAME") && - NULL != (body = body->child) && - MAN_TEXT == body->type) { - - fl = (uint32_t)MANDOC_NAME; - memcpy(val->data, &fl, 4); - - assert(body->string); - start = sv = body->string; - - /* - * Go through a special heuristic dance here. - * This is why -man manuals are great! - * (I'm being sarcastic: my eyes are bleeding.) - * Conventionally, one or more manual names are - * comma-specified prior to a whitespace, then a - * dash, then a description. Try to puzzle out - * the name parts here. - */ - - for ( ;; ) { - sz = strcspn(start, " ,"); - if ('\0' == start[(int)sz]) - break; - - dbt_init(key, ksz); - dbt_appendb(key, ksz, start, sz); - dbt_appendb(key, ksz, "", 1); - - dbt_put(db, dbn, key, val); - - if (' ' == start[(int)sz]) { - start += (int)sz + 1; - break; - } - - assert(',' == start[(int)sz]); - start += (int)sz + 1; - while (' ' == *start) - start++; - } - - if (sv == start) { - dbt_init(key, ksz); - dbt_append(key, ksz, start); - return(1); - } - - while (' ' == *start) - start++; - - if (0 == strncmp(start, "-", 1)) - start += 1; - else if (0 == strncmp(start, "\\-", 2)) - start += 2; - else if (0 == strncmp(start, "\\(en", 4)) - start += 4; - else if (0 == strncmp(start, "\\(em", 4)) - start += 4; - - while (' ' == *start) - start++; - - dbt_appendb(rval, rsz, start, strlen(start) + 1); - } - } - - if (pman_node(db, dbn, key, ksz, val, rval, rsz, n->child)) - return(1); - if (pman_node(db, dbn, key, ksz, val, rval, rsz, n->next)) - return(1); - - return(0); -} - -static void -pman(DB *db, const char *dbn, DBT *key, size_t *ksz, - DBT *val, DBT *rval, size_t *rsz, struct man *m) -{ - - pman_node(db, dbn, key, ksz, val, rval, rsz, man_node(m)); -} - - -static void -pmdoc(DB *db, const char *dbn, DBT *key, size_t *ksz, - DBT *val, DBT *rval, size_t *rsz, struct mdoc *m) -{ - - pmdoc_node(db, dbn, key, ksz, val, rval, rsz, mdoc_node(m)); -} - -static void -usage(void) -{ - - fprintf(stderr, "usage: %s " - "[-d path] " - "[file...]\n", - progname); -} diff --git a/contrib/mdocml/man-cgi.css b/contrib/mdocml/man-cgi.css new file mode 100644 index 0000000000..5300267cfb --- /dev/null +++ b/contrib/mdocml/man-cgi.css @@ -0,0 +1,13 @@ +body { font-family: Helvetica, Arial, sans-serif; } +body > div { padding-left: 2em; + padding-top: 1em; } +body > div#mancgi { padding-left: 0em; + padding-top: 0em; } +body > div.results { font-size: smaller; } +#mancgi fieldset { text-align: center; + border: thin solid silver; + border-radius: 1em; + font-size: small; } +#mancgi input[name=expr] { width: 25%; } +.results td.title { vertical-align: top; + padding-right: 1em; } diff --git a/contrib/mdocml/man.7 b/contrib/mdocml/man.7 index 8aa1f2c0ec..1715a7ca11 100644 --- a/contrib/mdocml/man.7 +++ b/contrib/mdocml/man.7 @@ -1,6 +1,7 @@ -.\" $Id: man.7,v 1.100 2011/05/26 09:26:16 kristaps Exp $ +.\" $Id: man.7,v 1.113 2012/01/03 15:16:24 kristaps Exp $ .\" -.\" Copyright (c) 2009, 2010 Kristaps Dzonsons +.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons +.\" Copyright (c) 2011 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 @@ -14,185 +15,68 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 26 2011 $ +.Dd $Mdocdate: January 3 2012 $ .Dt MAN 7 .Os .Sh NAME .Nm man -.Nd man language reference +.Nd legacy formatting language for manual pages .Sh DESCRIPTION -The +Traditionally, the .Nm man -language was historically used to format +language has been used to write .Ux -manuals. -This reference document describes its syntax, structure, and usage. +manuals for the +.Xr man 1 +utility. +It supports limited control of presentational details like fonts, +indentation and spacing. +This reference document describes the structure of manual pages +and the syntax and usage of the man language. .Pp .Bf -emphasis Do not use .Nm -to write your manuals. +to write your manuals: .Ef +It lacks support for semantic markup. Use the .Xr mdoc 7 language, instead. .Pp -A +In a .Nm -document follows simple rules: lines beginning with the control -character +document, lines beginning with the control character .Sq \&. -are parsed for macros. -Other lines are interpreted within the scope of -prior macros: +are called +.Dq macro lines . +The first word is the macro name. +It usually consists of two capital letters. +For a list of available macros, see +.Sx MACRO OVERVIEW . +The words following the macro name are arguments to the macro. +.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. -Other lines are interpreted within the current state. +Text lines are interpreted within the current state. .Ed -.Sh INPUT ENCODING +.Pp +Many aspects of the basic syntax of the .Nm -documents may contain only graphable 7-bit ASCII characters, the -space character, and the tab character. -.Pp -Blank lines are acceptable; where found, the output will assert a -vertical space. -.Pp -If the first character of a line is a space, that line is printed -with a leading newline. -.Ss Comments -Text following a -.Sq \e\*q , -whether in a macro or free-form text line, is ignored to the end of -line. -A macro line with only a control character and comment escape, -.Sq \&.\e\*q , -is also ignored. -Macro lines with only a control character and optionally whitespace are -stripped from input. -.Ss Special Characters -Special characters may occur in both macro and free-form lines. -Sequences begin with the escape character -.Sq \e -followed by either an open-parenthesis -.Sq \&( -for two-character sequences; an open-bracket -.Sq \&[ -for n-character sequences (terminated at a close-bracket -.Sq \&] ) ; -or a single one-character sequence. -See -.Xr mandoc_char 7 -for a complete list. -Examples include -.Sq \e(em -.Pq em-dash +language are based on the +.Xr roff 7 +language; see the +.Em LANGUAGE SYNTAX and -.Sq \ee -.Pq back-slash . -.Ss Text Decoration -Terms may be text-decorated using the -.Sq \ef -escape followed by an indicator: B (bold), I (italic), R (Roman), or P -(revert to previous mode): -.Pp -.D1 \efBbold\efR \efIitalic\efP -.Pp -A numerical representation 3, 2, or 1 (bold, italic, and Roman, -respectively) may be used instead. -A text decoration is only valid, if specified in free-form text, until -the next macro invocation; if specified within a macro, it's only valid -until the macro closes scope. -Note that macros like -.Sx \&BR -open and close a font scope with each argument. -.Pp -The -.Sq \ef -attribute is forgotten when entering or exiting a macro block. -.Ss Whitespace -Whitespace consists of the space character. -In free-form lines, whitespace is preserved within a line; unescaped -trailing spaces are stripped from input (unless in a literal context). -Blank free-form lines, which may include spaces, are permitted and -rendered as an empty line. -.Pp -In macro lines, whitespace delimits arguments and is discarded. -If arguments are quoted, whitespace within the quotes is retained. -.Ss Scaling Widths -Many macros support scaled widths for their arguments, such as -stipulating a two-inch paragraph indentation with the following: -.Bd -literal -offset indent -\&.HP 2i -.Ed -.Pp -The syntax for scaled widths is -.Sq Li [+-]?[0-9]*.[0-9]*[:unit:]? , -where a decimal must be preceded or proceeded by at least one digit. -Negative numbers, while accepted, are truncated to zero. -The following scaling units are accepted: -.Pp -.Bl -tag -width Ds -offset indent -compact -.It c -centimetre -.It i -inch -.It P -pica (~1/6 inch) -.It p -point (~1/72 inch) -.It f -synonym for -.Sq u -.It v -default vertical span -.It m -width of rendered -.Sq m -.Pq em -character -.It n -width of rendered -.Sq n -.Pq en -character -.It u -default horizontal span -.It M -mini-em (~1/100 em) -.El -.Pp -Using anything other than -.Sq m , -.Sq n , -.Sq u , -or -.Sq v -is necessarily non-portable across output media. -.Pp -If a scaling unit is not provided, the numerical value is interpreted -under the default rules of -.Sq v -for vertical spaces and -.Sq u -for horizontal ones. -.Em Note : -this differs from -.Xr mdoc 7 , -which, if a unit is not provided, will instead interpret the string as -literal text. -.Ss Sentence Spacing -When composing a manual, make sure that sentences end at the end of -a line. -By doing so, front-ends will be able to apply the proper amount of -spacing after the end of sentence (unescaped) period, exclamation mark, -or question mark followed by zero or more non-sentence closing -delimiters -.Po -.Sq \&) , -.Sq \&] , -.Sq \&' , -.Sq \&" -.Pc . +.Em MACRO SYNTAX +sections in the +.Xr roff 7 +manual for details, in particular regarding +comments, escape sequences, whitespace, and quoting. .Sh MANUAL STRUCTURE Each .Nm @@ -204,7 +88,7 @@ appears as the first macro. .Pp Beyond .Sx \&TH , -at least one macro or text node must appear in the document. +at least one macro or text line must appear in the document. .Pp The following is a well-formed skeleton .Nm @@ -214,36 +98,36 @@ file for a utility \&.TH PROGNAME 1 2009-10-10 \&.SH NAME \efBprogname\efR \e(en a description goes here -\&.\e\*q .SH LIBRARY -\&.\e\*q For sections 2 & 3 only. -\&.\e\*q Not used in OpenBSD. +\&.\e\(dq .SH LIBRARY +\&.\e\(dq For sections 2 & 3 only. +\&.\e\(dq Not used in OpenBSD. \&.SH SYNOPSIS \efBprogname\efR [\efB\e-options\efR] arguments... \&.SH DESCRIPTION The \efBfoo\efR utility processes files... -\&.\e\*q .SH IMPLEMENTATION NOTES -\&.\e\*q Not used in OpenBSD. -\&.\e\*q .SH RETURN VALUES -\&.\e\*q For sections 2, 3, & 9 only. -\&.\e\*q .SH ENVIRONMENT -\&.\e\*q For sections 1, 6, 7, & 8 only. -\&.\e\*q .SH FILES -\&.\e\*q .SH EXIT STATUS -\&.\e\*q For sections 1, 6, & 8 only. -\&.\e\*q .SH EXAMPLES -\&.\e\*q .SH DIAGNOSTICS -\&.\e\*q For sections 1, 4, 6, 7, & 8 only. -\&.\e\*q .SH ERRORS -\&.\e\*q For sections 2, 3, & 9 only. -\&.\e\*q .SH SEE ALSO -\&.\e\*q .BR foo ( 1 ) -\&.\e\*q .SH STANDARDS -\&.\e\*q .SH HISTORY -\&.\e\*q .SH AUTHORS -\&.\e\*q .SH CAVEATS -\&.\e\*q .SH BUGS -\&.\e\*q .SH SECURITY CONSIDERATIONS -\&.\e\*q 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, & 9 only. +\&.\e\(dq .SH ENVIRONMENT +\&.\e\(dq For sections 1, 6, 7, & 8 only. +\&.\e\(dq .SH FILES +\&.\e\(dq .SH EXIT STATUS +\&.\e\(dq For sections 1, 6, & 8 only. +\&.\e\(dq .SH EXAMPLES +\&.\e\(dq .SH DIAGNOSTICS +\&.\e\(dq For sections 1, 4, 6, 7, & 8 only. +\&.\e\(dq .SH ERRORS +\&.\e\(dq For sections 2, 3, & 9 only. +\&.\e\(dq .SH SEE ALSO +\&.\e\(dq .BR foo ( 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 a @@ -349,153 +233,50 @@ in this section. .It Em SECURITY CONSIDERATIONS Documents any security precautions that operators should consider. .El -.Sh MACRO SYNTAX -Macros are one to three characters in length and begin with a -control character, -.Sq \&. , -at the beginning of the line. -The -.Sq \(aq -macro control character is also accepted. -An arbitrary amount of whitespace (spaces or tabs) may sit between the -control character and the macro name. -Thus, the following are equivalent: -.Bd -literal -offset indent -\&.PP -\&.\ \ \ PP -.Ed -.Pp -To include space characters in macro arguments, arguments may be quoted; -see the -.Sq MACRO SYNTAX -section in the -.Xr roff 7 -manual for details. -.Pp -The -.Nm -macros are classified by scope: line scope or block scope. -Line macros are only scoped to the current line (and, in some -situations, the subsequent line). -Block macros are scoped to the current line and subsequent lines until -closed by another block macro. -.Ss Line Macros -Line macros are generally scoped to the current line, with the body -consisting of zero or more arguments. -If a macro is scoped to the next line and the line arguments are empty, -the next line, which must be text, is used instead. -Thus: -.Bd -literal -offset indent -\&.I -foo -.Ed -.Pp -is equivalent to -.Sq \&.I foo . -If next-line macros are invoked consecutively, only the last is used. -If a next-line macro is followed by a non-next-line macro, an error is -raised, except for -.Sx \&br , -.Sx \&sp , -and -.Sx \&na . -.Pp -The syntax is as follows: -.Bd -literal -offset indent -\&.YO \(lBbody...\(rB -\(lBbody...\(rB -.Ed -.Pp -.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX" -.It Em Macro Ta Em Arguments Ta Em Scope Ta Em Notes -.It Sx \&AT Ta <=1 Ta current Ta \& -.It Sx \&B Ta n Ta next-line Ta \& -.It Sx \&BI Ta n Ta current Ta \& -.It Sx \&BR Ta n Ta current Ta \& -.It Sx \&DT Ta 0 Ta current Ta \& -.It Sx \&I Ta n Ta next-line Ta \& -.It Sx \&IB Ta n Ta current Ta \& -.It Sx \&IR Ta n Ta current Ta \& -.It Sx \&R Ta n Ta next-line Ta \& -.It Sx \&RB Ta n Ta current Ta \& -.It Sx \&RI Ta n Ta current Ta \& -.It Sx \&SB Ta n Ta next-line Ta \& -.It Sx \&SM Ta n Ta next-line Ta \& -.It Sx \&TH Ta >1, <6 Ta current Ta \& -.It Sx \&UC Ta <=1 Ta current Ta \& -.It Sx \&br Ta 0 Ta current Ta compat -.It Sx \&fi Ta 0 Ta current Ta compat -.It Sx \&ft Ta 1 Ta current Ta compat -.It Sx \&in Ta 1 Ta current Ta compat -.It Sx \&na Ta 0 Ta current Ta compat -.It Sx \&nf Ta 0 Ta current Ta compat -.It Sx \&sp Ta 1 Ta current Ta compat +.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 +in the alphabetical reference below. +.Ss Page header and footer meta-data +.Bl -column "PP, LP, P" description +.It Sx TH Ta set the title: Ar title section date Op Ar source Op Ar volume +.It Sx AT Ta display AT&T UNIX version in the page footer (<= 1 argument) +.It Sx UC Ta display BSD version in the page footer (<= 1 argument) .El -.Pp -Macros marked as -.Qq compat -are included for compatibility with the significant corpus of existing -manuals that mix dialects of roff. -These macros should not be used for portable -.Nm -manuals. -.Ss Block Macros -Block macros comprise a head and body. -As with in-line macros, the head is scoped to the current line and, in -one circumstance, the next line (the next-line stipulations as in -.Sx Line Macros -apply here as well). -.Pp -The syntax is as follows: -.Bd -literal -offset indent -\&.YO \(lBhead...\(rB -\(lBhead...\(rB -\(lBbody...\(rB -.Ed -.Pp -The closure of body scope may be to the section, where a macro is closed -by -.Sx \&SH ; -sub-section, closed by a section or -.Sx \&SS ; -part, closed by a section, sub-section, or -.Sx \&RE ; -or paragraph, closed by a section, sub-section, part, -.Sx \&HP , -.Sx \&IP , -.Sx \&LP , -.Sx \&P , -.Sx \&PP , -or -.Sx \&TP . -No closure refers to an explicit block closing macro. -.Pp -As a rule, block macros may not be nested; thus, calling a block macro -while another block macro scope is open, and the open scope is not -implicitly closed, is syntactically incorrect. -.Pp -.Bl -column -compact -offset indent "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX" -.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes -.It Sx \&HP Ta <2 Ta current Ta paragraph Ta \& -.It Sx \&IP Ta <3 Ta current Ta paragraph Ta \& -.It Sx \&LP Ta 0 Ta current Ta paragraph Ta \& -.It Sx \&P Ta 0 Ta current Ta paragraph Ta \& -.It Sx \&PP Ta 0 Ta current Ta paragraph Ta \& -.It Sx \&RE Ta 0 Ta current Ta none Ta compat -.It Sx \&RS Ta 1 Ta current Ta part Ta compat -.It Sx \&SH Ta >0 Ta next-line Ta section Ta \& -.It Sx \&SS Ta >0 Ta next-line Ta sub-section Ta \& -.It Sx \&TP Ta n Ta next-line Ta paragraph Ta \& +.Ss Sections and paragraphs +.Bl -column "PP, LP, P" description +.It Sx SH Ta section header (one line) +.It Sx SS Ta subsection header (one line) +.It Sx PP , LP , P Ta start an undecorated paragraph (no arguments) +.It Sx RS , RE Ta reset the left margin: Op Ar width +.It Sx IP Ta indented paragraph: Op Ar head Op Ar width +.It Sx TP Ta tagged paragraph: Op Ar width +.It Sx HP Ta hanged paragraph: Op Ar width +.It Sx \&br Ta force output line break in text mode (no arguments) +.It Sx \&sp Ta force vertical space: Op Ar height +.It Sx fi , nf Ta fill mode and no-fill mode (no arguments) +.It Sx in Ta additional indent: Op Ar width .El -.Pp -Macros marked -.Qq compat -are as mentioned in -.Sx Line Macros . -.Pp -If a block macro is next-line scoped, it may only be followed by in-line -macros for decorating text. -.Sh REFERENCE +.Ss Physical markup +.Bl -column "PP, LP, P" description +.It Sx B Ta boldface font +.It Sx I Ta italic font +.It Sx R Ta roman (default) font +.It Sx SB Ta small boldface font +.It Sx SM Ta small roman font +.It Sx BI Ta alternate between boldface and italic fonts +.It Sx BR Ta alternate between boldface and roman fonts +.It Sx IB Ta alternate between italic and boldface fonts +.It Sx IR Ta alternate between italic and roman fonts +.It Sx RB Ta alternate between roman and boldface fonts +.It Sx RI Ta alternate between roman and italic fonts +.El +.Ss Semantic markup +.Bl -column "PP, LP, P" description +.It Sx OP Ta optional arguments +.El +.Sh MACRO REFERENCE This section is a canonical reference to all macros, arranged alphabetically. For the scoping of individual macros, see @@ -660,6 +441,19 @@ See also .Sx \&PP , and .Sx \&TP . +.Ss \&OP +Optional command-line argument. +This has the following syntax: +.Bd -filled -offset indent +.Pf \. Sx \&OP +.Cm key Op Cm value +.Ed +.Pp +The +.Cm key +is usually a command-line flag and +.Cm value +its argument. .Ss \&P Synonym for .Sx \&LP . @@ -707,6 +501,9 @@ and .Ss \&RE Explicitly close out the scope of a prior .Sx \&RS . +The default left margin is restored to the state of the original +.Sx \&RS +invocation. .Ss \&RI Text is rendered alternately in roman (the default font) and italics. Whitespace between arguments is omitted in output. @@ -723,13 +520,10 @@ See also and .Sx \&IR . .Ss \&RS -Begin a part setting the left margin. -The left margin controls the offset, following an initial indentation, -to un-indented text such as that of -.Sx \&PP . +Temporarily reset the default left margin. This has the following syntax: .Bd -filled -offset indent -.Pf \. Sx \&Rs +.Pf \. Sx \&RS .Op Cm width .Ed .Pp @@ -738,6 +532,9 @@ The argument must conform to .Sx Scaling Widths . If not specified, the saved or default width is used. +.Pp +See also +.Sx \&RE . .Ss \&SB Text is rendered in small size (one point smaller than the default font) bold face. @@ -845,6 +642,10 @@ Begin literal mode: all subsequent free-form lines have their end of line boundaries preserved. May be ended by .Sx \&fi . +Literal mode is implicitly ended by +.Sx \&SH +or +.Sx \&SS . .Ss \&sp Insert vertical spaces into output with the following syntax: .Bd -filled -offset indent @@ -863,6 +664,144 @@ Defaults to 1, if unspecified. .Pp See also .Sx \&br . +.Sh MACRO SYNTAX +The +.Nm +macros are classified by scope: line scope or block scope. +Line macros are only scoped to the current line (and, in some +situations, the subsequent line). +Block macros are scoped to the current line and subsequent lines until +closed by another block macro. +.Ss Line Macros +Line macros are generally scoped to the current line, with the body +consisting of zero or more arguments. +If a macro is scoped to the next line and the line arguments are empty, +the next line, which must be text, is used instead. +Thus: +.Bd -literal -offset indent +\&.I +foo +.Ed +.Pp +is equivalent to +.Sq \&.I foo . +If next-line macros are invoked consecutively, only the last is used. +If a next-line macro is followed by a non-next-line macro, an error is +raised, except for +.Sx \&br , +.Sx \&sp , +and +.Sx \&na . +.Pp +The syntax is as follows: +.Bd -literal -offset indent +\&.YO \(lBbody...\(rB +\(lBbody...\(rB +.Ed +.Bl -column "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX" -offset indent +.It Em Macro Ta Em Arguments Ta Em Scope Ta Em Notes +.It Sx \&AT Ta <=1 Ta current Ta \& +.It Sx \&B Ta n Ta next-line Ta \& +.It Sx \&BI Ta n Ta current Ta \& +.It Sx \&BR Ta n Ta current Ta \& +.It Sx \&DT Ta 0 Ta current Ta \& +.It Sx \&I Ta n Ta next-line Ta \& +.It Sx \&IB Ta n Ta current Ta \& +.It Sx \&IR Ta n Ta current Ta \& +.It Sx \&OP Ta 0, 1 Ta current Ta compat +.It Sx \&R Ta n Ta next-line Ta \& +.It Sx \&RB Ta n Ta current Ta \& +.It Sx \&RI Ta n Ta current Ta \& +.It Sx \&SB Ta n Ta next-line Ta \& +.It Sx \&SM Ta n Ta next-line Ta \& +.It Sx \&TH Ta >1, <6 Ta current Ta \& +.It Sx \&UC Ta <=1 Ta current Ta \& +.It Sx \&br Ta 0 Ta current Ta compat +.It Sx \&fi Ta 0 Ta current Ta compat +.It Sx \&ft Ta 1 Ta current Ta compat +.It Sx \&in Ta 1 Ta current Ta compat +.It Sx \&na Ta 0 Ta current Ta compat +.It Sx \&nf Ta 0 Ta current Ta compat +.It Sx \&sp Ta 1 Ta current Ta compat +.El +.Pp +Macros marked as +.Qq compat +are included for compatibility with the significant corpus of existing +manuals that mix dialects of roff. +These macros should not be used for portable +.Nm +manuals. +.Ss Block Macros +Block macros comprise a head and body. +As with in-line macros, the head is scoped to the current line and, in +one circumstance, the next line (the next-line stipulations as in +.Sx Line Macros +apply here as well). +.Pp +The syntax is as follows: +.Bd -literal -offset indent +\&.YO \(lBhead...\(rB +\(lBhead...\(rB +\(lBbody...\(rB +.Ed +.Pp +The closure of body scope may be to the section, where a macro is closed +by +.Sx \&SH ; +sub-section, closed by a section or +.Sx \&SS ; +part, closed by a section, sub-section, or +.Sx \&RE ; +or paragraph, closed by a section, sub-section, part, +.Sx \&HP , +.Sx \&IP , +.Sx \&LP , +.Sx \&P , +.Sx \&PP , +or +.Sx \&TP . +No closure refers to an explicit block closing macro. +.Pp +As a rule, block macros may not be nested; thus, calling a block macro +while another block macro scope is open, and the open scope is not +implicitly closed, is syntactically incorrect. +.Bl -column "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX" -offset indent +.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes +.It Sx \&HP Ta <2 Ta current Ta paragraph Ta \& +.It Sx \&IP Ta <3 Ta current Ta paragraph Ta \& +.It Sx \&LP Ta 0 Ta current Ta paragraph Ta \& +.It Sx \&P Ta 0 Ta current Ta paragraph Ta \& +.It Sx \&PP Ta 0 Ta current Ta paragraph Ta \& +.It Sx \&RE Ta 0 Ta current Ta none Ta compat +.It Sx \&RS Ta 1 Ta current Ta part Ta compat +.It Sx \&SH Ta >0 Ta next-line Ta section Ta \& +.It Sx \&SS Ta >0 Ta next-line Ta sub-section Ta \& +.It Sx \&TP Ta n Ta next-line Ta paragraph Ta \& +.El +.Pp +Macros marked +.Qq compat +are as mentioned in +.Sx Line Macros . +.Pp +If a block macro is next-line scoped, it may only be followed by in-line +macros for decorating text. +.Ss Font handling +In +.Nm +documents, both +.Sx Physical markup +macros and +.Xr roff 7 +.Ql \ef +font escape sequences can be used to choose fonts. +In text lines, the effect of manual font selection by escape sequences +only lasts until the next macro invocation; in macro lines, it only lasts +until the end of the macro scope. +Note that macros like +.Sx \&BR +open and close a font scope for each argument. .Sh COMPATIBILITY This section documents areas of questionable portability between implementations of the @@ -871,6 +810,14 @@ language. .Pp .Bl -dash -compact .It +Do not depend on +.Sx \&SH +or +.Sx \&SS +to close out a literal context opened with +.Sx \&nf . +This behaviour may not be portable. +.It In quoted literals, GNU troff allowed pair-wise double-quotes to produce a standalone double-quote in formatted output. It is not known whether this behaviour is exhibited by other formatters. @@ -911,7 +858,26 @@ The .Sx \&sp macro does not accept negative values in mandoc. In GNU troff, this would result in strange behaviour. +.It +In page header lines, GNU troff versions up to and including 1.21 +only print +.Ar volume +names explicitly specified in the +.Sx \&TH +macro; mandoc and newer groff print the default volume name +corresponding to the +.Ar section +number when no +.Ar volume +is given, like in +.Xr mdoc 7 . .El +.Pp +The +.Sx OP +macro is part of the extended +.Nm +macro set, and may not be portable to non-GNU troff implementations. .Sh SEE ALSO .Xr man 1 , .Xr mandoc 1 , @@ -927,6 +893,9 @@ language first appeared as a macro package for the roff typesetting system in .At v7 . It was later rewritten by James Clark as a macro package for groff. +Eric S. Raymond wrote the extended +.Nm +macros for groff in 2007. The stand-alone implementation that is part of the .Xr mandoc 1 utility written by Kristaps Dzonsons appeared in @@ -935,7 +904,8 @@ utility written by Kristaps Dzonsons appeared in This .Nm reference was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . .Sh CAVEATS Do not use this language. Use diff --git a/contrib/mdocml/man.c b/contrib/mdocml/man.c index 50a0cf0e95..1bea5610e3 100644 --- a/contrib/mdocml/man.c +++ b/contrib/mdocml/man.c @@ -1,4 +1,4 @@ -/* $Id: man.c,v 1.107 2011/03/29 08:30:49 kristaps Exp $ */ +/* $Id: man.c,v 1.115 2012/01/03 15:16:24 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * @@ -40,7 +40,7 @@ const char *const __man_macronames[MAN_MAX] = { "RI", "na", "sp", "nf", "fi", "RE", "RS", "DT", "UC", "PD", "AT", "in", - "ft" + "ft", "OP" }; const char * const *man_macronames = __man_macronames; @@ -96,7 +96,7 @@ man_free(struct man *man) struct man * -man_alloc(struct regset *regs, struct mparse *parse) +man_alloc(struct roff *roff, struct mparse *parse) { struct man *p; @@ -104,7 +104,7 @@ man_alloc(struct regset *regs, struct mparse *parse) man_hash_init(); p->parse = parse; - p->regs = regs; + p->roff = roff; man_alloc1(p); return(p); @@ -320,16 +320,9 @@ int man_word_alloc(struct man *m, int line, int pos, const char *word) { struct man_node *n; - size_t sv, len; - - len = strlen(word); n = man_node_alloc(m, line, pos, MAN_TEXT, MAN_MAX); - n->string = mandoc_malloc(len + 1); - sv = strlcpy(n->string, word, len + 1); - - /* Prohibit truncation. */ - assert(sv < len + 1); + n->string = roff_strdup(m->roff, word); if ( ! man_node_append(m, n)) return(0); @@ -371,14 +364,14 @@ man_addeqn(struct man *m, const struct eqn *ep) assert( ! (MAN_HALT & m->flags)); - n = man_node_alloc(m, ep->line, ep->pos, MAN_EQN, MAN_MAX); + n = man_node_alloc(m, ep->ln, ep->pos, MAN_EQN, MAN_MAX); n->eqn = ep; if ( ! man_node_append(m, n)) return(0); m->next = MAN_NEXT_SIBLING; - return(man_descope(m, ep->line, ep->pos)); + return(man_descope(m, ep->ln, ep->pos)); } int @@ -550,12 +543,44 @@ man_pmacro(struct man *m, int ln, char *buf, int offs) n = n->parent; mandoc_vmsg(MANDOCERR_LINESCOPE, m->parse, n->line, - n->pos, "%s", man_macronames[n->tok]); + n->pos, "%s breaks %s", man_macronames[tok], + man_macronames[n->tok]); man_node_delete(m, n); m->flags &= ~MAN_ELINE; } + /* + * Remove prior BLINE macro that is being clobbered. + */ + if ((m->flags & MAN_BLINE) && + (MAN_BSCOPE & man_macros[tok].flags)) { + n = m->last; + + /* Might be a text node like 8 in + * .TP 8 + * .SH foo + */ + if (MAN_TEXT == n->type) + n = n->parent; + + /* Remove element that didn't end BLINE, if any. */ + if ( ! (MAN_BSCOPE & man_macros[n->tok].flags)) + n = n->parent; + + assert(MAN_HEAD == n->type); + n = n->parent; + assert(MAN_BLOCK == n->type); + assert(MAN_SCOPED & man_macros[n->tok].flags); + + mandoc_vmsg(MANDOCERR_LINESCOPE, m->parse, n->line, + n->pos, "%s breaks %s", man_macronames[tok], + man_macronames[n->tok]); + + man_node_delete(m, n); + m->flags &= ~MAN_BLINE; + } + /* * Save the fact that we're in the next-line for a block. In * this way, embedded roff instructions can "remember" state @@ -655,3 +680,11 @@ man_node_unlink(struct man *m, struct man_node *n) if (m && m->first == n) m->first = NULL; } + +const struct mparse * +man_mparse(const struct man *m) +{ + + assert(m && m->parse); + return(m->parse); +} diff --git a/contrib/mdocml/man.cgi.7 b/contrib/mdocml/man.cgi.7 new file mode 100644 index 0000000000..b7afd84b90 --- /dev/null +++ b/contrib/mdocml/man.cgi.7 @@ -0,0 +1,123 @@ +.Dd $Mdocdate: March 24 2012 $ +.Dt MAN.CGI 7 +.Os +.Sh NAME +.Nm man.cgi +.Nd cgi for manpage query and display +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +script queries and displays manual pages. +It interfaces with +.Xr mandocdb 8 +databases cached with +.Xr catman 8 . +.Pp +To use +.Nm , +create a manual cache in +.Xr catman 8 . +Assign this directory to the environment variable +.Ev CACHE_DIR , +defaulting to +.Pa /cache/man.cgi . +Copy the +.Pa man.cgi +script into your CGI directory (see +.Sx FILES +for other relevant files). +.Pp +Multiple +.Xr catman 8 +trees may be managed by +.Nm : +directories under +.Ev CACHE_DIR +containing +.Pa etc/catman.conf +are identified as +.Qq manroots . +The path of a manroot under +.Ev CACHE_DIR +is converted to a name by replacing path separators with spaces. +.Pp +Thus, if +.Ev CACHE_DIR +is the default +.Pa /cache/man.cgi , +the web-server is jailed to +.Pa /var/www , +and cache subdirectories +.Pa ./foo/1 +and +.Pa ./bar/2 +contain +.Pa etc/catman.conf , +.Nm +will assign these to manroots +.Qq foo 1 +and +.Qq bar 2 , +respectively. +These names will appear as choices when searching for manuals. +.Pp +If +.Nm +finds only one manroot, or none, then the selection box is omitted. +If no manroot is specified during search, the first manroot is used by +default. +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev CACHE_DIR +The absolute path of the +.Xr catman 8 +cache directory. +This must not have a trailing slash. +.It Ev CSS_DIR +Prepended to CSS file links in outputted HTML files. +This must not have a trailing slash. +.El +.Sh FILES +.Bl -tag -width Ds +.It Pa etc/catman.conf +Built by +.Xr catman 8 +and must exist at least once under the configuration directory root. +.It Pa man.css +Should be visible in the server document root or within +.Ev CSS_DIR . +Included in each page after +.Pa man-cgi.css , +ostensibly for +.Xr mandoc 1 +HTML output styling. +.It Pa man.cgi.css +Should be visible in the server document root or within +.Ev CSS_DIR . +Included in each page, ostensibly for general +.Nm +styling. +.El +.Sh COMPATIBILITY +The +.Nm +script is call-compatible with queries from the traditional +.Pa man.cgi +script by Wolfram Schneider. +However, the results may not be quite the same. +.Sh SEE ALSO +.Xr catman 8 , +.Xr mandocdb 8 +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . +.Sh CAVEATS +If you're running in a jailed web-server, make sure the +.Pa /tmp +directory exists and is writable. +The databases may need this for scratch space. diff --git a/contrib/mdocml/man.h b/contrib/mdocml/man.h index 1b0bb6878d..4fc3934e6f 100644 --- a/contrib/mdocml/man.h +++ b/contrib/mdocml/man.h @@ -1,4 +1,4 @@ -/* $Id: man.h,v 1.58 2011/03/23 12:33:01 kristaps Exp $ */ +/* $Id: man.h,v 1.60 2012/01/03 15:16:24 kristaps Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * @@ -51,6 +51,7 @@ enum mant { MAN_AT, MAN_in, MAN_ft, + MAN_OP, MAN_MAX }; @@ -105,6 +106,7 @@ struct man; const struct man_node *man_node(const struct man *); const struct man_meta *man_meta(const struct man *); +const struct mparse *man_mparse(const struct man *); __END_DECLS diff --git a/contrib/mdocml/man_hash.c b/contrib/mdocml/man_hash.c index c52640e802..86c5c40a19 100644 --- a/contrib/mdocml/man_hash.c +++ b/contrib/mdocml/man_hash.c @@ -1,4 +1,4 @@ -/* $Id: man_hash.c,v 1.24 2011/03/22 14:33:05 kristaps Exp $ */ +/* $Id: man_hash.c,v 1.25 2011/07/24 18:15:14 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons * @@ -33,7 +33,7 @@ #define HASH_DEPTH 6 #define HASH_ROW(x) do { \ - if (isupper((u_char)(x))) \ + if (isupper((unsigned char)(x))) \ (x) -= 65; \ else \ (x) -= 97; \ @@ -47,7 +47,7 @@ * macro (the integer value of the enum stored as a char to save a bit * of space). */ -static u_char table[26 * HASH_DEPTH]; +static unsigned char table[26 * HASH_DEPTH]; /* * XXX - this hash has global scope, so if intended for use as a library @@ -66,13 +66,13 @@ man_hash_init(void) for (i = 0; i < (int)MAN_MAX; i++) { x = man_macronames[i][0]; - assert(isalpha((u_char)x)); + assert(isalpha((unsigned char)x)); HASH_ROW(x); for (j = 0; j < HASH_DEPTH; j++) if (UCHAR_MAX == table[x + j]) { - table[x + j] = (u_char)i; + table[x + j] = (unsigned char)i; break; } @@ -89,7 +89,7 @@ man_hash_find(const char *tmp) if ('\0' == (x = tmp[0])) return(MAN_MAX); - if ( ! (isalpha((u_char)x))) + if ( ! (isalpha((unsigned char)x))) return(MAN_MAX); HASH_ROW(x); diff --git a/contrib/mdocml/man_html.c b/contrib/mdocml/man_html.c index 73953ecdd2..a76ea2d707 100644 --- a/contrib/mdocml/man_html.c +++ b/contrib/mdocml/man_html.c @@ -1,4 +1,4 @@ -/* $Id: man_html.c,v 1.72 2011/05/17 11:34:31 kristaps Exp $ */ +/* $Id: man_html.c,v 1.86 2012/01/03 15:16:24 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * @@ -36,7 +36,6 @@ /* FIXME: have PD set the default vspace width. */ #define INDENT 5 -#define HALFINDENT 3 #define MAN_ARGS const struct man_meta *m, \ const struct man_node *n, \ @@ -53,30 +52,31 @@ struct htmlman { int (*post)(MAN_ARGS); }; +static void print_bvspace(struct html *, + const struct man_node *); static void print_man(MAN_ARGS); static void print_man_head(MAN_ARGS); static void print_man_nodelist(MAN_ARGS); static void print_man_node(MAN_ARGS); - static int a2width(const struct man_node *, struct roffsu *); - -static int man_alt_pre(MAN_ARGS); -static int man_br_pre(MAN_ARGS); -static int man_ign_pre(MAN_ARGS); -static int man_in_pre(MAN_ARGS); -static int man_literal_pre(MAN_ARGS); -static void man_root_post(MAN_ARGS); -static void man_root_pre(MAN_ARGS); static int man_B_pre(MAN_ARGS); static int man_HP_pre(MAN_ARGS); -static int man_I_pre(MAN_ARGS); static int man_IP_pre(MAN_ARGS); +static int man_I_pre(MAN_ARGS); +static int man_OP_pre(MAN_ARGS); static int man_PP_pre(MAN_ARGS); static int man_RS_pre(MAN_ARGS); static int man_SH_pre(MAN_ARGS); static int man_SM_pre(MAN_ARGS); static int man_SS_pre(MAN_ARGS); +static int man_alt_pre(MAN_ARGS); +static int man_br_pre(MAN_ARGS); +static int man_ign_pre(MAN_ARGS); +static int man_in_pre(MAN_ARGS); +static int man_literal_pre(MAN_ARGS); +static void man_root_post(MAN_ARGS); +static void man_root_pre(MAN_ARGS); static const struct htmlman mans[MAN_MAX] = { { man_br_pre, NULL }, /* br */ @@ -112,40 +112,61 @@ static const struct htmlman mans[MAN_MAX] = { { man_ign_pre, NULL }, /* AT */ { man_in_pre, NULL }, /* in */ { man_ign_pre, NULL }, /* ft */ + { man_OP_pre, NULL }, /* OP */ }; +/* + * Printing leading vertical space before a block. + * This is used for the paragraph macros. + * The rules are pretty simple, since there's very little nesting going + * on here. Basically, if we're the first within another block (SS/SH), + * then don't emit vertical space. If we are (RS), then do. If not the + * first, print it. + */ +static void +print_bvspace(struct html *h, const struct man_node *n) +{ + + if (n->body && n->body->child) + if (MAN_TBL == n->body->child->type) + return; + + if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) + if (NULL == n->prev) + return; + + print_otag(h, TAG_P, 0, NULL); +} void html_man(void *arg, const struct man *m) { - struct html *h; - struct tag *t; struct mhtml mh; - h = (struct html *)arg; - - print_gen_decls(h); - memset(&mh, 0, sizeof(struct mhtml)); - - t = print_otag(h, TAG_HTML, 0, NULL); - print_man(man_meta(m), man_node(m), &mh, h); - print_tagq(h, t); - - printf("\n"); + print_man(man_meta(m), man_node(m), &mh, (struct html *)arg); + putchar('\n'); } - static void print_man(MAN_ARGS) { - struct tag *t; + struct tag *t, *tt; + struct htmlpair tag; - t = print_otag(h, TAG_HEAD, 0, NULL); - print_man_head(m, n, mh, h); - print_tagq(h, t); + PAIR_CLASS_INIT(&tag, "mandoc"); + + if ( ! (HTML_FRAGMENT & h->oflags)) { + print_gen_decls(h); + t = print_otag(h, TAG_HTML, 0, NULL); + tt = print_otag(h, TAG_HEAD, 0, NULL); + print_man_head(m, n, mh, h); + print_tagq(h, tt); + print_otag(h, TAG_BODY, 0, NULL); + print_otag(h, TAG_DIV, 1, &tag); + } else + t = print_otag(h, TAG_DIV, 1, &tag); - t = print_otag(h, TAG_BODY, 0, NULL); print_man_nodelist(m, n, mh, h); print_tagq(h, t); } @@ -157,6 +178,8 @@ print_man_head(MAN_ARGS) { print_gen_head(h); + assert(m->title); + assert(m->msec); bufcat_fmt(h, "%s(%s)", m->title, m->msec); print_otag(h, TAG_TITLE, 0, NULL); print_text(h, h->buf); @@ -178,7 +201,6 @@ print_man_node(MAN_ARGS) { int child; struct tag *t; - struct htmlpair tag; child = 1; t = h->tags.head; @@ -196,27 +218,17 @@ print_man_node(MAN_ARGS) if ('\0' == *n->string) { print_otag(h, TAG_P, 0, NULL); return; - } else if (' ' == *n->string && MAN_LINE & n->flags) + } + + if (' ' == *n->string && MAN_LINE & n->flags) + print_otag(h, TAG_BR, 0, NULL); + else if (MANH_LITERAL & mh->fl && n->prev) print_otag(h, TAG_BR, 0, NULL); print_text(h, n->string); - - /* - * If we're in a literal context, make sure that words - * togehter on the same line stay together. This is a - * POST-printing call, so we check the NEXT word. Since - * -man doesn't have nested macros, we don't need to be - * more specific than this. - */ - if (MANH_LITERAL & mh->fl && - (NULL == n->next || - n->next->line > n->line)) - print_otag(h, TAG_BR, 0, NULL); return; case (MAN_EQN): - PAIR_CLASS_INIT(&tag, "eqn"); - print_otag(h, TAG_SPAN, 1, &tag); - print_text(h, n->eqn->data); + print_eqn(h, n->eqn); break; case (MAN_TBL): /* @@ -295,19 +307,18 @@ man_root_pre(MAN_ARGS) if (m->vol) (void)strlcat(b, m->vol, BUFSIZ); + assert(m->title); + assert(m->msec); snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec); PAIR_SUMMARY_INIT(&tag[0], "Document Header"); PAIR_CLASS_INIT(&tag[1], "head"); - if (NULL == h->style) { - PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); - t = print_otag(h, TAG_TABLE, 3, tag); - PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); - print_otag(h, TAG_COL, 1, tag); - print_otag(h, TAG_COL, 1, tag); - print_otag(h, TAG_COL, 1, tag); - } else - t = print_otag(h, TAG_TABLE, 2, tag); + PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); + t = print_otag(h, TAG_TABLE, 3, tag); + PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); + print_otag(h, TAG_COL, 1, tag); + print_otag(h, TAG_COL, 1, tag); + print_otag(h, TAG_COL, 1, tag); print_otag(h, TAG_TBODY, 0, NULL); @@ -315,27 +326,18 @@ man_root_pre(MAN_ARGS) PAIR_CLASS_INIT(&tag[0], "head-ltitle"); print_otag(h, TAG_TD, 1, tag); - print_text(h, title); print_stagq(h, tt); PAIR_CLASS_INIT(&tag[0], "head-vol"); - if (NULL == h->style) { - PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); - print_otag(h, TAG_TD, 2, tag); - } else - print_otag(h, TAG_TD, 1, tag); - + PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); + print_otag(h, TAG_TD, 2, tag); print_text(h, b); print_stagq(h, tt); PAIR_CLASS_INIT(&tag[0], "head-rtitle"); - if (NULL == h->style) { - PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); - print_otag(h, TAG_TD, 2, tag); - } else - print_otag(h, TAG_TD, 1, tag); - + PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); + print_otag(h, TAG_TD, 2, tag); print_text(h, title); print_tagq(h, t); } @@ -350,29 +352,24 @@ man_root_post(MAN_ARGS) PAIR_SUMMARY_INIT(&tag[0], "Document Footer"); PAIR_CLASS_INIT(&tag[1], "foot"); - if (NULL == h->style) { - PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); - t = print_otag(h, TAG_TABLE, 3, tag); - PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); - print_otag(h, TAG_COL, 1, tag); - print_otag(h, TAG_COL, 1, tag); - } else - t = print_otag(h, TAG_TABLE, 2, tag); + PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); + t = print_otag(h, TAG_TABLE, 3, tag); + PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); + print_otag(h, TAG_COL, 1, tag); + print_otag(h, TAG_COL, 1, tag); tt = print_otag(h, TAG_TR, 0, NULL); PAIR_CLASS_INIT(&tag[0], "foot-date"); print_otag(h, TAG_TD, 1, tag); + assert(m->date); print_text(h, m->date); print_stagq(h, tt); PAIR_CLASS_INIT(&tag[0], "foot-os"); - if (NULL == h->style) { - PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); - print_otag(h, TAG_TD, 2, tag); - } else - print_otag(h, TAG_TD, 1, tag); + PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); + print_otag(h, TAG_TD, 2, tag); if (m->source) print_text(h, m->source); @@ -380,7 +377,6 @@ man_root_post(MAN_ARGS) } - /* ARGSUSED */ static int man_br_pre(MAN_ARGS) @@ -391,8 +387,9 @@ man_br_pre(MAN_ARGS) SCALE_VS_INIT(&su, 1); if (MAN_sp == n->tok) { - if (n->child) - a2roffsu(n->child->string, &su, SCALE_VS); + if (NULL != (n = n->child)) + if ( ! a2roffsu(n->string, &su, SCALE_VS)) + SCALE_VS_INIT(&su, atoi(n->string)); } else su.scale = 0; @@ -407,7 +404,6 @@ man_br_pre(MAN_ARGS) return(0); } - /* ARGSUSED */ static int man_SH_pre(MAN_ARGS) @@ -415,6 +411,7 @@ man_SH_pre(MAN_ARGS) struct htmlpair tag; if (MAN_BLOCK == n->type) { + mh->fl &= ~MANH_LITERAL; PAIR_CLASS_INIT(&tag, "section"); print_otag(h, TAG_DIV, 1, &tag); return(1); @@ -425,16 +422,20 @@ man_SH_pre(MAN_ARGS) return(1); } - /* ARGSUSED */ static int man_alt_pre(MAN_ARGS) { const struct man_node *nn; - int i; + int i, savelit; enum htmltag fp; struct tag *t; + if ((savelit = mh->fl & MANH_LITERAL)) + print_otag(h, TAG_BR, 0, NULL); + + mh->fl &= ~MANH_LITERAL; + for (i = 0, nn = n->child; nn; nn = nn->next, i++) { t = NULL; switch (n->tok) { @@ -473,10 +474,12 @@ man_alt_pre(MAN_ARGS) print_tagq(h, t); } + if (savelit) + mh->fl |= MANH_LITERAL; + return(0); } - /* ARGSUSED */ static int man_SM_pre(MAN_ARGS) @@ -488,7 +491,6 @@ man_SM_pre(MAN_ARGS) return(1); } - /* ARGSUSED */ static int man_SS_pre(MAN_ARGS) @@ -496,6 +498,7 @@ man_SS_pre(MAN_ARGS) struct htmlpair tag; if (MAN_BLOCK == n->type) { + mh->fl &= ~MANH_LITERAL; PAIR_CLASS_INIT(&tag, "subsection"); print_otag(h, TAG_DIV, 1, &tag); return(1); @@ -506,7 +509,6 @@ man_SS_pre(MAN_ARGS) return(1); } - /* ARGSUSED */ static int man_PP_pre(MAN_ARGS) @@ -514,68 +516,29 @@ man_PP_pre(MAN_ARGS) if (MAN_HEAD == n->type) return(0); - else if (MAN_BODY == n->type && n->prev) - print_otag(h, TAG_P, 0, NULL); + else if (MAN_BLOCK == n->type) + print_bvspace(h, n); return(1); } - /* ARGSUSED */ static int man_IP_pre(MAN_ARGS) { - struct roffsu su; - struct htmlpair tag; const struct man_node *nn; - /* - * This scattering of 1-BU margins and pads is to make sure that - * when text overruns its box, the subsequent text isn't flush - * up against it. However, the rest of the right-hand box must - * also be adjusted in consideration of this 1-BU space. - */ - if (MAN_BODY == n->type) { - print_otag(h, TAG_TD, 0, NULL); + print_otag(h, TAG_DD, 0, NULL); + return(1); + } else if (MAN_HEAD != n->type) { + print_otag(h, TAG_DL, 0, NULL); return(1); } - nn = MAN_BLOCK == n->type ? - n->head->child : n->parent->head->child; - - SCALE_HS_INIT(&su, INDENT); - - /* Width is the second token. */ - - if (MAN_IP == n->tok && NULL != nn) - if (NULL != (nn = nn->next)) - a2width(nn, &su); - - /* Width is the first token. */ - - if (MAN_TP == n->tok && NULL != nn) { - /* Skip past non-text children. */ - while (nn && MAN_TEXT != nn->type) - nn = nn->next; - if (nn) - a2width(nn, &su); - } - - if (MAN_BLOCK == n->type) { - print_otag(h, TAG_P, 0, NULL); - print_otag(h, TAG_TABLE, 0, NULL); - bufinit(h); - bufcat_su(h, "width", &su); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_COL, 1, &tag); - print_otag(h, TAG_COL, 0, NULL); - print_otag(h, TAG_TBODY, 0, NULL); - print_otag(h, TAG_TR, 0, NULL); - return(1); - } + /* FIXME: width specification. */ - print_otag(h, TAG_TD, 0, NULL); + print_otag(h, TAG_DT, 0, NULL); /* For IP, only print the first header element. */ @@ -592,7 +555,6 @@ man_IP_pre(MAN_ARGS) return(0); } - /* ARGSUSED */ static int man_HP_pre(MAN_ARGS) @@ -601,37 +563,57 @@ man_HP_pre(MAN_ARGS) struct roffsu su; const struct man_node *np; - bufinit(h); + if (MAN_HEAD == n->type) + return(0); + else if (MAN_BLOCK != n->type) + return(1); - np = MAN_BLOCK == n->type ? - n->head->child : - n->parent->head->child; + np = n->head->child; if (NULL == np || ! a2width(np, &su)) SCALE_HS_INIT(&su, INDENT); - if (MAN_HEAD == n->type) { - print_otag(h, TAG_TD, 0, NULL); - return(0); - } else if (MAN_BLOCK == n->type) { - print_otag(h, TAG_P, 0, NULL); - print_otag(h, TAG_TABLE, 0, NULL); - bufcat_su(h, "width", &su); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_COL, 1, &tag); - print_otag(h, TAG_COL, 0, NULL); - print_otag(h, TAG_TBODY, 0, NULL); - print_otag(h, TAG_TR, 0, NULL); - return(1); - } + bufinit(h); + print_bvspace(h, n); + bufcat_su(h, "margin-left", &su); su.scale = -su.scale; bufcat_su(h, "text-indent", &su); PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_TD, 1, &tag); + print_otag(h, TAG_P, 1, &tag); return(1); } +/* ARGSUSED */ +static int +man_OP_pre(MAN_ARGS) +{ + struct tag *tt; + struct htmlpair tag; + + print_text(h, "["); + h->flags |= HTML_NOSPACE; + PAIR_CLASS_INIT(&tag, "opt"); + tt = print_otag(h, TAG_SPAN, 1, &tag); + + if (NULL != (n = n->child)) { + print_otag(h, TAG_B, 0, NULL); + print_text(h, n->string); + } + + print_stagq(h, tt); + + if (NULL != n && NULL != n->next) { + print_otag(h, TAG_I, 0, NULL); + print_text(h, n->next->string); + } + + print_stagq(h, tt); + h->flags |= HTML_NOSPACE; + print_text(h, "]"); + return(0); +} + /* ARGSUSED */ static int @@ -642,7 +624,6 @@ man_B_pre(MAN_ARGS) return(1); } - /* ARGSUSED */ static int man_I_pre(MAN_ARGS) @@ -652,22 +633,20 @@ man_I_pre(MAN_ARGS) return(1); } - /* ARGSUSED */ static int man_literal_pre(MAN_ARGS) { - if (MAN_nf == n->tok) { + if (MAN_nf != n->tok) { print_otag(h, TAG_BR, 0, NULL); - mh->fl |= MANH_LITERAL; - } else mh->fl &= ~MANH_LITERAL; + } else + mh->fl |= MANH_LITERAL; return(0); } - /* ARGSUSED */ static int man_in_pre(MAN_ARGS) @@ -677,7 +656,6 @@ man_in_pre(MAN_ARGS) return(0); } - /* ARGSUSED */ static int man_ign_pre(MAN_ARGS) @@ -686,7 +664,6 @@ man_ign_pre(MAN_ARGS) return(0); } - /* ARGSUSED */ static int man_RS_pre(MAN_ARGS) diff --git a/contrib/mdocml/man_macro.c b/contrib/mdocml/man_macro.c index 915648b430..4bbbc4fa7f 100644 --- a/contrib/mdocml/man_macro.c +++ b/contrib/mdocml/man_macro.c @@ -1,6 +1,6 @@ -/* $Id: man_macro.c,v 1.62 2011/04/19 16:38:48 kristaps Exp $ */ +/* $Id: man_macro.c,v 1.71 2012/01/03 15:16:24 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2008, 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 @@ -52,15 +52,15 @@ static void rew_warn(struct man *, const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, MAN_NSCOPED }, /* br */ - { in_line_eoln, 0 }, /* TH */ - { blk_imp, MAN_SCOPED }, /* SH */ - { blk_imp, MAN_SCOPED }, /* SS */ - { blk_imp, MAN_SCOPED | MAN_FSCOPED }, /* TP */ - { blk_imp, 0 }, /* LP */ - { blk_imp, 0 }, /* PP */ - { blk_imp, 0 }, /* P */ - { blk_imp, 0 }, /* IP */ - { blk_imp, 0 }, /* HP */ + { in_line_eoln, MAN_BSCOPE }, /* TH */ + { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */ + { blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */ + { blk_imp, MAN_BSCOPE | MAN_SCOPED | MAN_FSCOPED }, /* TP */ + { blk_imp, MAN_BSCOPE }, /* LP */ + { blk_imp, MAN_BSCOPE }, /* PP */ + { blk_imp, MAN_BSCOPE }, /* P */ + { blk_imp, MAN_BSCOPE }, /* IP */ + { blk_imp, MAN_BSCOPE }, /* HP */ { in_line_eoln, MAN_SCOPED }, /* SM */ { in_line_eoln, MAN_SCOPED }, /* SB */ { in_line_eoln, 0 }, /* BI */ @@ -74,8 +74,8 @@ const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, 0 }, /* RI */ { in_line_eoln, MAN_NSCOPED }, /* na */ { in_line_eoln, MAN_NSCOPED }, /* sp */ - { in_line_eoln, 0 }, /* nf */ - { in_line_eoln, 0 }, /* fi */ + { in_line_eoln, MAN_BSCOPE }, /* nf */ + { in_line_eoln, MAN_BSCOPE }, /* fi */ { blk_close, 0 }, /* RE */ { blk_exp, MAN_EXPLICIT }, /* RS */ { in_line_eoln, 0 }, /* DT */ @@ -84,6 +84,7 @@ const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, 0 }, /* AT */ { in_line_eoln, 0 }, /* in */ { in_line_eoln, 0 }, /* ft */ + { in_line_eoln, 0 }, /* OP */ }; const struct man_macro * const man_macros = __man_macros; @@ -120,6 +121,8 @@ man_unscope(struct man *m, const struct man_node *to, assert(to); + m->next = MAN_NEXT_SIBLING; + /* LINTED */ while (m->last != to) { /* @@ -140,9 +143,6 @@ man_unscope(struct man *m, const struct man_node *to, if ( ! man_valid_post(m)) return(0); - m->next = MAN_ROOT == m->last->type ? - MAN_NEXT_CHILD : MAN_NEXT_SIBLING; - return(1); } @@ -307,11 +307,6 @@ blk_exp(MACRO_PROT_ARGS) * anywhere. */ - if ( ! rew_scope(MAN_BODY, m, tok)) - return(0); - if ( ! rew_scope(MAN_BLOCK, m, tok)) - return(0); - if ( ! man_block_alloc(m, line, ppos, tok)) return(0); if ( ! man_head_alloc(m, line, ppos, tok)) @@ -433,6 +428,9 @@ in_line_eoln(MACRO_PROT_ARGS) assert( ! (MAN_SCOPED & man_macros[tok].flags)); m->flags |= MAN_ILINE; } + + assert(MAN_ROOT != m->last->type); + m->next = MAN_NEXT_SIBLING; /* * Rewind our element scope. Note that when TH is pruned, we'll @@ -458,9 +456,6 @@ in_line_eoln(MACRO_PROT_ARGS) if (m->last->type != MAN_ROOT && ! man_valid_post(m)) return(0); - m->next = MAN_ROOT == m->last->type ? - MAN_NEXT_CHILD : MAN_NEXT_SIBLING; - return(1); } diff --git a/contrib/mdocml/man_term.c b/contrib/mdocml/man_term.c index 38ceeabdbd..69c5c95e44 100644 --- a/contrib/mdocml/man_term.c +++ b/contrib/mdocml/man_term.c @@ -1,4 +1,4 @@ -/* $Id: man_term.c,v 1.109 2011/05/17 14:38:34 kristaps Exp $ */ +/* $Id: man_term.c,v 1.127 2012/01/03 15:16:24 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010, 2011 Ingo Schwarze @@ -33,29 +33,17 @@ #include "term.h" #include "main.h" -#define INDENT 7 -#define HALFINDENT 3 +#define MAXMARGINS 64 /* maximum number of indented scopes */ /* FIXME: have PD set the default vspace width. */ struct mtermp { int fl; #define MANT_LITERAL (1 << 0) - /* - * Default amount to indent the left margin after leading text - * has been printed (e.g., `HP' left-indent, `TP' and `IP' body - * indent). This needs to be saved because `HP' and so on, if - * not having a specified value, must default. - * - * Note that this is the indentation AFTER the left offset, so - * the total offset is usually offset + lmargin. - */ - size_t lmargin; - /* - * The default offset, i.e., the amount between any text and the - * page boundary. - */ - size_t offset; + size_t lmargin[MAXMARGINS]; /* margins (incl. visible page) */ + int lmargincur; /* index of current margin */ + int lmarginsz; /* actual number of nested margins */ + size_t offset; /* default offset to visible page */ }; #define DECL_ARGS struct termp *p, \ @@ -80,21 +68,22 @@ static void print_man_foot(struct termp *, const void *); static void print_bvspace(struct termp *, const struct man_node *); -static int pre_alternate(DECL_ARGS); static int pre_B(DECL_ARGS); static int pre_HP(DECL_ARGS); static int pre_I(DECL_ARGS); static int pre_IP(DECL_ARGS); +static int pre_OP(DECL_ARGS); static int pre_PP(DECL_ARGS); static int pre_RS(DECL_ARGS); static int pre_SH(DECL_ARGS); static int pre_SS(DECL_ARGS); static int pre_TP(DECL_ARGS); +static int pre_alternate(DECL_ARGS); +static int pre_ft(DECL_ARGS); static int pre_ign(DECL_ARGS); static int pre_in(DECL_ARGS); static int pre_literal(DECL_ARGS); static int pre_sp(DECL_ARGS); -static int pre_ft(DECL_ARGS); static void post_IP(DECL_ARGS); static void post_HP(DECL_ARGS); @@ -137,6 +126,7 @@ static const struct termact termacts[MAN_MAX] = { { pre_ign, NULL, 0 }, /* AT */ { pre_in, NULL, MAN_NOTEXT }, /* in */ { pre_ft, NULL, MAN_NOTEXT }, /* ft */ + { pre_OP, NULL, 0 }, /* OP */ }; @@ -151,6 +141,9 @@ terminal_man(void *arg, const struct man *man) p = (struct termp *)arg; + if (0 == p->defindent) + p->defindent = 7; + p->overstep = 0; p->maxrmargin = p->defrmargin; p->tabwidth = term_len(p, 5); @@ -164,9 +157,10 @@ terminal_man(void *arg, const struct man *man) term_begin(p, print_man_head, print_man_foot, m); p->flags |= TERMP_NOSPACE; - mt.fl = 0; - mt.lmargin = term_len(p, INDENT); - mt.offset = term_len(p, INDENT); + memset(&mt, 0, sizeof(struct mtermp)); + + mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); + mt.offset = term_len(p, p->defindent); if (n->child) print_man_nodelist(p, &mt, n->child, m); @@ -181,7 +175,7 @@ a2height(const struct termp *p, const char *cp) struct roffsu su; if ( ! a2roffsu(cp, &su, SCALE_VS)) - SCALE_VS_INIT(&su, term_strlen(p, cp)); + SCALE_VS_INIT(&su, atoi(cp)); return(term_vspan(p, &su)); } @@ -198,27 +192,31 @@ a2width(const struct termp *p, const char *cp) return((int)term_hspan(p, &su)); } - +/* + * Printing leading vertical space before a block. + * This is used for the paragraph macros. + * The rules are pretty simple, since there's very little nesting going + * on here. Basically, if we're the first within another block (SS/SH), + * then don't emit vertical space. If we are (RS), then do. If not the + * first, print it. + */ static void print_bvspace(struct termp *p, const struct man_node *n) { - term_newln(p); - if (n->body && n->body->child && MAN_TBL == n->body->child->type) - return; + term_newln(p); - if (NULL == n->prev) - return; + if (n->body && n->body->child) + if (MAN_TBL == n->body->child->type) + return; - if (MAN_SS == n->prev->tok) - return; - if (MAN_SH == n->prev->tok) - return; + if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok) + if (NULL == n->prev) + return; term_vspace(p); } - /* ARGSUSED */ static int pre_ign(DECL_ARGS) @@ -250,6 +248,18 @@ pre_literal(DECL_ARGS) else mt->fl &= ~MANT_LITERAL; + /* + * Unlike .IP and .TP, .HP does not have a HEAD. + * So in case a second call to term_flushln() is needed, + * indentation has to be set up explicitly. + */ + if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE); + p->flags |= TERMP_NOSPACE; + } + return(0); } @@ -314,6 +324,29 @@ pre_B(DECL_ARGS) return(1); } +/* ARGSUSED */ +static int +pre_OP(DECL_ARGS) +{ + + term_word(p, "["); + p->flags |= TERMP_NOSPACE; + + if (NULL != (n = n->child)) { + term_fontrepl(p, TERMFONT_BOLD); + term_word(p, n->string); + } + if (NULL != n && NULL != n->next) { + term_fontrepl(p, TERMFONT_UNDER); + term_word(p, n->next->string); + } + + term_fontrepl(p, TERMFONT_NONE); + p->flags |= TERMP_NOSPACE; + term_word(p, "]"); + return(0); +} + /* ARGSUSED */ static int pre_ft(DECL_ARGS) @@ -407,6 +440,13 @@ pre_sp(DECL_ARGS) { size_t i, len; + if ((NULL == n->prev && n->parent)) { + if (MAN_SS == n->parent->tok) + return(0); + if (MAN_SH == n->parent->tok) + return(0); + } + switch (n->tok) { case (MAN_br): len = 0; @@ -429,7 +469,7 @@ pre_sp(DECL_ARGS) static int pre_HP(DECL_ARGS) { - size_t len; + size_t len, one; int ival; const struct man_node *nn; @@ -445,7 +485,7 @@ pre_HP(DECL_ARGS) return(0); } - len = mt->lmargin; + len = mt->lmargin[mt->lmargincur]; ival = -1; /* Calculate offset. */ @@ -454,14 +494,15 @@ pre_HP(DECL_ARGS) if ((ival = a2width(p, nn->string)) >= 0) len = (size_t)ival; - if (0 == len) - len = term_len(p, 1); + one = term_len(p, 1); + if (len < one) + len = one; p->offset = mt->offset; p->rmargin = mt->offset + len; if (ival >= 0) - mt->lmargin = (size_t)ival; + mt->lmargin[mt->lmargincur] = (size_t)ival; return(1); } @@ -496,7 +537,7 @@ pre_PP(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->lmargin = term_len(p, INDENT); + mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); print_bvspace(p, n); break; default: @@ -518,7 +559,6 @@ pre_IP(DECL_ARGS) switch (n->type) { case (MAN_BODY): - p->flags |= TERMP_NOLPAD; p->flags |= TERMP_NOSPACE; break; case (MAN_HEAD): @@ -531,7 +571,7 @@ pre_IP(DECL_ARGS) return(1); } - len = mt->lmargin; + len = mt->lmargin[mt->lmargincur]; ival = -1; /* Calculate the offset from the optional second argument. */ @@ -552,7 +592,7 @@ pre_IP(DECL_ARGS) break; /* Set the saved left-margin. */ - mt->lmargin = (size_t)ival; + mt->lmargin[mt->lmargincur] = (size_t)ival; savelit = MANT_LITERAL & mt->fl; mt->fl &= ~MANT_LITERAL; @@ -589,7 +629,6 @@ post_IP(DECL_ARGS) break; case (MAN_BODY): term_newln(p); - p->flags &= ~TERMP_NOLPAD; break; default: break; @@ -610,7 +649,6 @@ pre_TP(DECL_ARGS) p->flags |= TERMP_NOBREAK; break; case (MAN_BODY): - p->flags |= TERMP_NOLPAD; p->flags |= TERMP_NOSPACE; break; case (MAN_BLOCK): @@ -620,18 +658,15 @@ pre_TP(DECL_ARGS) return(1); } - len = (size_t)mt->lmargin; + len = (size_t)mt->lmargin[mt->lmargincur]; ival = -1; /* Calculate offset. */ - if (NULL != (nn = n->parent->head->child)) { - while (nn && MAN_TEXT != nn->type) - nn = nn->next; - if (nn && nn->next) + if (NULL != (nn = n->parent->head->child)) + if (nn->string && nn->parent->line == nn->line) if ((ival = a2width(p, nn->string)) >= 0) len = (size_t)ival; - } switch (n->type) { case (MAN_HEAD): @@ -652,9 +687,8 @@ pre_TP(DECL_ARGS) if (savelit) mt->fl |= MANT_LITERAL; - if (ival >= 0) - mt->lmargin = (size_t)ival; + mt->lmargin[mt->lmargincur] = (size_t)ival; return(0); case (MAN_BODY): @@ -683,7 +717,6 @@ post_TP(DECL_ARGS) break; case (MAN_BODY): term_newln(p); - p->flags &= ~TERMP_NOLPAD; break; default: break; @@ -698,8 +731,9 @@ pre_SS(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->lmargin = term_len(p, INDENT); - mt->offset = term_len(p, INDENT); + mt->fl &= ~MANT_LITERAL; + mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); + mt->offset = term_len(p, p->defindent); /* If following a prior empty `SS', no vspace. */ if (n->prev && MAN_SS == n->prev->tok) if (NULL == n->prev->body->child) @@ -710,7 +744,7 @@ pre_SS(DECL_ARGS) break; case (MAN_HEAD): term_fontrepl(p, TERMFONT_BOLD); - p->offset = term_len(p, HALFINDENT); + p->offset = term_len(p, p->defindent/2); break; case (MAN_BODY): p->offset = mt->offset; @@ -748,8 +782,9 @@ pre_SH(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->lmargin = term_len(p, INDENT); - mt->offset = term_len(p, INDENT); + mt->fl &= ~MANT_LITERAL; + mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); + mt->offset = term_len(p, p->defindent); /* If following a prior empty `SH', no vspace. */ if (n->prev && MAN_SH == n->prev->tok) if (NULL == n->prev->body->child) @@ -791,13 +826,12 @@ post_SH(DECL_ARGS) } } - /* ARGSUSED */ static int pre_RS(DECL_ARGS) { - const struct man_node *nn; - int ival; + int ival; + size_t sz; switch (n->type) { case (MAN_BLOCK): @@ -809,40 +843,52 @@ pre_RS(DECL_ARGS) break; } - if (NULL == (nn = n->parent->head->child)) { - mt->offset = mt->lmargin + term_len(p, INDENT); - p->offset = mt->offset; - return(1); - } + sz = term_len(p, p->defindent); - if ((ival = a2width(p, nn->string)) < 0) - return(1); + if (NULL != (n = n->parent->head->child)) + if ((ival = a2width(p, n->string)) >= 0) + sz = (size_t)ival; - mt->offset = term_len(p, INDENT) + (size_t)ival; - p->offset = mt->offset; + mt->offset += sz; + p->rmargin = p->maxrmargin; + p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin; + if (++mt->lmarginsz < MAXMARGINS) + mt->lmargincur = mt->lmarginsz; + + mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1]; return(1); } - /* ARGSUSED */ static void post_RS(DECL_ARGS) { + int ival; + size_t sz; switch (n->type) { case (MAN_BLOCK): - mt->offset = mt->lmargin = term_len(p, INDENT); - break; + return; case (MAN_HEAD): - break; + return; default: term_newln(p); - p->offset = term_len(p, INDENT); break; } -} + sz = term_len(p, p->defindent); + + if (NULL != (n = n->parent->head->child)) + if ((ival = a2width(p, n->string)) >= 0) + sz = (size_t)ival; + + mt->offset = mt->offset < sz ? 0 : mt->offset - sz; + p->offset = mt->offset; + + if (--mt->lmarginsz < MAXMARGINS) + mt->lmargincur = mt->lmarginsz; +} static void print_man_node(DECL_ARGS) @@ -872,7 +918,7 @@ print_man_node(DECL_ARGS) * -man doesn't have nested macros, we don't need to be * more specific than this. */ - if (MANT_LITERAL & mt->fl && + if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) && (NULL == n->next || n->next->line > n->line)) { rm = p->rmargin; @@ -880,7 +926,6 @@ print_man_node(DECL_ARGS) p->rmargin = p->maxrmargin = TERM_MAXMARGIN; p->flags |= TERMP_NOSPACE; term_flushln(p); - p->flags &= ~TERMP_NOLPAD; p->rmargin = rm; p->maxrmargin = rmax; } @@ -889,7 +934,7 @@ print_man_node(DECL_ARGS) p->flags |= TERMP_SENTENCE; return; case (MAN_EQN): - term_word(p, n->eqn->data); + term_eqn(p, n->eqn); return; case (MAN_TBL): /* @@ -938,37 +983,66 @@ print_man_nodelist(DECL_ARGS) static void print_man_foot(struct termp *p, const void *arg) { + char title[BUFSIZ]; + size_t datelen; const struct man_meta *meta; meta = (const struct man_meta *)arg; + assert(meta->title); + assert(meta->msec); + assert(meta->date); term_fontrepl(p, TERMFONT_NONE); - term_vspace(p); - term_vspace(p); term_vspace(p); + /* + * Temporary, undocumented option to imitate mdoc(7) output. + * In the bottom right corner, use the source instead of + * the title. + */ + + if ( ! p->mdocstyle) { + term_vspace(p); + term_vspace(p); + snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec); + } else if (meta->source) { + strlcpy(title, meta->source, BUFSIZ); + } else { + title[0] = '\0'; + } + datelen = term_strlen(p, meta->date); + + /* Bottom left corner: manual source. */ + p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; - p->rmargin = p->maxrmargin - term_strlen(p, meta->date); p->offset = 0; - - /* term_strlen() can return zero. */ - if (p->rmargin == p->maxrmargin) - p->rmargin--; + p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2; if (meta->source) term_word(p, meta->source); - if (meta->source) - term_word(p, ""); term_flushln(p); - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + /* At the bottom in the middle: manual date. */ + + p->flags |= TERMP_NOSPACE; p->offset = p->rmargin; - p->rmargin = p->maxrmargin; - p->flags &= ~TERMP_NOBREAK; + p->rmargin = p->maxrmargin - term_strlen(p, title); + if (p->offset + datelen >= p->rmargin) + p->rmargin = p->offset + datelen; term_word(p, meta->date); term_flushln(p); + + /* Bottom right corner: manual title and section. */ + + p->flags &= ~TERMP_NOBREAK; + p->flags |= TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + + term_word(p, title); + term_flushln(p); } @@ -980,36 +1054,33 @@ print_man_head(struct termp *p, const void *arg) const struct man_meta *m; m = (const struct man_meta *)arg; - - /* - * Note that old groff would spit out some spaces before the - * header. We discontinue this strange behaviour, but at one - * point we did so here. - */ - - p->rmargin = p->maxrmargin; - - p->offset = 0; - buf[0] = title[0] = '\0'; + assert(m->title); + assert(m->msec); if (m->vol) strlcpy(buf, m->vol, BUFSIZ); + else + buf[0] = '\0'; buflen = term_strlen(p, buf); + /* Top left corner: manual title and section. */ + snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); titlen = term_strlen(p, title); + p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; p->offset = 0; p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? (p->maxrmargin - term_strlen(p, buf) + term_len(p, 1)) / 2 : p->maxrmargin - buflen; - p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; term_word(p, title); term_flushln(p); - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + /* At the top in the middle: manual volume. */ + + p->flags |= TERMP_NOSPACE; p->offset = p->rmargin; p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin; @@ -1017,25 +1088,30 @@ print_man_head(struct termp *p, const void *arg) term_word(p, buf); term_flushln(p); + /* Top right corner: title and section, again. */ + p->flags &= ~TERMP_NOBREAK; if (p->rmargin + titlen <= p->maxrmargin) { - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->flags |= TERMP_NOSPACE; p->offset = p->rmargin; p->rmargin = p->maxrmargin; term_word(p, title); term_flushln(p); } - p->rmargin = p->maxrmargin; - p->offset = 0; p->flags &= ~TERMP_NOSPACE; + p->offset = 0; + p->rmargin = p->maxrmargin; /* - * Groff likes to have some leading spaces before content. Well - * that's fine by me. + * Groff prints three blank lines before the content. + * Do the same, except in the temporary, undocumented + * mode imitating mdoc(7) output. */ term_vspace(p); - term_vspace(p); - term_vspace(p); + if ( ! p->mdocstyle) { + term_vspace(p); + term_vspace(p); + } } diff --git a/contrib/mdocml/man_validate.c b/contrib/mdocml/man_validate.c index e0c882d49b..e40b089f53 100644 --- a/contrib/mdocml/man_validate.c +++ b/contrib/mdocml/man_validate.c @@ -1,4 +1,4 @@ -/* $Id: man_validate.c,v 1.69 2011/04/13 09:57:08 kristaps Exp $ */ +/* $Id: man_validate.c,v 1.80 2012/01/03 15:16:24 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010 Ingo Schwarze @@ -44,49 +44,52 @@ struct man_valid { v_check *posts; }; -static int check_bline(CHKARGS); static int check_eq0(CHKARGS); -static int check_ft(CHKARGS); +static int check_eq2(CHKARGS); static int check_le1(CHKARGS); static int check_ge2(CHKARGS); static int check_le5(CHKARGS); static int check_par(CHKARGS); static int check_part(CHKARGS); static int check_root(CHKARGS); -static int check_sec(CHKARGS); static void check_text(CHKARGS); static int post_AT(CHKARGS); +static int post_vs(CHKARGS); static int post_fi(CHKARGS); +static int post_ft(CHKARGS); static int post_nf(CHKARGS); +static int post_sec(CHKARGS); static int post_TH(CHKARGS); static int post_UC(CHKARGS); +static int pre_sec(CHKARGS); static v_check posts_at[] = { post_AT, NULL }; +static v_check posts_br[] = { post_vs, check_eq0, NULL }; static v_check posts_eq0[] = { check_eq0, NULL }; +static v_check posts_eq2[] = { check_eq2, NULL }; static v_check posts_fi[] = { check_eq0, post_fi, NULL }; -static v_check posts_le1[] = { check_le1, NULL }; -static v_check posts_ft[] = { check_ft, NULL }; +static v_check posts_ft[] = { post_ft, NULL }; static v_check posts_nf[] = { check_eq0, post_nf, NULL }; static v_check posts_par[] = { check_par, NULL }; static v_check posts_part[] = { check_part, NULL }; -static v_check posts_sec[] = { check_sec, NULL }; +static v_check posts_sec[] = { post_sec, NULL }; +static v_check posts_sp[] = { post_vs, check_le1, NULL }; static v_check posts_th[] = { check_ge2, check_le5, post_TH, NULL }; static v_check posts_uc[] = { post_UC, NULL }; -static v_check pres_bline[] = { check_bline, NULL }; - +static v_check pres_sec[] = { pre_sec, NULL }; static const struct man_valid man_valids[MAN_MAX] = { - { NULL, posts_eq0 }, /* br */ - { pres_bline, posts_th }, /* TH */ - { pres_bline, posts_sec }, /* SH */ - { pres_bline, posts_sec }, /* SS */ - { pres_bline, NULL }, /* TP */ - { pres_bline, posts_par }, /* LP */ - { pres_bline, posts_par }, /* PP */ - { pres_bline, posts_par }, /* P */ - { pres_bline, NULL }, /* IP */ - { pres_bline, NULL }, /* HP */ + { NULL, posts_br }, /* br */ + { NULL, posts_th }, /* TH */ + { pres_sec, posts_sec }, /* SH */ + { pres_sec, posts_sec }, /* SS */ + { NULL, NULL }, /* TP */ + { NULL, posts_par }, /* LP */ + { NULL, posts_par }, /* PP */ + { NULL, posts_par }, /* P */ + { NULL, NULL }, /* IP */ + { NULL, NULL }, /* HP */ { NULL, NULL }, /* SM */ { NULL, NULL }, /* SB */ { NULL, NULL }, /* BI */ @@ -98,10 +101,10 @@ static const struct man_valid man_valids[MAN_MAX] = { { NULL, NULL }, /* I */ { NULL, NULL }, /* IR */ { NULL, NULL }, /* RI */ - { NULL, posts_eq0 }, /* na */ /* FIXME: should warn only. */ - { NULL, posts_le1 }, /* sp */ /* FIXME: should warn only. */ - { pres_bline, posts_nf }, /* nf */ - { pres_bline, posts_fi }, /* fi */ + { NULL, posts_eq0 }, /* na */ + { NULL, posts_sp }, /* sp */ + { NULL, posts_nf }, /* nf */ + { NULL, posts_fi }, /* fi */ { NULL, NULL }, /* RE */ { NULL, posts_part }, /* RS */ { NULL, NULL }, /* DT */ @@ -110,6 +113,7 @@ static const struct man_valid man_valids[MAN_MAX] = { { NULL, posts_at }, /* AT */ { NULL, NULL }, /* in */ { NULL, posts_ft }, /* ft */ + { NULL, posts_eq2 }, /* OP */ }; @@ -206,46 +210,16 @@ check_root(CHKARGS) } static void -check_text(CHKARGS) +check_text(CHKARGS) { - char *p, *pp, *cpp; - int pos; - size_t sz; - - p = n->string; - pos = n->pos + 1; - - while ('\0' != *p) { - sz = strcspn(p, "\t\\"); - - p += (int)sz; - pos += (int)sz; - - if ('\t' == *p) { - if ( ! (MAN_LITERAL & m->flags)) - man_pmsg(m, n->line, pos, MANDOCERR_BADTAB); - p++; - pos++; - continue; - } else if ('\0' == *p) - break; - - pos++; - pp = ++p; - - if (ESCAPE_ERROR == mandoc_escape - ((const char **)&pp, NULL, NULL)) { - man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE); - break; - } + char *cp, *p; - cpp = p; - while (NULL != (cpp = memchr(cpp, ASCII_HYPH, pp - cpp))) - *cpp = '-'; + if (MAN_LITERAL & m->flags) + return; - pos += pp - p; - p = pp; - } + cp = n->string; + for (p = cp; NULL != (p = strchr(p, '\t')); p++) + man_pmsg(m, n->line, (int)(p - cp), MANDOCERR_BADTAB); } #define INEQ_DEFINE(x, ineq, name) \ @@ -261,12 +235,13 @@ check_##name(CHKARGS) \ } INEQ_DEFINE(0, ==, eq0) +INEQ_DEFINE(2, ==, eq2) INEQ_DEFINE(1, <=, le1) INEQ_DEFINE(2, >=, ge2) INEQ_DEFINE(5, <=, le5) static int -check_ft(CHKARGS) +post_ft(CHKARGS) { char *cp; int ok; @@ -322,7 +297,16 @@ check_ft(CHKARGS) } static int -check_sec(CHKARGS) +pre_sec(CHKARGS) +{ + + if (MAN_BLOCK == n->type) + m->flags &= ~MAN_LITERAL; + return(1); +} + +static int +post_sec(CHKARGS) { if ( ! (MAN_HEAD == n->type && 0 == n->nchild)) @@ -332,7 +316,6 @@ check_sec(CHKARGS) return(0); } - static int check_part(CHKARGS) { @@ -370,19 +353,6 @@ check_par(CHKARGS) } -static int -check_bline(CHKARGS) -{ - - assert( ! (MAN_ELINE & m->flags)); - if (MAN_BLINE & m->flags) { - man_nmsg(m, n, MANDOCERR_SYNTLINESCOPE); - return(0); - } - - return(1); -} - static int post_TH(CHKARGS) { @@ -411,7 +381,8 @@ post_TH(CHKARGS) if (n && n->string) { for (p = n->string; '\0' != *p; p++) { /* Only warn about this once... */ - if (isalpha((u_char)*p) && ! isupper((u_char)*p)) { + if (isalpha((unsigned char)*p) && + ! isupper((unsigned char)*p)) { man_nmsg(m, n, MANDOCERR_UPPERCASE); break; } @@ -433,10 +404,12 @@ post_TH(CHKARGS) if (n) n = n->next; - if (n) + if (n && n->string && '\0' != n->string[0]) { pos = n->pos; - m->meta.date = mandoc_normdate - (m->parse, n ? n->string : NULL, line, pos); + m->meta.date = mandoc_normdate + (m->parse, n->string, line, pos); + } else + m->meta.date = mandoc_strdup(""); /* TITLE MSEC DATE ->SOURCE<- VOL */ @@ -444,9 +417,13 @@ post_TH(CHKARGS) m->meta.source = mandoc_strdup(n->string); /* TITLE MSEC DATE SOURCE ->VOL<- */ + /* If missing, use the default VOL name for MSEC. */ if (n && (n = n->next)) m->meta.vol = mandoc_strdup(n->string); + else if ('\0' != m->meta.msec[0] && + (NULL != (p = mandoc_a2msec(m->meta.msec)))) + m->meta.vol = mandoc_strdup(p); /* * Remove the `TH' node after we've processed it for our @@ -492,7 +469,6 @@ post_UC(CHKARGS) const char *p, *s; n = n->child; - n = m->last->child; if (NULL == n || MAN_TEXT != n->type) p = bsd_versions[0]; @@ -558,3 +534,17 @@ post_AT(CHKARGS) m->meta.source = mandoc_strdup(p); return(1); } + +static int +post_vs(CHKARGS) +{ + + /* + * Don't warn about this because it occurs in pod2man and would + * cause considerable (unfixable) warnage. + */ + if (NULL == n->prev && MAN_ROOT == n->parent->type) + man_node_delete(m, n); + + return(1); +} diff --git a/contrib/mdocml/mandoc-db.1 b/contrib/mdocml/mandoc-db.1 deleted file mode 100644 index 24d147c170..0000000000 --- a/contrib/mdocml/mandoc-db.1 +++ /dev/null @@ -1,132 +0,0 @@ -.\" $Id: mandoc-db.1,v 1.1 2011/04/04 10:53:15 kristaps Exp $ -.\" -.\" Copyright (c) 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: April 4 2011 $ -.Dt MANDOC-DB 1 -.Os -.Sh NAME -.Nm mandoc-db -.Nd index UNIX manuals -.Sh SYNOPSIS -.Nm -.Op Fl d Ar dir -.Ar -.Sh DESCRIPTION -The -.Nm -utility extracts keywords from -.Ux -manuals and indexes them for fast retrieval. -The arguments are as follows: -.Bl -tag -width Ds -.It Fl d Ar dir -The directory into which to write the keyword and index databases. -.It Ar -Read input from zero or more files in -.Xr mdoc 7 -or -.Xr man 7 -.Ux -manual format. -.El -.Pp -By default, -.Nm -constructs the -.Sx Index Database -and -.Sx Keyword Database -in the current working directory. -.Pp -If fatal parse errors are encountered, the offending file is printed to -stderr, omitted from the index, and the parse continues with the next -input file. -.Ss Index Database -The index database, -.Pa mandoc.index , -is a -.Xr recno 3 -database of nil-terminated filenames (record length is non-zero string -length plus one) passed to -.Nm . -Entries are sequentially-numbered, but the filenames are unordered. -.Ss Keyword Database -The keyword database, -.Pa mandoc.db , -is a -.Xr btree 3 -database of nil-terminated keywords (record length is non-zero string -length plus one) mapping to a 8-byte binary field consisting of the -keyword type and source -.Sx Index Database -record number. -The type, an unsigned 32-bit integer in host order, is one of the -following: -.Pp -.Bl -tag -width Ds -offset indent -compact -.It Li 0x01 -The name of a manual page as given in the NAME section. -.It Li 0x02 -A function prototype name as given in the SYNOPSIS section. -.It Li 0x03 -A utility name as given in the SYNOPSIS section. -.It Li 0x04 -An include file as given in the SYNOPSIS section. -.It Li 0x05 -A variable name as given in the SYNOPSIS section. -.El -.Pp -If a value is encountered outside of this range, the database is -corrupt. -.Pp -The latter four bytes are a host-ordered record number within the -.Sx Index Database . -.Pp -The -.Nm -utility is -.Ud -.Sh FILES -.Bl -tag -width Ds -.It Pa mandoc.db -A -.Xr btree 3 -keyword database mapping keywords to a type and file reference in -.Pa mandoc.index . -.It Pa mandoc.db~ -Working copy of -.Pa mandoc.db . -.It Pa mandoc.index -A -.Xr recno 3 -database of indexed file-names. -.It Pa mandoc.index~ -Working copy of -.Pa mandoc.index . -.El -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr mandoc 1 -.Sh AUTHORS -The -.Nm -utility was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . -.Sh CAVEATS -Only -.Xr mdoc 7 -manuals are processed. diff --git a/contrib/mdocml/mandoc-db.c b/contrib/mdocml/mandoc-db.c deleted file mode 100644 index 0c66efaf2b..0000000000 --- a/contrib/mdocml/mandoc-db.c +++ /dev/null @@ -1,669 +0,0 @@ -/* $Id: mandoc-db.c,v 1.5 2011/04/04 16:49:03 kristaps Exp $ */ -/* - * Copyright (c) 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. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include -#ifdef __linux__ -# include -#else -# include -#endif -#include -#include -#include -#include -#include -#include - -#include "man.h" -#include "mdoc.h" -#include "mandoc.h" - -#define MANDOC_DB "mandoc.db" -#define MANDOC_IDX "mandoc.index" -#define MANDOC_BUFSZ BUFSIZ -#define MANDOC_FLAGS O_CREAT|O_TRUNC|O_RDWR - -enum type { - MANDOC_NONE = 0, - MANDOC_NAME, - MANDOC_FUNCTION, - MANDOC_UTILITY, - MANDOC_INCLUDES, - MANDOC_VARIABLE -}; - -#define MDOC_ARGS DB *db, \ - const char *dbn, \ - DBT *key, size_t *ksz, \ - DBT *val, \ - const struct mdoc_node *n - -static void dbt_append(DBT *, size_t *, const char *); -static void dbt_appendb(DBT *, size_t *, - const void *, size_t); -static void dbt_init(DBT *, size_t *); -static void usage(void); -static void pmdoc(DB *, const char *, - DBT *, size_t *, DBT *, - const char *, struct mdoc *); -static void pmdoc_node(MDOC_ARGS); -static void pmdoc_Fd(MDOC_ARGS); -static void pmdoc_In(MDOC_ARGS); -static void pmdoc_Fn(MDOC_ARGS); -static void pmdoc_Fo(MDOC_ARGS); -static void pmdoc_Nm(MDOC_ARGS); -static void pmdoc_Vt(MDOC_ARGS); - -typedef void (*pmdoc_nf)(MDOC_ARGS); - -static const char *progname; - -static const pmdoc_nf mdocs[MDOC_MAX] = { - NULL, /* Ap */ - NULL, /* Dd */ - NULL, /* Dt */ - NULL, /* Os */ - NULL, /* Sh */ - NULL, /* Ss */ - NULL, /* Pp */ - NULL, /* D1 */ - NULL, /* Dl */ - NULL, /* Bd */ - NULL, /* Ed */ - NULL, /* Bl */ - NULL, /* El */ - NULL, /* It */ - NULL, /* Ad */ - NULL, /* An */ - NULL, /* Ar */ - NULL, /* Cd */ - NULL, /* Cm */ - NULL, /* Dv */ - NULL, /* Er */ - NULL, /* Ev */ - NULL, /* Ex */ - NULL, /* Fa */ - pmdoc_Fd, /* Fd */ - NULL, /* Fl */ - pmdoc_Fn, /* Fn */ - NULL, /* Ft */ - NULL, /* Ic */ - pmdoc_In, /* In */ - NULL, /* Li */ - NULL, /* Nd */ - pmdoc_Nm, /* Nm */ - NULL, /* Op */ - NULL, /* Ot */ - NULL, /* Pa */ - NULL, /* Rv */ - NULL, /* St */ - pmdoc_Vt, /* Va */ - pmdoc_Vt, /* Vt */ - NULL, /* Xr */ - NULL, /* %A */ - NULL, /* %B */ - NULL, /* %D */ - NULL, /* %I */ - NULL, /* %J */ - NULL, /* %N */ - NULL, /* %O */ - NULL, /* %P */ - NULL, /* %R */ - NULL, /* %T */ - NULL, /* %V */ - NULL, /* Ac */ - NULL, /* Ao */ - NULL, /* Aq */ - NULL, /* At */ - NULL, /* Bc */ - NULL, /* Bf */ - NULL, /* Bo */ - NULL, /* Bq */ - NULL, /* Bsx */ - NULL, /* Bx */ - NULL, /* Db */ - NULL, /* Dc */ - NULL, /* Do */ - NULL, /* Dq */ - NULL, /* Ec */ - NULL, /* Ef */ - NULL, /* Em */ - NULL, /* Eo */ - NULL, /* Fx */ - NULL, /* Ms */ - NULL, /* No */ - NULL, /* Ns */ - NULL, /* Nx */ - NULL, /* Ox */ - NULL, /* Pc */ - NULL, /* Pf */ - NULL, /* Po */ - NULL, /* Pq */ - NULL, /* Qc */ - NULL, /* Ql */ - NULL, /* Qo */ - NULL, /* Qq */ - NULL, /* Re */ - NULL, /* Rs */ - NULL, /* Sc */ - NULL, /* So */ - NULL, /* Sq */ - NULL, /* Sm */ - NULL, /* Sx */ - NULL, /* Sy */ - NULL, /* Tn */ - NULL, /* Ux */ - NULL, /* Xc */ - NULL, /* Xo */ - pmdoc_Fo, /* Fo */ - NULL, /* Fc */ - NULL, /* Oo */ - NULL, /* Oc */ - NULL, /* Bk */ - NULL, /* Ek */ - NULL, /* Bt */ - NULL, /* Hf */ - NULL, /* Fr */ - NULL, /* Ud */ - NULL, /* Lb */ - NULL, /* Lp */ - NULL, /* Lk */ - NULL, /* Mt */ - NULL, /* Brq */ - NULL, /* Bro */ - NULL, /* Brc */ - NULL, /* %C */ - NULL, /* Es */ - NULL, /* En */ - NULL, /* Dx */ - NULL, /* %Q */ - NULL, /* br */ - NULL, /* sp */ - NULL, /* %U */ - NULL, /* Ta */ -}; - -int -main(int argc, char *argv[]) -{ - struct mparse *mp; /* parse sequence */ - struct mdoc *mdoc; /* resulting mdoc */ - char *fn; - const char *dir; /* result dir (default: cwd) */ - char ibuf[MAXPATHLEN], /* index fname */ - ibbuf[MAXPATHLEN], /* index backup fname */ - fbuf[MAXPATHLEN], /* btree fname */ - fbbuf[MAXPATHLEN]; /* btree backup fname */ - int c; - DB *index, /* index database */ - *db; /* keyword database */ - DBT rkey, rval, /* recno entries */ - key, val; /* persistent keyword entries */ - size_t ksz; /* entry buffer size */ - char vbuf[8]; - BTREEINFO info; /* btree configuration */ - recno_t rec; - extern int optind; - extern char *optarg; - - progname = strrchr(argv[0], '/'); - if (progname == NULL) - progname = argv[0]; - else - ++progname; - - dir = ""; - - while (-1 != (c = getopt(argc, argv, "d:"))) - switch (c) { - case ('d'): - dir = optarg; - break; - default: - usage(); - return((int)MANDOCLEVEL_BADARG); - } - - argc -= optind; - argv += optind; - - /* - * Set up temporary file-names into which we're going to write - * all of our data (both for the index and database). These - * will be securely renamed to the real file-names after we've - * written all of our data. - */ - - ibuf[0] = ibuf[MAXPATHLEN - 2] = - ibbuf[0] = ibbuf[MAXPATHLEN - 2] = - fbuf[0] = fbuf[MAXPATHLEN - 2] = - fbbuf[0] = fbbuf[MAXPATHLEN - 2] = '\0'; - - strlcat(fbuf, dir, MAXPATHLEN); - strlcat(fbuf, MANDOC_DB, MAXPATHLEN); - - strlcat(fbbuf, fbuf, MAXPATHLEN); - strlcat(fbbuf, "~", MAXPATHLEN); - - strlcat(ibuf, dir, MAXPATHLEN); - strlcat(ibuf, MANDOC_IDX, MAXPATHLEN); - - strlcat(ibbuf, ibuf, MAXPATHLEN); - strlcat(ibbuf, "~", MAXPATHLEN); - - if ('\0' != fbuf[MAXPATHLEN - 2] || - '\0' != fbbuf[MAXPATHLEN - 2] || - '\0' != ibuf[MAXPATHLEN - 2] || - '\0' != ibbuf[MAXPATHLEN - 2]) { - fprintf(stderr, "%s: Path too long\n", progname); - exit((int)MANDOCLEVEL_SYSERR); - } - - /* - * For the keyword database, open a BTREE database that allows - * duplicates. For the index database, use a standard RECNO - * database type. - */ - - memset(&info, 0, sizeof(BTREEINFO)); - info.flags = R_DUP; - db = dbopen(fbbuf, MANDOC_FLAGS, 0644, DB_BTREE, &info); - - if (NULL == db) { - perror(fbbuf); - exit((int)MANDOCLEVEL_SYSERR); - } - - index = dbopen(ibbuf, MANDOC_FLAGS, 0644, DB_RECNO, NULL); - - if (NULL == db) { - perror(ibbuf); - (*db->close)(db); - exit((int)MANDOCLEVEL_SYSERR); - } - - /* - * Try parsing the manuals given on the command line. If we - * totally fail, then just keep on going. Take resulting trees - * and push them down into the database code. - * Use the auto-parser and don't report any errors. - */ - - mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); - - memset(&key, 0, sizeof(DBT)); - memset(&val, 0, sizeof(DBT)); - memset(&rkey, 0, sizeof(DBT)); - memset(&rval, 0, sizeof(DBT)); - - val.size = sizeof(vbuf); - val.data = vbuf; - rkey.size = sizeof(recno_t); - - rec = 1; - ksz = 0; - - while (NULL != (fn = *argv++)) { - mparse_reset(mp); - - if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL) { - fprintf(stderr, "%s: Parse failure\n", fn); - continue; - } - - mparse_result(mp, &mdoc, NULL); - if (NULL == mdoc) - continue; - - rkey.data = &rec; - rval.data = fn; - rval.size = strlen(fn) + 1; - - if (-1 == (*index->put)(index, &rkey, &rval, 0)) { - perror(ibbuf); - break; - } - - memset(val.data, 0, sizeof(uint32_t)); - memcpy(val.data + 4, &rec, sizeof(uint32_t)); - - pmdoc(db, fbbuf, &key, &ksz, &val, fn, mdoc); - rec++; - } - - (*db->close)(db); - (*index->close)(index); - - mparse_free(mp); - - free(key.data); - - /* Atomically replace the file with our temporary one. */ - - if (-1 == rename(fbbuf, fbuf)) - perror(fbuf); - if (-1 == rename(ibbuf, ibuf)) - perror(fbuf); - - return((int)MANDOCLEVEL_OK); -} - -/* - * Initialise the stored database key whose data buffer is shared - * between uses (as the key must sometimes be constructed from an array - * of - */ -static void -dbt_init(DBT *key, size_t *ksz) -{ - - if (0 == *ksz) { - assert(0 == key->size); - assert(NULL == key->data); - key->data = mandoc_malloc(MANDOC_BUFSZ); - *ksz = MANDOC_BUFSZ; - } - - key->size = 0; -} - -/* - * Append a binary value to a database entry. This can be invoked - * multiple times; the buffer is automatically resized. - */ -static void -dbt_appendb(DBT *key, size_t *ksz, const void *cp, size_t sz) -{ - - assert(key->data); - - /* Overshoot by MANDOC_BUFSZ. */ - - while (key->size + sz >= *ksz) { - *ksz = key->size + sz + MANDOC_BUFSZ; - key->data = mandoc_realloc(key->data, *ksz); - } - - memcpy(key->data + (int)key->size, cp, sz); - key->size += sz; -} - -/* - * Append a nil-terminated string to the database entry. This can be - * invoked multiple times. The database entry will be nil-terminated as - * well; if invoked multiple times, a space is put between strings. - */ -static void -dbt_append(DBT *key, size_t *ksz, const char *cp) -{ - size_t sz; - - if (0 == (sz = strlen(cp))) - return; - - assert(key->data); - - if (key->size) - ((char *)key->data)[(int)key->size - 1] = ' '; - - dbt_appendb(key, ksz, cp, sz + 1); -} - -/* ARGSUSED */ -static void -pmdoc_Fd(MDOC_ARGS) -{ - uint32_t fl; - const char *start, *end; - size_t sz; - char nil; - - if (SEC_SYNOPSIS != n->sec) - return; - if (NULL == (n = n->child) || MDOC_TEXT != n->type) - return; - - /* - * Only consider those `Fd' macro fields that begin with an - * "inclusion" token (versus, e.g., #define). - */ - if (strcmp("#include", n->string)) - return; - - if (NULL == (n = n->next) || MDOC_TEXT != n->type) - return; - - /* - * Strip away the enclosing angle brackets and make sure we're - * not zero-length. - */ - - start = n->string; - if ('<' == *start || '"' == *start) - start++; - - if (0 == (sz = strlen(start))) - return; - - end = &start[(int)sz - 1]; - if ('>' == *end || '"' == *end) - end--; - - nil = '\0'; - dbt_appendb(key, ksz, start, end - start + 1); - dbt_appendb(key, ksz, &nil, 1); - - fl = MANDOC_INCLUDES; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_In(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_SYNOPSIS != n->sec) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - dbt_append(key, ksz, n->child->string); - fl = MANDOC_INCLUDES; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Fn(MDOC_ARGS) -{ - uint32_t fl; - const char *cp; - - if (SEC_SYNOPSIS != n->sec) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - /* .Fn "struct type *arg" "foo" */ - - cp = strrchr(n->child->string, ' '); - if (NULL == cp) - cp = n->child->string; - - /* Strip away pointer symbol. */ - - while ('*' == *cp) - cp++; - - dbt_append(key, ksz, cp); - fl = MANDOC_FUNCTION; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Vt(MDOC_ARGS) -{ - uint32_t fl; - const char *start, *end; - size_t sz; - char nil; - - if (SEC_SYNOPSIS != n->sec) - return; - if (MDOC_Vt == n->tok && MDOC_BODY != n->type) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - /* - * Strip away leading pointer symbol '*' and trailing ';'. - */ - - start = n->last->string; - - while ('*' == *start) - start++; - - if (0 == (sz = strlen(start))) - return; - - end = &start[sz - 1]; - while (end > start && ';' == *end) - end--; - - if (end == start) - return; - - nil = '\0'; - dbt_appendb(key, ksz, start, end - start + 1); - dbt_appendb(key, ksz, &nil, 1); - fl = MANDOC_VARIABLE; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Fo(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type) - return; - if (NULL == n->child || MDOC_TEXT != n->child->type) - return; - - dbt_append(key, ksz, n->child->string); - fl = MANDOC_FUNCTION; - memcpy(val->data, &fl, 4); -} - -/* ARGSUSED */ -static void -pmdoc_Nm(MDOC_ARGS) -{ - uint32_t fl; - - if (SEC_NAME == n->sec) { - for (n = n->child; n; n = n->next) { - if (MDOC_TEXT != n->type) - continue; - dbt_append(key, ksz, n->string); - } - fl = MANDOC_NAME; - memcpy(val->data, &fl, 4); - return; - } else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type) - return; - - for (n = n->child; n; n = n->next) { - if (MDOC_TEXT != n->type) - continue; - dbt_append(key, ksz, n->string); - } - - fl = MANDOC_UTILITY; - memcpy(val->data, &fl, 4); -} - -/* - * Call out to per-macro handlers after clearing the persistent database - * key. If the macro sets the database key, flush it to the database. - */ -static void -pmdoc_node(MDOC_ARGS) -{ - - if (NULL == n) - return; - - switch (n->type) { - case (MDOC_HEAD): - /* FALLTHROUGH */ - case (MDOC_BODY): - /* FALLTHROUGH */ - case (MDOC_TAIL): - /* FALLTHROUGH */ - case (MDOC_BLOCK): - /* FALLTHROUGH */ - case (MDOC_ELEM): - if (NULL == mdocs[n->tok]) - break; - - dbt_init(key, ksz); - (*mdocs[n->tok])(db, dbn, key, ksz, val, n); - - if (0 == key->size) - break; - if (0 == (*db->put)(db, key, val, 0)) - break; - - perror(dbn); - exit((int)MANDOCLEVEL_SYSERR); - /* NOTREACHED */ - default: - break; - } - - pmdoc_node(db, dbn, key, ksz, val, n->child); - pmdoc_node(db, dbn, key, ksz, val, n->next); -} - -static void -pmdoc(DB *db, const char *dbn, - DBT *key, size_t *ksz, DBT *val, - const char *path, struct mdoc *m) -{ - - pmdoc_node(db, dbn, key, ksz, val, mdoc_node(m)); -} - -static void -usage(void) -{ - - fprintf(stderr, "usage: %s " - "[-d path] " - "[file...]\n", - progname); -} diff --git a/contrib/mdocml/mandoc.1 b/contrib/mdocml/mandoc.1 index 7cf9ca5c9b..dbff0e31ca 100644 --- a/contrib/mdocml/mandoc.1 +++ b/contrib/mdocml/mandoc.1 @@ -1,6 +1,6 @@ -.\" $Id: mandoc.1,v 1.88 2011/05/20 15:51:18 kristaps Exp $ +.\" $Id: mandoc.1,v 1.100 2011/12/25 19:35:44 kristaps Exp $ .\" -.\" Copyright (c) 2009, 2010 Kristaps Dzonsons +.\" 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 @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 20 2011 $ +.Dd $Mdocdate: December 25 2011 $ .Dt MANDOC 1 .Os .Sh NAME @@ -27,13 +27,26 @@ .Op Fl O Ns Ar option .Op Fl T Ns Ar output .Op Fl W Ns Ar level -.Op Ar file... +.Op Ar .Sh DESCRIPTION The .Nm utility formats .Ux manual pages for display. +.Pp +By default, +.Nm +reads +.Xr mdoc 7 +or +.Xr man 7 +text from stdin, implying +.Fl m Ns Cm andoc , +and produces +.Fl T Ns Cm ascii +output. +.Pp The arguments are as follows: .Bl -tag -width Ds .It Fl m Ns Ar format @@ -96,18 +109,6 @@ If multiple files are specified, .Nm will halt with the first failed parse. .El -.Pp -By default, -.Nm -reads -.Xr mdoc 7 -or -.Xr man 7 -text from stdin, implying -.Fl m Ns Cm andoc , -and produces -.Fl T Ns Cm ascii -output. .Ss Input Formats The .Nm @@ -157,16 +158,7 @@ The utility accepts the following .Fl T arguments, which correspond to output modes: -.Bl -tag -width Ds -.It Fl T Ns Cm utf8 -Encode output in the UTF-8 multi-byte format. -See -.Xr UTF-8 Output . -.It Fl T Ns Cm locale -Encode output using the current -.Xr locale 1 . -See -.Sx Locale Output . +.Bl -tag -width "-Tlocale" .It Fl T Ns Cm ascii Produce 7-bit ASCII output. This is the default. @@ -180,6 +172,16 @@ See Parse only: produce no output. Implies .Fl W Ns Cm warning . +.It Fl T Ns Cm locale +Encode output using the current locale. +See +.Sx Locale Output . +.It Fl T Ns Cm man +Produce +.Xr man 7 +format output. +See +.Sx Man Output . .It Fl T Ns Cm pdf Produce PDF output. See @@ -190,6 +192,10 @@ See .Sx PostScript Output . .It Fl T Ns Cm tree Produce an indented parse tree. +.It Fl T Ns Cm utf8 +Encode output in the UTF\-8 multi-byte format. +See +.Sx UTF\-8 Output . .It Fl T Ns Cm xhtml Produce strict CSS1/XHTML-1.0 output. See @@ -198,23 +204,6 @@ See .Pp If multiple input files are specified, these will be processed by the corresponding filter in-order. -.Ss UTF-8 Output -Use -.Fl T Ns Cm utf8 -to force a UTF-8 locale. -See -.Sx Locale Output -for details and options. -.Ss Locale Output -Locale-depending output encoding is triggered with -.Fl T Ns Cm locale . -This option is not available on all systems: systems without locale -support, or those whose internal representation is not natively UCS-4, -will fall back to -.Fl T Ns Cm ascii . -See -.Sx ASCII Output -for font style specification and available command-line arguments. .Ss ASCII Output Output produced by .Fl T Ns Cm ascii , @@ -246,6 +235,15 @@ 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 for +.Xr mdoc 7 +and seven for +.Xr man 7 . +Increasing this is not recommended; it may result in degraded formatting, +for example overfull lines or ugly line breaks. .It Cm width Ns = Ns Ar width The output width is set to .Ar width , @@ -265,12 +263,27 @@ If a style-sheet is not specified with defaults to simple output readable in any graphical or text-based web browser. .Pp -Special characters are rendered in decimal-encoded UTF-8. +Special characters are rendered in decimal-encoded UTF\-8. .Pp The following .Fl O arguments are accepted: .Bl -tag -width Ds +.It Cm fragment +Omit the +.Aq !DOCTYPE +declaration and the +.Aq html , +.Aq head , +and +.Aq body +elements and only emit the subtree below the +.Aq body +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 , @@ -307,6 +320,48 @@ is used for an external style-sheet. This must be a valid absolute or relative URI. .El +.Ss Locale Output +Locale-depending output encoding is triggered with +.Fl T Ns Cm locale . +This option is not available on all systems: systems without locale +support, or those whose internal representation is not natively UCS-4, +will fall back to +.Fl T Ns Cm ascii . +See +.Sx ASCII Output +for font style specification and available command-line arguments. +.Ss Man Output +Translate input format into +.Xr man 7 +output format. +This is useful for distributing manual sources to legancy systems +lacking +.Xr mdoc 7 +formatters. +.Pp +If +.Xr mdoc 7 +is passed as input, it is translated into +.Xr man 7 . +If the input format is +.Xr man 7 , +the input is copied to the output, expanding any +.Xr roff 7 +.Sq so +requests. +The parser is also run, and as usual, the +.Fl W +level controls which +.Sx DIAGNOSTICS +are displayed before copying the input to the output. +.Ss PDF Output +PDF-1.1 output may be generated by +.Fl T Ns Cm pdf . +See +.Sx PostScript Output +for +.Fl O +arguments and defaults. .Ss PostScript Output PostScript .Qq Adobe-3.0 @@ -341,14 +396,13 @@ If an unknown value is encountered, .Ar letter is used. .El -.Ss PDF Output -PDF-1.1 output may be generated by -.Fl T Ns Cm pdf . +.Ss UTF\-8 Output +Use +.Fl T Ns Cm utf8 +to force a UTF\-8 locale. See -.Sx PostScript Output -for -.Fl O -arguments and defaults. +.Sx Locale Output +for details and options. .Ss XHTML Output Output produced by .Fl T Ns Cm xhtml @@ -418,6 +472,16 @@ To check over a large set of manuals: To produce a series of PostScript manuals for A4 paper: .Pp .Dl $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt 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 \-Tman foo.mdoc \*(Gt foo.man .Sh DIAGNOSTICS Standard error messages reporting parsing errors are prefixed by .Pp @@ -573,7 +637,8 @@ lists render similarly. The .Nm utility was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . .Sh CAVEATS In .Fl T Ns Cm html diff --git a/contrib/mdocml/mandoc.3 b/contrib/mdocml/mandoc.3 index 300f2981bb..4d0b20d650 100644 --- a/contrib/mdocml/mandoc.3 +++ b/contrib/mdocml/mandoc.3 @@ -1,4 +1,4 @@ -.\" $Id: mandoc.3,v 1.10 2011/05/24 21:41:11 kristaps Exp $ +.\" $Id: mandoc.3,v 1.17 2012/01/13 15:27:14 joerg Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons .\" Copyright (c) 2010 Ingo Schwarze @@ -15,13 +15,14 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 24 2011 $ +.Dd $Mdocdate: January 13 2012 $ .Dt MANDOC 3 .Os .Sh NAME .Nm mandoc , .Nm mandoc_escape , .Nm man_meta , +.Nm man_mparse , .Nm man_node , .Nm mchars_alloc , .Nm mchars_free , @@ -33,6 +34,8 @@ .Nm mdoc_node , .Nm mparse_alloc , .Nm mparse_free , +.Nm mparse_getkeep , +.Nm mparse_keep , .Nm mparse_readfd , .Nm mparse_reset , .Nm mparse_result , @@ -47,14 +50,18 @@ .In mandoc.h .Ft "enum mandoc_esc" .Fo mandoc_escape -.Fa "const char **in" -.Fa "const char **seq" -.Fa "int *len" +.Fa "const char **end" +.Fa "const char **start" +.Fa "int *sz" .Fc .Ft "const struct man_meta *" .Fo man_meta .Fa "const struct man *man" .Fc +.Ft "const struct mparse *" +.Fo man_mparse +.Fa "const struct man *man" +.Fc .Ft "const struct man_node *" .Fo man_node .Fa "const struct man *man" @@ -69,14 +76,14 @@ .Fn mchars_num2uc "const char *cp" "size_t sz" .Ft "const char *" .Fo mchars_spec2str -.Fa "struct mchars *p" +.Fa "const struct mchars *p" .Fa "const char *cp" .Fa "size_t sz" .Fa "size_t *rsz" .Fc .Ft int .Fo mchars_spec2cp -.Fa "struct mchars *p" +.Fa "const struct mchars *p" .Fa "const char *cp" .Fa "size_t sz" .Ft "const char *" @@ -100,6 +107,14 @@ .Fo mparse_free .Fa "struct mparse *parse" .Fc +.Ft void +.Fo mparse_getkeep +.Fa "const struct mparse *parse" +.Fc +.Ft void +.Fo mparse_keep +.Fa "struct mparse *parse" +.Fc .Ft "enum mandoclevel" .Fo mparse_readfd .Fa "struct mparse *parse" @@ -177,9 +192,6 @@ library also contains routines for translating character strings into glyphs .Pq see Fn mchars_alloc and parsing escape sequences from strings .Pq see Fn mandoc_escape . -.Pp -This library is -.Ud .Sh REFERENCE This section documents the functions, types, and variables available via @@ -187,28 +199,54 @@ via .Ss Types .Bl -ohang .It Vt "enum mandoc_esc" +An escape sequence classification. .It Vt "enum mandocerr" +A fatal error, error, or warning message during parsing. .It Vt "enum mandoclevel" +A classification of an +.Vt "enum mandoclevel" +as regards system operation. .It Vt "struct mchars" An opaque pointer to an object allowing for translation between character strings and glyphs. See .Fn mchars_alloc . .It Vt "enum mparset" +The type of parser when reading input. +This should usually be +.Dv MPARSE_AUTO +for auto-detection. .It Vt "struct mparse" +An opaque pointer to a running parse sequence. +Created with +.Fn mparse_alloc +and freed with +.Fn mparse_free . +This may be used across parsed input if +.Fn mparse_reset +is called between parses. .It Vt "mandocmsg" +A prototype for a function to handle fatal error, error, and warning +messages emitted by the parser. .El .Ss Functions .Bl -ohang .It Fn mandoc_escape Scan an escape sequence, i.e., a character string beginning with .Sq \e . -Pass a pointer to this string as +Pass a pointer to the character after the +.Sq \e +as .Va end ; it will be set to the supremum of the parsed escape sequence unless -returning ESCAPE_ERROR, in which case the string is bogus and should be +returning +.Dv ESCAPE_ERROR , +in which case the string is bogus and should be thrown away. -If not ESCAPE_ERROR or ESCAPE_IGNORE, +If not +.Dv ESCAPE_ERROR +or +.Dv ESCAPE_IGNORE , .Va start is set to the first relevant character of the substring (font, glyph, whatever) of length @@ -217,11 +255,14 @@ Both .Va start and .Va sz -may be NULL. +may be +.Dv NULL . .It Fn man_meta Obtain the meta-data of a successful parse. This may only be used on a pointer returned by .Fn mparse_result . +.It Fn man_mparse +Get the parser used for the current output. .It Fn man_node Obtain the root node of a successful parse. This may only be used on a pointer returned by @@ -251,7 +292,9 @@ Convert a special character into a valid Unicode codepoint. Returns \-1 on failure or a non-zero Unicode codepoint on success. .It Fn mchars_spec2str Convert a special character into an ASCII string. -Returns NULL on failure. +Returns +.Dv NULL +on failure. .It Fn mdoc_meta Obtain the meta-data of a successful parse. This may only be used on a pointer returned by @@ -270,6 +313,15 @@ must be called to free the memory allocated by this function. .It Fn mparse_free Free all memory allocated by .Fn mparse_alloc . +.It Fn mparse_getkeep +Acquire the keep buffer. +Must follow a call of +.Fn mparse_keep . +.It Fn mparse_keep +Instruct the parser to retain a copy of its parsed input. +This can be acquired with subsequent +.Fn mparse_getkeep +calls. .It Fn mparse_readfd Parse a file or file descriptor. If @@ -320,7 +372,36 @@ This section consists of structural documentation for .Xr mdoc 7 and .Xr man 7 -syntax trees. +syntax trees and strings. +.Ss Man and Mdoc Strings +Strings may be extracted from mdoc and man meta-data, or from text +nodes (MDOC_TEXT and MAN_TEXT, respectively). +These strings have special non-printing formatting cues embedded in the +text itself, as well as +.Xr roff 7 +escapes preserved from input. +Implementing systems will need to handle both situations to produce +human-readable text. +In general, strings may be assumed to consist of 7-bit ASCII characters. +.Pp +The following non-printing characters may be embedded in text strings: +.Bl -tag -width Ds +.It Dv ASCII_NBRSP +A non-breaking space character. +.It Dv ASCII_HYPH +A soft hyphen. +.El +.Pp +Escape characters are also passed verbatim into text strings. +An escape character is a sequence of characters beginning with the +backslash +.Pq Sq \e . +To construct human-readable text, these should be intercepted with +.Fn mandoc_escape +and converted with one of +.Fn mchars_num2char , +.Fn mchars_spec2str , +and so on. .Ss Man Abstract Syntax Tree This AST is governed by the ontological rules dictated in .Xr man 7 @@ -361,7 +442,7 @@ where capitalised non-terminals represent nodes. .It ELEMENT \(<- ELEMENT | TEXT* .It TEXT -\(<- [[:alpha:]]* +\(<- [[:ascii:]]* .El .Pp The only elements capable of nesting other elements are those with @@ -420,7 +501,7 @@ where capitalised non-terminals represent nodes. .It TAIL \(<- mnode* .It TEXT -\(<- [[:printable:],0x1e]* +\(<- [[:ascii:]]* .El .Pp Of note are the TEXT nodes following the HEAD, BODY and TAIL nodes of @@ -515,4 +596,5 @@ levels of badly-nested blocks. The .Nm library was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/mandoc.c b/contrib/mdocml/mandoc.c index 465965a469..604bb67e6a 100644 --- a/contrib/mdocml/mandoc.c +++ b/contrib/mdocml/mandoc.c @@ -1,6 +1,6 @@ -/* $Id: mandoc.c,v 1.53 2011/05/24 21:31:23 kristaps Exp $ */ +/* $Id: mandoc.c,v 1.62 2011/12/03 16:08:51 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2011 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any @@ -161,8 +161,7 @@ mandoc_escape(const char **end, const char **start, int *sz) case ('V'): /* FALLTHROUGH */ case ('Y'): - if (ESCAPE_ERROR == gly) - gly = ESCAPE_IGNORE; + gly = ESCAPE_IGNORE; /* FALLTHROUGH */ case ('f'): if (ESCAPE_ERROR == gly) @@ -222,10 +221,7 @@ mandoc_escape(const char **end, const char **start, int *sz) case ('L'): /* FALLTHROUGH */ case ('l'): - /* FALLTHROUGH */ - case ('N'): - if (ESCAPE_ERROR == gly) - gly = ESCAPE_NUMBERED; + gly = ESCAPE_NUMBERED; /* FALLTHROUGH */ case ('S'): /* FALLTHROUGH */ @@ -241,6 +237,26 @@ mandoc_escape(const char **end, const char **start, int *sz) term = numeric = '\''; break; + /* + * Special handling for the numbered character escape. + * XXX Do any other escapes need similar handling? + */ + case ('N'): + if ('\0' == cp[i]) + return(ESCAPE_ERROR); + *end = &cp[++i]; + if (isdigit((unsigned char)cp[i-1])) + return(ESCAPE_IGNORE); + while (isdigit((unsigned char)**end)) + (*end)++; + if (start) + *start = &cp[i]; + if (sz) + *sz = *end - &cp[i]; + if ('\0' != **end) + (*end)++; + return(ESCAPE_NUMBERED); + /* * Sizes get a special category of their own. */ @@ -353,8 +369,15 @@ out: switch (gly) { case (ESCAPE_FONT): - if (1 != rlim) + /* + * Pretend that the constant-width font modes are the + * same as the regular font modes. + */ + if (2 == rlim && 'C' == *rstart) + rstart++; + else if (1 != rlim) break; + switch (*rstart) { case ('3'): /* FALLTHROUGH */ @@ -432,6 +455,16 @@ mandoc_realloc(void *ptr, size_t size) return(ptr); } +char * +mandoc_strndup(const char *ptr, size_t sz) +{ + char *p; + + p = mandoc_malloc(sz + 1); + memcpy(p, ptr, sz); + p[(int)sz] = '\0'; + return(p); +} char * mandoc_strdup(const char *ptr) @@ -532,7 +565,10 @@ a2time(time_t *t, const char *fmt, const char *p) memset(&tm, 0, sizeof(struct tm)); + pp = NULL; +#ifdef HAVE_STRPTIME pp = strptime(p, fmt, &tm); +#endif if (NULL != pp && '\0' == *pp) { *t = mktime(&tm); return(1); @@ -544,12 +580,12 @@ a2time(time_t *t, const char *fmt, const char *p) static char * time2a(time_t t) { - struct tm tm; + struct tm *tm; char *buf, *p; size_t ssz; int isz; - localtime_r(&t, &tm); + tm = localtime(&t); /* * Reserve space: @@ -559,15 +595,15 @@ time2a(time_t t) */ p = buf = mandoc_malloc(10 + 4 + 4 + 1); - if (0 == (ssz = strftime(p, 10 + 1, "%B ", &tm))) + if (0 == (ssz = strftime(p, 10 + 1, "%B ", tm))) goto fail; p += (int)ssz; - if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm.tm_mday))) + if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday))) goto fail; p += isz; - if (0 == strftime(p, 4 + 1, "%Y", &tm)) + if (0 == strftime(p, 4 + 1, "%Y", tm)) goto fail; return(buf); @@ -587,9 +623,10 @@ mandoc_normdate(struct mparse *parse, char *in, int ln, int pos) mandoc_msg(MANDOCERR_NODATE, parse, ln, pos, NULL); time(&t); } + else if (a2time(&t, "%Y-%m-%d", in)) + t = 0; else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) && - !a2time(&t, "%b %d, %Y", in) && - !a2time(&t, "%Y-%m-%d", in)) { + !a2time(&t, "%b %d, %Y", in)) { mandoc_msg(MANDOCERR_BADDATE, parse, ln, pos, NULL); t = 0; } @@ -640,33 +677,6 @@ mandoc_eos(const char *p, size_t sz, int enclosed) return(found && !enclosed); } -int -mandoc_hyph(const char *start, const char *c) -{ - - /* - * Choose whether to break at a hyphenated character. We only - * do this if it's free-standing within a word. - */ - - /* Skip first/last character of buffer. */ - if (c == start || '\0' == *(c + 1)) - return(0); - /* Skip first/last character of word. */ - if ('\t' == *(c + 1) || '\t' == *(c - 1)) - return(0); - if (' ' == *(c + 1) || ' ' == *(c - 1)) - return(0); - /* Skip double invocations. */ - if ('-' == *(c + 1) || '-' == *(c - 1)) - return(0); - /* Skip escapes. */ - if ('\\' == *(c - 1)) - return(0); - - return(1); -} - /* * Find out whether a line is a macro line or not. If it is, adjust the * current position and return one; if it isn't, return zero and don't @@ -698,7 +708,7 @@ mandoc_getcontrol(const char *cp, int *ppos) * If the string is invalid, or is less than 0, return -1. */ int -mandoc_strntou(const char *p, size_t sz, int base) +mandoc_strntoi(const char *p, size_t sz, int base) { char buf[32]; char *ep; @@ -716,11 +726,10 @@ mandoc_strntou(const char *p, size_t sz, int base) if (buf[0] == '\0' || *ep != '\0') return(-1); - if ((errno == ERANGE && - (v == LONG_MAX || v == LONG_MIN)) || - (v > INT_MAX || v < 0)) - return(-1); + if (v > INT_MAX) + v = INT_MAX; + if (v < INT_MIN) + v = INT_MIN; return((int)v); } - diff --git a/contrib/mdocml/mandoc.h b/contrib/mdocml/mandoc.h index 20ab87a7b9..a37effc5f5 100644 --- a/contrib/mdocml/mandoc.h +++ b/contrib/mdocml/mandoc.h @@ -1,4 +1,4 @@ -/* $Id: mandoc.h,v 1.77 2011/05/24 21:31:23 kristaps Exp $ */ +/* $Id: mandoc.h,v 1.99 2012/02/16 20:51:31 joerg Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons * @@ -104,8 +104,18 @@ enum mandocerr { MANDOCERR_BADESCAPE, /* unknown escape sequence */ MANDOCERR_BADQUOTE, /* unterminated quoted string */ + /* related to equations */ + MANDOCERR_EQNQUOTE, /* unexpected literal in equation */ + MANDOCERR_ERROR, /* ===== start of errors ===== */ + /* related to equations */ + MANDOCERR_EQNNSCOPE, /* unexpected equation scope closure*/ + MANDOCERR_EQNSCOPE, /* equation scope open on exit */ + MANDOCERR_EQNBADSCOPE, /* overlapping equation scopes */ + MANDOCERR_EQNEOF, /* unexpected end of equation */ + MANDOCERR_EQNSYNT, /* equation syntax error */ + /* related to tables */ MANDOCERR_TBL, /* bad table syntax */ MANDOCERR_TBLOPT, /* bad table option */ @@ -140,7 +150,6 @@ enum mandocerr { MANDOCERR_NOTMANUAL, /* manual isn't really a manual */ MANDOCERR_COLUMNS, /* column syntax is inconsistent */ MANDOCERR_BADDISP, /* NOT IMPLEMENTED: .Bd -file */ - MANDOCERR_SYNTLINESCOPE, /* line scope broken, syntax violated */ MANDOCERR_SYNTARGVCOUNT, /* argument count wrong, violates syntax */ MANDOCERR_SYNTCHILD, /* child violates parent syntax */ MANDOCERR_SYNTARGCOUNT, /* argument count wrong, violates syntax */ @@ -270,10 +279,88 @@ struct tbl_span { struct tbl_span *next; }; +enum eqn_boxt { + EQN_ROOT, /* root of parse tree */ + EQN_TEXT, /* text (number, variable, whatever) */ + EQN_SUBEXPR, /* nested `eqn' subexpression */ + EQN_LIST, /* subexpressions list */ + EQN_MATRIX /* matrix subexpression */ +}; + +enum eqn_markt { + EQNMARK_NONE = 0, + EQNMARK_DOT, + EQNMARK_DOTDOT, + EQNMARK_HAT, + EQNMARK_TILDE, + EQNMARK_VEC, + EQNMARK_DYAD, + EQNMARK_BAR, + EQNMARK_UNDER, + EQNMARK__MAX +}; + +enum eqn_fontt { + EQNFONT_NONE = 0, + EQNFONT_ROMAN, + EQNFONT_BOLD, + EQNFONT_FAT, + EQNFONT_ITALIC, + EQNFONT__MAX +}; + +enum eqn_post { + EQNPOS_NONE = 0, + EQNPOS_OVER, + EQNPOS_SUP, + EQNPOS_SUB, + EQNPOS_TO, + EQNPOS_FROM, + EQNPOS__MAX +}; + +enum eqn_pilet { + EQNPILE_NONE = 0, + EQNPILE_PILE, + EQNPILE_CPILE, + EQNPILE_RPILE, + EQNPILE_LPILE, + EQNPILE_COL, + EQNPILE_CCOL, + EQNPILE_RCOL, + EQNPILE_LCOL, + EQNPILE__MAX +}; + + /* + * A "box" is a parsed mathematical expression as defined by the eqn.7 + * grammar. + */ +struct eqn_box { + int size; /* font size of expression */ +#define EQN_DEFSIZE INT_MIN + enum eqn_boxt type; /* type of node */ + struct eqn_box *first; /* first child node */ + struct eqn_box *last; /* last child node */ + struct eqn_box *next; /* node sibling */ + struct eqn_box *parent; /* node sibling */ + char *text; /* text (or NULL) */ + char *left; + char *right; + enum eqn_post pos; /* position of next box */ + enum eqn_markt mark; /* a mark about the box */ + enum eqn_fontt font; /* font of box */ + enum eqn_pilet pile; /* equation piling */ +}; + +/* + * An equation consists of a tree of expressions starting at a given + * line and position. + */ struct eqn { - size_t sz; - char *data; - int line; /* invocation line */ + char *name; /* identifier (or NULL) */ + struct eqn_box *root; /* root mathematical expression */ + int ln; /* invocation line */ int pos; /* invocation position */ }; @@ -312,28 +399,33 @@ struct man; __BEGIN_DECLS -void mparse_free(struct mparse *); -void mparse_reset(struct mparse *); -struct mparse *mparse_alloc(enum mparset, - enum mandoclevel, mandocmsg, void *); -enum mandoclevel mparse_readfd(struct mparse *, int, const char *); -void mparse_result(struct mparse *, struct mdoc **, struct man **); -const char *mparse_strerror(enum mandocerr); -const char *mparse_strlevel(enum mandoclevel); - void *mandoc_calloc(size_t, size_t); +enum mandoc_esc mandoc_escape(const char **, const char **, int *); void *mandoc_malloc(size_t); void *mandoc_realloc(void *, size_t); - -enum mandoc_esc mandoc_escape(const char **, const char **, int *); - +char *mandoc_strdup(const char *); +char *mandoc_strndup(const char *, size_t); struct mchars *mchars_alloc(void); +void mchars_free(struct mchars *); char mchars_num2char(const char *, size_t); int mchars_num2uc(const char *, size_t); -const char *mchars_spec2str(struct mchars *, const char *, size_t, size_t *); -int mchars_spec2cp(struct mchars *, const char *, size_t); -void mchars_free(struct mchars *); - +int mchars_spec2cp(const struct mchars *, + const char *, size_t); +const char *mchars_spec2str(const struct mchars *, + const char *, size_t, size_t *); +struct mparse *mparse_alloc(enum mparset, + enum mandoclevel, mandocmsg, void *); +void mparse_free(struct mparse *); +void mparse_keep(struct mparse *); +enum mandoclevel mparse_readfd(struct mparse *, int, const char *); +enum mandoclevel mparse_readmem(struct mparse *, const void *, size_t, + const char *); +void mparse_reset(struct mparse *); +void mparse_result(struct mparse *, + struct mdoc **, struct man **); +const char *mparse_getkeep(const struct mparse *); +const char *mparse_strerror(enum mandocerr); +const char *mparse_strlevel(enum mandoclevel); __END_DECLS diff --git a/contrib/mdocml/mandoc_char.7 b/contrib/mdocml/mandoc_char.7 index d0c5dd7f80..acc1b6100d 100644 --- a/contrib/mdocml/mandoc_char.7 +++ b/contrib/mdocml/mandoc_char.7 @@ -1,6 +1,8 @@ -.\" $Id: mandoc_char.7,v 1.45 2011/05/15 15:30:33 kristaps Exp $ +.\" $Id: mandoc_char.7,v 1.51 2011/11/23 10:09:30 kristaps Exp $ .\" -.\" Copyright (c) 2009 Kristaps Dzonsons +.\" Copyright (c) 2003 Jason McIntyre +.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons +.\" Copyright (c) 2011 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 @@ -14,26 +16,168 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 15 2011 $ +.Dd $Mdocdate: November 23 2011 $ .Dt MANDOC_CHAR 7 .Os .Sh NAME .Nm mandoc_char .Nd mandoc special characters .Sh DESCRIPTION -This page documents the special characters and predefined strings accepted by +This page documents the +.Xr roff 7 +escape sequences accepted by .Xr mandoc 1 -to format +to represent special characters in .Xr mdoc 7 and .Xr man 7 documents. .Pp -Both -.Xr mdoc 7 -and -.Xr man 7 -encode special characters with +The rendering depends on the +.Xr mandoc 1 +output mode; in ASCII output, most characters are completely +unintelligible. +For that reason, using any of the special characters documented here, +except those discussed in the +.Sx DESCRIPTION , +is strongly discouraged; they are supported merely for backwards +compatibility with existing documents. +.Pp +In particular, in English manual pages, do not use special-character +escape sequences to represent national language characters in author +names; instead, provide ASCII transcriptions of the names. +.Ss Dashes and Hyphens +In typography there are different types of dashes of various width: +the hyphen (-), +the minus sign (\-), +the en-dash (\(en), +and the em-dash (\(em). +.Pp +Hyphens are used for adjectives; +to separate the two parts of a compound word; +or to separate a word across two successive lines of text. +The hyphen does not need to be escaped: +.Bd -unfilled -offset indent +blue-eyed +lorry-driver +.Ed +.Pp +The mathematical minus sign is used for negative numbers or subtraction. +It should be written as +.Sq \e- : +.Bd -unfilled -offset indent +a = 3 \e- 1; +b = \e-2; +.Ed +.Pp +The en-dash is used to separate the two elements of a range, +or can be used the same way as an em-dash. +It should be written as +.Sq \e(en : +.Bd -unfilled -offset indent +pp. 95\e(en97. +Go away \e(en or else! +.Ed +.Pp +The em-dash can be used to show an interruption +or can be used the same way as colons, semi-colons, or parentheses. +It should be written as +.Sq \e(em : +.Bd -unfilled -offset indent +Three things \e(em apples, oranges, and bananas. +This is not that \e(em rather, this is that. +.Ed +.Pp +Note: +hyphens, minus signs, and en-dashes look identical under normal ASCII output. +Other formats, such as PostScript, render them correctly, +with differing widths. +.Ss Spaces +To separate words in normal text, for indenting and alignment +in literal context, and when none of the following special cases apply, +just use the normal space character +.Pq Sq \ . +.Pp +When filling text, lines may be broken between words, i.e. at space +characters. +To prevent a line break between two particular words, +use the non-breaking space escape sequence +.Pq Sq \e~ +instead of the normal space character. +For example, the input string +.Dq number\e~1 +will be kept together as +.Dq number\~1 +on the same output line. +.Pp +On request and macro lines, the normal space character serves as an +argument delimiter. +To include whitespace into arguments, quoting is usually the best choice. +In some cases, using either the non-breaking +.Pq Sq \e~ +or the breaking +.Pq Sq \e\ \& +space escape sequence may be preferable. +To escape macro names and to protect whitespace at the end +of input lines, the zero-width space +.Pq Sq \e& +is often useful. +For example, in +.Xr mdoc 7 , +a normal space character can be displayed in single quotes in either +of the following ways: +.Pp +.Dl .Sq \(dq \(dq +.Dl .Sq \e \e& +.Ss Quotes +On request and macro lines, the double-quote character +.Pq Sq \(dq +is handled specially to allow quoting. +One way to prevent this special handling is by using the +.Sq \e(dq +escape sequence. +.Pp +Note that on text lines, literal double-quote characters can be used +verbatim. +All other quote-like characters can be used verbatim as well, +even on request and macro lines. +.Ss Periods +The period +.Pq Sq \&. +is handled specially at the beginning of an input line, +where it introduces a +.Xr roff 7 +request or a macro, and when appearing alone as a macro argument in +.Xr mdoc 7 . +In such situations, prepend a zero-width space +.Pq Sq \e&. +to make it behave like normal text. +.Pp +Do not use the +.Sq \e. +escape sequence. +It does not prevent special handling of the period. +.Ss Backslashes +To include a literal backslash +.Pq Sq \e +into the output, use the +.Pq Sq \ee +escape sequence. +.Pp +Note that doubling it +.Pq Sq \e\e +is not the right way to output a backslash. +Because +.Xr mandoc 1 +does not implement full +.Xr roff 7 +functionality, it may work with +.Xr mandoc 1 , +but it may have weird effects on complete +.Xr roff 7 +implementations. +.Sh SPECIAL CHARACTERS +Special characters are encoded as .Sq \eX .Pq for a one-character escape , .Sq \e(XX @@ -41,40 +185,14 @@ encode special characters with and .Sq \e[N] .Pq N-character . -One may generalise -.Sq \e(XX -as -.Sq \e[XX] -and -.Sq \eX -as -.Sq \e[X] . -Predefined strings are functionally similar to special characters, using -.Sq \e*X -.Pq for a one-character escape , -.Sq \e*(XX -.Pq two-character , -and -.Sq \e*[N] -.Pq N-character . -One may generalise -.Sq \e*(XX -as -.Sq \e*[XX] -and -.Sq \e*X -as -.Sq \e*[X] . -.Pp -Note that each output mode will have a different rendering of the -characters. -It's guaranteed that each input symbol will correspond to a -(more or less) meaningful output rendering, regardless the mode. -.Sh SPECIAL CHARACTERS -These are the preferred input symbols for producing special characters. +For details, see the +.Em Special Characters +subsection of the +.Xr roff 7 +manual. .Pp Spacing: -.Bl -column -compact -offset indent "Input" "Description" +.Bl -column "Input" "Description" -offset indent -compact .It Em Input Ta Em Description .It \e~ Ta non-breaking, non-collapsing space .It \e Ta breaking, non-collapsing n-width space @@ -87,7 +205,7 @@ Spacing: .El .Pp Lines: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(ba Ta \(ba Ta bar .It \e(br Ta \(br Ta box rule @@ -99,7 +217,7 @@ Lines: .El .Pp Text markers: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(ci Ta \(ci Ta circle .It \e(bu Ta \(bu Ta bullet @@ -118,7 +236,7 @@ Text markers: .El .Pp Legal symbols: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(co Ta \(co Ta copyright .It \e(rg Ta \(rg Ta registered @@ -126,7 +244,7 @@ Legal symbols: .El .Pp Punctuation: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(em Ta \(em Ta em-dash .It \e(en Ta \(en Ta en-dash @@ -138,7 +256,7 @@ Punctuation: .El .Pp Quotes: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(Bq Ta \(Bq Ta right low double-quote .It \e(bq Ta \(bq Ta right low single-quote @@ -155,7 +273,7 @@ Quotes: .El .Pp Brackets: -.Bl -column -compact -offset indent "xxbracketrightbpx" Rendered Description +.Bl -column "xxbracketrightbpx" Rendered Description -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(lB Ta \(lB Ta left bracket .It \e(rB Ta \(rB Ta right bracket @@ -194,7 +312,7 @@ Brackets: .El .Pp Arrows: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(<- Ta \(<- Ta left arrow .It \e(-> Ta \(-> Ta right arrow @@ -211,7 +329,7 @@ Arrows: .El .Pp Logical: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(AN Ta \(AN Ta logical and .It \e(OR Ta \(OR Ta logical or @@ -226,7 +344,7 @@ Logical: .El .Pp Mathematical: -.Bl -column -compact -offset indent "xxcoproductxx" "Rendered" "Description" +.Bl -column "xxcoproductxx" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(pl Ta \(pl Ta plus .It \e(mi Ta \(mi Ta minus @@ -288,10 +406,13 @@ Mathematical: .It \e(Re Ta \(Re Ta real .It \e(pd Ta \(pd Ta partial differential .It \e(-h Ta \(-h Ta Planck constant over 2\(*p +.It \e[12] Ta \[12] Ta one-half +.It \e[14] Ta \[14] Ta one-fourth +.It \e[34] Ta \[34] Ta three-fourths .El .Pp Ligatures: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(ff Ta \(ff Ta ff ligature .It \e(fi Ta \(fi Ta fi ligature @@ -308,7 +429,7 @@ Ligatures: .El .Pp Accents: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(a" Ta \(a" Ta Hungarian umlaut .It \e(a- Ta \(a- Ta macron @@ -330,7 +451,7 @@ Accents: .El .Pp Accented letters: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e('A Ta \('A Ta acute A .It \e('E Ta \('E Ta acute E @@ -390,7 +511,7 @@ Accented letters: .El .Pp Special letters: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(-D Ta \(-D Ta Eth .It \e(Sd Ta \(Sd Ta eth @@ -401,7 +522,7 @@ Special letters: .El .Pp Currency: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(Do Ta \(Do Ta dollar .It \e(ct Ta \(ct Ta cent @@ -414,7 +535,7 @@ Currency: .El .Pp Units: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(de Ta \(de Ta degree .It \e(%0 Ta \(%0 Ta per-thousand @@ -424,7 +545,7 @@ Units: .El .Pp Greek letters: -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +.Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(*A Ta \(*A Ta Alpha .It \e(*B Ta \(*B Ta Beta @@ -489,7 +610,20 @@ for use, as they differ across implementations. Manuals using these predefined strings are almost certainly not portable. .Pp -.Bl -column -compact -offset indent "Input" "Rendered" "Description" +Their syntax is similar to special characters, using +.Sq \e*X +.Pq for a one-character escape , +.Sq \e*(XX +.Pq two-character , +and +.Sq \e*[N] +.Pq N-character . +For details, see the +.Em Predefined Strings +subsection of the +.Xr roff 7 +manual. +.Bl -column "Input" "Rendered" "Description" -offset indent .It Em Input Ta Em Rendered Ta Em Description .It \e*(Ba Ta \*(Ba Ta vertical bar .It \e*(Ne Ta \*(Ne Ta not equal @@ -586,12 +720,16 @@ from mandoc either because they are poorly documented or they have no known representation. .El .Sh SEE ALSO -.Xr mandoc 1 +.Xr mandoc 1 , +.Xr man 7 , +.Xr mdoc 7 , +.Xr roff 7 .Sh AUTHORS The .Nm manual page was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . .Sh CAVEATS The .Sq \e*(Ba diff --git a/contrib/mdocml/mandocdb.8 b/contrib/mdocml/mandocdb.8 new file mode 100644 index 0000000000..cb48359b34 --- /dev/null +++ b/contrib/mdocml/mandocdb.8 @@ -0,0 +1,293 @@ +.\" $Id: mandocdb.8,v 1.17 2011/12/25 21:00:23 schwarze Exp $ +.\" +.\" Copyright (c) 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: December 25 2011 $ +.Dt MANDOCDB 8 +.Os +.Sh NAME +.Nm mandocdb +.Nd index UNIX manuals +.Sh SYNOPSIS +.Nm +.Op Fl avW +.Op Fl C Ar file +.Nm +.Op Fl avW +.Ar dir ... +.Nm +.Op Fl vW +.Fl d Ar dir +.Op Ar +.Nm +.Op Fl vW +.Fl u Ar dir +.Op Ar +.Nm +.Fl t Ar +.Sh DESCRIPTION +The +.Nm +utility extracts keywords from +.Ux +manuals and indexes them in a +.Sx Keyword Database +and +.Sx Index Database +for fast retrieval by +.Xr apropos 1 , +.Xr whatis 1 , +and +.Xr man 1 Ns 's +.Fl k +option. +.Pp +By default, +.Nm +creates databases in each +.Ar dir +using the files +.Sm off +.Sy man Ar section Li / +.Op Ar arch Li / +.Ar title . section +.Sm on +and +.Sm off +.Sy cat Ar section Li / +.Op Ar arch Li / +.Ar title . Sy 0 +.Sm on +in that directory; +existing databases are truncated. +If +.Ar dir +is not provided, +.Nm +uses the default paths stipulated by +.Xr man 1 . +.Pp +The arguments are as follows: +.Bl -tag -width "-C file" +.It Fl a +Use all directories and files found below +.Ar dir ... . +.It Fl C Ar file +Specify an alternative configuration +.Ar file +in +.Xr man.conf 5 +format. +.It Fl d Ar dir +Merge (remove and re-add) +.Ar +to the database in +.Ar dir +without truncating it. +.It Fl t Ar +Check the given +.Ar files +for potential problems. +No databases are modified. +Implies +.Fl a +and +.Fl W . +All diagnostic messages are printed to the standard output; +the standard error output is not used. +.It Fl u Ar dir +Remove +.Ar +from the database in +.Ar dir +without truncating it. +.It Fl v +Display all files added or removed to the index. +.It Fl W +Print warnings about potential problems with manual pages +to the standard error output. +.El +.Pp +If fatal parse errors are encountered while parsing, the offending file +is printed to stderr, omitted from the index, and the parse continues +with the next input file. +.Ss Index Database +The index database, +.Pa whatis.index , +is a +.Xr recno 3 +database with record values consisting of +.Pp +.Bl -enum -compact +.It +the character +.Cm d , +.Cm a , +or +.Cm c +to indicate the file type +.Po +.Xr mdoc 7 , +.Xr man 7 , +and post-formatted, respectively +.Pc , +.It +the filename relative to the databases' path, +.It +the manual section, +.It +the manual title, +.It +the architecture +.Pq often empty , +.It +and the description. +.El +.Pp +Each of the above is NUL-terminated. +.Pp +If the record value is zero-length, it is unassigned. +.Ss Keyword Database +The keyword database, +.Pa whatis.db , +is a +.Xr btree 3 +database of NUL-terminated keywords (record length is non-zero string +length plus one) mapping to a 16-byte binary field consisting of the +64-bit keyword type and the 64-bit +.Sx Index Database +record number, both in network-byte order. +.Pp +The type bit-mask consists of the following +values mapping into +.Xr mdoc 7 +macro identifiers: +.Pp +.Bl -column "x0x0000000000000001ULLx" "xLix" -offset indent -compact +.It Li 0x0000000000000001ULL Ta \&An +.It Li 0x0000000000000002ULL Ta \&Ar +.It Li 0x0000000000000004ULL Ta \&At +.It Li 0x0000000000000008ULL Ta \&Bsx +.It Li 0x0000000000000010ULL Ta \&Bx +.It Li 0x0000000000000020ULL Ta \&Cd +.It Li 0x0000000000000040ULL Ta \&Cm +.It Li 0x0000000000000080ULL Ta \&Dv +.It Li 0x0000000000000100ULL Ta \&Dx +.It Li 0x0000000000000200ULL Ta \&Em +.It Li 0x0000000000000400ULL Ta \&Er +.It Li 0x0000000000000800ULL Ta \&Ev +.It Li 0x0000000000001000ULL Ta \&Fa +.It Li 0x0000000000002000ULL Ta \&Fl +.It Li 0x0000000000004000ULL Ta \&Fn +.It Li 0x0000000000008000ULL Ta \&Ft +.It Li 0x0000000000010000ULL Ta \&Fx +.It Li 0x0000000000020000ULL Ta \&Ic +.It Li 0x0000000000040000ULL Ta \&In +.It Li 0x0000000000080000ULL Ta \&Lb +.It Li 0x0000000000100000ULL Ta \&Li +.It Li 0x0000000000200000ULL Ta \&Lk +.It Li 0x0000000000400000ULL Ta \&Ms +.It Li 0x0000000000800000ULL Ta \&Mt +.It Li 0x0000000001000000ULL Ta \&Nd +.It Li 0x0000000002000000ULL Ta \&Nm +.It Li 0x0000000004000000ULL Ta \&Nx +.It Li 0x0000000008000000ULL Ta \&Ox +.It Li 0x0000000010000000ULL Ta \&Pa +.It Li 0x0000000020000000ULL Ta \&Rs +.It Li 0x0000000040000000ULL Ta \&Sh +.It Li 0x0000000080000000ULL Ta \&Ss +.It Li 0x0000000100000000ULL Ta \&St +.It Li 0x0000000200000000ULL Ta \&Sy +.It Li 0x0000000400000000ULL Ta \&Tn +.It Li 0x0000000800000000ULL Ta \&Va +.It Li 0x0000001000000000ULL Ta \&Vt +.It Li 0x0000002000000000ULL Ta \&Xr +.El +.Sh IMPLEMENTATION NOTES +The time to construct a new database pair grows linearly with the +number of keywords in the input files. +However, removing or updating entries with +.Fl u +or +.Fl d , +respectively, grows as a multiple of the index length and input size. +.Sh FILES +.Bl -tag -width Ds +.It Pa whatis.db +A +.Xr btree 3 +keyword database mapping keywords to a type and file reference in +.Pa whatis.index . +.It Pa whatis.index +A +.Xr recno 3 +database of indexed file-names. +.It Pa /etc/man.conf +The default +.Xr man 1 +configuration file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Pp +.Bl -tag -width Ds -compact +.It 0 +No errors occurred. +.It 5 +Invalid command line arguments were specified. +No input files have been read. +.It 6 +An operating system error occurred, for example memory exhaustion or an +error accessing input files. +Such errors cause +.Nm +to exit at once, possibly in the middle of parsing or formatting a file. +The output databases are corrupt and should be removed. +.El +.Sh DIAGNOSTICS +If the following errors occur, the +.Nm +databases should be rebuilt. +.Bl -diag +.It "%s: Corrupt database" +The keyword database file indicated by +.Pa %s +is unreadable. +.It "%s: Corrupt index" +The index database file indicated by +.Pa %s +is unreadable. +.It "%s: Path too long" +The file +.Pa %s +is too long. +This usually indicates database corruption or invalid command-line +arguments. +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr man 1 , +.Xr whatis 1 , +.Xr btree 3 , +.Xr recno 3 , +.Xr man.conf 5 +.Sh AUTHORS +The +.Nm +utility was written by +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/mandocdb.c b/contrib/mdocml/mandocdb.c new file mode 100644 index 0000000000..e621c1d2a2 --- /dev/null +++ b/contrib/mdocml/mandocdb.c @@ -0,0 +1,1909 @@ +/* $Id: mandocdb.c,v 1.46 2012/03/23 06:52:17 kristaps Exp $ */ +/* + * Copyright (c) 2011 Kristaps Dzonsons + * Copyright (c) 2011 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. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) +# include +# include +#elif defined(__APPLE__) +# include +# include +#else +# include +#endif + +#include "man.h" +#include "mdoc.h" +#include "mandoc.h" +#include "mandocdb.h" +#include "manpath.h" + +#define MANDOC_BUFSZ BUFSIZ +#define MANDOC_SLOP 1024 + +#define MANDOC_SRC 0x1 +#define MANDOC_FORM 0x2 + +#define WARNING(_f, _b, _fmt, _args...) \ + do if (warnings) { \ + fprintf(stderr, "%s: ", (_b)); \ + fprintf(stderr, (_fmt), ##_args); \ + if ('\0' != *(_f)) \ + fprintf(stderr, ": %s", (_f)); \ + fprintf(stderr, "\n"); \ + } while (/* CONSTCOND */ 0) + +/* Access to the mandoc database on disk. */ + +struct mdb { + char idxn[MAXPATHLEN]; /* index db filename */ + char dbn[MAXPATHLEN]; /* keyword db filename */ + DB *idx; /* index recno database */ + DB *db; /* keyword btree database */ +}; + +/* Stack of temporarily unused index records. */ + +struct recs { + recno_t *stack; /* pointer to a malloc'ed array */ + size_t size; /* number of allocated slots */ + size_t cur; /* current number of empty records */ + recno_t last; /* last record number in the index */ +}; + +/* Tiny list for files. No need to bring in QUEUE. */ + +struct of { + char *fname; /* heap-allocated */ + char *sec; + char *arch; + char *title; + int src_form; + struct of *next; /* NULL for last one */ + struct of *first; /* first in list */ +}; + +/* Buffer for storing growable data. */ + +struct buf { + char *cp; + size_t len; /* current length */ + size_t size; /* total buffer size */ +}; + +/* Operation we're going to perform. */ + +enum op { + OP_DEFAULT = 0, /* new dbs from dir list or default config */ + OP_CONFFILE, /* new databases from custom config file */ + OP_UPDATE, /* delete/add entries in existing database */ + OP_DELETE, /* delete entries from existing database */ + OP_TEST /* change no databases, report potential problems */ +}; + +#define MAN_ARGS DB *hash, \ + struct buf *buf, \ + struct buf *dbuf, \ + const struct man_node *n +#define MDOC_ARGS DB *hash, \ + struct buf *buf, \ + struct buf *dbuf, \ + const struct mdoc_node *n, \ + const struct mdoc_meta *m + +static void buf_appendmdoc(struct buf *, + const struct mdoc_node *, int); +static void buf_append(struct buf *, const char *); +static void buf_appendb(struct buf *, + const void *, size_t); +static void dbt_put(DB *, const char *, DBT *, DBT *); +static void hash_put(DB *, const struct buf *, uint64_t); +static void hash_reset(DB **); +static void index_merge(const struct of *, struct mparse *, + struct buf *, struct buf *, DB *, + struct mdb *, struct recs *, + const char *); +static void index_prune(const struct of *, struct mdb *, + struct recs *, const char *); +static void ofile_argbuild(int, char *[], + struct of **, const char *); +static void ofile_dirbuild(const char *, const char *, + const char *, int, struct of **, char *); +static void ofile_free(struct of *); +static void pformatted(DB *, struct buf *, struct buf *, + const struct of *, const char *); +static int pman_node(MAN_ARGS); +static void pmdoc_node(MDOC_ARGS); +static int pmdoc_head(MDOC_ARGS); +static int pmdoc_body(MDOC_ARGS); +static int pmdoc_Fd(MDOC_ARGS); +static int pmdoc_In(MDOC_ARGS); +static int pmdoc_Fn(MDOC_ARGS); +static int pmdoc_Nd(MDOC_ARGS); +static int pmdoc_Nm(MDOC_ARGS); +static int pmdoc_Sh(MDOC_ARGS); +static int pmdoc_St(MDOC_ARGS); +static int pmdoc_Xr(MDOC_ARGS); + +#define MDOCF_CHILD 0x01 /* Automatically index child nodes. */ + +struct mdoc_handler { + int (*fp)(MDOC_ARGS); /* Optional handler. */ + uint64_t mask; /* Set unless handler returns 0. */ + int flags; /* For use by pmdoc_node. */ +}; + +static const struct mdoc_handler mdocs[MDOC_MAX] = { + { NULL, 0, 0 }, /* Ap */ + { NULL, 0, 0 }, /* Dd */ + { NULL, 0, 0 }, /* Dt */ + { NULL, 0, 0 }, /* Os */ + { pmdoc_Sh, TYPE_Sh, MDOCF_CHILD }, /* Sh */ + { pmdoc_head, TYPE_Ss, MDOCF_CHILD }, /* Ss */ + { NULL, 0, 0 }, /* Pp */ + { NULL, 0, 0 }, /* D1 */ + { NULL, 0, 0 }, /* Dl */ + { NULL, 0, 0 }, /* Bd */ + { NULL, 0, 0 }, /* Ed */ + { NULL, 0, 0 }, /* Bl */ + { NULL, 0, 0 }, /* El */ + { NULL, 0, 0 }, /* It */ + { NULL, 0, 0 }, /* Ad */ + { NULL, TYPE_An, MDOCF_CHILD }, /* An */ + { NULL, TYPE_Ar, MDOCF_CHILD }, /* Ar */ + { NULL, TYPE_Cd, MDOCF_CHILD }, /* Cd */ + { NULL, TYPE_Cm, MDOCF_CHILD }, /* Cm */ + { NULL, TYPE_Dv, MDOCF_CHILD }, /* Dv */ + { NULL, TYPE_Er, MDOCF_CHILD }, /* Er */ + { NULL, TYPE_Ev, MDOCF_CHILD }, /* Ev */ + { NULL, 0, 0 }, /* Ex */ + { NULL, TYPE_Fa, MDOCF_CHILD }, /* Fa */ + { pmdoc_Fd, TYPE_In, 0 }, /* Fd */ + { NULL, TYPE_Fl, MDOCF_CHILD }, /* Fl */ + { pmdoc_Fn, 0, 0 }, /* Fn */ + { NULL, TYPE_Ft, MDOCF_CHILD }, /* Ft */ + { NULL, TYPE_Ic, MDOCF_CHILD }, /* Ic */ + { pmdoc_In, TYPE_In, 0 }, /* In */ + { NULL, TYPE_Li, MDOCF_CHILD }, /* Li */ + { pmdoc_Nd, TYPE_Nd, MDOCF_CHILD }, /* Nd */ + { pmdoc_Nm, TYPE_Nm, MDOCF_CHILD }, /* Nm */ + { NULL, 0, 0 }, /* Op */ + { NULL, 0, 0 }, /* Ot */ + { NULL, TYPE_Pa, MDOCF_CHILD }, /* Pa */ + { NULL, 0, 0 }, /* Rv */ + { pmdoc_St, TYPE_St, 0 }, /* St */ + { NULL, TYPE_Va, MDOCF_CHILD }, /* Va */ + { pmdoc_body, TYPE_Va, MDOCF_CHILD }, /* Vt */ + { pmdoc_Xr, TYPE_Xr, 0 }, /* Xr */ + { NULL, 0, 0 }, /* %A */ + { NULL, 0, 0 }, /* %B */ + { NULL, 0, 0 }, /* %D */ + { NULL, 0, 0 }, /* %I */ + { NULL, 0, 0 }, /* %J */ + { NULL, 0, 0 }, /* %N */ + { NULL, 0, 0 }, /* %O */ + { NULL, 0, 0 }, /* %P */ + { NULL, 0, 0 }, /* %R */ + { NULL, 0, 0 }, /* %T */ + { NULL, 0, 0 }, /* %V */ + { NULL, 0, 0 }, /* Ac */ + { NULL, 0, 0 }, /* Ao */ + { NULL, 0, 0 }, /* Aq */ + { NULL, TYPE_At, MDOCF_CHILD }, /* At */ + { NULL, 0, 0 }, /* Bc */ + { NULL, 0, 0 }, /* Bf */ + { NULL, 0, 0 }, /* Bo */ + { NULL, 0, 0 }, /* Bq */ + { NULL, TYPE_Bsx, MDOCF_CHILD }, /* Bsx */ + { NULL, TYPE_Bx, MDOCF_CHILD }, /* Bx */ + { NULL, 0, 0 }, /* Db */ + { NULL, 0, 0 }, /* Dc */ + { NULL, 0, 0 }, /* Do */ + { NULL, 0, 0 }, /* Dq */ + { NULL, 0, 0 }, /* Ec */ + { NULL, 0, 0 }, /* Ef */ + { NULL, TYPE_Em, MDOCF_CHILD }, /* Em */ + { NULL, 0, 0 }, /* Eo */ + { NULL, TYPE_Fx, MDOCF_CHILD }, /* Fx */ + { NULL, TYPE_Ms, MDOCF_CHILD }, /* Ms */ + { NULL, 0, 0 }, /* No */ + { NULL, 0, 0 }, /* Ns */ + { NULL, TYPE_Nx, MDOCF_CHILD }, /* Nx */ + { NULL, TYPE_Ox, MDOCF_CHILD }, /* Ox */ + { NULL, 0, 0 }, /* Pc */ + { NULL, 0, 0 }, /* Pf */ + { NULL, 0, 0 }, /* Po */ + { NULL, 0, 0 }, /* Pq */ + { NULL, 0, 0 }, /* Qc */ + { NULL, 0, 0 }, /* Ql */ + { NULL, 0, 0 }, /* Qo */ + { NULL, 0, 0 }, /* Qq */ + { NULL, 0, 0 }, /* Re */ + { NULL, 0, 0 }, /* Rs */ + { NULL, 0, 0 }, /* Sc */ + { NULL, 0, 0 }, /* So */ + { NULL, 0, 0 }, /* Sq */ + { NULL, 0, 0 }, /* Sm */ + { NULL, 0, 0 }, /* Sx */ + { NULL, TYPE_Sy, MDOCF_CHILD }, /* Sy */ + { NULL, TYPE_Tn, MDOCF_CHILD }, /* Tn */ + { NULL, 0, 0 }, /* Ux */ + { NULL, 0, 0 }, /* Xc */ + { NULL, 0, 0 }, /* Xo */ + { pmdoc_head, TYPE_Fn, 0 }, /* Fo */ + { NULL, 0, 0 }, /* Fc */ + { NULL, 0, 0 }, /* Oo */ + { NULL, 0, 0 }, /* Oc */ + { NULL, 0, 0 }, /* Bk */ + { NULL, 0, 0 }, /* Ek */ + { NULL, 0, 0 }, /* Bt */ + { NULL, 0, 0 }, /* Hf */ + { NULL, 0, 0 }, /* Fr */ + { NULL, 0, 0 }, /* Ud */ + { NULL, TYPE_Lb, MDOCF_CHILD }, /* Lb */ + { NULL, 0, 0 }, /* Lp */ + { NULL, TYPE_Lk, MDOCF_CHILD }, /* Lk */ + { NULL, TYPE_Mt, MDOCF_CHILD }, /* Mt */ + { NULL, 0, 0 }, /* Brq */ + { NULL, 0, 0 }, /* Bro */ + { NULL, 0, 0 }, /* Brc */ + { NULL, 0, 0 }, /* %C */ + { NULL, 0, 0 }, /* Es */ + { NULL, 0, 0 }, /* En */ + { NULL, TYPE_Dx, MDOCF_CHILD }, /* Dx */ + { NULL, 0, 0 }, /* %Q */ + { NULL, 0, 0 }, /* br */ + { NULL, 0, 0 }, /* sp */ + { NULL, 0, 0 }, /* %U */ + { NULL, 0, 0 }, /* Ta */ +}; + +static const char *progname; +static int use_all; /* Use all directories and files. */ +static int verb; /* Output verbosity level. */ +static int warnings; /* Potential problems in manuals. */ + +int +main(int argc, char *argv[]) +{ + struct mparse *mp; /* parse sequence */ + struct manpaths dirs; + struct mdb mdb; + struct recs recs; + enum op op; /* current operation */ + const char *dir; + int ch, i, flags; + char dirbuf[MAXPATHLEN]; + DB *hash; /* temporary keyword hashtable */ + BTREEINFO info; /* btree configuration */ + size_t sz1, sz2; + struct buf buf, /* keyword buffer */ + dbuf; /* description buffer */ + struct of *of; /* list of files for processing */ + extern int optind; + extern char *optarg; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + ++progname; + + memset(&dirs, 0, sizeof(struct manpaths)); + memset(&mdb, 0, sizeof(struct mdb)); + memset(&recs, 0, sizeof(struct recs)); + + of = NULL; + mp = NULL; + hash = NULL; + op = OP_DEFAULT; + dir = NULL; + + while (-1 != (ch = getopt(argc, argv, "aC:d:tu:vW"))) + switch (ch) { + case ('a'): + use_all = 1; + break; + case ('C'): + if (op) { + fprintf(stderr, + "-C: conflicting options\n"); + goto usage; + } + dir = optarg; + op = OP_CONFFILE; + break; + case ('d'): + if (op) { + fprintf(stderr, + "-d: conflicting options\n"); + goto usage; + } + dir = optarg; + op = OP_UPDATE; + break; + case ('t'): + dup2(STDOUT_FILENO, STDERR_FILENO); + if (op) { + fprintf(stderr, + "-t: conflicting options\n"); + goto usage; + } + op = OP_TEST; + use_all = 1; + warnings = 1; + break; + case ('u'): + if (op) { + fprintf(stderr, + "-u: conflicting options\n"); + goto usage; + } + dir = optarg; + op = OP_DELETE; + break; + case ('v'): + verb++; + break; + case ('W'): + warnings = 1; + break; + default: + goto usage; + } + + argc -= optind; + argv += optind; + + if (OP_CONFFILE == op && argc > 0) { + fprintf(stderr, "-C: too many arguments\n"); + goto usage; + } + + memset(&info, 0, sizeof(BTREEINFO)); + info.lorder = 4321; + info.flags = R_DUP; + + mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); + + memset(&buf, 0, sizeof(struct buf)); + memset(&dbuf, 0, sizeof(struct buf)); + + buf.size = dbuf.size = MANDOC_BUFSZ; + + buf.cp = mandoc_malloc(buf.size); + dbuf.cp = mandoc_malloc(dbuf.size); + + if (OP_TEST == op) { + ofile_argbuild(argc, argv, &of, "."); + if (NULL == of) + goto out; + index_merge(of, mp, &dbuf, &buf, + hash, &mdb, &recs, "."); + goto out; + } + + if (OP_UPDATE == op || OP_DELETE == op) { + strlcat(mdb.dbn, dir, MAXPATHLEN); + strlcat(mdb.dbn, "/", MAXPATHLEN); + sz1 = strlcat(mdb.dbn, MANDOC_DB, MAXPATHLEN); + + strlcat(mdb.idxn, dir, MAXPATHLEN); + strlcat(mdb.idxn, "/", MAXPATHLEN); + sz2 = strlcat(mdb.idxn, MANDOC_IDX, MAXPATHLEN); + + if (sz1 >= MAXPATHLEN || sz2 >= MAXPATHLEN) { + fprintf(stderr, "%s: path too long\n", dir); + exit((int)MANDOCLEVEL_BADARG); + } + + flags = O_CREAT | O_RDWR; + mdb.db = dbopen(mdb.dbn, flags, 0644, DB_BTREE, &info); + mdb.idx = dbopen(mdb.idxn, flags, 0644, DB_RECNO, NULL); + + if (NULL == mdb.db) { + perror(mdb.dbn); + exit((int)MANDOCLEVEL_SYSERR); + } else if (NULL == mdb.idx) { + perror(mdb.idxn); + exit((int)MANDOCLEVEL_SYSERR); + } + + ofile_argbuild(argc, argv, &of, dir); + + if (NULL == of) + goto out; + + index_prune(of, &mdb, &recs, dir); + + /* + * Go to the root of the respective manual tree. + * This must work or no manuals may be found (they're + * indexed relative to the root). + */ + + if (OP_UPDATE == op) { + if (-1 == chdir(dir)) { + perror(dir); + exit((int)MANDOCLEVEL_SYSERR); + } + index_merge(of, mp, &dbuf, &buf, hash, + &mdb, &recs, dir); + } + + goto out; + } + + /* + * Configure the directories we're going to scan. + * If we have command-line arguments, use them. + * If not, we use man(1)'s method (see mandocdb.8). + */ + + if (argc > 0) { + dirs.paths = mandoc_calloc(argc, sizeof(char *)); + dirs.sz = argc; + for (i = 0; i < argc; i++) + dirs.paths[i] = mandoc_strdup(argv[i]); + } else + manpath_parse(&dirs, dir, NULL, NULL); + + for (i = 0; i < dirs.sz; i++) { + /* + * Go to the root of the respective manual tree. + * This must work or no manuals may be found: + * They are indexed relative to the root. + */ + + if (-1 == chdir(dirs.paths[i])) { + perror(dirs.paths[i]); + exit((int)MANDOCLEVEL_SYSERR); + } + + strlcpy(mdb.dbn, MANDOC_DB, MAXPATHLEN); + strlcpy(mdb.idxn, MANDOC_IDX, MAXPATHLEN); + + flags = O_CREAT | O_TRUNC | O_RDWR; + mdb.db = dbopen(mdb.dbn, flags, 0644, DB_BTREE, &info); + mdb.idx = dbopen(mdb.idxn, flags, 0644, DB_RECNO, NULL); + + if (NULL == mdb.db) { + perror(mdb.dbn); + exit((int)MANDOCLEVEL_SYSERR); + } else if (NULL == mdb.idx) { + perror(mdb.idxn); + exit((int)MANDOCLEVEL_SYSERR); + } + + /* + * Search for manuals and fill the new database. + */ + + strlcpy(dirbuf, dirs.paths[i], MAXPATHLEN); + ofile_dirbuild(".", "", "", 0, &of, dirbuf); + + if (NULL != of) { + index_merge(of, mp, &dbuf, &buf, hash, + &mdb, &recs, dirs.paths[i]); + ofile_free(of); + of = NULL; + } + + (*mdb.db->close)(mdb.db); + (*mdb.idx->close)(mdb.idx); + mdb.db = NULL; + mdb.idx = NULL; + } + +out: + if (mdb.db) + (*mdb.db->close)(mdb.db); + if (mdb.idx) + (*mdb.idx->close)(mdb.idx); + if (hash) + (*hash->close)(hash); + if (mp) + mparse_free(mp); + + manpath_free(&dirs); + ofile_free(of); + free(buf.cp); + free(dbuf.cp); + free(recs.stack); + + return(MANDOCLEVEL_OK); + +usage: + fprintf(stderr, + "usage: %s [-av] [-C file] | dir ... | -t file ...\n" + " -d dir [file ...] | " + "-u dir [file ...]\n", + progname); + + return((int)MANDOCLEVEL_BADARG); +} + +void +index_merge(const struct of *of, struct mparse *mp, + struct buf *dbuf, struct buf *buf, DB *hash, + struct mdb *mdb, struct recs *recs, + const char *basedir) +{ + recno_t rec; + int ch, skip; + DBT key, val; + DB *files; /* temporary file name table */ + char emptystring[1] = {'\0'}; + struct mdoc *mdoc; + struct man *man; + char *p; + const char *fn, *msec, *march, *mtitle; + uint64_t mask; + size_t sv; + unsigned seq; + uint64_t vbuf[2]; + char type; + + if (warnings) { + files = NULL; + hash_reset(&files); + } + + rec = 0; + for (of = of->first; of; of = of->next) { + fn = of->fname; + + /* + * Try interpreting the file as mdoc(7) or man(7) + * source code, unless it is already known to be + * formatted. Fall back to formatted mode. + */ + + mparse_reset(mp); + mdoc = NULL; + man = NULL; + + if ((MANDOC_SRC & of->src_form || + ! (MANDOC_FORM & of->src_form)) && + MANDOCLEVEL_FATAL > mparse_readfd(mp, -1, fn)) + mparse_result(mp, &mdoc, &man); + + if (NULL != mdoc) { + msec = mdoc_meta(mdoc)->msec; + march = mdoc_meta(mdoc)->arch; + if (NULL == march) + march = ""; + mtitle = mdoc_meta(mdoc)->title; + } else if (NULL != man) { + msec = man_meta(man)->msec; + march = ""; + mtitle = man_meta(man)->title; + } else { + msec = of->sec; + march = of->arch; + mtitle = of->title; + } + + /* + * Check whether the manual section given in a file + * agrees with the directory where the file is located. + * Some manuals have suffixes like (3p) on their + * section number either inside the file or in the + * directory name, some are linked into more than one + * section, like encrypt(1) = makekey(8). Do not skip + * manuals for such reasons. + */ + + skip = 0; + assert(of->sec); + assert(msec); + if (strcasecmp(msec, of->sec)) + WARNING(fn, basedir, "Section \"%s\" manual " + "in \"%s\" directory", msec, of->sec); + /* + * Manual page directories exist for each kernel + * architecture as returned by machine(1). + * However, many manuals only depend on the + * application architecture as returned by arch(1). + * For example, some (2/ARM) manuals are shared + * across the "armish" and "zaurus" kernel + * architectures. + * A few manuals are even shared across completely + * different architectures, for example fdformat(1) + * on amd64, i386, sparc, and sparc64. + * Thus, warn about architecture mismatches, + * but don't skip manuals for this reason. + */ + + assert(of->arch); + assert(march); + if (strcasecmp(march, of->arch)) + WARNING(fn, basedir, "Architecture \"%s\" " + "manual in \"%s\" directory", + march, of->arch); + + /* + * By default, skip a file if the title given + * in the file disagrees with the file name. + * Do not warn, this happens for all MLINKs. + */ + + assert(of->title); + assert(mtitle); + if (strcasecmp(mtitle, of->title)) + skip = 1; + + /* + * Build a title string for the file. If it matches + * the location of the file, remember the title as + * found; else, remember it as missing. + */ + + if (warnings) { + buf->len = 0; + buf_appendb(buf, mtitle, strlen(mtitle)); + buf_appendb(buf, "(", 1); + buf_appendb(buf, msec, strlen(msec)); + if ('\0' != *march) { + buf_appendb(buf, "/", 1); + buf_appendb(buf, march, strlen(march)); + } + buf_appendb(buf, ")", 2); + for (p = buf->cp; '\0' != *p; p++) + *p = tolower(*p); + key.data = buf->cp; + key.size = buf->len; + val.data = NULL; + val.size = 0; + if (0 == skip) + val.data = emptystring; + else { + ch = (*files->get)(files, &key, &val, 0); + if (ch < 0) { + perror("hash"); + exit((int)MANDOCLEVEL_SYSERR); + } else if (ch > 0) { + val.data = (void *)fn; + val.size = strlen(fn) + 1; + } else + val.data = NULL; + } + if (NULL != val.data && + (*files->put)(files, &key, &val, 0) < 0) { + perror("hash"); + exit((int)MANDOCLEVEL_SYSERR); + } + } + + if (skip && !use_all) + continue; + + /* + * The index record value consists of a nil-terminated + * filename, a nil-terminated manual section, and a + * nil-terminated description. Use the actual + * location of the file, such that the user can find + * it with man(1). Since the description may not be + * set, we set a sentinel to see if we're going to + * write a nil byte in its place. + */ + + dbuf->len = 0; + type = mdoc ? 'd' : (man ? 'a' : 'c'); + buf_appendb(dbuf, &type, 1); + buf_appendb(dbuf, fn, strlen(fn) + 1); + buf_appendb(dbuf, of->sec, strlen(of->sec) + 1); + buf_appendb(dbuf, of->title, strlen(of->title) + 1); + buf_appendb(dbuf, of->arch, strlen(of->arch) + 1); + + sv = dbuf->len; + + /* + * Collect keyword/mask pairs. + * Each pair will become a new btree node. + */ + + hash_reset(&hash); + if (mdoc) + pmdoc_node(hash, buf, dbuf, + mdoc_node(mdoc), mdoc_meta(mdoc)); + else if (man) + pman_node(hash, buf, dbuf, man_node(man)); + else + pformatted(hash, buf, dbuf, of, basedir); + + /* Test mode, do not access any database. */ + + if (NULL == mdb->db || NULL == mdb->idx) + continue; + + /* + * Make sure the file name is always registered + * as an .Nm search key. + */ + buf->len = 0; + buf_append(buf, of->title); + hash_put(hash, buf, TYPE_Nm); + + /* + * Reclaim an empty index record, if available. + * Use its record number for all new btree nodes. + */ + + if (recs->cur > 0) { + recs->cur--; + rec = recs->stack[(int)recs->cur]; + } else if (recs->last > 0) { + rec = recs->last; + recs->last = 0; + } else + rec++; + vbuf[1] = htobe64(rec); + + /* + * Copy from the in-memory hashtable of pending + * keyword/mask pairs into the database. + */ + + seq = R_FIRST; + while (0 == (ch = (*hash->seq)(hash, &key, &val, seq))) { + seq = R_NEXT; + assert(sizeof(uint64_t) == val.size); + memcpy(&mask, val.data, val.size); + vbuf[0] = htobe64(mask); + val.size = sizeof(vbuf); + val.data = &vbuf; + dbt_put(mdb->db, mdb->dbn, &key, &val); + } + if (ch < 0) { + perror("hash"); + exit((int)MANDOCLEVEL_SYSERR); + } + + /* + * Apply to the index. If we haven't had a description + * set, put an empty one in now. + */ + + if (dbuf->len == sv) + buf_appendb(dbuf, "", 1); + + key.data = &rec; + key.size = sizeof(recno_t); + + val.data = dbuf->cp; + val.size = dbuf->len; + + if (verb) + printf("%s: Adding to index: %s\n", basedir, fn); + + dbt_put(mdb->idx, mdb->idxn, &key, &val); + } + + /* + * Iterate the remembered file titles and check that + * all files can be found by their main title. + */ + + if (warnings) { + seq = R_FIRST; + while (0 == (*files->seq)(files, &key, &val, seq)) { + seq = R_NEXT; + if (val.size) + WARNING((char *)val.data, basedir, + "Probably unreachable, title " + "is %s", (char *)key.data); + } + (*files->close)(files); + } +} + +/* + * Scan through all entries in the index file `idx' and prune those + * entries in `ofile'. + * Pruning consists of removing from `db', then invalidating the entry + * in `idx' (zeroing its value size). + */ +static void +index_prune(const struct of *ofile, struct mdb *mdb, + struct recs *recs, const char *basedir) +{ + const struct of *of; + const char *fn; + uint64_t vbuf[2]; + unsigned seq, sseq; + DBT key, val; + int ch; + + recs->cur = 0; + seq = R_FIRST; + while (0 == (ch = (*mdb->idx->seq)(mdb->idx, &key, &val, seq))) { + seq = R_NEXT; + assert(sizeof(recno_t) == key.size); + memcpy(&recs->last, key.data, key.size); + + /* Deleted records are zero-sized. Skip them. */ + + if (0 == val.size) + goto cont; + + /* + * Make sure we're sane. + * Read past our mdoc/man/cat type to the next string, + * then make sure it's bounded by a NUL. + * Failing any of these, we go into our error handler. + */ + + fn = (char *)val.data + 1; + if (NULL == memchr(fn, '\0', val.size - 1)) + break; + + /* + * Search for the file in those we care about. + * XXX: build this into a tree. Too slow. + */ + + for (of = ofile->first; of; of = of->next) + if (0 == strcmp(fn, of->fname)) + break; + + if (NULL == of) + continue; + + /* + * Search through the keyword database, throwing out all + * references to our file. + */ + + sseq = R_FIRST; + while (0 == (ch = (*mdb->db->seq)(mdb->db, + &key, &val, sseq))) { + sseq = R_NEXT; + if (sizeof(vbuf) != val.size) + break; + + memcpy(vbuf, val.data, val.size); + if (recs->last != betoh64(vbuf[1])) + continue; + + if ((ch = (*mdb->db->del)(mdb->db, + &key, R_CURSOR)) < 0) + break; + } + + if (ch < 0) { + perror(mdb->dbn); + exit((int)MANDOCLEVEL_SYSERR); + } else if (1 != ch) { + fprintf(stderr, "%s: corrupt database\n", + mdb->dbn); + exit((int)MANDOCLEVEL_SYSERR); + } + + if (verb) + printf("%s: Deleting from index: %s\n", + basedir, fn); + + val.size = 0; + ch = (*mdb->idx->put)(mdb->idx, &key, &val, R_CURSOR); + + if (ch < 0) + break; +cont: + if (recs->cur >= recs->size) { + recs->size += MANDOC_SLOP; + recs->stack = mandoc_realloc(recs->stack, + recs->size * sizeof(recno_t)); + } + + recs->stack[(int)recs->cur] = recs->last; + recs->cur++; + } + + if (ch < 0) { + perror(mdb->idxn); + exit((int)MANDOCLEVEL_SYSERR); + } else if (1 != ch) { + fprintf(stderr, "%s: corrupt index\n", mdb->idxn); + exit((int)MANDOCLEVEL_SYSERR); + } + + recs->last++; +} + +/* + * Grow the buffer (if necessary) and copy in a binary string. + */ +static void +buf_appendb(struct buf *buf, const void *cp, size_t sz) +{ + + /* Overshoot by MANDOC_BUFSZ. */ + + while (buf->len + sz >= buf->size) { + buf->size = buf->len + sz + MANDOC_BUFSZ; + buf->cp = mandoc_realloc(buf->cp, buf->size); + } + + memcpy(buf->cp + (int)buf->len, cp, sz); + buf->len += sz; +} + +/* + * Append a nil-terminated string to the buffer. + * This can be invoked multiple times. + * The buffer string will be nil-terminated. + * If invoked multiple times, a space is put between strings. + */ +static void +buf_append(struct buf *buf, const char *cp) +{ + size_t sz; + + if (0 == (sz = strlen(cp))) + return; + + if (buf->len) + buf->cp[(int)buf->len - 1] = ' '; + + buf_appendb(buf, cp, sz + 1); +} + +/* + * Recursively add all text from a given node. + * This is optimised for general mdoc nodes in this context, which do + * not consist of subexpressions and having a recursive call for n->next + * would be wasteful. + * The "f" variable should be 0 unless called from pmdoc_Nd for the + * description buffer, which does not start at the beginning of the + * buffer. + */ +static void +buf_appendmdoc(struct buf *buf, const struct mdoc_node *n, int f) +{ + + for ( ; n; n = n->next) { + if (n->child) + buf_appendmdoc(buf, n->child, f); + + if (MDOC_TEXT == n->type && f) { + f = 0; + buf_appendb(buf, n->string, + strlen(n->string) + 1); + } else if (MDOC_TEXT == n->type) + buf_append(buf, n->string); + + } +} + +static void +hash_reset(DB **db) +{ + DB *hash; + + if (NULL != (hash = *db)) + (*hash->close)(hash); + + *db = dbopen(NULL, O_CREAT|O_RDWR, 0644, DB_HASH, NULL); + if (NULL == *db) { + perror("hash"); + exit((int)MANDOCLEVEL_SYSERR); + } +} + +/* ARGSUSED */ +static int +pmdoc_head(MDOC_ARGS) +{ + + return(MDOC_HEAD == n->type); +} + +/* ARGSUSED */ +static int +pmdoc_body(MDOC_ARGS) +{ + + return(MDOC_BODY == n->type); +} + +/* ARGSUSED */ +static int +pmdoc_Fd(MDOC_ARGS) +{ + const char *start, *end; + size_t sz; + + if (SEC_SYNOPSIS != n->sec) + return(0); + if (NULL == (n = n->child) || MDOC_TEXT != n->type) + return(0); + + /* + * Only consider those `Fd' macro fields that begin with an + * "inclusion" token (versus, e.g., #define). + */ + if (strcmp("#include", n->string)) + return(0); + + if (NULL == (n = n->next) || MDOC_TEXT != n->type) + return(0); + + /* + * Strip away the enclosing angle brackets and make sure we're + * not zero-length. + */ + + start = n->string; + if ('<' == *start || '"' == *start) + start++; + + if (0 == (sz = strlen(start))) + return(0); + + end = &start[(int)sz - 1]; + if ('>' == *end || '"' == *end) + end--; + + assert(end >= start); + + buf_appendb(buf, start, (size_t)(end - start + 1)); + buf_appendb(buf, "", 1); + return(1); +} + +/* ARGSUSED */ +static int +pmdoc_In(MDOC_ARGS) +{ + + if (NULL == n->child || MDOC_TEXT != n->child->type) + return(0); + + buf_append(buf, n->child->string); + return(1); +} + +/* ARGSUSED */ +static int +pmdoc_Fn(MDOC_ARGS) +{ + struct mdoc_node *nn; + const char *cp; + + nn = n->child; + + if (NULL == nn || MDOC_TEXT != nn->type) + return(0); + + /* .Fn "struct type *name" "char *arg" */ + + cp = strrchr(nn->string, ' '); + if (NULL == cp) + cp = nn->string; + + /* Strip away pointer symbol. */ + + while ('*' == *cp) + cp++; + + /* Store the function name. */ + + buf_append(buf, cp); + hash_put(hash, buf, TYPE_Fn); + + /* Store the function type. */ + + if (nn->string < cp) { + buf->len = 0; + buf_appendb(buf, nn->string, cp - nn->string); + buf_appendb(buf, "", 1); + hash_put(hash, buf, TYPE_Ft); + } + + /* Store the arguments. */ + + for (nn = nn->next; nn; nn = nn->next) { + if (MDOC_TEXT != nn->type) + continue; + buf->len = 0; + buf_append(buf, nn->string); + hash_put(hash, buf, TYPE_Fa); + } + + return(0); +} + +/* ARGSUSED */ +static int +pmdoc_St(MDOC_ARGS) +{ + + if (NULL == n->child || MDOC_TEXT != n->child->type) + return(0); + + buf_append(buf, n->child->string); + return(1); +} + +/* ARGSUSED */ +static int +pmdoc_Xr(MDOC_ARGS) +{ + + if (NULL == (n = n->child)) + return(0); + + buf_appendb(buf, n->string, strlen(n->string)); + + if (NULL != (n = n->next)) { + buf_appendb(buf, ".", 1); + buf_appendb(buf, n->string, strlen(n->string) + 1); + } else + buf_appendb(buf, ".", 2); + + return(1); +} + +/* ARGSUSED */ +static int +pmdoc_Nd(MDOC_ARGS) +{ + + if (MDOC_BODY != n->type) + return(0); + + buf_appendmdoc(dbuf, n->child, 1); + return(1); +} + +/* ARGSUSED */ +static int +pmdoc_Nm(MDOC_ARGS) +{ + + if (SEC_NAME == n->sec) + return(1); + else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type) + return(0); + + if (NULL == n->child) + buf_append(buf, m->name); + + return(1); +} + +/* ARGSUSED */ +static int +pmdoc_Sh(MDOC_ARGS) +{ + + return(SEC_CUSTOM == n->sec && MDOC_HEAD == n->type); +} + +static void +hash_put(DB *db, const struct buf *buf, uint64_t mask) +{ + uint64_t oldmask; + DBT key, val; + int rc; + + if (buf->len < 2) + return; + + key.data = buf->cp; + key.size = buf->len; + + if ((rc = (*db->get)(db, &key, &val, 0)) < 0) { + perror("hash"); + exit((int)MANDOCLEVEL_SYSERR); + } else if (0 == rc) { + assert(sizeof(uint64_t) == val.size); + memcpy(&oldmask, val.data, val.size); + mask |= oldmask; + } + + val.data = &mask; + val.size = sizeof(uint64_t); + + if ((rc = (*db->put)(db, &key, &val, 0)) < 0) { + perror("hash"); + exit((int)MANDOCLEVEL_SYSERR); + } +} + +static void +dbt_put(DB *db, const char *dbn, DBT *key, DBT *val) +{ + + assert(key->size); + assert(val->size); + + if (0 == (*db->put)(db, key, val, 0)) + return; + + perror(dbn); + exit((int)MANDOCLEVEL_SYSERR); + /* NOTREACHED */ +} + +/* + * Call out to per-macro handlers after clearing the persistent database + * key. If the macro sets the database key, flush it to the database. + */ +static void +pmdoc_node(MDOC_ARGS) +{ + + if (NULL == n) + return; + + switch (n->type) { + case (MDOC_HEAD): + /* FALLTHROUGH */ + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_TAIL): + /* FALLTHROUGH */ + case (MDOC_BLOCK): + /* FALLTHROUGH */ + case (MDOC_ELEM): + buf->len = 0; + + /* + * Both NULL handlers and handlers returning true + * request using the data. Only skip the element + * when the handler returns false. + */ + + if (NULL != mdocs[n->tok].fp && + 0 == (*mdocs[n->tok].fp)(hash, buf, dbuf, n, m)) + break; + + /* + * For many macros, use the text from all children. + * Set zero flags for macros not needing this. + * In that case, the handler must fill the buffer. + */ + + if (MDOCF_CHILD & mdocs[n->tok].flags) + buf_appendmdoc(buf, n->child, 0); + + /* + * Cover the most common case: + * Automatically stage one string per element. + * Set a zero mask for macros not needing this. + * Additional staging can be done in the handler. + */ + + if (mdocs[n->tok].mask) + hash_put(hash, buf, mdocs[n->tok].mask); + break; + default: + break; + } + + pmdoc_node(hash, buf, dbuf, n->child, m); + pmdoc_node(hash, buf, dbuf, n->next, m); +} + +static int +pman_node(MAN_ARGS) +{ + const struct man_node *head, *body; + char *start, *sv, *title; + size_t sz, titlesz; + + if (NULL == n) + return(0); + + /* + * We're only searching for one thing: the first text child in + * the BODY of a NAME section. Since we don't keep track of + * sections in -man, run some hoops to find out whether we're in + * the correct section or not. + */ + + if (MAN_BODY == n->type && MAN_SH == n->tok) { + body = n; + assert(body->parent); + if (NULL != (head = body->parent->head) && + 1 == head->nchild && + NULL != (head = (head->child)) && + MAN_TEXT == head->type && + 0 == strcmp(head->string, "NAME") && + NULL != (body = body->child) && + MAN_TEXT == body->type) { + + title = NULL; + titlesz = 0; + /* + * Suck the entire NAME section into memory. + * Yes, we might run away. + * But too many manuals have big, spread-out + * NAME sections over many lines. + */ + for ( ; NULL != body; body = body->next) { + if (MAN_TEXT != body->type) + break; + if (0 == (sz = strlen(body->string))) + continue; + title = mandoc_realloc + (title, titlesz + sz + 1); + memcpy(title + titlesz, body->string, sz); + titlesz += sz + 1; + title[(int)titlesz - 1] = ' '; + } + if (NULL == title) + return(0); + + title = mandoc_realloc(title, titlesz + 1); + title[(int)titlesz] = '\0'; + + /* Skip leading space. */ + + sv = title; + while (isspace((unsigned char)*sv)) + sv++; + + if (0 == (sz = strlen(sv))) { + free(title); + return(0); + } + + /* Erase trailing space. */ + + start = &sv[sz - 1]; + while (start > sv && isspace((unsigned char)*start)) + *start-- = '\0'; + + if (start == sv) { + free(title); + return(0); + } + + start = sv; + + /* + * Go through a special heuristic dance here. + * This is why -man manuals are great! + * (I'm being sarcastic: my eyes are bleeding.) + * Conventionally, one or more manual names are + * comma-specified prior to a whitespace, then a + * dash, then a description. Try to puzzle out + * the name parts here. + */ + + for ( ;; ) { + sz = strcspn(start, " ,"); + if ('\0' == start[(int)sz]) + break; + + buf->len = 0; + buf_appendb(buf, start, sz); + buf_appendb(buf, "", 1); + + hash_put(hash, buf, TYPE_Nm); + + if (' ' == start[(int)sz]) { + start += (int)sz + 1; + break; + } + + assert(',' == start[(int)sz]); + start += (int)sz + 1; + while (' ' == *start) + start++; + } + + buf->len = 0; + + if (sv == start) { + buf_append(buf, start); + free(title); + return(1); + } + + while (isspace((unsigned char)*start)) + start++; + + if (0 == strncmp(start, "-", 1)) + start += 1; + else if (0 == strncmp(start, "\\-\\-", 4)) + start += 4; + else if (0 == strncmp(start, "\\-", 2)) + start += 2; + else if (0 == strncmp(start, "\\(en", 4)) + start += 4; + else if (0 == strncmp(start, "\\(em", 4)) + start += 4; + + while (' ' == *start) + start++; + + sz = strlen(start) + 1; + buf_appendb(dbuf, start, sz); + buf_appendb(buf, start, sz); + + hash_put(hash, buf, TYPE_Nd); + free(title); + } + } + + for (n = n->child; n; n = n->next) + if (pman_node(hash, buf, dbuf, n)) + return(1); + + return(0); +} + +/* + * Parse a formatted manual page. + * By necessity, this involves rather crude guesswork. + */ +static void +pformatted(DB *hash, struct buf *buf, struct buf *dbuf, + const struct of *of, const char *basedir) +{ + FILE *stream; + char *line, *p, *title; + size_t len, plen, titlesz; + + if (NULL == (stream = fopen(of->fname, "r"))) { + WARNING(of->fname, basedir, "%s", strerror(errno)); + return; + } + + /* + * Always use the title derived from the filename up front, + * do not even try to find it in the file. This also makes + * sure we don't end up with an orphan index record, even if + * the file content turns out to be completely unintelligible. + */ + + buf->len = 0; + buf_append(buf, of->title); + hash_put(hash, buf, TYPE_Nm); + + /* Skip to first blank line. */ + + while (NULL != (line = fgetln(stream, &len))) + if ('\n' == *line) + break; + + /* + * Assume the first line that is not indented + * is the first section header. Skip to it. + */ + + while (NULL != (line = fgetln(stream, &len))) + if ('\n' != *line && ' ' != *line) + break; + + /* + * Read up until the next section into a buffer. + * Strip the leading and trailing newline from each read line, + * appending a trailing space. + * Ignore empty (whitespace-only) lines. + */ + + titlesz = 0; + title = NULL; + + while (NULL != (line = fgetln(stream, &len))) { + if (' ' != *line || '\n' != line[(int)len - 1]) + break; + while (len > 0 && isspace((unsigned char)*line)) { + line++; + len--; + } + if (1 == len) + continue; + title = mandoc_realloc(title, titlesz + len); + memcpy(title + titlesz, line, len); + titlesz += len; + title[(int)titlesz - 1] = ' '; + } + + /* + * If no page content can be found, or the input line + * is already the next section header, or there is no + * trailing newline, reuse the page title as the page + * description. + */ + + if (NULL == title || '\0' == *title) { + WARNING(of->fname, basedir, + "Cannot find NAME section"); + buf_appendb(dbuf, buf->cp, buf->size); + hash_put(hash, buf, TYPE_Nd); + fclose(stream); + free(title); + return; + } + + title = mandoc_realloc(title, titlesz + 1); + title[(int)titlesz] = '\0'; + + /* + * Skip to the first dash. + * Use the remaining line as the description (no more than 70 + * bytes). + */ + + if (NULL != (p = strstr(title, "- "))) { + for (p += 2; ' ' == *p || '\b' == *p; p++) + /* Skip to next word. */ ; + } else { + WARNING(of->fname, basedir, + "No dash in title line"); + p = title; + } + + plen = strlen(p); + + /* Strip backspace-encoding from line. */ + + while (NULL != (line = memchr(p, '\b', plen))) { + len = line - p; + if (0 == len) { + memmove(line, line + 1, plen--); + continue; + } + memmove(line - 1, line + 1, plen - len); + plen -= 2; + } + + buf_appendb(dbuf, p, plen + 1); + buf->len = 0; + buf_appendb(buf, p, plen + 1); + hash_put(hash, buf, TYPE_Nd); + fclose(stream); + free(title); +} + +static void +ofile_argbuild(int argc, char *argv[], + struct of **of, const char *basedir) +{ + char buf[MAXPATHLEN]; + const char *sec, *arch, *title; + char *p; + int i, src_form; + struct of *nof; + + for (i = 0; i < argc; i++) { + + /* + * Try to infer the manual section, architecture and + * page title from the path, assuming it looks like + * man*[/]/.<section> or + * cat<section>[/<arch>]/<title>.0 + */ + + if (strlcpy(buf, argv[i], sizeof(buf)) >= sizeof(buf)) { + fprintf(stderr, "%s: Path too long\n", argv[i]); + continue; + } + sec = arch = title = ""; + src_form = 0; + p = strrchr(buf, '\0'); + while (p-- > buf) { + if ('\0' == *sec && '.' == *p) { + sec = p + 1; + *p = '\0'; + if ('0' == *sec) + src_form |= MANDOC_FORM; + else if ('1' <= *sec && '9' >= *sec) + src_form |= MANDOC_SRC; + continue; + } + if ('/' != *p) + continue; + if ('\0' == *title) { + title = p + 1; + *p = '\0'; + continue; + } + if (0 == strncmp("man", p + 1, 3)) + src_form |= MANDOC_SRC; + else if (0 == strncmp("cat", p + 1, 3)) + src_form |= MANDOC_FORM; + else + arch = p + 1; + break; + } + if ('\0' == *title) { + WARNING(argv[i], basedir, + "Cannot deduce title from filename"); + title = buf; + } + + /* + * Build the file structure. + */ + + nof = mandoc_calloc(1, sizeof(struct of)); + nof->fname = mandoc_strdup(argv[i]); + nof->sec = mandoc_strdup(sec); + nof->arch = mandoc_strdup(arch); + nof->title = mandoc_strdup(title); + nof->src_form = src_form; + + /* + * Add the structure to the list. + */ + + if (NULL == *of) { + *of = nof; + (*of)->first = nof; + } else { + nof->first = (*of)->first; + (*of)->next = nof; + *of = nof; + } + } +} + +/* + * Recursively build up a list of files to parse. + * We use this instead of ftw() and so on because I don't want global + * variables hanging around. + * This ignores the mandocdb.db and mandocdb.index files, but assumes that + * everything else is a manual. + * Pass in a pointer to a NULL structure for the first invocation. + */ +static void +ofile_dirbuild(const char *dir, const char* psec, const char *parch, + int p_src_form, struct of **of, char *basedir) +{ + char buf[MAXPATHLEN]; + size_t sz; + DIR *d; + const char *fn, *sec, *arch; + char *p, *q, *suffix; + struct of *nof; + struct dirent *dp; + int src_form; + + if (NULL == (d = opendir(dir))) { + WARNING("", dir, "%s", strerror(errno)); + return; + } + + while (NULL != (dp = readdir(d))) { + fn = dp->d_name; + + if ('.' == *fn) + continue; + + src_form = p_src_form; + + if (DT_DIR == dp->d_type) { + sec = psec; + arch = parch; + + /* + * By default, only use directories called: + * man<section>/[<arch>/] or + * cat<section>/[<arch>/] + */ + + if ('\0' == *sec) { + if(0 == strncmp("man", fn, 3)) { + src_form |= MANDOC_SRC; + sec = fn + 3; + } else if (0 == strncmp("cat", fn, 3)) { + src_form |= MANDOC_FORM; + sec = fn + 3; + } else { + WARNING(fn, basedir, "Bad section"); + if (use_all) + sec = fn; + else + continue; + } + } else if ('\0' == *arch) { + if (NULL != strchr(fn, '.')) { + WARNING(fn, basedir, "Bad architecture"); + if (0 == use_all) + continue; + } + arch = fn; + } else { + WARNING(fn, basedir, "Excessive subdirectory"); + if (0 == use_all) + continue; + } + + buf[0] = '\0'; + strlcat(buf, dir, MAXPATHLEN); + strlcat(buf, "/", MAXPATHLEN); + strlcat(basedir, "/", MAXPATHLEN); + strlcat(basedir, fn, MAXPATHLEN); + sz = strlcat(buf, fn, MAXPATHLEN); + + if (MAXPATHLEN <= sz) { + WARNING(fn, basedir, "Path too long"); + continue; + } + + ofile_dirbuild(buf, sec, arch, + src_form, of, basedir); + + p = strrchr(basedir, '/'); + *p = '\0'; + continue; + } + + if (DT_REG != dp->d_type) { + WARNING(fn, basedir, "Not a regular file"); + continue; + } + if (!strcmp(MANDOC_DB, fn) || !strcmp(MANDOC_IDX, fn)) + continue; + if ('\0' == *psec) { + WARNING(fn, basedir, "File outside section"); + if (0 == use_all) + continue; + } + + /* + * By default, skip files where the file name suffix + * does not agree with the section directory + * they are located in. + */ + + suffix = strrchr(fn, '.'); + if (NULL == suffix) { + WARNING(fn, basedir, "No filename suffix"); + if (0 == use_all) + continue; + } else if ((MANDOC_SRC & src_form && + strcmp(suffix + 1, psec)) || + (MANDOC_FORM & src_form && + strcmp(suffix + 1, "0"))) { + WARNING(fn, basedir, "Wrong filename suffix"); + if (0 == use_all) + continue; + if ('0' == suffix[1]) + src_form |= MANDOC_FORM; + else if ('1' <= suffix[1] && '9' >= suffix[1]) + src_form |= MANDOC_SRC; + } + + /* + * Skip formatted manuals if a source version is + * available. Ignore the age: it is very unlikely + * that people install newer formatted base manuals + * when they used to have source manuals before, + * and in ports, old manuals get removed on update. + */ + if (0 == use_all && MANDOC_FORM & src_form && + '\0' != *psec) { + buf[0] = '\0'; + strlcat(buf, dir, MAXPATHLEN); + p = strrchr(buf, '/'); + if ('\0' != *parch && NULL != p) + for (p--; p > buf; p--) + if ('/' == *p) + break; + if (NULL == p) + p = buf; + else + p++; + if (0 == strncmp("cat", p, 3)) + memcpy(p, "man", 3); + strlcat(buf, "/", MAXPATHLEN); + sz = strlcat(buf, fn, MAXPATHLEN); + if (sz >= MAXPATHLEN) { + WARNING(fn, basedir, "Path too long"); + continue; + } + q = strrchr(buf, '.'); + if (NULL != q && p < q++) { + *q = '\0'; + sz = strlcat(buf, psec, MAXPATHLEN); + if (sz >= MAXPATHLEN) { + WARNING(fn, basedir, "Path too long"); + continue; + } + if (0 == access(buf, R_OK)) + continue; + } + } + + buf[0] = '\0'; + assert('.' == dir[0]); + if ('/' == dir[1]) { + strlcat(buf, dir + 2, MAXPATHLEN); + strlcat(buf, "/", MAXPATHLEN); + } + sz = strlcat(buf, fn, MAXPATHLEN); + if (sz >= MAXPATHLEN) { + WARNING(fn, basedir, "Path too long"); + continue; + } + + nof = mandoc_calloc(1, sizeof(struct of)); + nof->fname = mandoc_strdup(buf); + nof->sec = mandoc_strdup(psec); + nof->arch = mandoc_strdup(parch); + nof->src_form = src_form; + + /* + * Remember the file name without the extension, + * to be used as the page title in the database. + */ + + if (NULL != suffix) + *suffix = '\0'; + nof->title = mandoc_strdup(fn); + + /* + * Add the structure to the list. + */ + + if (NULL == *of) { + *of = nof; + (*of)->first = nof; + } else { + nof->first = (*of)->first; + (*of)->next = nof; + *of = nof; + } + } + + closedir(d); +} + +static void +ofile_free(struct of *of) +{ + struct of *nof; + + if (NULL != of) + of = of->first; + + while (NULL != of) { + nof = of->next; + free(of->fname); + free(of->sec); + free(of->arch); + free(of->title); + free(of); + of = nof; + } +} diff --git a/contrib/mdocml/mandocdb.h b/contrib/mdocml/mandocdb.h new file mode 100644 index 0000000000..bda0536e79 --- /dev/null +++ b/contrib/mdocml/mandocdb.h @@ -0,0 +1,62 @@ +/* $Id: mandocdb.h,v 1.6 2012/03/23 02:52:33 kristaps Exp $ */ +/* + * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef MANDOCDB_H +#define MANDOCDB_H + +#define MANDOC_DB "mandocdb.db" +#define MANDOC_IDX "mandocdb.index" + +#define TYPE_An 0x0000000000000001ULL +#define TYPE_Ar 0x0000000000000002ULL +#define TYPE_At 0x0000000000000004ULL +#define TYPE_Bsx 0x0000000000000008ULL +#define TYPE_Bx 0x0000000000000010ULL +#define TYPE_Cd 0x0000000000000020ULL +#define TYPE_Cm 0x0000000000000040ULL +#define TYPE_Dv 0x0000000000000080ULL +#define TYPE_Dx 0x0000000000000100ULL +#define TYPE_Em 0x0000000000000200ULL +#define TYPE_Er 0x0000000000000400ULL +#define TYPE_Ev 0x0000000000000800ULL +#define TYPE_Fa 0x0000000000001000ULL +#define TYPE_Fl 0x0000000000002000ULL +#define TYPE_Fn 0x0000000000004000ULL +#define TYPE_Ft 0x0000000000008000ULL +#define TYPE_Fx 0x0000000000010000ULL +#define TYPE_Ic 0x0000000000020000ULL +#define TYPE_In 0x0000000000040000ULL +#define TYPE_Lb 0x0000000000080000ULL +#define TYPE_Li 0x0000000000100000ULL +#define TYPE_Lk 0x0000000000200000ULL +#define TYPE_Ms 0x0000000000400000ULL +#define TYPE_Mt 0x0000000000800000ULL +#define TYPE_Nd 0x0000000001000000ULL +#define TYPE_Nm 0x0000000002000000ULL +#define TYPE_Nx 0x0000000004000000ULL +#define TYPE_Ox 0x0000000008000000ULL +#define TYPE_Pa 0x0000000010000000ULL +#define TYPE_Rs 0x0000000020000000ULL +#define TYPE_Sh 0x0000000040000000ULL +#define TYPE_Ss 0x0000000080000000ULL +#define TYPE_St 0x0000000100000000ULL +#define TYPE_Sy 0x0000000200000000ULL +#define TYPE_Tn 0x0000000400000000ULL +#define TYPE_Va 0x0000000800000000ULL +#define TYPE_Vt 0x0000001000000000ULL +#define TYPE_Xr 0x0000002000000000ULL + +#endif /*!MANDOCDB_H */ diff --git a/contrib/mdocml/manpath.c b/contrib/mdocml/manpath.c new file mode 100644 index 0000000000..b6d9574e30 --- /dev/null +++ b/contrib/mdocml/manpath.c @@ -0,0 +1,225 @@ +/* $Id: manpath.c,v 1.8 2011/12/24 22:37:16 kristaps Exp $ */ +/* + * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/param.h> + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "mandoc.h" +#include "manpath.h" + +#define MAN_CONF_FILE "/etc/man.conf" +#define MAN_CONF_KEY "_whatdb" + +static void manpath_add(struct manpaths *, const char *); +static void manpath_parseline(struct manpaths *, char *); + +void +manpath_parse(struct manpaths *dirs, const char *file, + char *defp, char *auxp) +{ +#ifdef USE_MANPATH + char cmd[(MAXPATHLEN * 3) + 20]; + FILE *stream; + char *buf; + size_t sz, bsz; + + strlcpy(cmd, "manpath", sizeof(cmd)); + if (file) { + strlcat(cmd, " -C ", sizeof(cmd)); + strlcat(cmd, file, sizeof(cmd)); + } + if (auxp) { + strlcat(cmd, " -m ", sizeof(cmd)); + strlcat(cmd, auxp, sizeof(cmd)); + } + if (defp) { + strlcat(cmd, " -M ", sizeof(cmd)); + strlcat(cmd, defp, sizeof(cmd)); + } + + /* Open manpath(1). Ignore errors. */ + + stream = popen(cmd, "r"); + if (NULL == stream) + return; + + buf = NULL; + bsz = 0; + + /* Read in as much output as we can. */ + + do { + buf = mandoc_realloc(buf, bsz + 1024); + sz = fread(buf + (int)bsz, 1, 1024, stream); + bsz += sz; + } while (sz > 0); + + if ( ! ferror(stream) && feof(stream) && + bsz && '\n' == buf[bsz - 1]) { + buf[bsz - 1] = '\0'; + manpath_parseline(dirs, buf); + } + + free(buf); + pclose(stream); +#else + char *insert; + + /* Always prepend -m. */ + manpath_parseline(dirs, auxp); + + /* If -M is given, it overrides everything else. */ + if (NULL != defp) { + manpath_parseline(dirs, defp); + return; + } + + /* MANPATH and man.conf(5) cooperate. */ + defp = getenv("MANPATH"); + if (NULL == file) + file = MAN_CONF_FILE; + + /* No MANPATH; use man.conf(5) only. */ + if (NULL == defp || '\0' == defp[0]) { + manpath_manconf(dirs, file); + return; + } + + /* Prepend man.conf(5) to MANPATH. */ + if (':' == defp[0]) { + manpath_manconf(dirs, file); + manpath_parseline(dirs, defp); + return; + } + + /* Append man.conf(5) to MANPATH. */ + if (':' == defp[(int)strlen(defp) - 1]) { + manpath_parseline(dirs, defp); + manpath_manconf(dirs, file); + return; + } + + /* Insert man.conf(5) into MANPATH. */ + insert = strstr(defp, "::"); + if (NULL != insert) { + *insert++ = '\0'; + manpath_parseline(dirs, defp); + manpath_manconf(dirs, file); + manpath_parseline(dirs, insert + 1); + return; + } + + /* MANPATH overrides man.conf(5) completely. */ + manpath_parseline(dirs, defp); +#endif +} + +/* + * Parse a FULL pathname from a colon-separated list of arrays. + */ +static void +manpath_parseline(struct manpaths *dirs, char *path) +{ + char *dir; + + if (NULL == path) + return; + + for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) + manpath_add(dirs, dir); +} + +/* + * Add a directory to the array, ignoring bad directories. + * Grow the array one-by-one for simplicity's sake. + */ +static void +manpath_add(struct manpaths *dirs, const char *dir) +{ + char buf[PATH_MAX]; + char *cp; + int i; + + if (NULL == (cp = realpath(dir, buf))) + return; + + for (i = 0; i < dirs->sz; i++) + if (0 == strcmp(dirs->paths[i], dir)) + return; + + dirs->paths = mandoc_realloc + (dirs->paths, + ((size_t)dirs->sz + 1) * sizeof(char *)); + + dirs->paths[dirs->sz++] = mandoc_strdup(cp); +} + +void +manpath_free(struct manpaths *p) +{ + int i; + + for (i = 0; i < p->sz; i++) + free(p->paths[i]); + + free(p->paths); +} + +void +manpath_manconf(struct manpaths *dirs, const char *file) +{ + FILE *stream; + char *p, *q; + size_t len, keysz; + + keysz = strlen(MAN_CONF_KEY); + assert(keysz > 0); + + if (NULL == (stream = fopen(file, "r"))) + return; + + while (NULL != (p = fgetln(stream, &len))) { + if (0 == len || '\n' != p[--len]) + break; + p[len] = '\0'; + while (isspace((unsigned char)*p)) + p++; + if (strncmp(MAN_CONF_KEY, p, keysz)) + continue; + p += keysz; + while (isspace(*p)) + p++; + if ('\0' == *p) + continue; + if (NULL == (q = strrchr(p, '/'))) + continue; + *q = '\0'; + manpath_add(dirs, p); + } + + fclose(stream); +} diff --git a/contrib/mdocml/msec.c b/contrib/mdocml/manpath.h similarity index 56% copy from contrib/mdocml/msec.c copy to contrib/mdocml/manpath.h index f51360e597..167ee01021 100644 --- a/contrib/mdocml/msec.c +++ b/contrib/mdocml/manpath.h @@ -1,6 +1,7 @@ -/* $Id: msec.c,v 1.9 2011/03/22 14:33:05 kristaps Exp $ */ +/* $Id: manpath.h,v 1.5 2011/12/13 20:56:46 kristaps Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> + * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,25 +15,24 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#ifndef MANPATH_H +#define MANPATH_H -#include <stdlib.h> -#include <string.h> - -#include "mdoc.h" -#include "mandoc.h" -#include "libmdoc.h" +/* + * Unsorted list of unique, absolute paths to be searched for manual + * databases. + */ +struct manpaths { + int sz; + char **paths; +}; -#define LINE(x, y) \ - if (0 == strcmp(p, x)) return(y); +__BEGIN_DECLS -const char * -mdoc_a2msec(const char *p) -{ +void manpath_manconf(struct manpaths *, const char *); +void manpath_parse(struct manpaths *, const char *, char *, char *); +void manpath_free(struct manpaths *); -#include "msec.in" +__END_DECLS - return(NULL); -} +#endif /*!MANPATH_H*/ diff --git a/contrib/mdocml/mdoc.7 b/contrib/mdocml/mdoc.7 index 5bd8aa109f..44d927b56b 100644 --- a/contrib/mdocml/mdoc.7 +++ b/contrib/mdocml/mdoc.7 @@ -1,7 +1,7 @@ -.\" $Id: mdoc.7,v 1.188 2011/05/26 09:26:16 kristaps Exp $ +.\" $Id: mdoc.7,v 1.214 2012/01/03 10:18:05 kristaps Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,259 +15,78 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 26 2011 $ +.Dd $Mdocdate: January 3 2012 $ .Dt MDOC 7 .Os .Sh NAME .Nm mdoc -.Nd mdoc language reference +.Nd semantic markup language for formatting manual pages .Sh DESCRIPTION The .Nm mdoc -language is used to format -.Bx -.Ux -manuals. -This reference document describes its syntax, structure, and -usage. -The reference implementation is +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 troff \-mdoc implementations. +section describes compatibility with other implementations. .Pp -An +In an .Nm -document follows simple rules: lines beginning with the control -character +document, lines beginning with the control character .Sq \&. -are parsed for macros. -Text lines, those not beginning with the control character, are -interpreted within the scope of prior macros: +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 -.Sh LANGUAGE SYNTAX -.Nm -documents may contain only graphable 7-bit ASCII characters, the space -character, and, in certain circumstances, the tab character. -.Pp -If the first character of a text line is a space, that line is printed -with a leading newline. -.Ss Comments -Text following a -.Sq \e\*q , -whether in a macro or text line, is ignored to the end of -line. -A macro line with only a control character and comment escape, -.Sq \&.\e\*q , -is also ignored. -Macro lines with only a control character and optional whitespace are -stripped from input. -.Ss Reserved Terms -Within a macro line, the following terms are reserved: -.Pp -.Bl -tag -width Ds -offset indent -compact -.It \&. -.Pq period -.It \e. -.Pq escaped period -.It \&, -.Pq comma -.It \&: -.Pq colon -.It \&; -.Pq semicolon -.It \&( -.Pq left-parenthesis -.It \&) -.Pq right-parenthesis -.It \&[ -.Pq left-bracket -.It \&] -.Pq right-bracket -.It \&? -.Pq question -.It \&! -.Pq exclamation -.It \&| -.Pq vertical bar -.It \e*(Ba -.Pq reserved-word vertical bar -.El -.Pp -For general use in macro lines, these can be escaped with a non-breaking -space -.Pq Sq \e& . -In text lines, these may be used as normal punctuation. -.Ss Special Characters -Special characters may occur in both macro and text lines. -Sequences begin with the escape character -.Sq \e -followed by either an open-parenthesis -.Sq \&( -for two-character sequences; an open-bracket -.Sq \&[ -for n-character sequences (terminated at a close-bracket -.Sq \&] ) ; -or a single one character sequence. -See -.Xr mandoc_char 7 -for a complete list. -Examples include -.Sq \e(em -.Pq em-dash -and -.Sq \ee -.Pq back-slash . -.Ss Text Decoration -Terms may be text-decorated using the -.Sq \ef -escape followed by an indicator: B (bold), I (italic), R (Roman), or P -(revert to previous mode): -.Pp -.Dl \efBbold\efR \efIitalic\efP -.Pp -A numerical representation 3, 2, or 1 (bold, italic, and Roman, -respectively) may be used instead. -If a macro opens a font scope after calling -.Sq \ef , -such as with -.Sx \&Bf , -the -.Sq \ef -mode will be restored upon exiting the -.Sx \&Bf -scope. .Pp -Note this form is -.Em not -recommended for -.Nm , -which encourages semantic annotation. -.Ss Predefined Strings -Historically, -troff -also defined a set of package-specific -.Dq predefined strings , -which, like -.Sx Special Characters , -mark special output characters and strings by way of input codes. -Predefined strings are escaped with the slash-asterisk, -.Sq \e* : -single-character -.Sq \e*X , -two-character -.Sq \e*(XX , -and N-character -.Sq \e*[N] . -See -.Xr mandoc_char 7 -for a complete list. -Examples include -.Sq \e*(Am -.Pq ampersand +Many aspects of the basic syntax of the +.Nm +language are based on the +.Xr roff 7 +language; see the +.Em LANGUAGE SYNTAX and -.Sq \e*(Ba -.Pq vertical bar . -.Ss Whitespace -Whitespace consists of the space character. -In text lines, whitespace is preserved within a line; unescaped -trailing spaces are stripped from input (unless in a literal context). -Blank text lines, which may include whitespace, are only permitted -within literal contexts. -.Pp -In macro lines, whitespace delimits arguments and is discarded. -If arguments are quoted, whitespace within the quotes is retained. -.Ss Quotation -Macro arguments may be quoted with double-quotes to group -space-delimited terms or to retain blocks of whitespace. -A quoted argument begins with a double-quote preceded by whitespace. -The next double-quote not pairwise adjacent to another double-quote -terminates the literal, regardless of surrounding whitespace. -.Pp -Note that any quoted text, even if it would cause a macro invocation -when unquoted, is considered literal text. -Thus, the following produces -.Sq Op "Fl a" : -.Bd -literal -offset indent -\&.Op "Fl a" -.Ed -.Pp -In text lines, quotes are regarded as opaque text. -.Ss Scaling Widths -Many macros support scaled widths for their arguments, such as -stipulating a two-inch list indentation with the following: -.Bd -literal -offset indent -\&.Bl -tag -width 2i -.Ed -.Pp -The syntax for scaled widths is -.Sq Li [+-]?[0-9]*.[0-9]*[:unit:] , -where a decimal must be preceded or proceeded by at least one digit. -Negative numbers, while accepted, are truncated to zero. -The following scaling units are accepted: -.Pp -.Bl -tag -width Ds -offset indent -compact -.It c -centimetre -.It i -inch -.It P -pica (~1/6 inch) -.It p -point (~1/72 inch) -.It f -synonym for -.Sq u -.It v -default vertical span -.It m -width of rendered -.Sq m -.Pq em -character -.It n -width of rendered -.Sq n -.Pq en -character -.It u -default horizontal span -.It M -mini-em (~1/100 em) -.El -.Pp -Using anything other than -.Sq m , -.Sq n , -.Sq u , -or -.Sq v -is necessarily non-portable across output media. -See -.Sx COMPATIBILITY . -.Ss Sentence Spacing -When composing a manual, make sure that sentences end at the end of -a line. -By doing so, front-ends will be able to apply the proper amount of -spacing after the end of sentence (unescaped) period, exclamation mark, -or question mark followed by zero or more non-sentence closing -delimiters -.Po -.Sq \&) , -.Sq \&] , -.Sq \&' , -.Sq \&" -.Pc . -.Pp -The proper spacing is also intelligently preserved if a sentence ends at -the boundary of a macro line. -For example: -.Pp -.Dl \&.Xr mandoc 1 \&. -.Dl \&.Fl T \&Ns \&Cm ascii \&. +.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 @@ -304,10 +123,10 @@ file for a utility \&.Os \&.Sh NAME \&.Nm progname -\&.Nd a description goes here -\&.\e\*q .Sh LIBRARY -\&.\e\*q For sections 2, 3, & 9 only. -\&.\e\*q Not used in OpenBSD. +\&.Nd one line about what it does +\&.\e\(dq .Sh LIBRARY +\&.\e\(dq For sections 2, 3, & 9 only. +\&.\e\(dq Not used in OpenBSD. \&.Sh SYNOPSIS \&.Nm progname \&.Op Fl options @@ -316,29 +135,29 @@ file for a utility The \&.Nm utility processes files ... -\&.\e\*q .Sh IMPLEMENTATION NOTES -\&.\e\*q Not used in OpenBSD. -\&.\e\*q .Sh RETURN VALUES -\&.\e\*q For sections 2, 3, & 9 only. -\&.\e\*q .Sh ENVIRONMENT -\&.\e\*q For sections 1, 6, 7, & 8 only. -\&.\e\*q .Sh FILES -\&.\e\*q .Sh EXIT STATUS -\&.\e\*q For sections 1, 6, & 8 only. -\&.\e\*q .Sh EXAMPLES -\&.\e\*q .Sh DIAGNOSTICS -\&.\e\*q For sections 1, 4, 6, 7, & 8 only. -\&.\e\*q .Sh ERRORS -\&.\e\*q For sections 2, 3, & 9 only. -\&.\e\*q .Sh SEE ALSO -\&.\e\*q .Xr foobar 1 -\&.\e\*q .Sh STANDARDS -\&.\e\*q .Sh HISTORY -\&.\e\*q .Sh AUTHORS -\&.\e\*q .Sh CAVEATS -\&.\e\*q .Sh BUGS -\&.\e\*q .Sh SECURITY CONSIDERATIONS -\&.\e\*q 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, & 9 only. +\&.\e\(dq .Sh ENVIRONMENT +\&.\e\(dq For sections 1, 6, 7, & 8 only. +\&.\e\(dq .Sh FILES +\&.\e\(dq .Sh EXIT STATUS +\&.\e\(dq For sections 1, 6, & 8 only. +\&.\e\(dq .Sh EXAMPLES +\&.\e\(dq .Sh DIAGNOSTICS +\&.\e\(dq For sections 1, 4, 6, 7, & 8 only. +\&.\e\(dq .Sh ERRORS +\&.\e\(dq For sections 2, 3, & 9 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 @@ -419,8 +238,8 @@ macros should follow C header-file conventions. .Pp And for the third, configurations (section 4): .Bd -literal -offset indent -\&.Cd \*qit* at isa? port 0x2e\*q -\&.Cd \*qit* at isa? port 0x4e\*q +\&.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 @@ -484,6 +303,21 @@ Print verbose information. .Ed .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 +.Sx \&Ss +macro to form subsections. +In very long manuals, the +.Em DESCRIPTION +may be split into multiple sections, each started by an +.Sx \&Sh +macro followed by a non-standard section name, and each having +several subsections, like in the present +.Nm +manual. .It Em IMPLEMENTATION NOTES Implementation-specific notes should be kept here. This is useful when implementing standard functions that may have side @@ -545,7 +379,13 @@ This section should exist for most manuals. Cross-references should conventionally be ordered first by section, then alphabetically. .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 +.Sx \&Rs +and .Sx \&Xr . .It Em STANDARDS References any standards implemented or used. @@ -556,7 +396,8 @@ section should be used instead. See .Sx \&St . .It Em HISTORY -A brief history of the subject, including where support first appeared. +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. @@ -572,620 +413,484 @@ in this section. .It Em SECURITY CONSIDERATIONS Documents any security precautions that operators should consider. .El -.Sh MACRO SYNTAX -Macros are one to three three characters in length and begin with a -control character, -.Sq \&. , -at the beginning of the line. -An arbitrary amount of whitespace may sit between the control character -and the macro name. -Thus, the following are equivalent: -.Bd -literal -offset indent -\&.Pp -\&.\ \ \ \&Pp -.Ed +.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 Sx \&Dd Ta document date: Cm $\&Mdocdate$ | Ar month day , year +.It Sx \&Dt Ta document title: Ar TITLE section Op Ar volume | arch +.It Sx \&Os Ta operating system version: Op Ar system Op Ar version +.It Sx \&Nm Ta document name (one argument) +.It Sx \&Nd Ta document description (one line) +.El +.Ss Sections and cross references +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Sh Ta section header (one line) +.It Sx \&Ss Ta subsection header (one line) +.It Sx \&Sx Ta internal cross reference to a section or subsection +.It Sx \&Xr Ta cross reference to another manual page: Ar name section +.It Sx \&Pp , \&Lp Ta start a text paragraph (no arguments) +.El +.Ss Displays and lists +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Bd , \&Ed Ta display block: +.Fl Ar type +.Op Fl offset Ar width +.Op Fl compact +.It Sx \&D1 Ta indented display (one line) +.It Sx \&Dl Ta indented literal display (one line) +.It Sx \&Bl , \&El Ta list block: +.Fl Ar type +.Op Fl width Ar val +.Op Fl offset Ar val +.Op Fl compact +.It Sx \&It Ta list item (syntax depends on Fl Ar type ) +.It Sx \&Ta Ta table cell separator in Sx \&Bl Fl column No lists +.It Sx \&Rs , \&%* , \&Re Ta bibliographic block (references) +.El +.Ss Spacing control +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Pf Ta prefix, no following horizontal space (one argument) +.It Sx \&Ns Ta roman font, no preceding horizontal space (no arguments) +.It Sx \&Ap Ta apostrophe without surrounding whitespace (no arguments) +.It Sx \&Sm Ta switch horizontal spacing mode: Cm on | off +.It Sx \&Bk , \&Ek Ta keep block: Fl words +.It Sx \&br Ta force output line break in text mode (no arguments) +.It Sx \&sp Ta force vertical space: Op Ar height +.El +.Ss Semantic markup for command line utilities: +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Nm Ta start a SYNOPSIS block with the name of a utility +.It Sx \&Fl Ta command line options (flags) (>=0 arguments) +.It Sx \&Cm Ta command modifier (>0 arguments) +.It Sx \&Ar Ta command arguments (>=0 arguments) +.It Sx \&Op , \&Oo , \&Oc Ta optional syntax elements (enclosure) +.It Sx \&Ic Ta internal or interactive command (>0 arguments) +.It Sx \&Ev Ta environmental variable (>0 arguments) +.It Sx \&Pa Ta file system path (>=0 arguments) +.El +.Ss Semantic markup for function libraries: +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Lb Ta function library (one argument) +.It Sx \&In Ta include file (one argument) +.It Sx \&Ft Ta function type (>0 arguments) +.It Sx \&Fo , \&Fc Ta function block: Ar funcname +.It Sx \&Fn Ta function name: +.Op Ar functype +.Ar funcname +.Oo +.Op Ar argtype +.Ar argname +.Oc +.It Sx \&Fa Ta function argument (>0 arguments) +.It Sx \&Vt Ta variable type (>0 arguments) +.It Sx \&Va Ta variable name (>0 arguments) +.It Sx \&Dv Ta defined variable or preprocessor constant (>0 arguments) +.It Sx \&Er Ta error constant (>0 arguments) +.It Sx \&Ev Ta environmental variable (>0 arguments) +.El +.Ss Various semantic markup: +.Bl -column "Brq, Bro, Brc" description +.It Sx \&An Ta author name (>0 arguments) +.It Sx \&Lk Ta hyperlink: Ar uri Op Ar name +.It Sx \&Mt Ta Do mailto Dc hyperlink: Ar address +.It Sx \&Cd Ta kernel configuration declaration (>0 arguments) +.It Sx \&Ad Ta memory address (>0 arguments) +.It Sx \&Ms Ta mathematical symbol (>0 arguments) +.It Sx \&Tn Ta tradename (>0 arguments) +.El +.Ss Physical markup +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Em Ta italic font or underline (emphasis) (>0 arguments) +.It Sx \&Sy Ta boldface font (symbolic) (>0 arguments) +.It Sx \&Li Ta typewriter font (literal) (>0 arguments) +.It Sx \&No Ta return to roman font (normal) (no arguments) +.It Sx \&Bf , \&Ef Ta font block: +.Op Fl Ar type | Cm \&Em | \&Li | \&Sy +.El +.Ss Physical enclosures +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Dq , \&Do , \&Dc Ta enclose in typographic double quotes: Dq text +.It Sx \&Qq , \&Qo , \&Qc Ta enclose in typewriter double quotes: Qq text +.It Sx \&Sq , \&So , \&Sc Ta enclose in single quotes: Sq text +.It Sx \&Ql Ta single-quoted literal text: Ql text +.It Sx \&Pq , \&Po , \&Pc Ta enclose in parentheses: Pq text +.It Sx \&Bq , \&Bo , \&Bc Ta enclose in square brackets: Bq text +.It Sx \&Brq , \&Bro , \&Brc Ta enclose in curly braces: Brq text +.It Sx \&Aq , \&Ao , \&Ac Ta enclose in angle brackets: Aq text +.It Sx \&Eo , \&Ec Ta generic enclosure +.El +.Ss Text production +.Bl -column "Brq, Bro, Brc" description +.It Sx \&Ex Fl std Ta standard command exit values: Op Ar utility ... +.It Sx \&Rv Fl std Ta standard function return values: Op Ar function ... +.It Sx \&St Ta reference to a standards document (one argument) +.It Sx \&Ux Ta Ux +.It Sx \&At Ta At +.It Sx \&Bx Ta Bx +.It Sx \&Bsx Ta Bsx +.It Sx \&Nx Ta Nx +.It Sx \&Fx Ta Fx +.It Sx \&Ox Ta Ox +.It Sx \&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 . +.Ss \&%A +Author name of an +.Sx \&Rs +block. +Multiple authors should each be accorded their own +.Sx \%%A +line. +Author names should be ordered with full or abbreviated forename(s) +first, then full surname. +.Ss \&%B +Book title of an +.Sx \&Rs +block. +This macro may also be used in a non-bibliographic context when +referring to book titles. +.Ss \&%C +Publication city or location of an +.Sx \&Rs +block. +.Ss \&%D +Publication date of an +.Sx \&Rs +block. +Recommended formats of arguments are +.Ar month day , year +or just +.Ar year . +.Ss \&%I +Publisher or issuer name of an +.Sx \&Rs +block. +.Ss \&%J +Journal name of an +.Sx \&Rs +block. +.Ss \&%N +Issue number (usually for journals) of an +.Sx \&Rs +block. +.Ss \&%O +Optional information of an +.Sx \&Rs +block. +.Ss \&%P +Book or journal page number of an +.Sx \&Rs +block. +.Ss \&%Q +Institutional author (school, government, etc.) of an +.Sx \&Rs +block. +Multiple institutional authors should each be accorded their own +.Sx \&%Q +line. +.Ss \&%R +Technical report name of an +.Sx \&Rs +block. +.Ss \&%T +Article title of an +.Sx \&Rs +block. +This macro may also be used in a non-bibliographical context when +referring to article titles. +.Ss \&%U +URI of reference document. +.Ss \&%V +Volume number of an +.Sx \&Rs +block. +.Ss \&Ac +Close an +.Sx \&Ao +block. +Does not have any tail arguments. +.Ss \&Ad +Memory address. +Do not use this for postal addresses. .Pp -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. +Examples: +.Dl \&.Ad [0,$] +.Dl \&.Ad 0x00000000 +.Ss \&An +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 -The -.Em Callable -column indicates that the macro may also be called by passing its name -as an argument to another macro. -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 . +.Bl -tag -width "-nosplitX" -offset indent -compact +.It Fl split +Start a new output line before each subsequent invocation of +.Sx \&An . +.It Fl nosplit +The opposite of +.Fl split . +.El .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. +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 -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 -.Sx \&Bf -and -.Pq optionally -.Sx \&Bl -contain a head. -.Bd -literal -offset indent -\&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB -\(lBbody...\(rB -\&.Yc -.Ed +Examples: +.Dl \&.An -nosplit +.Dl \&.An Kristaps Dzonsons \&Aq kristaps@bsd.lv +.Ss \&Ao +Begin a block enclosed by angle brackets. +Does not have any head arguments. .Pp -.Bl -column -compact -offset indent "MacroX" "CallableX" "ParsedX" "closed by XXX" -.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope -.It Sx \&Bd Ta \&No Ta \&No Ta closed by Sx \&Ed -.It Sx \&Bf Ta \&No Ta \&No Ta closed by Sx \&Ef -.It Sx \&Bk Ta \&No Ta \&No Ta closed by Sx \&Ek -.It Sx \&Bl Ta \&No Ta \&No Ta closed by Sx \&El -.It Sx \&Ed Ta \&No Ta \&No Ta opened by Sx \&Bd -.It Sx \&Ef Ta \&No Ta \&No Ta opened by Sx \&Bf -.It Sx \&Ek Ta \&No Ta \&No Ta opened by Sx \&Bk -.It Sx \&El Ta \&No Ta \&No Ta opened by Sx \&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 -.Sx \&It Fl bullet , -.Fl hyphen , -.Fl dash , -.Fl enum , -.Fl item -.Pc -don't have heads; only one -.Po -.Sx \&It -in -.Sx \&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 +Examples: +.Dl \&.Fl -key= \&Ns \&Ao \&Ar val \&Ac .Pp -.Bl -column -compact -offset indent "MacroX" "CallableX" "ParsedX" "closed by XXXXXXXXXXX" -.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope -.It Sx \&It Ta \&No Ta Yes Ta closed by Sx \&It , Sx \&El -.It Sx \&Nd Ta \&No Ta \&No Ta closed by Sx \&Sh -.It Sx \&Nm Ta \&No Ta Yes Ta closed by Sx \&Nm , Sx \&Sh , Sx \&Ss -.It Sx \&Sh Ta \&No Ta \&No Ta closed by Sx \&Sh -.It Sx \&Ss Ta \&No Ta \&No Ta closed by Sx \&Sh , Sx \&Ss +See also +.Sx \&Aq . +.Ss \&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 +.Ss \&Aq +Encloses its arguments in angle brackets. +.Pp +Examples: +.Dl \&.Fl -key= \&Ns \&Aq \&Ar val +.Pp +.Em Remarks : +this macro is often abused for rendering URIs, which should instead use +.Sx \&Lk +or +.Sx \&Mt , +or to note pre-processor +.Dq Li #include +statements, which should use +.Sx \&In . +.Pp +See also +.Sx \&Ao . +.Ss \&Ar +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 +.Sx \&Ar +macro are names and placeholders for command arguments; +for fixed strings to be passed verbatim as arguments, use +.Sx \&Fl +or +.Sx \&Cm . +.Ss \&At +Formats an AT&T 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[.[1-4]]? +A version of +.At V . .El .Pp -Note that the -.Sx \&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 -.Sx \&Fo , -.Sx \&Eo -.Pc -and/or tail -.Pq Sx \&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 +Note that these arguments do not begin with a hyphen. +.Pp +Examples: +.Dl \&.At +.Dl \&.At III +.Dl \&.At V.1 +.Pp +See also +.Sx \&Bsx , +.Sx \&Bx , +.Sx \&Dx , +.Sx \&Fx , +.Sx \&Nx , +.Sx \&Ox , +and +.Sx \&Ux . +.Ss \&Bc +Close a +.Sx \&Bo +block. +Does not have any tail arguments. +.Ss \&Bd +Begin a display block. +Its syntax is as follows: +.Bd -ragged -offset indent +.Pf \. Sx \&Bd +.Fl Ns Ar type +.Op Fl offset Ar width +.Op Fl compact .Ed .Pp -.Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -compact -offset indent -.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope -.It Sx \&Ac Ta Yes Ta Yes Ta opened by Sx \&Ao -.It Sx \&Ao Ta Yes Ta Yes Ta closed by Sx \&Ac -.It Sx \&Bc Ta Yes Ta Yes Ta closed by Sx \&Bo -.It Sx \&Bo Ta Yes Ta Yes Ta opened by Sx \&Bc -.It Sx \&Brc Ta Yes Ta Yes Ta opened by Sx \&Bro -.It Sx \&Bro Ta Yes Ta Yes Ta closed by Sx \&Brc -.It Sx \&Dc Ta Yes Ta Yes Ta opened by Sx \&Do -.It Sx \&Do Ta Yes Ta Yes Ta closed by Sx \&Dc -.It Sx \&Ec Ta Yes Ta Yes Ta opened by Sx \&Eo -.It Sx \&Eo Ta Yes Ta Yes Ta closed by Sx \&Ec -.It Sx \&Fc Ta Yes Ta Yes Ta opened by Sx \&Fo -.It Sx \&Fo Ta \&No Ta \&No Ta closed by Sx \&Fc -.It Sx \&Oc Ta Yes Ta Yes Ta closed by Sx \&Oo -.It Sx \&Oo Ta Yes Ta Yes Ta opened by Sx \&Oc -.It Sx \&Pc Ta Yes Ta Yes Ta closed by Sx \&Po -.It Sx \&Po Ta Yes Ta Yes Ta opened by Sx \&Pc -.It Sx \&Qc Ta Yes Ta Yes Ta opened by Sx \&Oo -.It Sx \&Qo Ta Yes Ta Yes Ta closed by Sx \&Oc -.It Sx \&Re Ta \&No Ta \&No Ta opened by Sx \&Rs -.It Sx \&Rs Ta \&No Ta \&No Ta closed by Sx \&Re -.It Sx \&Sc Ta Yes Ta Yes Ta opened by Sx \&So -.It Sx \&So Ta Yes Ta Yes Ta closed by Sx \&Sc -.It Sx \&Xc Ta Yes Ta Yes Ta opened by Sx \&Xo -.It Sx \&Xo Ta Yes Ta Yes Ta closed by Sx \&Xc +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 centre-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 -.Ss Block partial-implicit -Like block full-implicit, but with single-line scope closed by -.Sx Reserved Terms -or end of line. -.Bd -literal -offset indent -\&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBbody...\(rB \(lBres...\(rB -.Ed .Pp -.Bl -column "MacroX" "CallableX" "ParsedX" -compact -offset indent -.It Em Macro Ta Em Callable Ta Em Parsed -.It Sx \&Aq Ta Yes Ta Yes -.It Sx \&Bq Ta Yes Ta Yes -.It Sx \&Brq Ta Yes Ta Yes -.It Sx \&D1 Ta \&No Ta \&Yes -.It Sx \&Dl Ta \&No Ta Yes -.It Sx \&Dq Ta Yes Ta Yes -.It Sx \&Op Ta Yes Ta Yes -.It Sx \&Pq Ta Yes Ta Yes -.It Sx \&Ql Ta Yes Ta Yes -.It Sx \&Qq Ta Yes Ta Yes -.It Sx \&Sq Ta Yes Ta Yes -.It Sx \&Vt Ta Yes Ta Yes +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 centre 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 width using the syntax described in +.Sx Scaling Widths . +.It +An arbitrary string, which indents by the length of this string. .El .Pp -Note that the -.Sx \&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 In-line -Closed by -.Sx Reserved Terms , -end of 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. +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 -\&.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 +\&.Bd \-literal \-offset indent \-compact + Hello world. +\&.Ed .Ed .Pp -.Bl -column "MacroX" "CallableX" "ParsedX" "Arguments" -compact -offset indent -.It Em Macro Ta Em Callable Ta Em Parsed Ta Em Arguments -.It Sx \&%A Ta \&No Ta \&No Ta >0 -.It Sx \&%B Ta \&No Ta \&No Ta >0 -.It Sx \&%C Ta \&No Ta \&No Ta >0 -.It Sx \&%D Ta \&No Ta \&No Ta >0 -.It Sx \&%I Ta \&No Ta \&No Ta >0 -.It Sx \&%J Ta \&No Ta \&No Ta >0 -.It Sx \&%N Ta \&No Ta \&No Ta >0 -.It Sx \&%O Ta \&No Ta \&No Ta >0 -.It Sx \&%P Ta \&No Ta \&No Ta >0 -.It Sx \&%Q Ta \&No Ta \&No Ta >0 -.It Sx \&%R Ta \&No Ta \&No Ta >0 -.It Sx \&%T Ta \&No Ta \&No Ta >0 -.It Sx \&%U Ta \&No Ta \&No Ta >0 -.It Sx \&%V Ta \&No Ta \&No Ta >0 -.It Sx \&Ad Ta Yes Ta Yes Ta >0 -.It Sx \&An Ta Yes Ta Yes Ta >0 -.It Sx \&Ap Ta Yes Ta Yes Ta 0 -.It Sx \&Ar Ta Yes Ta Yes Ta n -.It Sx \&At Ta Yes Ta Yes Ta 1 -.It Sx \&Bsx Ta Yes Ta Yes Ta n -.It Sx \&Bt Ta \&No Ta \&No Ta 0 -.It Sx \&Bx Ta Yes Ta Yes Ta n -.It Sx \&Cd Ta Yes Ta Yes Ta >0 -.It Sx \&Cm Ta Yes Ta Yes Ta >0 -.It Sx \&Db Ta \&No Ta \&No Ta 1 -.It Sx \&Dd Ta \&No Ta \&No Ta n -.It Sx \&Dt Ta \&No Ta \&No Ta n -.It Sx \&Dv Ta Yes Ta Yes Ta >0 -.It Sx \&Dx Ta Yes Ta Yes Ta n -.It Sx \&Em Ta Yes Ta Yes Ta >0 -.It Sx \&En Ta \&No Ta \&No Ta 0 -.It Sx \&Er Ta Yes Ta Yes Ta >0 -.It Sx \&Es Ta \&No Ta \&No Ta 0 -.It Sx \&Ev Ta Yes Ta Yes Ta >0 -.It Sx \&Ex Ta \&No Ta \&No Ta n -.It Sx \&Fa Ta Yes Ta Yes Ta >0 -.It Sx \&Fd Ta \&No Ta \&No Ta >0 -.It Sx \&Fl Ta Yes Ta Yes Ta n -.It Sx \&Fn Ta Yes Ta Yes Ta >0 -.It Sx \&Fr Ta \&No Ta \&No Ta n -.It Sx \&Ft Ta Yes Ta Yes Ta >0 -.It Sx \&Fx Ta Yes Ta Yes Ta n -.It Sx \&Hf Ta \&No Ta \&No Ta n -.It Sx \&Ic Ta Yes Ta Yes Ta >0 -.It Sx \&In Ta \&No Ta \&No Ta 1 -.It Sx \&Lb Ta \&No Ta \&No Ta 1 -.It Sx \&Li Ta Yes Ta Yes Ta >0 -.It Sx \&Lk Ta Yes Ta Yes Ta >0 -.It Sx \&Lp Ta \&No Ta \&No Ta 0 -.It Sx \&Ms Ta Yes Ta Yes Ta >0 -.It Sx \&Mt Ta Yes Ta Yes Ta >0 -.It Sx \&Nm Ta Yes Ta Yes Ta n -.It Sx \&No Ta Yes Ta Yes Ta 0 -.It Sx \&Ns Ta Yes Ta Yes Ta 0 -.It Sx \&Nx Ta Yes Ta Yes Ta n -.It Sx \&Os Ta \&No Ta \&No Ta n -.It Sx \&Ot Ta \&No Ta \&No Ta n -.It Sx \&Ox Ta Yes Ta Yes Ta n -.It Sx \&Pa Ta Yes Ta Yes Ta n -.It Sx \&Pf Ta Yes Ta Yes Ta 1 -.It Sx \&Pp Ta \&No Ta \&No Ta 0 -.It Sx \&Rv Ta \&No Ta \&No Ta n -.It Sx \&Sm Ta \&No Ta \&No Ta 1 -.It Sx \&St Ta \&No Ta Yes Ta 1 -.It Sx \&Sx Ta Yes Ta Yes Ta >0 -.It Sx \&Sy Ta Yes Ta Yes Ta >0 -.It Sx \&Tn Ta Yes Ta Yes Ta >0 -.It Sx \&Ud Ta \&No Ta \&No Ta 0 -.It Sx \&Ux Ta Yes Ta Yes Ta n -.It Sx \&Va Ta Yes Ta Yes Ta n -.It Sx \&Vt Ta Yes Ta Yes Ta >0 -.It Sx \&Xr Ta Yes Ta Yes Ta >0 -.It Sx \&br Ta \&No Ta \&No Ta 0 -.It Sx \&sp Ta \&No Ta \&No Ta 1 -.El -.Sh REFERENCE -This section is a canonical reference of all macros, arranged -alphabetically. -For the scoping of individual macros, see -.Sx MACRO SYNTAX . -.Ss \&%A -Author name of an -.Sx \&Rs -block. -Multiple authors should each be accorded their own -.Sx \%%A -line. -Author names should be ordered with full or abbreviated forename(s) -first, then full surname. -.Ss \&%B -Book title of an -.Sx \&Rs -block. -This macro may also be used in a non-bibliographic context when -referring to book titles. -.Ss \&%C -Publication city or location of an -.Sx \&Rs -block. -.Ss \&%D -Publication date of an -.Sx \&Rs -block. -Recommended formats of arguments are -.Ar month day , year -or just -.Ar year . -.Ss \&%I -Publisher or issuer name of an -.Sx \&Rs -block. -.Ss \&%J -Journal name of an -.Sx \&Rs -block. -.Ss \&%N -Issue number (usually for journals) of an -.Sx \&Rs -block. -.Ss \&%O -Optional information of an -.Sx \&Rs -block. -.Ss \&%P -Book or journal page number of an -.Sx \&Rs -block. -.Ss \&%Q -Institutional author (school, government, etc.) of an -.Sx \&Rs -block. -Multiple institutional authors should each be accorded their own -.Sx \&%Q -line. -.Ss \&%R -Technical report name of an -.Sx \&Rs -block. -.Ss \&%T -Article title of an -.Sx \&Rs -block. -This macro may also be used in a non-bibliographical context when -referring to article titles. -.Ss \&%U -URI of reference document. -.Ss \&%V -Volume number of an -.Sx \&Rs -block. -.Ss \&Ac -Close an -.Sx \&Ao -block. -Does not have any tail arguments. -.Ss \&Ad -Memory address. -Do not use this for postal addresses. -.Pp -Examples: -.Dl \&.Ad [0,$] -.Dl \&.Ad 0x00000000 -.Ss \&An -Author name. -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 -.Sx \&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 kristaps@bsd.lv -.Ss \&Ao -Begin a block enclosed by angle brackets. -Does not have any head arguments. +See also +.Sx \&D1 +and +.Sx \&Dl . +.Ss \&Bf +Change the font mode for a scoped block of text. +Its syntax is as follows: +.Bd -ragged -offset indent +.Pf \. Sx \&Bf +.Oo +.Fl emphasis | literal | symbolic | +.Cm \&Em | \&Li | \&Sy +.Oc +.Ed .Pp -Examples: -.Dl \&.Fl -key= \&Ns \&Ao \&Ar val \&Ac +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 +.Sx \&Ef +is encountered. .Pp See also -.Sx \&Aq . -.Ss \&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 -.Ss \&Aq -Encloses its arguments in angle brackets. +.Sx \&Li , +.Sx \&Ef , +.Sx \&Em , +and +.Sx \&Sy . +.Ss \&Bk +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. +The syntax is as follows: .Pp -Examples: -.Dl \&.Fl -key= \&Ns \&Aq \&Ar val +.D1 Pf \. Sx \&Bk Fl words .Pp -.Em Remarks : -this macro is often abused for rendering URIs, which should instead use -.Sx \&Lk -or -.Sx \&Mt , -or to note pre-processor -.Dq Li #include -statements, which should use -.Sx \&In . -.Pp -See also -.Sx \&Ao . -.Ss \&Ar -Command arguments. -If an argument is not provided, the string -.Dq file ...\& -is used as a default. -.Pp -Examples: -.Dl \&.Fl o \&Ns \&Ar file1 -.Dl \&.Ar -.Dl \&.Ar arg1 , arg2 . -.Ss \&At -Formats an AT&T 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 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 V.1 -.Pp -See also -.Sx \&Bsx , -.Sx \&Bx , -.Sx \&Dx , -.Sx \&Fx , -.Sx \&Nx , -.Sx \&Ox , -and -.Sx \&Ux . -.Ss \&Bc -Close a -.Sx \&Bo -block. -Does not have any tail arguments. -.Ss \&Bd -Begin a display block. -Its syntax is as follows: -.Bd -ragged -offset indent -.Pf \. Sx \&Bd -.Fl Ns Ar type -.Op Fl offset Ar width -.Op Fl compact -.Ed -.Pp -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 -Centre-justify each line. -Using this display type is not recommended; many -.Nm -implementations render it poorly. -.It Fl filled -Left- and right-justify the block. -.It Fl literal -Do not justify the block at all. -Preserve white space as it appears in the input. -.It Fl ragged -Only left-justify the block. -.It Fl unfilled -An alias for -.Fl literal . -.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 standard indentation; -.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 centre 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 width using the syntax described in -.Sx Scaling Widths . -.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 -.Sx \&D1 -and -.Sx \&Dl . -.Ss \&Bf -Change the font mode for a scoped block of text. -Its syntax is as follows: -.Bd -ragged -offset indent -.Pf \. Sx \&Bf -.Oo -.Fl emphasis | literal | symbolic | -.Cm \&Em | \&Li | \&Sy -.Oc -.Ed -.Pp -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 -.Sx \&Ef -is encountered. -.Pp -See also -.Sx \&Li , -.Sx \&Ef , -.Sx \&Em , -and -.Sx \&Sy . -.Ss \&Bk -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. -The syntax is as follows: -.Pp -.D1 Pf \. Sx \&Bk Fl words -.Pp -The -.Fl words -argument is required; additional arguments are ignored. +The +.Fl words +argument is required; additional arguments are ignored. .Pp The following example will not break within each .Sx \&Op @@ -1274,9 +979,12 @@ except that dashes are used in place of bullets. Like .Fl inset , except that item heads are not parsed for macro invocations. -.\" but with additional formatting to the head. +Most often used in the +.Em DIAGNOSTICS +section with error constants in the item heads. .It Fl enum A numbered list. +No item heads can be specified. Formatted like .Fl bullet , except that cardinal numbers are used in place of bullets, @@ -1316,6 +1024,13 @@ 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 .Sx \&El and @@ -1392,12 +1107,13 @@ and .Sx \&Ux . .Ss \&Bt Prints -.Dq is currently in beta test . +.Dq is currently in beta test. .Ss \&Bx Format the BSD 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 @@ -1414,6 +1130,7 @@ and 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? @@ -1426,14 +1143,17 @@ declarations. This practise is discouraged. .Ss \&Cm Command modifiers. -Useful when specifying configuration options or keys. +Typically used for fixed strings passed as arguments, unless +.Sx \&Fl +is more appropriate. +Also useful when specifying configuration options or keys. .Pp Examples: -.Dl \&.Cm ControlPath -.Dl \&.Cm ControlMaster -.Pp -See also -.Sx \&Fl . +.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 ".Cm IdentityFile Pa ~/.ssh/id_rsa" +.Dl ".Cm LogLevel Dv DEBUG" .Ss \&D1 One-line indented display. This is formatted by the default rules and is useful for simple indented @@ -1562,7 +1282,8 @@ Its syntax is as follows: .Ar title .Oo .Ar section -.Op Ar volume | arch +.Op Ar volume +.Op Ar arch .Oc .Oc .Ed @@ -1643,42 +1364,19 @@ or .Ar CON .Pq contributed manuals . .It Ar arch -This specifies a specific relevant architecture. -If -.Ar volume -is not provided, it may be used in its place, else it may be used -subsequent that. -It, too, is optional. -It must be one of -.Ar alpha , -.Ar amd64 , -.Ar amiga , -.Ar arc , -.Ar arm , -.Ar armish , -.Ar aviion , -.Ar hp300 , -.Ar hppa , -.Ar hppa64 , -.Ar i386 , -.Ar landisk , -.Ar loongson , -.Ar luna88k , -.Ar mac68k , -.Ar macppc , -.Ar mips64 , -.Ar mvme68k , -.Ar mvme88k , -.Ar mvmeppc , -.Ar pmax , -.Ar sgi , -.Ar socppc , -.Ar sparc , -.Ar sparc64 , -.Ar sun3 , -.Ar vax , +This specifies the machine architecture a manual page applies to, +where relevant, for example +.Cm alpha , +.Cm amd64 , +.Cm i386 , or -.Ar zaurus . +.Cm sparc64 . +The list of supported architectures varies by operating system. +For the full list of all architectures recognized by +.Xr mandoc 1 , +see the file +.Pa arch.in +in the source distribution. .El .Pp Examples: @@ -1691,14 +1389,21 @@ See also and .Sx \&Os . .Ss \&Dv -Defined variables such as preprocessor constants. +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 -.Sx \&Er . +.Sx \&Er +and +.Sx \&Ev +for special-purpose constants and +.Sx \&Va +for variable symbols. .Ss \&Dx Format the DragonFly BSD version provided as an argument, or a default value if no argument is provided. @@ -1746,9 +1451,12 @@ See also and .Sx \&It . .Ss \&Em -Denotes text that should be emphasised. +Denotes text that should be +.Em emphasised . Note that this is a presentation term and should not be used for stylistically decorating technical terms. +Depending on the output device, this is usually represented +using an italic font or underlined characters. .Pp Examples: .Dl \&.Em Warnings! @@ -1756,9 +1464,10 @@ Examples: .Pp See also .Sx \&Bf , -.Sx \&Sy , +.Sx \&Li , +.Sx \&No , and -.Sx \&Li . +.Sx \&Sy . .Ss \&En This macro is obsolete and not implemented in .Xr mandoc 1 . @@ -1774,14 +1483,18 @@ argument is used as the enclosure head, for example, specifying \e(lq will emulate .Sx \&Do . .Ss \&Er -Display error constants. +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 -.Sx \&Dv . +.Sx \&Dv +for general constants. .Ss \&Es This macro is obsolete and not implemented. .Ss \&Ev @@ -1791,17 +1504,26 @@ Environmental variables such as those specified in Examples: .Dl \&.Ev DISPLAY .Dl \&.Ev PATH +.Pp +See also +.Sx \&Dv +for general constants. .Ss \&Ex -Insert a standard sentence regarding exit values. +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. Its syntax is as follows: .Pp -.D1 Pf \. Sx \&Ex Fl std Op Ar utility +.D1 Pf \. Sx \&Ex Fl std Op Ar utility ... .Pp -When +If .Ar utility is not specified, the document's name set by .Sx \&Nm is used. +Multiple +.Ar utility +arguments are treated as separate utilities. .Pp See also .Sx \&Rv . @@ -1850,7 +1572,7 @@ See also and .Sx \&In . .Ss \&Fl -Command-line flag. +Command-line flag or option. Used when listing arguments to command-line utilities. Prints a fixed-width hyphen .Sq \- @@ -1860,10 +1582,11 @@ If the argument is a macro, a hyphen is prefixed to the subsequent macro output. .Pp Examples: -.Dl \&.Fl a b c -.Dl \&.Fl \&Pf a b -.Dl \&.Fl -.Dl \&.Op \&Fl o \&Ns \&Ar file +.Dl ".Fl R Op Fl H | L | P" +.Dl ".Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux" +.Dl ".Fl type Cm d Fl name Pa CVS" +.Dl ".Fl Ar signal_number" +.Dl ".Fl o Fl" .Pp See also .Sx \&Cm . @@ -1880,11 +1603,16 @@ Its syntax is as follows: 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 \*qint funcname\*q \*qint arg0\*q \*qint arg1\*q -.Dl \&.Fn funcname \*qint arg0\*q +.Dl \&.Fn \(dqint funcname\(dq \(dqint arg0\(dq \(dqint arg1\(dq +.Dl \&.Fn funcname \(dqint arg0\(dq .Dl \&.Fn funcname arg0 +.Pp .Bd -literal -offset indent -compact \&.Ft functype \&.Fn funcname @@ -1894,7 +1622,8 @@ When referring to a function documented in another manual page, use .Sx \&Xr instead. See also -.Sx MANUAL STRUCTURE +.Sx MANUAL STRUCTURE , +.Sx \&Fo , and .Sx \&Ft . .Ss \&Fo @@ -1921,6 +1650,7 @@ Invocations usually occur in the following context: A .Sx \&Fo scope is closed by +.Sx \&Fc . .Pp See also .Sx MANUAL STRUCTURE , @@ -1928,12 +1658,24 @@ See also .Sx \&Fc , and .Sx \&Ft . +.Ss \&Fr +This macro is obsolete and not implemented in +.Xr mandoc 1 . +.Pp +It was used to show function return values. +The syntax was: +.Pp +.Dl Pf . Sx \&Fr Ar value .Ss \&Ft A function type. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Ft Ar functype .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 @@ -1966,7 +1708,13 @@ See also and .Sx \&Ux . .Ss \&Hf -This macro is obsolete and not implemented. +This macro is not implemented in +.Xr mandoc 1 . +.Pp +It was used to include the contents of a (header) file literally. +The syntax was: +.Pp +.Dl Pf . Sx \&Hf Ar filename .Ss \&Ic Designate an internal or interactive command. This is similar to @@ -1974,6 +1722,7 @@ This is similar to but used for instructions rather than values. .Pp Examples: +.Dl \&.Ic :wq .Dl \&.Ic hash .Dl \&.Ic alias .Pp @@ -1988,15 +1737,17 @@ macro is used when referring to specific instructions. An .Dq include file. -In the +When invoked as the first macro on an input line in the .Em SYNOPSIS -section (only if invoked as the line macro), the first argument is -preceded by +section, the argument is displayed in angle brackets +and preceded by .Dq #include , -the arguments is enclosed in angle brackets. +and a blank line is inserted in front if there is a preceding +function declaration. +This is most often used in section 2, 3, and 9 manual pages. .Pp Examples: -.Dl \&.In sys/types +.Dl \&.In sys/types.h .Pp See also .Sx MANUAL STRUCTURE . @@ -2050,31 +1801,27 @@ The list is the most complicated. Its syntax is as follows: .Pp -.D1 Pf \. Sx \&It Op Cm args +.D1 Pf \. Sx \&It Ar cell Op <TAB> Ar cell ... +.D1 Pf \. Sx \&It Ar cell Op Sx \&Ta Ar cell ... .Pp -The -.Cm args -are phrases, a mix of macros and text corresponding to a line column, -delimited by tabs or the special -.Sq \&Ta -pseudo-macro. -Lines subsequent the +The arguments consist of one or more lines of text and macros +representing a complete table line. +Cells within the line are delimited by tabs or by the special +.Sx \&Ta +block macro. +The tab cell delimiter may only be used within the .Sx \&It -are interpreted within the scope of the last phrase. -Calling the pseudo-macro -.Sq \&Ta -will open a new phrase scope (this must occur on a macro line to be -interpreted as a macro). -Note that the tab phrase delimiter may only be used within the +line itself; on following lines, only the +.Sx \&Ta +macro can be used to delimit cells, and +.Sx \&Ta +is only recognised as a macro when called by other macros, +not as the first macro on a line. +.Pp +Note that quoted strings may span tab-delimited cells on an .Sx \&It -line itself. -Subsequent this, only the -.Sq \&Ta -pseudo-macro may be used to delimit phrases. -Furthermore, note that quoted sections propagate over tab-delimited -phrases on an -.Sx \&It , -for example, +line. +For example, .Pp .Dl .It \(dqcol1 ; <TAB> col2 ;\(dq \&; .Pp @@ -2091,9 +1838,9 @@ The syntax is as follows: The .Ar library parameter may be a system library, such as -.Ar libz +.Cm libz or -.Ar libpam , +.Cm libpam , in which case a small library description is printed next to the linker invocation; or a custom library, in which case the library name is printed in quotes. @@ -2106,15 +1853,21 @@ Examples: .Dl \&.Lb libz .Dl \&.Lb mdoc .Ss \&Li -Denotes text that should be in a literal font mode. +Denotes text that should be in a +.Li literal +font mode. Note that this is a presentation term and should not be used for stylistically decorating technical terms. .Pp +On terminal output devices, this is often indistinguishable from +normal text. +.Pp See also .Sx \&Bf , -.Sx \&Sy , +.Sx \&Em , +.Sx \&No , and -.Sx \&Em . +.Sx \&Sy . .Ss \&Lk Format a hyperlink. Its syntax is as follows: @@ -2122,7 +1875,7 @@ Its syntax is as follows: .D1 Pf \. Sx \&Lk Ar uri Op Ar name .Pp Examples: -.Dl \&.Lk http://bsd.lv \*qThe BSD.lv Project\*q +.Dl \&.Lk http://bsd.lv \(dqThe BSD.lv Project\(dq .Dl \&.Lk http://bsd.lv .Pp See also @@ -2158,8 +1911,8 @@ section subsequent the macro. .Pp Examples: -.Dl \&.Sx \&Nd mdoc language reference -.Dl \&.Sx \&Nd format and display UNIX manuals +.Dl Pf . Sx \&Nd mdoc language reference +.Dl Pf . Sx \&Nd format and display UNIX manuals .Pp The .Sx \&Nd @@ -2211,21 +1964,44 @@ macro rather than .Sx \&Nm to mark up the name of the manual page. .Ss \&No -A -.Dq noop -macro used to terminate prior macro contexts. +Normal text. +Closes the scope of any preceding in-line macro. +When used after physical formatting macros like +.Sx \&Em +or +.Sx \&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 \&.Sx \&Fl ab \&No cd \&Fl ef +.Dl ".Em italic , Sy bold , No and roman" +.Pp +.Bd -literal -offset indent -compact +\&.Sm off +\&.Cm :C No / Ar pattern No / Ar replacement No / +\&.Sm on +.Ed +.Pp +See also +.Sx \&Em , +.Sx \&Li , +and +.Sx \&Sy . .Ss \&Ns -Suppress a space. -Following invocation, text is interpreted as free-form text until a -macro is encountered. +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 +.Sx \&No +macro. .Pp This has no effect when invoked at the start of a macro line. .Pp Examples: -.Dl \&.Fl o \&Ns \&Ar output +.Dl ".Ar name Ns = Ns Ar value" +.Dl ".Cm :M Ns Ar pattern" +.Dl ".Fl o Ns Ar output" .Pp See also .Sx \&No @@ -2265,9 +2041,11 @@ Examples: \&.Oc .Ed .Ss \&Op -Command-line option. -Used when listing options to command-line utilities. +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 @@ -2301,10 +2079,13 @@ See also and .Sx \&Dt . .Ss \&Ot -Unknown usage. +This macro is obsolete and not implemented in +.Xr mandoc 1 . .Pp -.Em Remarks : -this macro has been deprecated. +Historical +.Xr mdoc 7 +packages described it as +.Dq "old function type (FORTRAN)" . .Ss \&Ox Format the .Ox @@ -2325,9 +2106,9 @@ See also and .Sx \&Ux . .Ss \&Pa -A file-system path. -If an argument is not provided, the string -.Dq \(ti +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: @@ -2340,19 +2121,25 @@ See also Close parenthesised context opened by .Sx \&Po . .Ss \&Pf -Removes the space +Removes the space between its argument .Pq Dq prefix -between its arguments. +and the following macro. Its syntax is as follows: .Pp -.D1 Pf \. \&Pf Ar prefix suffix +.D1 .Pf Ar prefix macro arguments ... .Pp -The -.Ar suffix -argument may be a macro. +This is equivalent to: +.Pp +.D1 .No Ar prefix No \&Ns Ar macro arguments ... .Pp Examples: -.Dl \&.Pf \e. \&Sx \&Pf \&Ar prefix suffix +.Dl ".Pf $ Ar variable_name" +.Dl ".Pf 0x Ar hex_digits" +.Pp +See also +.Sx \&Ns +and +.Sx \&Sm . .Ss \&Po Multi-line version of .Sx \&Pq . @@ -2360,6 +2147,18 @@ Multi-line version of 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 +.Sx \&Sh +or +.Sx \&Ss +macros or before displays +.Pq Sx \&Bd +or lists +.Pq Sx \&Bl +unless the +.Fl compact +flag is given. .Ss \&Pq Parenthesised enclosure. .Pp @@ -2379,7 +2178,7 @@ Multi-line version of .Sx \&Qq . .Ss \&Qq Encloses its arguments in -.Dq typewriter +.Qq typewriter double-quotes. Consider using .Sx \&Dq . @@ -2435,16 +2234,22 @@ 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. .Ss \&Rv -Inserts text regarding a function call's return value. -This macro must consist of the -.Fl std -argument followed by an optional -.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. +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Rv Fl std Op Ar function ... +.Pp If .Ar function -is not provided, the document's name as stipulated by the first +is not specified, the document's name set by .Sx \&Nm -is provided. +is used. +Multiple +.Ar function +arguments are treated as separate functions. .Pp See also .Sx \&Ex . @@ -2460,6 +2265,9 @@ custom sections be used. .Pp Section names should be unique so that they may be keyed by .Sx \&Sx . +Although this macro is parsed, it should not consist of child node or it +may not be linked with +.Sx \&Sx . .Pp See also .Sx \&Pp , @@ -2473,9 +2281,9 @@ Its syntax is as follows: .D1 Pf \. Sx \&Sm Cm on | off .Pp By default, spacing is -.Ar on . +.Cm on . When switched -.Ar off , +.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. @@ -2484,7 +2292,7 @@ Multi-line version of .Sx \&Sq . .Ss \&Sq Encloses its arguments in -.Dq typewriter +.Sq typewriter single-quotes. .Pp See also @@ -2493,16 +2301,21 @@ See also and .Sx \&So . .Ss \&Ss -Begin a new sub-section. +Begin a new subsection. Unlike with .Sx \&Sh , -there's no convention for sub-sections. -Conventional sections, as described in -.Sx MANUAL STRUCTURE , -rarely have sub-sections. +there is no convention for the naming of subsections. +Except +.Em DESCRIPTION , +the conventional sections described in +.Sx MANUAL STRUCTURE +rarely have subsections. .Pp Sub-section names should be unique so that they may be keyed by .Sx \&Sx . +Although this macro is parsed, it should not consist of child node or it +may not be linked with +.Sx \&Sx . .Pp See also .Sx \&Pp , @@ -2560,6 +2373,8 @@ The following standards are recognised: .St -isoC-tcor2 .It \-isoC-99 .St -isoC-99 +.It \-isoC-2011 +.St -isoC-2011 .It \-iso9945-1-90 .St -iso9945-1-90 .It \-iso9945-1-96 @@ -2576,6 +2391,8 @@ The following standards are recognised: .St -ieee754 .It \-iso8802-3 .St -iso8802-3 +.It \-iso8601 +.St -iso8601 .It \-ieee1275-94 .St -ieee1275-94 .It \-xpg3 @@ -2584,6 +2401,7 @@ The following standards are recognised: .St -xpg4 .It \-xpg4.2 .St -xpg4.2 +.It \-xpg4.3 .St -xpg4.3 .It \-xbd5 .St -xbd5 @@ -2606,142 +2424,545 @@ The following standards are recognised: .It \-svid4 .St -svid4 .El -.Ss \&Sx -Reference a section or sub-section. -The referenced section or sub-section name must be identical to the -enclosed argument, including whitespace. +.Ss \&Sx +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 +.Sx \&Sh +and +.Sx \&Ss . +.Ss \&Sy +Format enclosed arguments in symbolic +.Pq Dq boldface . +Note that this is a presentation term and should not be used for +stylistically decorating technical terms. +.Pp +See also +.Sx \&Bf , +.Sx \&Em , +.Sx \&Li , +and +.Sx \&No . +.Ss \&Ta +Table cell separator in +.Sx \&Bl Fl column +lists; can only be used below +.Sx \&It . +.Ss \&Tn +Format a tradename. +.Pp +Since this macro is often implemented to use a small caps font, +it has historically been used for acronyms (like ASCII) as well. +Such usage is not recommended because it would use the same macro +sometimes for semantical annotation, sometimes for physical formatting. +.Pp +Examples: +.Dl \&.Tn IBM +.Ss \&Ud +Prints out +.Dq currently under development. +.Ss \&Ux +Format the UNIX name. +Accepts no argument. +.Pp +Examples: +.Dl \&.Ux +.Pp +See also +.Sx \&At , +.Sx \&Bsx , +.Sx \&Bx , +.Sx \&Dx , +.Sx \&Fx , +.Sx \&Nx , +and +.Sx \&Ox . +.Ss \&Va +A variable name. +.Pp +Examples: +.Dl \&.Va foo +.Dl \&.Va const char *bar ; +.Ss \&Vt +A variable type. +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 +Note that this should not be confused with +.Sx \&Ft , +which is used for function return types. +.Pp +Examples: +.Dl \&.Vt unsigned char +.Dl \&.Vt extern const char * const sys_signame[] \&; +.Pp +See also +.Sx MANUAL STRUCTURE +and +.Sx \&Va . +.Ss \&Xc +Close a scope opened by +.Sx \&Xo . +.Ss \&Xo +Extend the header of an +.Sx \&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 . +.Ss \&Xr +Link to another manual +.Pq Qq cross-reference . +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Xr Ar name section +.Pp +The +.Ar name +and +.Ar section +are the name and section of the linked manual. +If +.Ar section +is followed by non-punctuation, an +.Sx \&Ns +is inserted into the token stream. +This behaviour is for compatibility with +GNU troff. +.Pp +Examples: +.Dl \&.Xr mandoc 1 +.Dl \&.Xr mandoc 1 \&; +.Dl \&.Xr mandoc 1 \&Ns s behaviour +.Ss \&br +Emits a line-break. +This macro should not be used; it is implemented for compatibility with +historical manuals. +.Pp +Consider using +.Sx \&Pp +in the event of natural paragraph breaks. +.Ss \&sp +Emits vertical space. +This macro should not be used; it is implemented for compatibility with +historical manuals. +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&sp Op Ar height +.Pp +The +.Ar height +argument must be formatted as described in +.Sx Scaling Widths . +If unspecified, +.Sx \&sp +asserts a single vertical space. +.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 +.Sx \&Bf +and +.Pq optionally +.Sx \&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 Sx \&Bd Ta \&No Ta \&No Ta closed by Sx \&Ed +.It Sx \&Bf Ta \&No Ta \&No Ta closed by Sx \&Ef +.It Sx \&Bk Ta \&No Ta \&No Ta closed by Sx \&Ek +.It Sx \&Bl Ta \&No Ta \&No Ta closed by Sx \&El +.It Sx \&Ed Ta \&No Ta \&No Ta opened by Sx \&Bd +.It Sx \&Ef Ta \&No Ta \&No Ta opened by Sx \&Bf +.It Sx \&Ek Ta \&No Ta \&No Ta opened by Sx \&Bk +.It Sx \&El Ta \&No Ta \&No Ta opened by Sx \&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 +.Sx \&It Fl bullet , +.Fl hyphen , +.Fl dash , +.Fl enum , +.Fl item +.Pc +don't have heads; only one +.Po +.Sx \&It +in +.Sx \&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 Sx \&It Ta \&No Ta Yes Ta closed by Sx \&It , Sx \&El +.It Sx \&Nd Ta \&No Ta \&No Ta closed by Sx \&Sh +.It Sx \&Nm Ta \&No Ta Yes Ta closed by Sx \&Nm , Sx \&Sh , Sx \&Ss +.It Sx \&Sh Ta \&No Ta Yes Ta closed by Sx \&Sh +.It Sx \&Ss Ta \&No Ta Yes Ta closed by Sx \&Sh , Sx \&Ss +.El +.Pp +Note that the +.Sx \&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 +.Sx \&Fo , +.Sx \&Eo +.Pc +and/or tail +.Pq Sx \&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 Sx \&Ac Ta Yes Ta Yes Ta opened by Sx \&Ao +.It Sx \&Ao Ta Yes Ta Yes Ta closed by Sx \&Ac +.It Sx \&Bc Ta Yes Ta Yes Ta closed by Sx \&Bo +.It Sx \&Bo Ta Yes Ta Yes Ta opened by Sx \&Bc +.It Sx \&Brc Ta Yes Ta Yes Ta opened by Sx \&Bro +.It Sx \&Bro Ta Yes Ta Yes Ta closed by Sx \&Brc +.It Sx \&Dc Ta Yes Ta Yes Ta opened by Sx \&Do +.It Sx \&Do Ta Yes Ta Yes Ta closed by Sx \&Dc +.It Sx \&Ec Ta Yes Ta Yes Ta opened by Sx \&Eo +.It Sx \&Eo Ta Yes Ta Yes Ta closed by Sx \&Ec +.It Sx \&Fc Ta Yes Ta Yes Ta opened by Sx \&Fo +.It Sx \&Fo Ta \&No Ta \&No Ta closed by Sx \&Fc +.It Sx \&Oc Ta Yes Ta Yes Ta closed by Sx \&Oo +.It Sx \&Oo Ta Yes Ta Yes Ta opened by Sx \&Oc +.It Sx \&Pc Ta Yes Ta Yes Ta closed by Sx \&Po +.It Sx \&Po Ta Yes Ta Yes Ta opened by Sx \&Pc +.It Sx \&Qc Ta Yes Ta Yes Ta opened by Sx \&Oo +.It Sx \&Qo Ta Yes Ta Yes Ta closed by Sx \&Oc +.It Sx \&Re Ta \&No Ta \&No Ta opened by Sx \&Rs +.It Sx \&Rs Ta \&No Ta \&No Ta closed by Sx \&Re +.It Sx \&Sc Ta Yes Ta Yes Ta opened by Sx \&So +.It Sx \&So Ta Yes Ta Yes Ta closed by Sx \&Sc +.It Sx \&Xc Ta Yes Ta Yes Ta opened by Sx \&Xo +.It Sx \&Xo Ta Yes Ta Yes Ta closed by Sx \&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 Sx \&Aq Ta Yes Ta Yes +.It Sx \&Bq Ta Yes Ta Yes +.It Sx \&Brq Ta Yes Ta Yes +.It Sx \&D1 Ta \&No Ta \&Yes +.It Sx \&Dl Ta \&No Ta Yes +.It Sx \&Dq Ta Yes Ta Yes +.It Sx \&Op Ta Yes Ta Yes +.It Sx \&Pq Ta Yes Ta Yes +.It Sx \&Ql Ta Yes Ta Yes +.It Sx \&Qq Ta Yes Ta Yes +.It Sx \&Sq Ta Yes Ta Yes +.It Sx \&Vt Ta Yes Ta Yes +.El .Pp -Examples: -.Dl \&.Sx MANUAL STRUCTURE +Note that the +.Sx \&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 +.Sx \&Ta +macro can only be used below +.Sx \&It +in +.Sx \&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 Sx \&Ta Ta Yes Ta Yes Ta closed by Sx \&Ta , Sx \&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 Sx \&%A Ta \&No Ta \&No Ta >0 +.It Sx \&%B Ta \&No Ta \&No Ta >0 +.It Sx \&%C Ta \&No Ta \&No Ta >0 +.It Sx \&%D Ta \&No Ta \&No Ta >0 +.It Sx \&%I Ta \&No Ta \&No Ta >0 +.It Sx \&%J Ta \&No Ta \&No Ta >0 +.It Sx \&%N Ta \&No Ta \&No Ta >0 +.It Sx \&%O Ta \&No Ta \&No Ta >0 +.It Sx \&%P Ta \&No Ta \&No Ta >0 +.It Sx \&%Q Ta \&No Ta \&No Ta >0 +.It Sx \&%R Ta \&No Ta \&No Ta >0 +.It Sx \&%T Ta \&No Ta \&No Ta >0 +.It Sx \&%U Ta \&No Ta \&No Ta >0 +.It Sx \&%V Ta \&No Ta \&No Ta >0 +.It Sx \&Ad Ta Yes Ta Yes Ta >0 +.It Sx \&An Ta Yes Ta Yes Ta >0 +.It Sx \&Ap Ta Yes Ta Yes Ta 0 +.It Sx \&Ar Ta Yes Ta Yes Ta n +.It Sx \&At Ta Yes Ta Yes Ta 1 +.It Sx \&Bsx Ta Yes Ta Yes Ta n +.It Sx \&Bt Ta \&No Ta \&No Ta 0 +.It Sx \&Bx Ta Yes Ta Yes Ta n +.It Sx \&Cd Ta Yes Ta Yes Ta >0 +.It Sx \&Cm Ta Yes Ta Yes Ta >0 +.It Sx \&Db Ta \&No Ta \&No Ta 1 +.It Sx \&Dd Ta \&No Ta \&No Ta n +.It Sx \&Dt Ta \&No Ta \&No Ta n +.It Sx \&Dv Ta Yes Ta Yes Ta >0 +.It Sx \&Dx Ta Yes Ta Yes Ta n +.It Sx \&Em Ta Yes Ta Yes Ta >0 +.It Sx \&En Ta \&No Ta \&No Ta 0 +.It Sx \&Er Ta Yes Ta Yes Ta >0 +.It Sx \&Es Ta \&No Ta \&No Ta 0 +.It Sx \&Ev Ta Yes Ta Yes Ta >0 +.It Sx \&Ex Ta \&No Ta \&No Ta n +.It Sx \&Fa Ta Yes Ta Yes Ta >0 +.It Sx \&Fd Ta \&No Ta \&No Ta >0 +.It Sx \&Fl Ta Yes Ta Yes Ta n +.It Sx \&Fn Ta Yes Ta Yes Ta >0 +.It Sx \&Fr Ta \&No Ta \&No Ta n +.It Sx \&Ft Ta Yes Ta Yes Ta >0 +.It Sx \&Fx Ta Yes Ta Yes Ta n +.It Sx \&Hf Ta \&No Ta \&No Ta n +.It Sx \&Ic Ta Yes Ta Yes Ta >0 +.It Sx \&In Ta \&No Ta \&No Ta 1 +.It Sx \&Lb Ta \&No Ta \&No Ta 1 +.It Sx \&Li Ta Yes Ta Yes Ta >0 +.It Sx \&Lk Ta Yes Ta Yes Ta >0 +.It Sx \&Lp Ta \&No Ta \&No Ta 0 +.It Sx \&Ms Ta Yes Ta Yes Ta >0 +.It Sx \&Mt Ta Yes Ta Yes Ta >0 +.It Sx \&Nm Ta Yes Ta Yes Ta n +.It Sx \&No Ta Yes Ta Yes Ta 0 +.It Sx \&Ns Ta Yes Ta Yes Ta 0 +.It Sx \&Nx Ta Yes Ta Yes Ta n +.It Sx \&Os Ta \&No Ta \&No Ta n +.It Sx \&Ot Ta \&No Ta \&No Ta n +.It Sx \&Ox Ta Yes Ta Yes Ta n +.It Sx \&Pa Ta Yes Ta Yes Ta n +.It Sx \&Pf Ta Yes Ta Yes Ta 1 +.It Sx \&Pp Ta \&No Ta \&No Ta 0 +.It Sx \&Rv Ta \&No Ta \&No Ta n +.It Sx \&Sm Ta \&No Ta \&No Ta 1 +.It Sx \&St Ta \&No Ta Yes Ta 1 +.It Sx \&Sx Ta Yes Ta Yes Ta >0 +.It Sx \&Sy Ta Yes Ta Yes Ta >0 +.It Sx \&Tn Ta Yes Ta Yes Ta >0 +.It Sx \&Ud Ta \&No Ta \&No Ta 0 +.It Sx \&Ux Ta Yes Ta Yes Ta n +.It Sx \&Va Ta Yes Ta Yes Ta n +.It Sx \&Vt Ta Yes Ta Yes Ta >0 +.It Sx \&Xr Ta Yes Ta Yes Ta >0 +.It Sx \&br Ta \&No Ta \&No Ta 0 +.It Sx \&sp Ta \&No Ta \&No Ta 1 +.El +.Ss Delimiters +When a macro argument consists of one single input character +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 -See also -.Sx \&Sh -and -.Sx \&Ss . -.Ss \&Sy -Format enclosed arguments in symbolic -.Pq Dq boldface . -Note that this is a presentation term and should not be used for -stylistically decorating technical terms. +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. +For example, .Pp -See also -.Sx \&Bf , -.Sx \&Li , -and -.Sx \&Em . -.Ss \&Tn -Format a tradename. +.D1 Pf \. \&Aq "( [ word ] ) ." .Pp -Examples: -.Dl \&.Tn IBM -.Ss \&Ud -Prints out -.Dq currently under development . -.Ss \&Ux -Format the UNIX name. -Accepts no argument. +renders as: .Pp -Examples: -.Dl \&.Ux +.D1 Aq ( [ word ] ) . .Pp -See also -.Sx \&At , -.Sx \&Bsx , -.Sx \&Bx , -.Sx \&Dx , -.Sx \&Fx , -.Sx \&Nx , -and -.Sx \&Ox . -.Ss \&Va -A variable name. +Opening delimiters are: .Pp -Examples: -.Dl \&.Va foo -.Dl \&.Va const char *bar ; -.Ss \&Vt -A variable type. -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 in the -.Em SYNOPSIS -section, else it accepts ordinary -.Sx In-line -syntax. +.Bl -tag -width Ds -offset indent -compact +.It \&( +left parenthesis +.It \&[ +left bracket +.El .Pp -Note that this should not be confused with -.Sx \&Ft , -which is used for function return types. +Closing delimiters are: .Pp -Examples: -.Dl \&.Vt unsigned char -.Dl \&.Vt extern const char * const sys_signame[] \&; +.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 -See also -.Sx MANUAL STRUCTURE -and -.Sx \&Va . -.Ss \&Xc -Close a scope opened by -.Sx \&Xo . -.Ss \&Xo -Extend the header of an -.Sx \&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 . -.Ss \&Xr -Link to another manual -.Pq Qq cross-reference . -Its syntax is as follows: +Note that even a period preceded by a backslash +.Pq Sq \e.\& +gets this special handling; use +.Sq \e&. +to prevent that. .Pp -.D1 Pf \. Sx \&Xr Ar name section +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 -The -.Ar name -and -.Ar section -are the name and section of the linked manual. -If -.Ar section -is followed by non-punctuation, an -.Sx \&Ns -is inserted into the token stream. -This behaviour is for compatibility with -GNU troff. +.D1 Pf \. \&Fl "a ( b | c \e*(Ba d ) e" .Pp -Examples: -.Dl \&.Xr mandoc 1 -.Dl \&.Xr mandoc 1 \&; -.Dl \&.Xr mandoc 1 \&Ns s behaviour -.Ss \&br -Emits a line-break. -This macro should not be used; it is implemented for compatibility with -historical manuals. +renders as: .Pp -Consider using -.Sx \&Pp -in the event of natural paragraph breaks. -.Ss \&sp -Emits vertical space. -This macro should not be used; it is implemented for compatibility with -historical manuals. -Its syntax is as follows: +.D1 Fl a ( b | c \*(Ba d ) e .Pp -.D1 Pf \. Sx \&sp Op Ar height +This applies to both opening and closing delimiters, +and also to the middle delimiter: .Pp -The -.Ar height -argument must be formatted as described in -.Sx Scaling Widths . -If unspecified, -.Sx \&sp -asserts a single vertical space. +.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. +.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 documents compatibility between mandoc and other other troff implementations, at this time limited to GNU troff @@ -2778,8 +2999,8 @@ Newer groff and mandoc print .Qq AT&T UNIX and the arguments. .It -.Sx \&Bd Fl column -does not recognize trailing punctuation characters when they immediately +.Sx \&Bl Fl column +does not recognise trailing punctuation characters when they immediately precede tabulator characters, but treats them as normal text and outputs a space before them. .It @@ -2838,7 +3059,7 @@ In new groff and mandoc, any list may be nested by default and lists will restart the sequence only for the sub-list. .It .Sx \&Li -followed by a reserved character is incorrectly used in some manuals +followed by a delimiter is incorrectly used in some manuals instead of properly quoting that character, which sometimes works with historic groff. .It @@ -2891,7 +3112,7 @@ The following features are unimplemented in mandoc: .Fl offset Ar center and .Fl offset Ar right . -Groff does not implement centered and flush-right rendering either, +Groff does not implement centred and flush-right rendering either, but produces large indentations. .It The @@ -2929,7 +3150,7 @@ This is not supported by mandoc. .Xr mandoc 1 , .Xr eqn 7 , .Xr man 7 , -.Xr mandoc_char 7 +.Xr mandoc_char 7 , .Xr roff 7 , .Xr tbl 7 .Sh HISTORY @@ -2947,4 +3168,5 @@ utility written by Kristaps Dzonsons appeared in The .Nm reference was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/mdoc.c b/contrib/mdocml/mdoc.c index ea3fda2691..81a4ffc96f 100644 --- a/contrib/mdocml/mdoc.c +++ b/contrib/mdocml/mdoc.c @@ -1,4 +1,4 @@ -/* $Id: mdoc.c,v 1.188 2011/03/28 23:52:13 kristaps Exp $ */ +/* $Id: mdoc.c,v 1.196 2011/09/30 00:13:28 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org> @@ -97,6 +97,9 @@ static struct mdoc_node *node_alloc(struct mdoc *, int, int, enum mdoct, enum mdoc_type); static int node_append(struct mdoc *, struct mdoc_node *); +#if 0 +static int mdoc_preptext(struct mdoc *, int, char *, int); +#endif static int mdoc_ptext(struct mdoc *, int, char *, int); static int mdoc_pmacro(struct mdoc *, int, char *, int); @@ -157,6 +160,7 @@ mdoc_alloc1(struct mdoc *mdoc) mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node)); mdoc->first = mdoc->last; mdoc->last->type = MDOC_ROOT; + mdoc->last->tok = MDOC_MAX; mdoc->next = MDOC_NEXT_CHILD; } @@ -193,14 +197,14 @@ mdoc_free(struct mdoc *mdoc) * Allocate volatile and non-volatile parse resources. */ struct mdoc * -mdoc_alloc(struct regset *regs, struct mparse *parse) +mdoc_alloc(struct roff *roff, struct mparse *parse) { struct mdoc *p; p = mandoc_calloc(1, sizeof(struct mdoc)); p->parse = parse; - p->regs = regs; + p->roff = roff; mdoc_hash_init(); mdoc_alloc1(p); @@ -233,11 +237,11 @@ mdoc_addeqn(struct mdoc *m, const struct eqn *ep) /* No text before an initial macro. */ if (SEC_NONE == m->lastnamed) { - mdoc_pmsg(m, ep->line, ep->pos, MANDOCERR_NOTEXT); + mdoc_pmsg(m, ep->ln, ep->pos, MANDOCERR_NOTEXT); return(1); } - n = node_alloc(m, ep->line, ep->pos, MDOC_MAX, MDOC_EQN); + n = node_alloc(m, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN); n->eqn = ep; if ( ! node_append(m, n)) @@ -290,8 +294,8 @@ mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs) * whether this mode is on or off. * Note that this mode is also switched by the Sh macro. */ - if (m->regs->regs[(int)REG_nS].set) { - if (m->regs->regs[(int)REG_nS].v.u) + if (roff_regisset(m->roff, REG_nS)) { + if (roff_regget(m->roff, REG_nS)) m->flags |= MDOC_SYNOPSIS; else m->flags &= ~MDOC_SYNOPSIS; @@ -565,16 +569,9 @@ int mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p) { struct mdoc_node *n; - size_t sv, len; - - len = strlen(p); n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT); - n->string = mandoc_malloc(len + 1); - sv = strlcpy(n->string, p, len + 1); - - /* Prohibit truncation. */ - assert(sv < len + 1); + n->string = roff_strdup(m->roff, p); if ( ! node_append(m, n)) return(0); @@ -650,6 +647,59 @@ mdoc_node_delete(struct mdoc *m, struct mdoc_node *p) mdoc_node_free(p); } +#if 0 +/* + * Pre-treat a text line. + * Text lines can consist of equations, which must be handled apart from + * the regular text. + * Thus, use this function to step through a line checking if it has any + * equations embedded in it. + * This must handle multiple equations AND equations that do not end at + * the end-of-line, i.e., will re-enter in the next roff parse. + */ +static int +mdoc_preptext(struct mdoc *m, int line, char *buf, int offs) +{ + char *start, *end; + char delim; + + while ('\0' != buf[offs]) { + /* Mark starting position if eqn is set. */ + start = NULL; + if ('\0' != (delim = roff_eqndelim(m->roff))) + if (NULL != (start = strchr(buf + offs, delim))) + *start++ = '\0'; + + /* Parse text as normal. */ + if ( ! mdoc_ptext(m, line, buf, offs)) + return(0); + + /* Continue only if an equation exists. */ + if (NULL == start) + break; + + /* Read past the end of the equation. */ + offs += start - (buf + offs); + assert(start == &buf[offs]); + if (NULL != (end = strchr(buf + offs, delim))) { + *end++ = '\0'; + while (' ' == *end) + end++; + } + + /* Parse the equation itself. */ + roff_openeqn(m->roff, NULL, line, offs, buf); + + /* Process a finished equation? */ + if (roff_closeeqn(m->roff)) + if ( ! mdoc_addeqn(m, roff_eqn(m->roff))) + return(0); + offs += (end - (buf + offs)); + } + + return(1); +} +#endif /* * Parse free-form text, that is, a line that does not begin with the @@ -703,11 +753,6 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) ws = NULL; for (c = end = buf + offs; *c; c++) { switch (*c) { - case '-': - if (mandoc_hyph(buf + offs, c)) - *c = ASCII_HYPH; - ws = NULL; - break; case ' ': if (NULL == ws) ws = c; diff --git a/contrib/mdocml/mdoc_argv.c b/contrib/mdocml/mdoc_argv.c index 38909f94b9..08386e09b1 100644 --- a/contrib/mdocml/mdoc_argv.c +++ b/contrib/mdocml/mdoc_argv.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_argv.c,v 1.77 2011/05/12 23:44:01 kristaps Exp $ */ +/* $Id: mdoc_argv.c,v 1.82 2012/03/23 05:50:24 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -21,7 +21,6 @@ #include <sys/types.h> #include <assert.h> -#include <ctype.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -47,19 +46,21 @@ enum argvflag { ARGV_OPT_SINGLE /* optional arg (e.g., -offset [xxx]) */ }; -static enum mdocargt argv_a2arg(enum mdoct, const char *); +struct mdocarg { + enum argsflag flags; + const enum mdocargt *argvs; +}; + +static void argn_free(struct mdoc_arg *, int); static enum margserr args(struct mdoc *, int, int *, char *, enum argsflag, char **); static int args_checkpunct(const char *, int); -static int argv(struct mdoc *, int, - struct mdoc_argv *, int *, char *); -static int argv_single(struct mdoc *, int, +static int argv_multi(struct mdoc *, int, struct mdoc_argv *, int *, char *); static int argv_opt_single(struct mdoc *, int, struct mdoc_argv *, int *, char *); -static int argv_multi(struct mdoc *, int, +static int argv_single(struct mdoc *, int, struct mdoc_argv *, int *, char *); -static void argn_free(struct mdoc_arg *, int); static const enum argvflag argvflags[MDOC_ARG_MAX] = { ARGV_NONE, /* MDOC_Split */ @@ -80,7 +81,7 @@ static const enum argvflag argvflags[MDOC_ARG_MAX] = { ARGV_NONE, /* MDOC_Ohang */ ARGV_NONE, /* MDOC_Inset */ ARGV_MULTI, /* MDOC_Column */ - ARGV_SINGLE, /* MDOC_Width */ + ARGV_OPT_SINGLE, /* MDOC_Width */ ARGV_NONE, /* MDOC_Compact */ ARGV_NONE, /* MDOC_Std */ ARGV_NONE, /* MDOC_Filled */ @@ -90,131 +91,6 @@ static const enum argvflag argvflags[MDOC_ARG_MAX] = { ARGV_NONE /* MDOC_Symbolic */ }; -static const enum argsflag argflags[MDOC_MAX] = { - ARGSFL_NONE, /* Ap */ - ARGSFL_NONE, /* Dd */ - ARGSFL_NONE, /* Dt */ - ARGSFL_NONE, /* Os */ - ARGSFL_NONE, /* Sh */ - ARGSFL_NONE, /* Ss */ - ARGSFL_NONE, /* Pp */ - ARGSFL_DELIM, /* D1 */ - ARGSFL_DELIM, /* Dl */ - ARGSFL_NONE, /* Bd */ - ARGSFL_NONE, /* Ed */ - ARGSFL_NONE, /* Bl */ - ARGSFL_NONE, /* El */ - ARGSFL_NONE, /* It */ - ARGSFL_DELIM, /* Ad */ - ARGSFL_DELIM, /* An */ - ARGSFL_DELIM, /* Ar */ - ARGSFL_NONE, /* Cd */ - ARGSFL_DELIM, /* Cm */ - ARGSFL_DELIM, /* Dv */ - ARGSFL_DELIM, /* Er */ - ARGSFL_DELIM, /* Ev */ - ARGSFL_NONE, /* Ex */ - ARGSFL_DELIM, /* Fa */ - ARGSFL_NONE, /* Fd */ - ARGSFL_DELIM, /* Fl */ - ARGSFL_DELIM, /* Fn */ - ARGSFL_DELIM, /* Ft */ - ARGSFL_DELIM, /* Ic */ - ARGSFL_NONE, /* In */ - ARGSFL_DELIM, /* Li */ - ARGSFL_NONE, /* Nd */ - ARGSFL_DELIM, /* Nm */ - ARGSFL_DELIM, /* Op */ - ARGSFL_NONE, /* Ot */ - ARGSFL_DELIM, /* Pa */ - ARGSFL_NONE, /* Rv */ - ARGSFL_DELIM, /* St */ - ARGSFL_DELIM, /* Va */ - ARGSFL_DELIM, /* Vt */ - ARGSFL_DELIM, /* Xr */ - ARGSFL_NONE, /* %A */ - ARGSFL_NONE, /* %B */ - ARGSFL_NONE, /* %D */ - ARGSFL_NONE, /* %I */ - ARGSFL_NONE, /* %J */ - ARGSFL_NONE, /* %N */ - ARGSFL_NONE, /* %O */ - ARGSFL_NONE, /* %P */ - ARGSFL_NONE, /* %R */ - ARGSFL_NONE, /* %T */ - ARGSFL_NONE, /* %V */ - ARGSFL_DELIM, /* Ac */ - ARGSFL_NONE, /* Ao */ - ARGSFL_DELIM, /* Aq */ - ARGSFL_DELIM, /* At */ - ARGSFL_DELIM, /* Bc */ - ARGSFL_NONE, /* Bf */ - ARGSFL_NONE, /* Bo */ - ARGSFL_DELIM, /* Bq */ - ARGSFL_DELIM, /* Bsx */ - ARGSFL_DELIM, /* Bx */ - ARGSFL_NONE, /* Db */ - ARGSFL_DELIM, /* Dc */ - ARGSFL_NONE, /* Do */ - ARGSFL_DELIM, /* Dq */ - ARGSFL_DELIM, /* Ec */ - ARGSFL_NONE, /* Ef */ - ARGSFL_DELIM, /* Em */ - ARGSFL_NONE, /* Eo */ - ARGSFL_DELIM, /* Fx */ - ARGSFL_DELIM, /* Ms */ - ARGSFL_DELIM, /* No */ - ARGSFL_DELIM, /* Ns */ - ARGSFL_DELIM, /* Nx */ - ARGSFL_DELIM, /* Ox */ - ARGSFL_DELIM, /* Pc */ - ARGSFL_DELIM, /* Pf */ - ARGSFL_NONE, /* Po */ - ARGSFL_DELIM, /* Pq */ - ARGSFL_DELIM, /* Qc */ - ARGSFL_DELIM, /* Ql */ - ARGSFL_NONE, /* Qo */ - ARGSFL_DELIM, /* Qq */ - ARGSFL_NONE, /* Re */ - ARGSFL_NONE, /* Rs */ - ARGSFL_DELIM, /* Sc */ - ARGSFL_NONE, /* So */ - ARGSFL_DELIM, /* Sq */ - ARGSFL_NONE, /* Sm */ - ARGSFL_DELIM, /* Sx */ - ARGSFL_DELIM, /* Sy */ - ARGSFL_DELIM, /* Tn */ - ARGSFL_DELIM, /* Ux */ - ARGSFL_DELIM, /* Xc */ - ARGSFL_NONE, /* Xo */ - ARGSFL_NONE, /* Fo */ - ARGSFL_NONE, /* Fc */ - ARGSFL_NONE, /* Oo */ - ARGSFL_DELIM, /* Oc */ - ARGSFL_NONE, /* Bk */ - ARGSFL_NONE, /* Ek */ - ARGSFL_NONE, /* Bt */ - ARGSFL_NONE, /* Hf */ - ARGSFL_NONE, /* Fr */ - ARGSFL_NONE, /* Ud */ - ARGSFL_NONE, /* Lb */ - ARGSFL_NONE, /* Lp */ - ARGSFL_DELIM, /* Lk */ - ARGSFL_DELIM, /* Mt */ - ARGSFL_DELIM, /* Brq */ - ARGSFL_NONE, /* Bro */ - ARGSFL_DELIM, /* Brc */ - ARGSFL_NONE, /* %C */ - ARGSFL_NONE, /* Es */ - ARGSFL_NONE, /* En */ - ARGSFL_NONE, /* Dx */ - ARGSFL_NONE, /* %Q */ - ARGSFL_NONE, /* br */ - ARGSFL_NONE, /* sp */ - ARGSFL_NONE, /* %U */ - ARGSFL_NONE, /* Ta */ -}; - static const enum mdocargt args_Ex[] = { MDOC_Std, MDOC_ARG_MAX @@ -269,6 +145,132 @@ static const enum mdocargt args_Bl[] = { MDOC_ARG_MAX }; +static const struct mdocarg mdocargs[MDOC_MAX] = { + { ARGSFL_NONE, NULL }, /* Ap */ + { ARGSFL_NONE, NULL }, /* Dd */ + { ARGSFL_NONE, NULL }, /* Dt */ + { ARGSFL_NONE, NULL }, /* Os */ + { ARGSFL_NONE, NULL }, /* Sh */ + { ARGSFL_NONE, NULL }, /* Ss */ + { ARGSFL_NONE, NULL }, /* Pp */ + { ARGSFL_DELIM, NULL }, /* D1 */ + { ARGSFL_DELIM, NULL }, /* Dl */ + { ARGSFL_NONE, args_Bd }, /* Bd */ + { ARGSFL_NONE, NULL }, /* Ed */ + { ARGSFL_NONE, args_Bl }, /* Bl */ + { ARGSFL_NONE, NULL }, /* El */ + { ARGSFL_NONE, NULL }, /* It */ + { ARGSFL_DELIM, NULL }, /* Ad */ + { ARGSFL_DELIM, args_An }, /* An */ + { ARGSFL_DELIM, NULL }, /* Ar */ + { ARGSFL_NONE, NULL }, /* Cd */ + { ARGSFL_DELIM, NULL }, /* Cm */ + { ARGSFL_DELIM, NULL }, /* Dv */ + { ARGSFL_DELIM, NULL }, /* Er */ + { ARGSFL_DELIM, NULL }, /* Ev */ + { ARGSFL_NONE, args_Ex }, /* Ex */ + { ARGSFL_DELIM, NULL }, /* Fa */ + { ARGSFL_NONE, NULL }, /* Fd */ + { ARGSFL_DELIM, NULL }, /* Fl */ + { ARGSFL_DELIM, NULL }, /* Fn */ + { ARGSFL_DELIM, NULL }, /* Ft */ + { ARGSFL_DELIM, NULL }, /* Ic */ + { ARGSFL_NONE, NULL }, /* In */ + { ARGSFL_DELIM, NULL }, /* Li */ + { ARGSFL_NONE, NULL }, /* Nd */ + { ARGSFL_DELIM, NULL }, /* Nm */ + { ARGSFL_DELIM, NULL }, /* Op */ + { ARGSFL_NONE, NULL }, /* Ot */ + { ARGSFL_DELIM, NULL }, /* Pa */ + { ARGSFL_NONE, args_Ex }, /* Rv */ + { ARGSFL_DELIM, NULL }, /* St */ + { ARGSFL_DELIM, NULL }, /* Va */ + { ARGSFL_DELIM, NULL }, /* Vt */ + { ARGSFL_DELIM, NULL }, /* Xr */ + { ARGSFL_NONE, NULL }, /* %A */ + { ARGSFL_NONE, NULL }, /* %B */ + { ARGSFL_NONE, NULL }, /* %D */ + { ARGSFL_NONE, NULL }, /* %I */ + { ARGSFL_NONE, NULL }, /* %J */ + { ARGSFL_NONE, NULL }, /* %N */ + { ARGSFL_NONE, NULL }, /* %O */ + { ARGSFL_NONE, NULL }, /* %P */ + { ARGSFL_NONE, NULL }, /* %R */ + { ARGSFL_NONE, NULL }, /* %T */ + { ARGSFL_NONE, NULL }, /* %V */ + { ARGSFL_DELIM, NULL }, /* Ac */ + { ARGSFL_NONE, NULL }, /* Ao */ + { ARGSFL_DELIM, NULL }, /* Aq */ + { ARGSFL_DELIM, NULL }, /* At */ + { ARGSFL_DELIM, NULL }, /* Bc */ + { ARGSFL_NONE, args_Bf }, /* Bf */ + { ARGSFL_NONE, NULL }, /* Bo */ + { ARGSFL_DELIM, NULL }, /* Bq */ + { ARGSFL_DELIM, NULL }, /* Bsx */ + { ARGSFL_DELIM, NULL }, /* Bx */ + { ARGSFL_NONE, NULL }, /* Db */ + { ARGSFL_DELIM, NULL }, /* Dc */ + { ARGSFL_NONE, NULL }, /* Do */ + { ARGSFL_DELIM, NULL }, /* Dq */ + { ARGSFL_DELIM, NULL }, /* Ec */ + { ARGSFL_NONE, NULL }, /* Ef */ + { ARGSFL_DELIM, NULL }, /* Em */ + { ARGSFL_NONE, NULL }, /* Eo */ + { ARGSFL_DELIM, NULL }, /* Fx */ + { ARGSFL_DELIM, NULL }, /* Ms */ + { ARGSFL_DELIM, NULL }, /* No */ + { ARGSFL_DELIM, NULL }, /* Ns */ + { ARGSFL_DELIM, NULL }, /* Nx */ + { ARGSFL_DELIM, NULL }, /* Ox */ + { ARGSFL_DELIM, NULL }, /* Pc */ + { ARGSFL_DELIM, NULL }, /* Pf */ + { ARGSFL_NONE, NULL }, /* Po */ + { ARGSFL_DELIM, NULL }, /* Pq */ + { ARGSFL_DELIM, NULL }, /* Qc */ + { ARGSFL_DELIM, NULL }, /* Ql */ + { ARGSFL_NONE, NULL }, /* Qo */ + { ARGSFL_DELIM, NULL }, /* Qq */ + { ARGSFL_NONE, NULL }, /* Re */ + { ARGSFL_NONE, NULL }, /* Rs */ + { ARGSFL_DELIM, NULL }, /* Sc */ + { ARGSFL_NONE, NULL }, /* So */ + { ARGSFL_DELIM, NULL }, /* Sq */ + { ARGSFL_NONE, NULL }, /* Sm */ + { ARGSFL_DELIM, NULL }, /* Sx */ + { ARGSFL_DELIM, NULL }, /* Sy */ + { ARGSFL_DELIM, NULL }, /* Tn */ + { ARGSFL_DELIM, NULL }, /* Ux */ + { ARGSFL_DELIM, NULL }, /* Xc */ + { ARGSFL_NONE, NULL }, /* Xo */ + { ARGSFL_NONE, NULL }, /* Fo */ + { ARGSFL_NONE, NULL }, /* Fc */ + { ARGSFL_NONE, NULL }, /* Oo */ + { ARGSFL_DELIM, NULL }, /* Oc */ + { ARGSFL_NONE, args_Bk }, /* Bk */ + { ARGSFL_NONE, NULL }, /* Ek */ + { ARGSFL_NONE, NULL }, /* Bt */ + { ARGSFL_NONE, NULL }, /* Hf */ + { ARGSFL_NONE, NULL }, /* Fr */ + { ARGSFL_NONE, NULL }, /* Ud */ + { ARGSFL_NONE, NULL }, /* Lb */ + { ARGSFL_NONE, NULL }, /* Lp */ + { ARGSFL_DELIM, NULL }, /* Lk */ + { ARGSFL_DELIM, NULL }, /* Mt */ + { ARGSFL_DELIM, NULL }, /* Brq */ + { ARGSFL_NONE, NULL }, /* Bro */ + { ARGSFL_DELIM, NULL }, /* Brc */ + { ARGSFL_NONE, NULL }, /* %C */ + { ARGSFL_NONE, NULL }, /* Es */ + { ARGSFL_NONE, NULL }, /* En */ + { ARGSFL_NONE, NULL }, /* Dx */ + { ARGSFL_NONE, NULL }, /* %Q */ + { ARGSFL_NONE, NULL }, /* br */ + { ARGSFL_NONE, NULL }, /* sp */ + { ARGSFL_NONE, NULL }, /* %U */ + { ARGSFL_NONE, NULL }, /* Ta */ +}; + + /* * Parse an argument from line text. This comes in the form of -key * [value0...], which may either have a single mandatory value, at least @@ -281,52 +283,81 @@ mdoc_argv(struct mdoc *m, int line, enum mdoct tok, char *p, sv; struct mdoc_argv tmp; struct mdoc_arg *arg; + const enum mdocargt *ap; if ('\0' == buf[*pos]) return(ARGV_EOLN); + else if (NULL == (ap = mdocargs[tok].argvs)) + return(ARGV_WORD); + else if ('-' != buf[*pos]) + return(ARGV_WORD); - assert(' ' != buf[*pos]); - - /* Parse through to the first unescaped space. */ + /* Seek to the first unescaped space. */ p = &buf[++(*pos)]; assert(*pos > 0); - /* LINTED */ - while (buf[*pos]) { - if (' ' == buf[*pos]) - if ('\\' != buf[*pos - 1]) - break; - (*pos)++; - } + for ( ; buf[*pos] ; (*pos)++) + if (' ' == buf[*pos] && '\\' != buf[*pos - 1]) + break; - /* XXX - save zeroed byte, if not an argument. */ + /* + * We want to nil-terminate the word to look it up (it's easier + * that way). But we may not have a flag, in which case we need + * to restore the line as-is. So keep around the stray byte, + * which we'll reset upon exiting (if necessary). + */ - sv = '\0'; - if (buf[*pos]) { - sv = buf[*pos]; + if ('\0' != (sv = buf[*pos])) buf[(*pos)++] = '\0'; - } + + /* + * Now look up the word as a flag. Use temporary storage that + * we'll copy into the node's flags, if necessary. + */ memset(&tmp, 0, sizeof(struct mdoc_argv)); + tmp.line = line; tmp.pos = *pos; + tmp.arg = MDOC_ARG_MAX; - /* See if our token accepts the argument. */ + while (MDOC_ARG_MAX != (tmp.arg = *ap++)) + if (0 == strcmp(p, mdoc_argnames[tmp.arg])) + break; - if (MDOC_ARG_MAX == (tmp.arg = argv_a2arg(tok, p))) { - /* XXX - restore saved zeroed byte. */ + if (MDOC_ARG_MAX == tmp.arg) { + /* + * The flag was not found. + * Restore saved zeroed byte and return as a word. + */ if (sv) buf[*pos - 1] = sv; return(ARGV_WORD); } + /* Read to the next word (the argument). */ + while (buf[*pos] && ' ' == buf[*pos]) (*pos)++; - if ( ! argv(m, line, &tmp, pos, buf)) - return(ARGV_ERROR); + switch (argvflags[tmp.arg]) { + case (ARGV_SINGLE): + if ( ! argv_single(m, line, &tmp, pos, buf)) + return(ARGV_ERROR); + break; + case (ARGV_MULTI): + if ( ! argv_multi(m, line, &tmp, pos, buf)) + return(ARGV_ERROR); + break; + case (ARGV_OPT_SINGLE): + if ( ! argv_opt_single(m, line, &tmp, pos, buf)) + return(ARGV_ERROR); + break; + case (ARGV_NONE): + break; + } if (NULL == (arg = *v)) arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg)); @@ -395,7 +426,7 @@ mdoc_args(struct mdoc *m, int line, int *pos, enum argsflag fl; struct mdoc_node *n; - fl = argflags[tok]; + fl = mdocargs[tok].flags; if (MDOC_It != tok) return(args(m, line, pos, buf, fl, v)); @@ -424,8 +455,6 @@ args(struct mdoc *m, int line, int *pos, char *p, *pp; enum margserr rc; - assert(' ' != buf[*pos]); - if ('\0' == buf[*pos]) { if (MDOC_PPHRASE & m->flags) return(ARGS_EOLN); @@ -613,52 +642,6 @@ args_checkpunct(const char *buf, int i) return('\0' == buf[i]); } -/* - * Match up an argument string (e.g., `-foo bar' having "foo") with the - * correrct identifier. It must apply to the given macro. If none was - * found (including bad matches), return MDOC_ARG_MAX. - */ -static enum mdocargt -argv_a2arg(enum mdoct tok, const char *p) -{ - const enum mdocargt *argsp; - - argsp = NULL; - - switch (tok) { - case (MDOC_An): - argsp = args_An; - break; - case (MDOC_Bd): - argsp = args_Bd; - break; - case (MDOC_Bf): - argsp = args_Bf; - break; - case (MDOC_Bk): - argsp = args_Bk; - break; - case (MDOC_Bl): - argsp = args_Bl; - break; - case (MDOC_Rv): - /* FALLTHROUGH */ - case (MDOC_Ex): - argsp = args_Ex; - break; - default: - return(MDOC_ARG_MAX); - } - - assert(argsp); - - for ( ; MDOC_ARG_MAX != *argsp ; argsp++) - if (0 == strcmp(p, mdoc_argnames[*argsp])) - return(*argsp); - - return(MDOC_ARG_MAX); -} - static int argv_multi(struct mdoc *m, int line, struct mdoc_argv *v, int *pos, char *buf) @@ -708,9 +691,6 @@ argv_opt_single(struct mdoc *m, int line, return(1); } -/* - * Parse a single, mandatory value from the stream. - */ static int argv_single(struct mdoc *m, int line, struct mdoc_argv *v, int *pos, char *buf) @@ -734,33 +714,3 @@ argv_single(struct mdoc *m, int line, return(1); } - -/* - * Determine rules for parsing arguments. Arguments can either accept - * no parameters, an optional single parameter, one parameter, or - * multiple parameters. - */ -static int -argv(struct mdoc *mdoc, int line, - struct mdoc_argv *v, int *pos, char *buf) -{ - - v->sz = 0; - v->value = NULL; - - switch (argvflags[v->arg]) { - case (ARGV_SINGLE): - return(argv_single(mdoc, line, v, pos, buf)); - case (ARGV_MULTI): - return(argv_multi(mdoc, line, v, pos, buf)); - case (ARGV_OPT_SINGLE): - return(argv_opt_single(mdoc, line, v, pos, buf)); - case (ARGV_NONE): - break; - default: - abort(); - /* NOTREACHED */ - } - - return(1); -} diff --git a/contrib/mdocml/mdoc_hash.c b/contrib/mdocml/mdoc_hash.c index a1a03094f7..59a8d26a88 100644 --- a/contrib/mdocml/mdoc_hash.c +++ b/contrib/mdocml/mdoc_hash.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_hash.c,v 1.17 2011/03/22 14:33:05 kristaps Exp $ */ +/* $Id: mdoc_hash.c,v 1.18 2011/07/24 18:15:14 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -31,7 +31,7 @@ #include "mandoc.h" #include "libmdoc.h" -static u_char table[27 * 12]; +static unsigned char table[27 * 12]; /* * XXX - this hash has global scope, so if intended for use as a library @@ -48,14 +48,14 @@ mdoc_hash_init(void) for (i = 0; i < (int)MDOC_MAX; i++) { p = mdoc_macronames[i]; - if (isalpha((u_char)p[1])) - major = 12 * (tolower((u_char)p[1]) - 97); + if (isalpha((unsigned char)p[1])) + major = 12 * (tolower((unsigned char)p[1]) - 97); else major = 12 * 26; for (j = 0; j < 12; j++) if (UCHAR_MAX == table[major + j]) { - table[major + j] = (u_char)i; + table[major + j] = (unsigned char)i; break; } @@ -70,11 +70,11 @@ mdoc_hash_find(const char *p) if (0 == p[0]) return(MDOC_MAX); - if ( ! isalpha((u_char)p[0]) && '%' != p[0]) + if ( ! isalpha((unsigned char)p[0]) && '%' != p[0]) return(MDOC_MAX); - if (isalpha((u_char)p[1])) - major = 12 * (tolower((u_char)p[1]) - 97); + if (isalpha((unsigned char)p[1])) + major = 12 * (tolower((unsigned char)p[1]) - 97); else if ('1' == p[1]) major = 12 * 26; else diff --git a/contrib/mdocml/mdoc_html.c b/contrib/mdocml/mdoc_html.c index 57ebc34804..60ea6dc738 100644 --- a/contrib/mdocml/mdoc_html.c +++ b/contrib/mdocml/mdoc_html.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_html.c,v 1.169 2011/05/17 11:38:18 kristaps Exp $ */ +/* $Id: mdoc_html.c,v 1.182 2011/11/03 20:37:00 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -34,7 +34,6 @@ #include "main.h" #define INDENT 5 -#define HALFINDENT 3 #define MDOC_ARGS const struct mdoc_meta *m, \ const struct mdoc_node *n, \ @@ -190,7 +189,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {NULL, NULL}, /* Ec */ /* FIXME: no space */ {NULL, NULL}, /* Ef */ {mdoc_em_pre, NULL}, /* Em */ - {NULL, NULL}, /* Eo */ + {mdoc_quote_pre, mdoc_quote_post}, /* Eo */ {mdoc_xx_pre, NULL}, /* Fx */ {mdoc_ms_pre, NULL}, /* Ms */ {mdoc_igndelim_pre, NULL}, /* No */ @@ -263,17 +262,9 @@ static const char * const lists[LIST_MAX] = { void html_mdoc(void *arg, const struct mdoc *m) { - struct html *h; - struct tag *t; - - h = (struct html *)arg; - - print_gen_decls(h); - t = print_otag(h, TAG_HTML, 0, NULL); - print_mdoc(mdoc_meta(m), mdoc_node(m), h); - print_tagq(h, t); - printf("\n"); + print_mdoc(mdoc_meta(m), mdoc_node(m), (struct html *)arg); + putchar('\n'); } @@ -353,23 +344,30 @@ a2offs(const char *p, struct roffsu *su) SCALE_HS_INIT(su, INDENT); else if (0 == strcmp(p, "indent-two")) SCALE_HS_INIT(su, INDENT * 2); - else if ( ! a2roffsu(p, su, SCALE_MAX)) { - su->unit = SCALE_BU; - su->scale = html_strlen(p); - } + else if ( ! a2roffsu(p, su, SCALE_MAX)) + SCALE_HS_INIT(su, html_strlen(p)); } static void print_mdoc(MDOC_ARGS) { - struct tag *t; + struct tag *t, *tt; + struct htmlpair tag; - t = print_otag(h, TAG_HEAD, 0, NULL); - print_mdoc_head(m, n, h); - print_tagq(h, t); + PAIR_CLASS_INIT(&tag, "mandoc"); + + if ( ! (HTML_FRAGMENT & h->oflags)) { + print_gen_decls(h); + t = print_otag(h, TAG_HTML, 0, NULL); + tt = print_otag(h, TAG_HEAD, 0, NULL); + print_mdoc_head(m, n, h); + print_tagq(h, tt); + print_otag(h, TAG_BODY, 0, NULL); + print_otag(h, TAG_DIV, 1, &tag); + } else + t = print_otag(h, TAG_DIV, 1, &tag); - t = print_otag(h, TAG_BODY, 0, NULL); print_mdoc_nodelist(m, n, h); print_tagq(h, t); } @@ -407,7 +405,6 @@ print_mdoc_node(MDOC_ARGS) { int child; struct tag *t; - struct htmlpair tag; child = 1; t = h->tags.head; @@ -434,9 +431,7 @@ print_mdoc_node(MDOC_ARGS) h->flags |= HTML_NOSPACE; return; case (MDOC_EQN): - PAIR_CLASS_INIT(&tag, "eqn"); - print_otag(h, TAG_SPAN, 1, &tag); - print_text(h, n->eqn->data); + print_eqn(h, n->eqn); break; case (MDOC_TBL): /* @@ -502,32 +497,24 @@ mdoc_root_post(MDOC_ARGS) PAIR_SUMMARY_INIT(&tag[0], "Document Footer"); PAIR_CLASS_INIT(&tag[1], "foot"); - if (NULL == h->style) { - PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); - t = print_otag(h, TAG_TABLE, 3, tag); - PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); - print_otag(h, TAG_COL, 1, tag); - print_otag(h, TAG_COL, 1, tag); - } else - t = print_otag(h, TAG_TABLE, 2, tag); + PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); + t = print_otag(h, TAG_TABLE, 3, tag); + PAIR_INIT(&tag[0], ATTR_WIDTH, "50%"); + print_otag(h, TAG_COL, 1, tag); + print_otag(h, TAG_COL, 1, tag); - t = print_otag(h, TAG_TBODY, 0, NULL); + print_otag(h, TAG_TBODY, 0, NULL); tt = print_otag(h, TAG_TR, 0, NULL); PAIR_CLASS_INIT(&tag[0], "foot-date"); print_otag(h, TAG_TD, 1, tag); - print_text(h, m->date); print_stagq(h, tt); PAIR_CLASS_INIT(&tag[0], "foot-os"); - if (NULL == h->style) { - PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); - print_otag(h, TAG_TD, 2, tag); - } else - print_otag(h, TAG_TD, 1, tag); - + PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); + print_otag(h, TAG_TD, 2, tag); print_text(h, m->os); print_tagq(h, t); } @@ -553,15 +540,12 @@ mdoc_root_pre(MDOC_ARGS) PAIR_SUMMARY_INIT(&tag[0], "Document Header"); PAIR_CLASS_INIT(&tag[1], "head"); - if (NULL == h->style) { - PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); - t = print_otag(h, TAG_TABLE, 3, tag); - PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); - print_otag(h, TAG_COL, 1, tag); - print_otag(h, TAG_COL, 1, tag); - print_otag(h, TAG_COL, 1, tag); - } else - t = print_otag(h, TAG_TABLE, 2, tag); + PAIR_INIT(&tag[2], ATTR_WIDTH, "100%"); + t = print_otag(h, TAG_TABLE, 3, tag); + PAIR_INIT(&tag[0], ATTR_WIDTH, "30%"); + print_otag(h, TAG_COL, 1, tag); + print_otag(h, TAG_COL, 1, tag); + print_otag(h, TAG_COL, 1, tag); print_otag(h, TAG_TBODY, 0, NULL); @@ -569,27 +553,18 @@ mdoc_root_pre(MDOC_ARGS) PAIR_CLASS_INIT(&tag[0], "head-ltitle"); print_otag(h, TAG_TD, 1, tag); - print_text(h, title); print_stagq(h, tt); PAIR_CLASS_INIT(&tag[0], "head-vol"); - if (NULL == h->style) { - PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); - print_otag(h, TAG_TD, 2, tag); - } else - print_otag(h, TAG_TD, 1, tag); - + PAIR_INIT(&tag[1], ATTR_ALIGN, "center"); + print_otag(h, TAG_TD, 2, tag); print_text(h, b); print_stagq(h, tt); PAIR_CLASS_INIT(&tag[0], "head-rtitle"); - if (NULL == h->style) { - PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); - print_otag(h, TAG_TD, 2, tag); - } else - print_otag(h, TAG_TD, 1, tag); - + PAIR_INIT(&tag[1], ATTR_ALIGN, "right"); + print_otag(h, TAG_TD, 2, tag); print_text(h, title); print_tagq(h, t); return(1); @@ -610,18 +585,23 @@ mdoc_sh_pre(MDOC_ARGS) return(1); bufinit(h); - for (n = n->child; n; n = n->next) { + bufcat(h, "x"); + + for (n = n->child; n && MDOC_TEXT == n->type; ) { bufcat_id(h, n->string); - if (n->next) + if (NULL != (n = n->next)) bufcat_id(h, " "); } - PAIR_ID_INIT(&tag, h->buf); - print_otag(h, TAG_H1, 1, &tag); + if (NULL == n) { + PAIR_ID_INIT(&tag, h->buf); + print_otag(h, TAG_H1, 1, &tag); + } else + print_otag(h, TAG_H1, 0, NULL); + return(1); } - /* ARGSUSED */ static int mdoc_ss_pre(MDOC_ARGS) @@ -636,14 +616,20 @@ mdoc_ss_pre(MDOC_ARGS) return(1); bufinit(h); - for (n = n->child; n; n = n->next) { + bufcat(h, "x"); + + for (n = n->child; n && MDOC_TEXT == n->type; ) { bufcat_id(h, n->string); - if (n->next) + if (NULL != (n = n->next)) bufcat_id(h, " "); } - PAIR_ID_INIT(&tag, h->buf); - print_otag(h, TAG_H2, 1, &tag); + if (NULL == n) { + PAIR_ID_INIT(&tag, h->buf); + print_otag(h, TAG_H2, 1, &tag); + } else + print_otag(h, TAG_H2, 0, NULL); + return(1); } @@ -1172,9 +1158,10 @@ mdoc_sx_pre(MDOC_ARGS) bufinit(h); bufcat(h, "#x"); - for (n = n->child; n; n = n->next) { + + for (n = n->child; n; ) { bufcat_id(h, n->string); - if (n->next) + if (NULL != (n = n->next)) bufcat_id(h, " "); } @@ -1633,8 +1620,9 @@ mdoc_sp_pre(MDOC_ARGS) SCALE_VS_INIT(&su, 1); if (MDOC_sp == n->tok) { - if (n->child) - a2roffsu(n->child->string, &su, SCALE_VS); + if (NULL != (n = n->child)) + if ( ! a2roffsu(n->string, &su, SCALE_VS)) + SCALE_VS_INIT(&su, atoi(n->string)); } else su.scale = 0; @@ -1666,10 +1654,11 @@ mdoc_lk_pre(MDOC_ARGS) print_otag(h, TAG_A, 2, tag); - for (n = n->next; n; n = n->next) { - assert(MDOC_TEXT == n->type); + if (NULL == n->next) + print_text(h, n->string); + + for (n = n->next; n; n = n->next) print_text(h, n->string); - } return(0); } @@ -1983,7 +1972,7 @@ mdoc_li_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "lit"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_CODE, 1, &tag); return(1); } @@ -2196,6 +2185,8 @@ mdoc_quote_pre(MDOC_ARGS) PAIR_CLASS_INIT(&tag, "opt"); print_otag(h, TAG_SPAN, 1, &tag); break; + case (MDOC_Eo): + break; case (MDOC_Do): /* FALLTHROUGH */ case (MDOC_Dq): @@ -2211,7 +2202,11 @@ mdoc_quote_pre(MDOC_ARGS) print_text(h, "("); break; case (MDOC_Ql): - /* FALLTHROUGH */ + print_text(h, "\\(oq"); + h->flags |= HTML_NOSPACE; + PAIR_CLASS_INIT(&tag, "lit"); + print_otag(h, TAG_CODE, 1, &tag); + break; case (MDOC_So): /* FALLTHROUGH */ case (MDOC_Sq): @@ -2257,6 +2252,8 @@ mdoc_quote_post(MDOC_ARGS) case (MDOC_Bq): print_text(h, "\\(rB"); break; + case (MDOC_Eo): + break; case (MDOC_Qo): /* FALLTHROUGH */ case (MDOC_Qq): diff --git a/contrib/mdocml/mdoc_macro.c b/contrib/mdocml/mdoc_macro.c index 03d1b91cb7..11d147399e 100644 --- a/contrib/mdocml/mdoc_macro.c +++ b/contrib/mdocml/mdoc_macro.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_macro.c,v 1.109 2011/04/30 10:18:24 kristaps Exp $ */ +/* $Id: mdoc_macro.c,v 1.115 2012/01/05 00:43:51 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -74,8 +74,8 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { { in_line_eoln, MDOC_PROLOGUE }, /* Dd */ { in_line_eoln, MDOC_PROLOGUE }, /* Dt */ { in_line_eoln, MDOC_PROLOGUE }, /* Os */ - { blk_full, 0 }, /* Sh */ - { blk_full, 0 }, /* Ss */ + { blk_full, MDOC_PARSED }, /* Sh */ + { blk_full, MDOC_PARSED }, /* Ss */ { in_line_eoln, 0 }, /* Pp */ { blk_part_imp, MDOC_PARSED }, /* D1 */ { blk_part_imp, MDOC_PARSED }, /* Dl */ @@ -228,7 +228,6 @@ mdoc_macroend(struct mdoc *m) static enum mdoct lookup(enum mdoct from, const char *p) { - /* FIXME: make -diag lists be un-PARSED. */ if ( ! (MDOC_PARSED & mdoc_macros[from].flags)) return(MDOC_MAX); @@ -984,7 +983,7 @@ in_line(MACRO_PROT_ARGS) static int blk_full(MACRO_PROT_ARGS) { - int la, nl; + int la, nl, nparsed; struct mdoc_arg *arg; struct mdoc_node *head; /* save of head macro */ struct mdoc_node *body; /* save of body macro */ @@ -1038,6 +1037,14 @@ blk_full(MACRO_PROT_ARGS) head = body = NULL; + /* + * Exception: Heads of `It' macros in `-diag' lists are not + * parsed, even though `It' macros in general are parsed. + */ + nparsed = MDOC_It == tok && + MDOC_Bl == m->last->parent->tok && + LIST_diag == m->last->parent->norm->Bl.type; + /* * The `Nd' macro has all arguments in its body: it's a hybrid * of block partial-explicit and full-implicit. Stupid. @@ -1146,7 +1153,8 @@ blk_full(MACRO_PROT_ARGS) continue; } - ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p); + ntok = nparsed || ARGS_QWORD == ac ? + MDOC_MAX : lookup(tok, p); if (MDOC_MAX == ntok) { if ( ! dword(m, line, la, p, DELIM_MAX)) @@ -1443,18 +1451,15 @@ blk_part_exp(MACRO_PROT_ARGS) /* Clean-up to leave in a consistent state. */ - if (NULL == head) { + if (NULL == head) if ( ! mdoc_head_alloc(m, line, ppos, tok)) return(0); - head = m->last; - } if (NULL == body) { if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos)) return(0); if ( ! mdoc_body_alloc(m, line, ppos, tok)) return(0); - body = m->last; } /* Standard appending of delimiters. */ @@ -1572,22 +1577,6 @@ in_line_argn(MACRO_PROT_ARGS) flushed = 1; } - /* - * XXX: this is a hack to work around groff's ugliness - * as regards `Xr' and extraneous arguments. It should - * ideally be deprecated behaviour, but because this is - * code is no here, it's unlikely to be removed. - */ - -#ifdef __OpenBSD__ - if (MDOC_Xr == tok && j == maxargs) { - if ( ! mdoc_elem_alloc(m, line, la, MDOC_Ns, NULL)) - return(0); - if ( ! rew_elem(m, MDOC_Ns)) - return(0); - } -#endif - if ( ! dword(m, line, la, p, DELIM_MAX)) return(0); j++; diff --git a/contrib/mdocml/mdoc_man.c b/contrib/mdocml/mdoc_man.c new file mode 100644 index 0000000000..9d7d2ca238 --- /dev/null +++ b/contrib/mdocml/mdoc_man.c @@ -0,0 +1,637 @@ +/* $Id: mdoc_man.c,v 1.9 2011/10/24 21:47:59 schwarze Exp $ */ +/* + * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include "mandoc.h" +#include "man.h" +#include "mdoc.h" +#include "main.h" + +#define DECL_ARGS const struct mdoc_meta *m, \ + const struct mdoc_node *n, \ + struct mman *mm + +struct mman { + int need_space; /* next word needs prior ws */ + int need_nl; /* next word needs prior nl */ +}; + +struct manact { + int (*cond)(DECL_ARGS); /* DON'T run actions */ + int (*pre)(DECL_ARGS); /* pre-node action */ + void (*post)(DECL_ARGS); /* post-node action */ + const char *prefix; /* pre-node string constant */ + const char *suffix; /* post-node string constant */ +}; + +static int cond_body(DECL_ARGS); +static int cond_head(DECL_ARGS); +static void post_bd(DECL_ARGS); +static void post_dl(DECL_ARGS); +static void post_enc(DECL_ARGS); +static void post_nm(DECL_ARGS); +static void post_percent(DECL_ARGS); +static void post_pf(DECL_ARGS); +static void post_sect(DECL_ARGS); +static void post_sp(DECL_ARGS); +static int pre_ap(DECL_ARGS); +static int pre_bd(DECL_ARGS); +static int pre_br(DECL_ARGS); +static int pre_bx(DECL_ARGS); +static int pre_dl(DECL_ARGS); +static int pre_enc(DECL_ARGS); +static int pre_it(DECL_ARGS); +static int pre_nm(DECL_ARGS); +static int pre_ns(DECL_ARGS); +static int pre_pp(DECL_ARGS); +static int pre_sp(DECL_ARGS); +static int pre_sect(DECL_ARGS); +static int pre_ux(DECL_ARGS); +static int pre_xr(DECL_ARGS); +static void print_word(struct mman *, const char *); +static void print_node(DECL_ARGS); + +static const struct manact manacts[MDOC_MAX + 1] = { + { NULL, pre_ap, NULL, NULL, NULL }, /* Ap */ + { NULL, NULL, NULL, NULL, NULL }, /* Dd */ + { NULL, NULL, NULL, NULL, NULL }, /* Dt */ + { NULL, NULL, NULL, NULL, NULL }, /* Os */ + { NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */ + { NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */ + { NULL, pre_pp, NULL, NULL, NULL }, /* Pp */ + { cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */ + { cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */ + { cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */ + { NULL, NULL, NULL, NULL, NULL }, /* Ed */ + { NULL, NULL, NULL, NULL, NULL }, /* Bl */ + { NULL, NULL, NULL, NULL, NULL }, /* El */ + { NULL, pre_it, NULL, NULL, NULL }, /* _It */ + { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ad */ + { NULL, NULL, NULL, NULL, NULL }, /* _An */ + { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ar */ + { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cd */ + { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cm */ + { NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Dv */ + { NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Er */ + { NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Ev */ + { NULL, pre_enc, post_enc, "The \\fB", + "\\fP\nutility exits 0 on success, and >0 if an error occurs." + }, /* Ex */ + { NULL, NULL, NULL, NULL, NULL }, /* _Fa */ + { NULL, NULL, NULL, NULL, NULL }, /* _Fd */ + { NULL, pre_enc, post_enc, "\\fB-", "\\fP" }, /* Fl */ + { NULL, NULL, NULL, NULL, NULL }, /* _Fn */ + { NULL, NULL, NULL, NULL, NULL }, /* _Ft */ + { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ic */ + { NULL, NULL, NULL, NULL, NULL }, /* _In */ + { NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Li */ + { cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */ + { NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */ + { cond_body, pre_enc, post_enc, "[", "]" }, /* Op */ + { NULL, NULL, NULL, NULL, NULL }, /* Ot */ + { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Pa */ + { NULL, pre_enc, post_enc, "The \\fB", + "\\fP\nfunction returns the value 0 if successful;\n" + "otherwise the value -1 is returned and the global\n" + "variable \\fIerrno\\fP is set to indicate the error." + }, /* Rv */ + { NULL, NULL, NULL, NULL, NULL }, /* St */ + { NULL, NULL, NULL, NULL, NULL }, /* _Va */ + { NULL, NULL, NULL, NULL, NULL }, /* _Vt */ + { NULL, pre_xr, NULL, NULL, NULL }, /* Xr */ + { NULL, NULL, post_percent, NULL, NULL }, /* _%A */ + { NULL, NULL, NULL, NULL, NULL }, /* _%B */ + { NULL, NULL, post_percent, NULL, NULL }, /* _%D */ + { NULL, NULL, NULL, NULL, NULL }, /* _%I */ + { NULL, pre_enc, post_percent, "\\fI", "\\fP" }, /* %J */ + { NULL, NULL, NULL, NULL, NULL }, /* _%N */ + { NULL, NULL, NULL, NULL, NULL }, /* _%O */ + { NULL, NULL, NULL, NULL, NULL }, /* _%P */ + { NULL, NULL, NULL, NULL, NULL }, /* _%R */ + { NULL, pre_enc, post_percent, "\"", "\"" }, /* %T */ + { NULL, NULL, NULL, NULL, NULL }, /* _%V */ + { NULL, NULL, NULL, NULL, NULL }, /* Ac */ + { cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */ + { cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */ + { NULL, NULL, NULL, NULL, NULL }, /* At */ + { NULL, NULL, NULL, NULL, NULL }, /* Bc */ + { NULL, NULL, NULL, NULL, NULL }, /* _Bf */ + { cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */ + { cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */ + { NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */ + { NULL, pre_bx, NULL, NULL, NULL }, /* Bx */ + { NULL, NULL, NULL, NULL, NULL }, /* Db */ + { NULL, NULL, NULL, NULL, NULL }, /* Dc */ + { cond_body, pre_enc, post_enc, "``", "''" }, /* Do */ + { cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */ + { NULL, NULL, NULL, NULL, NULL }, /* _Ec */ + { NULL, NULL, NULL, NULL, NULL }, /* _Ef */ + { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Em */ + { NULL, NULL, NULL, NULL, NULL }, /* _Eo */ + { NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */ + { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ms */ + { NULL, NULL, NULL, NULL, NULL }, /* No */ + { NULL, pre_ns, NULL, NULL, NULL }, /* Ns */ + { NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */ + { NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */ + { NULL, NULL, NULL, NULL, NULL }, /* Pc */ + { NULL, NULL, post_pf, NULL, NULL }, /* Pf */ + { cond_body, pre_enc, post_enc, "(", ")" }, /* Po */ + { cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */ + { NULL, NULL, NULL, NULL, NULL }, /* Qc */ + { cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */ + { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */ + { cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */ + { NULL, NULL, NULL, NULL, NULL }, /* Re */ + { cond_body, pre_pp, NULL, NULL, NULL }, /* Rs */ + { NULL, NULL, NULL, NULL, NULL }, /* Sc */ + { cond_body, pre_enc, post_enc, "`", "'" }, /* So */ + { cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */ + { NULL, NULL, NULL, NULL, NULL }, /* _Sm */ + { NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Sx */ + { NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Sy */ + { NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Tn */ + { NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */ + { NULL, NULL, NULL, NULL, NULL }, /* _Xc */ + { NULL, NULL, NULL, NULL, NULL }, /* _Xo */ + { NULL, NULL, NULL, NULL, NULL }, /* _Fo */ + { NULL, NULL, NULL, NULL, NULL }, /* _Fc */ + { cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */ + { NULL, NULL, NULL, NULL, NULL }, /* Oc */ + { NULL, NULL, NULL, NULL, NULL }, /* _Bk */ + { NULL, NULL, NULL, NULL, NULL }, /* _Ek */ + { NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */ + { NULL, NULL, NULL, NULL, NULL }, /* Hf */ + { NULL, NULL, NULL, NULL, NULL }, /* Fr */ + { NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */ + { NULL, NULL, NULL, NULL, NULL }, /* _Lb */ + { NULL, pre_pp, NULL, NULL, NULL }, /* Lp */ + { NULL, NULL, NULL, NULL, NULL }, /* _Lk */ + { NULL, NULL, NULL, NULL, NULL }, /* _Mt */ + { cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */ + { cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */ + { NULL, NULL, NULL, NULL, NULL }, /* Brc */ + { NULL, NULL, NULL, NULL, NULL }, /* _%C */ + { NULL, NULL, NULL, NULL, NULL }, /* _Es */ + { NULL, NULL, NULL, NULL, NULL }, /* _En */ + { NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */ + { NULL, NULL, NULL, NULL, NULL }, /* _%Q */ + { NULL, pre_br, NULL, NULL, NULL }, /* br */ + { NULL, pre_sp, post_sp, NULL, NULL }, /* sp */ + { NULL, NULL, NULL, NULL, NULL }, /* _%U */ + { NULL, NULL, NULL, NULL, NULL }, /* _Ta */ + { NULL, NULL, NULL, NULL, NULL }, /* ROOT */ +}; + +static void +print_word(struct mman *mm, const char *s) +{ + + if (mm->need_nl) { + /* + * If we need a newline, print it now and start afresh. + */ + putchar('\n'); + mm->need_space = 0; + mm->need_nl = 0; + } else if (mm->need_space && '\0' != s[0]) + /* + * If we need a space, only print it before + * (1) a nonzero length word; + * (2) a word that is non-punctuation; and + * (3) if punctuation, non-terminating puncutation. + */ + if (NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) + putchar(' '); + + /* + * Reassign needing space if we're not following opening + * punctuation. + */ + mm->need_space = + ('(' != s[0] && '[' != s[0]) || '\0' != s[1]; + + for ( ; *s; s++) { + switch (*s) { + case (ASCII_NBRSP): + printf("\\~"); + break; + case (ASCII_HYPH): + putchar('-'); + break; + default: + putchar((unsigned char)*s); + break; + } + } +} + +void +man_man(void *arg, const struct man *man) +{ + + /* + * Dump the keep buffer. + * We're guaranteed by now that this exists (is non-NULL). + * Flush stdout afterward, just in case. + */ + fputs(mparse_getkeep(man_mparse(man)), stdout); + fflush(stdout); +} + +void +man_mdoc(void *arg, const struct mdoc *mdoc) +{ + const struct mdoc_meta *m; + const struct mdoc_node *n; + struct mman mm; + + m = mdoc_meta(mdoc); + n = mdoc_node(mdoc); + + printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"", + m->title, m->msec, m->date, m->os, m->vol); + + memset(&mm, 0, sizeof(struct mman)); + + mm.need_nl = 1; + print_node(m, n, &mm); + putchar('\n'); +} + +static void +print_node(DECL_ARGS) +{ + const struct mdoc_node *prev, *sub; + const struct manact *act; + int cond, do_sub; + + /* + * Break the line if we were parsed subsequent the current node. + * This makes the page structure be more consistent. + */ + prev = n->prev ? n->prev : n->parent; + if (prev && prev->line < n->line) + mm->need_nl = 1; + + act = NULL; + cond = 0; + do_sub = 1; + + if (MDOC_TEXT == n->type) { + /* + * Make sure that we don't happen to start with a + * control character at the start of a line. + */ + if (mm->need_nl && ('.' == *n->string || + '\'' == *n->string)) { + print_word(mm, "\\&"); + mm->need_space = 0; + } + print_word(mm, n->string); + } else { + /* + * Conditionally run the pre-node action handler for a + * node. + */ + act = manacts + n->tok; + cond = NULL == act->cond || (*act->cond)(m, n, mm); + if (cond && act->pre) + do_sub = (*act->pre)(m, n, mm); + } + + /* + * Conditionally run all child nodes. + * Note that this iterates over children instead of using + * recursion. This prevents unnecessary depth in the stack. + */ + if (do_sub) + for (sub = n->child; sub; sub = sub->next) + print_node(m, sub, mm); + + /* + * Lastly, conditionally run the post-node handler. + */ + if (cond && act->post) + (*act->post)(m, n, mm); +} + +static int +cond_head(DECL_ARGS) +{ + + return(MDOC_HEAD == n->type); +} + +static int +cond_body(DECL_ARGS) +{ + + return(MDOC_BODY == n->type); +} + +/* + * Output a font encoding before a node, e.g., \fR. + * This obviously has no trailing space. + */ +static int +pre_enc(DECL_ARGS) +{ + const char *prefix; + + prefix = manacts[n->tok].prefix; + if (NULL == prefix) + return(1); + print_word(mm, prefix); + mm->need_space = 0; + return(1); +} + +/* + * Output a font encoding subsequent a node, e.g., \fP. + */ +static void +post_enc(DECL_ARGS) +{ + const char *suffix; + + suffix = manacts[n->tok].suffix; + if (NULL == suffix) + return; + mm->need_space = 0; + print_word(mm, suffix); +} + +/* + * Used in listings (percent = %A, e.g.). + * FIXME: this is incomplete. + * It doesn't print a nice ", and" for lists. + */ +static void +post_percent(DECL_ARGS) +{ + + post_enc(m, n, mm); + if (n->next) + print_word(mm, ","); + else { + print_word(mm, "."); + mm->need_nl = 1; + } +} + +/* + * Print before a section header. + */ +static int +pre_sect(DECL_ARGS) +{ + + if (MDOC_HEAD != n->type) + return(1); + mm->need_nl = 1; + print_word(mm, manacts[n->tok].prefix); + print_word(mm, "\""); + mm->need_space = 0; + return(1); +} + +/* + * Print subsequent a section header. + */ +static void +post_sect(DECL_ARGS) +{ + + if (MDOC_HEAD != n->type) + return; + mm->need_space = 0; + print_word(mm, "\""); + mm->need_nl = 1; +} + +static int +pre_ap(DECL_ARGS) +{ + + mm->need_space = 0; + print_word(mm, "'"); + mm->need_space = 0; + return(0); +} + +static int +pre_bd(DECL_ARGS) +{ + + if (DISP_unfilled == n->norm->Bd.type || + DISP_literal == n->norm->Bd.type) { + mm->need_nl = 1; + print_word(mm, ".nf"); + } + mm->need_nl = 1; + return(1); +} + +static void +post_bd(DECL_ARGS) +{ + + if (DISP_unfilled == n->norm->Bd.type || + DISP_literal == n->norm->Bd.type) { + mm->need_nl = 1; + print_word(mm, ".fi"); + } + mm->need_nl = 1; +} + +static int +pre_br(DECL_ARGS) +{ + + mm->need_nl = 1; + print_word(mm, ".br"); + mm->need_nl = 1; + return(0); +} + +static int +pre_bx(DECL_ARGS) +{ + + n = n->child; + if (n) { + print_word(mm, n->string); + mm->need_space = 0; + n = n->next; + } + print_word(mm, "BSD"); + if (NULL == n) + return(0); + mm->need_space = 0; + print_word(mm, "-"); + mm->need_space = 0; + print_word(mm, n->string); + return(0); +} + +static int +pre_dl(DECL_ARGS) +{ + + mm->need_nl = 1; + print_word(mm, ".RS 6n"); + mm->need_nl = 1; + return(1); +} + +static void +post_dl(DECL_ARGS) +{ + + mm->need_nl = 1; + print_word(mm, ".RE"); + mm->need_nl = 1; +} + +static int +pre_it(DECL_ARGS) +{ + const struct mdoc_node *bln; + + if (MDOC_HEAD == n->type) { + mm->need_nl = 1; + print_word(mm, ".TP"); + bln = n->parent->parent->prev; + switch (bln->norm->Bl.type) { + case (LIST_bullet): + print_word(mm, "4n"); + mm->need_nl = 1; + print_word(mm, "\\fBo\\fP"); + break; + default: + if (bln->norm->Bl.width) + print_word(mm, bln->norm->Bl.width); + break; + } + mm->need_nl = 1; + } + return(1); +} + +static int +pre_nm(DECL_ARGS) +{ + + if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) + return(1); + print_word(mm, "\\fB"); + mm->need_space = 0; + if (NULL == n->child) + print_word(mm, m->name); + return(1); +} + +static void +post_nm(DECL_ARGS) +{ + + if (MDOC_ELEM != n->type && MDOC_HEAD != n->type) + return; + mm->need_space = 0; + print_word(mm, "\\fP"); +} + +static int +pre_ns(DECL_ARGS) +{ + + mm->need_space = 0; + return(0); +} + +static void +post_pf(DECL_ARGS) +{ + + mm->need_space = 0; +} + +static int +pre_pp(DECL_ARGS) +{ + + mm->need_nl = 1; + if (MDOC_It == n->parent->tok) + print_word(mm, ".sp"); + else + print_word(mm, ".PP"); + mm->need_nl = 1; + return(1); +} + +static int +pre_sp(DECL_ARGS) +{ + + mm->need_nl = 1; + print_word(mm, ".sp"); + return(1); +} + +static void +post_sp(DECL_ARGS) +{ + + mm->need_nl = 1; +} + +static int +pre_xr(DECL_ARGS) +{ + + n = n->child; + if (NULL == n) + return(0); + print_node(m, n, mm); + n = n->next; + if (NULL == n) + return(0); + mm->need_space = 0; + print_word(mm, "("); + print_node(m, n, mm); + print_word(mm, ")"); + return(0); +} + +static int +pre_ux(DECL_ARGS) +{ + + print_word(mm, manacts[n->tok].prefix); + if (NULL == n->child) + return(0); + mm->need_space = 0; + print_word(mm, "\\~"); + mm->need_space = 0; + return(1); +} diff --git a/contrib/mdocml/mdoc_term.c b/contrib/mdocml/mdoc_term.c index 1a5ce4c214..5333566444 100644 --- a/contrib/mdocml/mdoc_term.c +++ b/contrib/mdocml/mdoc_term.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_term.c,v 1.230 2011/05/17 14:38:34 kristaps Exp $ */ +/* $Id: mdoc_term.c,v 1.238 2011/11/13 13:15:14 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org> @@ -34,9 +34,6 @@ #include "mdoc.h" #include "main.h" -#define INDENT 5 -#define HALFINDENT 3 - struct termpair { struct termpair *ppair; int count; @@ -194,7 +191,7 @@ static const struct termact termacts[MDOC_MAX] = { { NULL, NULL }, /* Ec */ /* FIXME: no space */ { NULL, NULL }, /* Ef */ { termp_under_pre, NULL }, /* Em */ - { NULL, NULL }, /* Eo */ + { termp_quote_pre, termp_quote_post }, /* Eo */ { termp_xx_pre, NULL }, /* Fx */ { termp_bold_pre, NULL }, /* Ms */ { termp_igndelim_pre, NULL }, /* No */ @@ -259,6 +256,9 @@ terminal_mdoc(void *arg, const struct mdoc *mdoc) p = (struct termp *)arg; + if (0 == p->defindent) + p->defindent = 5; + p->overstep = 0; p->maxrmargin = p->defrmargin; p->tabwidth = term_len(p, 5); @@ -351,7 +351,7 @@ print_mdoc_node(DECL_ARGS) p->flags |= TERMP_NOSPACE; break; case (MDOC_EQN): - term_word(p, n->eqn->data); + term_eqn(p, n->eqn); break; case (MDOC_TBL): term_tbl(p, n->span); @@ -435,7 +435,7 @@ print_mdoc_foot(struct termp *p, const void *arg) p->offset = p->rmargin; p->rmargin = p->maxrmargin - term_strlen(p, m->os); - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->flags |= TERMP_NOSPACE; term_word(p, m->date); term_flushln(p); @@ -443,7 +443,7 @@ print_mdoc_foot(struct termp *p, const void *arg) p->offset = p->rmargin; p->rmargin = p->maxrmargin; p->flags &= ~TERMP_NOBREAK; - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->flags |= TERMP_NOSPACE; term_word(p, m->os); term_flushln(p); @@ -458,13 +458,11 @@ static void print_mdoc_head(struct termp *p, const void *arg) { char buf[BUFSIZ], title[BUFSIZ]; + size_t buflen, titlen; const struct mdoc_meta *m; m = (const struct mdoc_meta *)arg; - p->rmargin = p->maxrmargin; - p->offset = 0; - /* * The header is strange. It has three components, which are * really two with the first duplicated. It goes like this: @@ -478,8 +476,12 @@ print_mdoc_head(struct termp *p, const void *arg) * switches on the manual section. */ + p->offset = 0; + p->rmargin = p->maxrmargin; + assert(m->vol); strlcpy(buf, m->vol, BUFSIZ); + buflen = term_strlen(p, buf); if (m->arch) { strlcat(buf, " (", BUFSIZ); @@ -488,33 +490,38 @@ print_mdoc_head(struct termp *p, const void *arg) } snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); + titlen = term_strlen(p, title); - p->offset = 0; - p->rmargin = (p->maxrmargin - - term_strlen(p, buf) + term_len(p, 1)) / 2; p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; + p->offset = 0; + p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? + (p->maxrmargin - + term_strlen(p, buf) + term_len(p, 1)) / 2 : + p->maxrmargin - buflen; term_word(p, title); term_flushln(p); + p->flags |= TERMP_NOSPACE; p->offset = p->rmargin; - p->rmargin = p->maxrmargin - term_strlen(p, title); - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->rmargin = p->offset + buflen + titlen < p->maxrmargin ? + p->maxrmargin - titlen : p->maxrmargin; term_word(p, buf); term_flushln(p); - p->offset = p->rmargin; - p->rmargin = p->maxrmargin; p->flags &= ~TERMP_NOBREAK; - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; - - term_word(p, title); - term_flushln(p); + if (p->rmargin + titlen <= p->maxrmargin) { + p->flags |= TERMP_NOSPACE; + p->offset = p->rmargin; + p->rmargin = p->maxrmargin; + term_word(p, title); + term_flushln(p); + } + p->flags &= ~TERMP_NOSPACE; p->offset = 0; p->rmargin = p->maxrmargin; - p->flags &= ~TERMP_NOSPACE; } @@ -523,9 +530,10 @@ a2height(const struct termp *p, const char *v) { struct roffsu su; + assert(v); if ( ! a2roffsu(v, &su, SCALE_VS)) - SCALE_VS_INIT(&su, term_len(p, 1)); + SCALE_VS_INIT(&su, atoi(v)); return(term_vspan(p, &su)); } @@ -554,9 +562,9 @@ a2offs(const struct termp *p, const char *v) else if (0 == strcmp(v, "left")) return(0); else if (0 == strcmp(v, "indent")) - return(term_len(p, INDENT + 1)); + return(term_len(p, p->defindent + 1)); else if (0 == strcmp(v, "indent-two")) - return(term_len(p, (INDENT + 1) * 2)); + return(term_len(p, (p->defindent + 1) * 2)); else if ( ! a2roffsu(v, &su, SCALE_MAX)) SCALE_HS_INIT(&su, term_strlen(p, v)); @@ -576,6 +584,8 @@ print_bvspace(struct termp *p, { const struct mdoc_node *nn; + assert(n); + term_newln(p); if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) @@ -786,16 +796,11 @@ termp_it_pre(DECL_ARGS) case (LIST_hyphen): if (MDOC_HEAD == n->type) p->flags |= TERMP_NOBREAK; - else - p->flags |= TERMP_NOLPAD; break; case (LIST_hang): if (MDOC_HEAD == n->type) p->flags |= TERMP_NOBREAK; else - p->flags |= TERMP_NOLPAD; - - if (MDOC_HEAD != n->type) break; /* @@ -806,17 +811,14 @@ termp_it_pre(DECL_ARGS) */ if (n->next->child && (MDOC_Bl == n->next->child->tok || - MDOC_Bd == n->next->child->tok)) { + MDOC_Bd == n->next->child->tok)) p->flags &= ~TERMP_NOBREAK; - p->flags &= ~TERMP_NOLPAD; - } else + else p->flags |= TERMP_HANG; break; case (LIST_tag): if (MDOC_HEAD == n->type) p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE; - else - p->flags |= TERMP_NOLPAD; if (MDOC_HEAD != n->type) break; @@ -832,10 +834,6 @@ termp_it_pre(DECL_ARGS) else p->flags |= TERMP_NOBREAK; - assert(n->prev); - if (MDOC_BODY == n->prev->type) - p->flags |= TERMP_NOLPAD; - break; case (LIST_diag): if (MDOC_HEAD == n->type) @@ -992,7 +990,6 @@ termp_it_post(DECL_ARGS) p->flags &= ~TERMP_DANGLE; p->flags &= ~TERMP_NOBREAK; p->flags &= ~TERMP_TWOSPACE; - p->flags &= ~TERMP_NOLPAD; p->flags &= ~TERMP_HANG; } @@ -1008,7 +1005,7 @@ termp_nm_pre(DECL_ARGS) if (MDOC_BODY == n->type) { if (NULL == n->child) return(0); - p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->flags |= TERMP_NOSPACE; p->offset += term_len(p, 1) + (NULL == n->prev->child ? term_strlen(p, m->name) : MDOC_TEXT == n->prev->child->type ? @@ -1053,10 +1050,8 @@ termp_nm_post(DECL_ARGS) if (MDOC_HEAD == n->type && n->next->child) { term_flushln(p); p->flags &= ~(TERMP_NOBREAK | TERMP_HANG); - } else if (MDOC_BODY == n->type && n->child) { + } else if (MDOC_BODY == n->type && n->child) term_flushln(p); - p->flags &= ~TERMP_NOLPAD; - } } @@ -1429,7 +1424,7 @@ termp_sh_pre(DECL_ARGS) term_fontpush(p, TERMFONT_BOLD); break; case (MDOC_BODY): - p->offset = term_len(p, INDENT); + p->offset = term_len(p, p->defindent); break; default: break; @@ -1497,7 +1492,7 @@ termp_d1_pre(DECL_ARGS) if (MDOC_BLOCK != n->type) return(1); term_newln(p); - p->offset += term_len(p, (INDENT + 1)); + p->offset += term_len(p, p->defindent + 1); return(1); } @@ -1802,7 +1797,7 @@ termp_ss_pre(DECL_ARGS) break; case (MDOC_HEAD): term_fontpush(p, TERMFONT_BOLD); - p->offset = term_len(p, HALFINDENT); + p->offset = term_len(p, (p->defindent+1)/2); break; default: break; @@ -1930,6 +1925,8 @@ termp_quote_pre(DECL_ARGS) case (MDOC_Dq): term_word(p, "``"); break; + case (MDOC_Eo): + break; case (MDOC_Po): /* FALLTHROUGH */ case (MDOC_Pq): @@ -1994,6 +1991,8 @@ termp_quote_post(DECL_ARGS) case (MDOC_Dq): term_word(p, "''"); break; + case (MDOC_Eo): + break; case (MDOC_Po): /* FALLTHROUGH */ case (MDOC_Pq): diff --git a/contrib/mdocml/mdoc_validate.c b/contrib/mdocml/mdoc_validate.c index a34a221d69..060ccdadec 100644 --- a/contrib/mdocml/mdoc_validate.c +++ b/contrib/mdocml/mdoc_validate.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_validate.c,v 1.169 2011/04/30 10:18:24 kristaps Exp $ */ +/* $Id: mdoc_validate.c,v 1.182 2012/03/23 05:50:25 kristaps Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> @@ -72,9 +72,7 @@ static void check_text(struct mdoc *, int, int, char *); static void check_argv(struct mdoc *, struct mdoc_node *, struct mdoc_argv *); static void check_args(struct mdoc *, struct mdoc_node *); - -static int concat(struct mdoc *, char *, - const struct mdoc_node *, size_t); +static int concat(char *, const struct mdoc_node *, size_t); static enum mdoc_sec a2sec(const char *); static size_t macro2len(enum mdoct); @@ -545,40 +543,13 @@ check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v) static void check_text(struct mdoc *m, int ln, int pos, char *p) { - char *cpp, *pp; - size_t sz; - - while ('\0' != *p) { - sz = strcspn(p, "\t\\"); - - p += (int)sz; - pos += (int)sz; - - if ('\t' == *p) { - if ( ! (MDOC_LITERAL & m->flags)) - mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB); - p++; - pos++; - continue; - } else if ('\0' == *p) - break; + char *cp; - pos++; - pp = ++p; - - if (ESCAPE_ERROR == mandoc_escape - ((const char **)&pp, NULL, NULL)) { - mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE); - break; - } - - cpp = p; - while (NULL != (cpp = memchr(cpp, ASCII_HYPH, pp - cpp))) - *cpp = '-'; + if (MDOC_LITERAL & m->flags) + return; - pos += pp - p; - p = pp; - } + for (cp = p; NULL != (p = strchr(p, '\t')); p++) + mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB); } static int @@ -690,8 +661,13 @@ pre_bl(PRE_ARGS) comp = 1; break; case (MDOC_Width): - dup = (NULL != n->norm->Bl.width); - width = n->args->argv[i].value[0]; + /* NB: this can be empty! */ + if (n->args->argv[i].sz) { + width = n->args->argv[i].value[0]; + dup = (NULL != n->norm->Bl.width); + break; + } + mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); break; case (MDOC_Offset): /* NB: this can be empty! */ @@ -731,7 +707,7 @@ pre_bl(PRE_ARGS) if (LIST_column == lt) { n->norm->Bl.ncols = n->args->argv[i].sz; - n->norm->Bl.cols = (const char **) + n->norm->Bl.cols = (void *) n->args->argv[i].value; } } @@ -899,7 +875,7 @@ pre_sh(PRE_ARGS) if (MDOC_BLOCK != n->type) return(1); - mdoc->regs->regs[(int)REG_nS].set = 0; + roff_regunset(mdoc->roff, REG_nS); return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); } @@ -1133,6 +1109,7 @@ static int post_nm(POST_ARGS) { char buf[BUFSIZ]; + int c; /* If no child specified, make sure we have the meta name. */ @@ -1144,11 +1121,14 @@ post_nm(POST_ARGS) /* If no meta name, set it from the child. */ - if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ)) + buf[0] = '\0'; + if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) { + mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM); return(0); + } + assert(c); mdoc->meta.name = mandoc_strdup(buf); - return(1); } @@ -1419,7 +1399,7 @@ post_bl_block_width(POST_ARGS) assert(i < (int)n->args->argc); - snprintf(buf, NUMSIZ, "%zun", width); + snprintf(buf, NUMSIZ, "%un", (unsigned int)width); free(n->args->argv[i].value[0]); n->args->argv[i].value[0] = mandoc_strdup(buf); @@ -1469,7 +1449,7 @@ post_bl_block_tag(POST_ARGS) /* Defaults to ten ens. */ - snprintf(buf, NUMSIZ, "%zun", sz); + snprintf(buf, NUMSIZ, "%un", (unsigned int)sz); /* * We have to dynamically add this to the macro's argument list. @@ -1545,7 +1525,7 @@ post_bl_head(POST_ARGS) ((size_t)mdoc->last->nchild * sizeof(char *)); mdoc->last->norm->Bl.ncols = np->args->argv[j].sz; - mdoc->last->norm->Bl.cols = (const char **)np->args->argv[j].value; + mdoc->last->norm->Bl.cols = (void *)np->args->argv[j].value; for (i = 0, nn = mdoc->last->child; nn; i++) { np->args->argv[j].value[i] = nn->string; @@ -1718,6 +1698,14 @@ post_rs(POST_ARGS) mdoc_node_delete(mdoc, nn); } + /* + * Nothing to sort if only invalid nodes were found + * inside the `Rs' body. + */ + + if (NULL == mdoc->last->child) + return(1); + /* * The full `Rs' block needs special handling to order the * sub-elements according to `rsord'. Pick through each element @@ -1843,7 +1831,9 @@ static int post_sh_head(POST_ARGS) { char buf[BUFSIZ]; + struct mdoc_node *n; enum mdoc_sec sec; + int c; /* * Process a new section. Sections are either "named" or @@ -1852,10 +1842,13 @@ post_sh_head(POST_ARGS) * manual sections. */ - if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ)) + sec = SEC_CUSTOM; + buf[0] = '\0'; + if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) { + mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM); return(0); - - sec = a2sec(buf); + } else if (1 == c) + sec = a2sec(buf); /* The NAME should be first. */ @@ -1873,6 +1866,20 @@ post_sh_head(POST_ARGS) mdoc->lastsec = sec; + /* + * Set the section attribute for the current HEAD, for its + * parent BLOCK, and for the HEAD children; the latter can + * only be TEXT nodes, so no recursion is needed. + * For other blocks and elements, including .Sh BODY, this is + * done when allocating the node data structures, but for .Sh + * BLOCK and HEAD, the section is still unknown at that time. + */ + + mdoc->last->parent->sec = sec; + mdoc->last->sec = sec; + for (n = mdoc->last->child; n; n = n->next) + n->sec = sec; + /* We don't care about custom sections after this. */ if (SEC_CUSTOM == sec) @@ -2004,6 +2011,7 @@ post_dd(POST_ARGS) { char buf[DATESIZE]; struct mdoc_node *n; + int c; if (mdoc->meta.date) free(mdoc->meta.date); @@ -2015,9 +2023,13 @@ post_dd(POST_ARGS) return(1); } - if ( ! concat(mdoc, buf, n->child, DATESIZE)) + buf[0] = '\0'; + if (-1 == (c = concat(buf, n->child, DATESIZE))) { + mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM); return(0); + } + assert(c); mdoc->meta.date = mandoc_normdate (mdoc->parse, buf, n->line, n->pos); @@ -2046,7 +2058,7 @@ post_dt(POST_ARGS) if (NULL != (nn = n->child)) for (p = nn->string; *p; p++) { - if (toupper((u_char)*p) == *p) + if (toupper((unsigned char)*p) == *p) continue; /* @@ -2092,7 +2104,7 @@ post_dt(POST_ARGS) * arch = NULL */ - cp = mdoc_a2msec(nn->string); + cp = mandoc_a2msec(nn->string); if (cp) { mdoc->meta.vol = mandoc_strdup(cp); mdoc->meta.msec = mandoc_strdup(nn->string); @@ -2172,6 +2184,7 @@ post_os(POST_ARGS) { struct mdoc_node *n; char buf[BUFSIZ]; + int c; #ifndef OSNAME struct utsname utsname; #endif @@ -2188,8 +2201,13 @@ post_os(POST_ARGS) if (mdoc->meta.os) free(mdoc->meta.os); - if ( ! concat(mdoc, buf, n->child, BUFSIZ)) + buf[0] = '\0'; + if (-1 == (c = concat(buf, n->child, BUFSIZ))) { + mdoc_nmsg(mdoc, n->child, MANDOCERR_MEM); return(0); + } + + assert(c); /* XXX: yes, these can all be dynamically-adjusted buffers, but * it's really not worth the extra hackery. @@ -2256,34 +2274,24 @@ post_std(POST_ARGS) return(1); } +/* + * Concatenate a node, stopping at the first non-text. + * Concatenation is separated by a single whitespace. + * Returns -1 on fatal (string overrun) error, 0 if child nodes were + * encountered, 1 otherwise. + */ static int -concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz) +concat(char *p, const struct mdoc_node *n, size_t sz) { - p[0] = '\0'; - - /* - * Concatenate sibling nodes together. All siblings must be of - * type MDOC_TEXT or an assertion is raised. Concatenation is - * separated by a single whitespace. Returns 0 on fatal (string - * overrun) error. - */ - - for ( ; n; n = n->next) { - assert(MDOC_TEXT == n->type); - - if (strlcat(p, n->string, sz) >= sz) { - mdoc_nmsg(m, n, MANDOCERR_MEM); - return(0); - } - - if (NULL == n->next) - continue; - - if (strlcat(p, " ", sz) >= sz) { - mdoc_nmsg(m, n, MANDOCERR_MEM); + for ( ; NULL != n; n = n->next) { + if (MDOC_TEXT != n->type) return(0); - } + if ('\0' != p[0] && strlcat(p, " ", sz) >= sz) + return(-1); + if (strlcat(p, n->string, sz) >= sz) + return(-1); + concat(p, n->child, sz); } return(1); diff --git a/contrib/mdocml/msec.c b/contrib/mdocml/msec.c index f51360e597..dd7d11c650 100644 --- a/contrib/mdocml/msec.c +++ b/contrib/mdocml/msec.c @@ -1,4 +1,4 @@ -/* $Id: msec.c,v 1.9 2011/03/22 14:33:05 kristaps Exp $ */ +/* $Id: msec.c,v 1.10 2011/12/02 01:37:14 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -21,15 +21,14 @@ #include <stdlib.h> #include <string.h> -#include "mdoc.h" #include "mandoc.h" -#include "libmdoc.h" +#include "libmandoc.h" #define LINE(x, y) \ if (0 == strcmp(p, x)) return(y); const char * -mdoc_a2msec(const char *p) +mandoc_a2msec(const char *p) { #include "msec.in" diff --git a/contrib/mdocml/out.c b/contrib/mdocml/out.c index 225d4639d8..8dbd68ac11 100644 --- a/contrib/mdocml/out.c +++ b/contrib/mdocml/out.c @@ -1,4 +1,4 @@ -/* $Id: out.c,v 1.40 2011/04/09 15:29:40 kristaps Exp $ */ +/* $Id: out.c,v 1.43 2011/09/20 23:05:49 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> @@ -69,7 +69,7 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) return(0); while (i < BUFSIZ) { - if ( ! isdigit((u_char)*src)) { + if ( ! isdigit((unsigned char)*src)) { if ('.' != *src) break; else if (hasd) @@ -132,48 +132,6 @@ a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) return(1); } - -/* - * Correctly writes the time in nroff form, which differs from standard - * form in that a space isn't printed in lieu of the extra %e field for - * single-digit dates. - */ -void -time2a(time_t t, char *dst, size_t sz) -{ - struct tm tm; - char buf[5]; - char *p; - size_t nsz; - - assert(sz > 1); - localtime_r(&t, &tm); - - p = dst; - nsz = 0; - - dst[0] = '\0'; - - if (0 == (nsz = strftime(p, sz, "%B ", &tm))) - return; - - p += (int)nsz; - sz -= nsz; - - if (0 == strftime(buf, sizeof(buf), "%e, ", &tm)) - return; - - nsz = strlcat(p, buf + (' ' == buf[0] ? 1 : 0), sz); - - if (nsz >= sz) - return; - - p += (int)nsz; - sz -= nsz; - - (void)strftime(p, sz, "%Y", &tm); -} - /* * Calculate the abstract widths and decimal positions of columns in a * table. This routine allocates the columns structures then runs over @@ -186,6 +144,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) const struct tbl_dat *dp; const struct tbl_head *hp; struct roffcol *col; + int spans; /* * Allocate the master column specifiers. These will hold the @@ -202,11 +161,18 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) for ( ; sp; sp = sp->next) { if (TBL_SPAN_DATA != sp->pos) continue; + spans = 1; /* * Account for the data cells in the layout, matching it * to data cells in the data section. */ for (dp = sp->first; dp; dp = dp->next) { + /* Do not used spanned cells in the calculation. */ + if (0 < --spans) + continue; + spans = dp->spans; + if (1 < spans) + continue; assert(dp->layout); col = &tbl->cols[dp->layout->head->ident]; tblcalc_data(tbl, col, sp->tbl, dp); @@ -273,39 +239,12 @@ static void tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, const struct tbl_dat *dp) { - size_t sz, bufsz, spsz; + size_t sz; const char *str; - /* - * Calculate our width and use the spacing, with a minimum - * spacing dictated by position (centre, e.g,. gets a space on - * either side, while right/left get a single adjacent space). - */ - - bufsz = spsz = 0; str = dp->string ? dp->string : ""; sz = (*tbl->slen)(str, tbl->arg); - /* FIXME: TBL_DATA_HORIZ et al.? */ - - assert(dp->layout); - switch (dp->layout->pos) { - case (TBL_CELL_LONG): - /* FALLTHROUGH */ - case (TBL_CELL_CENTRE): - bufsz = (*tbl->len)(1, tbl->arg); - break; - default: - bufsz = (*tbl->len)(1, tbl->arg); - break; - } - - if (dp->layout->spacing) { - spsz = (*tbl->len)(dp->layout->spacing, tbl->arg); - bufsz = bufsz > spsz ? bufsz : spsz; - } - - sz += bufsz; if (col->width < sz) col->width = sz; } @@ -322,7 +261,7 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col, /* * First calculate number width and decimal place (last + 1 for - * no-decimal numbers). If the stored decimal is subsequent + * non-decimal numbers). If the stored decimal is subsequent to * ours, make our size longer by that difference * (right-"shifting"); similarly, if ours is subsequent the * stored, then extend the stored size by the difference. @@ -349,11 +288,6 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col, } else d = sz + psz; - /* Padding. */ - - sz += (*tbl->len)(2, tbl->arg); - d += (*tbl->len)(1, tbl->arg); - /* Adjust the settings for this column. */ if (col->decimal > d) { @@ -366,11 +300,4 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col, col->width = sz; if (d > col->decimal) col->decimal = d; - - /* Adjust for stipulated width. */ - - if (col->width < dp->layout->spacing) - col->width = dp->layout->spacing; } - - diff --git a/contrib/mdocml/out.h b/contrib/mdocml/out.h index 63f10c28ff..1c18c6c314 100644 --- a/contrib/mdocml/out.h +++ b/contrib/mdocml/out.h @@ -1,4 +1,4 @@ -/* $Id: out.h,v 1.20 2011/04/29 22:18:12 kristaps Exp $ */ +/* $Id: out.h,v 1.21 2011/07/17 15:24:25 kristaps Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -64,7 +64,6 @@ __BEGIN_DECLS while (/* CONSTCOND */ 0) int a2roffsu(const char *, struct roffsu *, enum roffscale); -void time2a(time_t, char *, size_t); void tblcalc(struct rofftbl *tbl, const struct tbl_span *); __END_DECLS diff --git a/contrib/mdocml/preconv.1 b/contrib/mdocml/preconv.1 index 96fcaeb12e..c4cb8c5738 100644 --- a/contrib/mdocml/preconv.1 +++ b/contrib/mdocml/preconv.1 @@ -1,4 +1,4 @@ -.\" $Id: preconv.1,v 1.4 2011/05/26 14:45:04 kristaps Exp $ +.\" $Id: preconv.1,v 1.6 2011/12/25 19:35:44 kristaps Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 26 2011 $ +.Dd $Mdocdate: December 25 2011 $ .Dt PRECONV 1 .Os .Sh NAME @@ -38,6 +38,11 @@ or other troff system supporting the escape sequence .Pc input. +.Pp +By default, it parses from standard output, determining encoding as +described in +.Sx Algorithm . +.Pp Its arguments are as follows: .Bl -tag -width Ds .It Fl D Ar enc @@ -48,15 +53,6 @@ The document's encoding. The input file. .El .Pp -If -.Ar file -is not provided, -.Nm -accepts standard input. -See -.Sx Algorithm -for encoding choice. -.Pp The recoded input is written to standard output: Unicode characters in the ASCII range are printed as regular ASCII characters, while those above this range are printed using the @@ -154,7 +150,8 @@ utility appeared in May 2011. The .Nm utility was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . .\" .Sh CAVEATS .\" .Sh BUGS .\" .Sh SECURITY CONSIDERATIONS diff --git a/contrib/mdocml/preconv.c b/contrib/mdocml/preconv.c index a53504ece9..a0b2d6415e 100644 --- a/contrib/mdocml/preconv.c +++ b/contrib/mdocml/preconv.c @@ -1,4 +1,4 @@ -/* $Id: preconv.c,v 1.4 2011/05/26 21:13:07 kristaps Exp $ */ +/* $Id: preconv.c,v 1.5 2011/07/24 18:15:14 kristaps Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -18,8 +18,10 @@ #include "config.h" #endif +#ifdef HAVE_MMAP #include <sys/stat.h> #include <sys/mman.h> +#endif #include <assert.h> #include <fcntl.h> @@ -244,10 +246,11 @@ static int read_whole_file(const char *f, int fd, struct buf *fb, int *with_mmap) { - struct stat st; size_t off; ssize_t ssz; +#ifdef HAVE_MMAP + struct stat st; if (-1 == fstat(fd, &st)) { perror(f); return(0); @@ -273,6 +276,7 @@ read_whole_file(const char *f, int fd, if (fb->buf != MAP_FAILED) return(1); } +#endif /* * If this isn't a regular file (like, say, stdin), then we must @@ -510,9 +514,11 @@ main(int argc, char *argv[]) rc = EXIT_SUCCESS; out: +#ifdef HAVE_MMAP if (map) munmap(b.buf, b.sz); else +#endif free(b.buf); if (fd > STDIN_FILENO) diff --git a/contrib/mdocml/predefs.in b/contrib/mdocml/predefs.in index 9c56112715..70074bb617 100644 --- a/contrib/mdocml/predefs.in +++ b/contrib/mdocml/predefs.in @@ -1,4 +1,4 @@ -/* $Id: predefs.in,v 1.2 2011/05/26 14:30:28 kristaps Exp $ */ +/* $Id: predefs.in,v 1.3 2011/07/31 11:36:49 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -29,13 +29,13 @@ PREDEF("Am", "&") PREDEF("Ba", "|") PREDEF("Ge", "\\(>=") PREDEF("Gt", ">") -PREDEF("If", "\\(if") +PREDEF("If", "infinity") PREDEF("Le", "\\(<=") PREDEF("Lq", "\\(lq") PREDEF("Lt", "<") PREDEF("Na", "NaN") PREDEF("Ne", "\\(!=") -PREDEF("Pi", "\\(*p") +PREDEF("Pi", "pi") PREDEF("Pm", "\\(+-") PREDEF("Rq", "\\(rq") PREDEF("left-bracket", "[") @@ -50,7 +50,7 @@ PREDEF("right-bracket", "]") PREDEF("right-parenthesis", ")") PREDEF("rp", ")") PREDEF("right-singlequote", "\\(cq") -PREDEF("Tm", "\\(tm") +PREDEF("Tm", "(Tm)") PREDEF("Px", "POSIX") PREDEF("Ai", "ANSI") PREDEF("\'", "\\\'") diff --git a/contrib/mdocml/read.c b/contrib/mdocml/read.c index da273ae183..5b14e357d2 100644 --- a/contrib/mdocml/read.c +++ b/contrib/mdocml/read.c @@ -1,4 +1,4 @@ -/* $Id: read.c,v 1.15 2011/05/26 20:36:21 kristaps Exp $ */ +/* $Id: read.c,v 1.28 2012/02/16 20:51:31 joerg Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> @@ -28,6 +28,7 @@ #include <ctype.h> #include <fcntl.h> #include <stdarg.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -37,6 +38,7 @@ #include "libmandoc.h" #include "mdoc.h" #include "man.h" +#include "main.h" #ifndef MAP_FILE #define MAP_FILE 0 @@ -59,18 +61,17 @@ struct mparse { struct man *man; /* man parser */ struct mdoc *mdoc; /* mdoc parser */ struct roff *roff; /* roff parser (!NULL) */ - struct regset regs; /* roff registers */ int reparse_count; /* finite interp. stack */ mandocmsg mmsg; /* warning/error message handler */ void *arg; /* argument to mmsg */ const char *file; + struct buf *secondary; }; static void resize_buf(struct buf *, size_t); static void mparse_buf_r(struct mparse *, struct buf, int); static void mparse_readfd_r(struct mparse *, int, const char *, int); static void pset(const char *, int, struct mparse *); -static void pdesc(struct mparse *, const char *, int); static int read_whole_file(const char *, int, struct buf *, int *); static void mparse_end(struct mparse *); @@ -146,9 +147,19 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "bad comment style", "bad escape sequence", "unterminated quoted string", + + /* related to equations */ + "unexpected literal in equation", "generic error", + /* related to equations */ + "unexpected equation scope closure", + "equation scope open on exit", + "overlapping equation scopes", + "unexpected end of equation", + "equation syntax error", + /* related to tables */ "bad table syntax", "bad table option", @@ -182,7 +193,6 @@ static const char * const mandocerrs[MANDOCERR_MAX] = { "not a manual", "column syntax is inconsistent", "NOT IMPLEMENTED: .Bd -file", - "line scope broken, syntax violated", "argument count wrong, violates syntax", "child violates parent syntax", "argument count wrong, violates syntax", @@ -237,13 +247,13 @@ pset(const char *buf, int pos, struct mparse *curp) switch (curp->inttype) { case (MPARSE_MDOC): if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc(&curp->regs, curp); + curp->pmdoc = mdoc_alloc(curp->roff, curp); assert(curp->pmdoc); curp->mdoc = curp->pmdoc; return; case (MPARSE_MAN): if (NULL == curp->pman) - curp->pman = man_alloc(&curp->regs, curp); + curp->pman = man_alloc(curp->roff, curp); assert(curp->pman); curp->man = curp->pman; return; @@ -253,14 +263,14 @@ pset(const char *buf, int pos, struct mparse *curp) if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc(&curp->regs, curp); + curp->pmdoc = mdoc_alloc(curp->roff, curp); assert(curp->pmdoc); curp->mdoc = curp->pmdoc; return; } if (NULL == curp->pman) - curp->pman = man_alloc(&curp->regs, curp); + curp->pman = man_alloc(curp->roff, curp); assert(curp->pman); curp->man = curp->pman; } @@ -316,9 +326,9 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start) * Warn about bogus characters. If you're using * non-ASCII encoding, you're screwing your * readers. Since I'd rather this not happen, - * I'll be helpful and drop these characters so - * we don't display gibberish. Note to manual - * writers: use special characters. + * I'll be helpful and replace these characters + * with "?", so we don't display gibberish. + * Note to manual writers: use special characters. */ c = (unsigned char) blk.buf[i]; @@ -326,8 +336,11 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start) if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) { mandoc_msg(MANDOCERR_BADCHAR, curp, - curp->line, pos, "ignoring byte"); + curp->line, pos, NULL); i++; + if (pos >= (int)ln.sz) + resize_buf(&ln, 256); + ln.buf[pos++] = '?'; continue; } @@ -402,6 +415,27 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start) of = 0; + /* + * Maintain a lookaside buffer of all parsed lines. We + * only do this if mparse_keep() has been invoked (the + * buffer may be accessed with mparse_getkeep()). + */ + + if (curp->secondary) { + curp->secondary->buf = + mandoc_realloc + (curp->secondary->buf, + curp->secondary->sz + pos + 2); + memcpy(curp->secondary->buf + + curp->secondary->sz, + ln.buf, pos); + curp->secondary->sz += pos; + curp->secondary->buf + [curp->secondary->sz] = '\n'; + curp->secondary->sz++; + curp->secondary->buf + [curp->secondary->sz] = '\0'; + } rerun: rr = roff_parseln (curp->roff, curp->line, @@ -428,6 +462,13 @@ rerun: assert(MANDOCLEVEL_FATAL <= curp->file_status); break; case (ROFF_SO): + /* + * We remove `so' clauses from our lookaside + * buffer because we're going to descend into + * the file recursively. + */ + if (curp->secondary) + curp->secondary->sz -= pos + 1; mparse_readfd_r(curp, -1, ln.buf + of, 1); if (MANDOCLEVEL_FATAL <= curp->file_status) break; @@ -507,38 +548,6 @@ rerun: free(ln.buf); } -static void -pdesc(struct mparse *curp, const char *file, int fd) -{ - struct buf blk; - int with_mmap; - - /* - * Run for each opened file; may be called more than once for - * each full parse sequence if the opened file is nested (i.e., - * from `so'). Simply sucks in the whole file and moves into - * the parse phase for the file. - */ - - if ( ! read_whole_file(file, fd, &blk, &with_mmap)) { - curp->file_status = MANDOCLEVEL_SYSERR; - return; - } - - /* Line number is per-file. */ - - curp->line = 1; - - mparse_buf_r(curp, blk, 1); - -#ifdef HAVE_MMAP - if (with_mmap) - munmap(blk.buf, blk.sz); - else -#endif - free(blk.buf); -} - static int read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap) { @@ -634,29 +643,72 @@ mparse_end(struct mparse *curp) } static void -mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re) +mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file, + int re) { const char *svfile; + /* Line number is per-file. */ + svfile = curp->file; + curp->file = file; + curp->line = 1; + + mparse_buf_r(curp, blk, 1); + + if (0 == re && MANDOCLEVEL_FATAL > curp->file_status) + mparse_end(curp); + + curp->file = svfile; +} + +enum mandoclevel +mparse_readmem(struct mparse *curp, const void *buf, size_t len, + const char *file) +{ + struct buf blk; + + blk.buf = UNCONST(buf); + blk.sz = len; + + mparse_parse_buffer(curp, blk, file, 0); + return(curp->file_status); +} + +static void +mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re) +{ + struct buf blk; + int with_mmap; + if (-1 == fd) if (-1 == (fd = open(file, O_RDONLY, 0))) { perror(file); curp->file_status = MANDOCLEVEL_SYSERR; return; } + /* + * Run for each opened file; may be called more than once for + * each full parse sequence if the opened file is nested (i.e., + * from `so'). Simply sucks in the whole file and moves into + * the parse phase for the file. + */ - svfile = curp->file; - curp->file = file; + if ( ! read_whole_file(file, fd, &blk, &with_mmap)) { + curp->file_status = MANDOCLEVEL_SYSERR; + return; + } - pdesc(curp, file, fd); + mparse_parse_buffer(curp, blk, file, re); - if (0 == re && MANDOCLEVEL_FATAL > curp->file_status) - mparse_end(curp); +#ifdef HAVE_MMAP + if (with_mmap) + munmap(blk.buf, blk.sz); + else +#endif + free(blk.buf); if (STDIN_FILENO != fd && -1 == close(fd)) perror(file); - - curp->file = svfile; } enum mandoclevel @@ -681,7 +733,7 @@ mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void curp->arg = arg; curp->inttype = inttype; - curp->roff = roff_alloc(&curp->regs, curp); + curp->roff = roff_alloc(curp); return(curp); } @@ -689,14 +741,14 @@ void mparse_reset(struct mparse *curp) { - memset(&curp->regs, 0, sizeof(struct regset)); - roff_reset(curp->roff); if (curp->mdoc) mdoc_reset(curp->mdoc); if (curp->man) man_reset(curp->man); + if (curp->secondary) + curp->secondary->sz = 0; curp->file_status = MANDOCLEVEL_OK; curp->mdoc = NULL; @@ -713,7 +765,10 @@ mparse_free(struct mparse *curp) man_free(curp->pman); if (curp->roff) roff_free(curp->roff); + if (curp->secondary) + free(curp->secondary->buf); + free(curp->secondary); free(curp); } @@ -773,3 +828,19 @@ mparse_strlevel(enum mandoclevel lvl) { return(mandoclevels[lvl]); } + +void +mparse_keep(struct mparse *p) +{ + + assert(NULL == p->secondary); + p->secondary = mandoc_calloc(1, sizeof(struct buf)); +} + +const char * +mparse_getkeep(const struct mparse *p) +{ + + assert(p->secondary); + return(p->secondary->sz ? p->secondary->buf : NULL); +} diff --git a/contrib/mdocml/roff.7 b/contrib/mdocml/roff.7 index 41837a1d3c..dd32a45b29 100644 --- a/contrib/mdocml/roff.7 +++ b/contrib/mdocml/roff.7 @@ -1,7 +1,7 @@ -.\" $Id: roff.7,v 1.29 2011/05/24 15:22:14 kristaps Exp $ +.\" $Id: roff.7,v 1.37 2011/12/11 00:38:11 schwarze Exp $ .\" -.\" Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> -.\" Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org> +.\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> +.\" Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 24 2011 $ +.Dd $Mdocdate: December 11 2011 $ .Dt ROFF 7 .Os .Sh NAME @@ -25,38 +25,281 @@ The .Nm roff language is a general purpose text formatting language. -In particular, it serves as the basis for the +Since traditional implementations of the .Xr mdoc 7 and .Xr man 7 -manual formatting macro languages. -This manual describes the subset of the +manual formatting languages are based on it, +many real-world manuals use small numbers of .Nm -language accepted by the +requests intermixed with their +.Xr mdoc 7 +or +.Xr man 7 +code. +To properly format such manuals, the +.Xr mandoc 1 +utility supports a tiny subset of +.Nm +requests. +Only these requests supported by .Xr mandoc 1 -utility. +are documented in the present manual, +together with the basic language syntax shared by +.Nm , +.Xr mdoc 7 , +and +.Xr man 7 . +For complete +.Nm +manuals, consult the +.Sx SEE ALSO +section. .Pp -Input lines beginning with the control characters +Input lines beginning with the control character .Sq \&. -or -.Sq \(aq are parsed for requests and macros. -These define the document structure, change the processing state -and manipulate the formatting. -Some requests and macros also produce formatted output, -while others do not. -.Pp -All other input lines provide free-form text to be printed; -the formatting of free-form text depends on the respective -processing context. +Such lines are called +.Dq request lines +or +.Dq macro lines , +respectively. +Requests change the processing state and manipulate the formatting; +some macros also define the document structure and produce formatted +output. +The single quote +.Pq Qq \(aq +is accepted as an alternative control character, +treated by +.Xr mandoc 1 +just like +.Ql \&. +.Pp +Lines not beginning with control characters are called +.Dq text lines . +They provide free-form text to be printed; the formatting of the text +depends on the respective processing context. .Sh LANGUAGE SYNTAX .Nm documents may contain only graphable 7-bit ASCII characters, the space character, and, in certain circumstances, the tab character. -To produce other characters in the output, use the escape sequences -documented in the +The back-space character +.Sq \e +indicates the start of an escape sequence for +.Sx Comments , +.Sx Special Characters , +.Sx Predefined Strings , +and +user-defined strings defined using the +.Sx ds +request. +.Ss Comments +Text following an escaped double-quote +.Sq \e\(dq , +whether in a request, macro, or text line, is ignored to the end of the line. +A request line beginning with a control character and comment escape +.Sq \&.\e\(dq +is also ignored. +Furthermore, request lines with only a control character and optional +trailing whitespace are stripped from input. +.Pp +Examples: +.Bd -literal -offset indent -compact +\&.\e\(dq This is a comment line. +\&.\e\(dq The next line is ignored: +\&. +\&.Sh EXAMPLES \e\(dq This is a comment, too. +\&example text \e\(dq And so is this. +.Ed +.Ss Special Characters +Special characters are used to encode special glyphs and are rendered +differently across output media. +They may occur in request, macro, and text lines. +Sequences begin with the escape character +.Sq \e +followed by either an open-parenthesis +.Sq \&( +for two-character sequences; an open-bracket +.Sq \&[ +for n-character sequences (terminated at a close-bracket +.Sq \&] ) ; +or a single one character sequence. +.Pp +Examples: +.Bl -tag -width Ds -offset indent -compact +.It Li \e(em +Two-letter em dash escape. +.It Li \ee +One-letter backslash escape. +.El +.Pp +See .Xr mandoc_char 7 -manual. +for a complete list. +.Ss Text Decoration +Terms may be text-decorated using the +.Sq \ef +escape followed by an indicator: B (bold), I (italic), R (regular), or P +(revert to previous mode). +A numerical representation 3, 2, or 1 (bold, italic, and regular, +respectively) may be used instead. +The indicator or numerical representative may be preceded by C +(constant-width), which is ignored. +.Pp +Examples: +.Bl -tag -width Ds -offset indent -compact +.It Li \efBbold\efR +Write in bold, then switch to regular font mode. +.It Li \efIitalic\efP +Write in italic, then return to previous font mode. +.El +.Pp +Text decoration is +.Em not +recommended for +.Xr mdoc 7 , +which encourages semantic annotation. +.Ss Predefined Strings +Predefined strings, like +.Sx Special Characters , +mark special output glyphs. +Predefined strings are escaped with the slash-asterisk, +.Sq \e* : +single-character +.Sq \e*X , +two-character +.Sq \e*(XX , +and N-character +.Sq \e*[N] . +.Pp +Examples: +.Bl -tag -width Ds -offset indent -compact +.It Li \e*(Am +Two-letter ampersand predefined string. +.It Li \e*q +One-letter double-quote predefined string. +.El +.Pp +Predefined strings are not recommended for use, +as they differ across implementations. +Those supported by +.Xr mandoc 1 +are listed in +.Xr mandoc_char 7 . +Manuals using these predefined strings are almost certainly not portable. +.Ss Whitespace +Whitespace consists of the space character. +In text lines, whitespace is preserved within a line. +In request and macro lines, whitespace delimits arguments and is discarded. +.Pp +Unescaped trailing spaces are stripped from text line input unless in a +literal context. +In general, trailing whitespace on any input line is discouraged for +reasons of portability. +In the rare case that a blank character is needed at the end of an +input line, it may be forced by +.Sq \e\ \e& . +.Pp +Literal space characters can be produced in the output +using escape sequences. +In macro lines, they can also be included in arguments using quotation; see +.Sx MACRO SYNTAX +for details. +.Pp +Blank text lines, which may include whitespace, are only permitted +within literal contexts. +If the first character of a text line is a space, that line is printed +with a leading newline. +.Ss Scaling Widths +Many requests and macros support scaled widths for their arguments. +The syntax for a scaled width is +.Sq Li [+-]?[0-9]*.[0-9]*[:unit:] , +where a decimal must be preceded or followed by at least one digit. +Negative numbers, while accepted, are truncated to zero. +.Pp +The following scaling units are accepted: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It c +centimetre +.It i +inch +.It P +pica (~1/6 inch) +.It p +point (~1/72 inch) +.It f +synonym for +.Sq u +.It v +default vertical span +.It m +width of rendered +.Sq m +.Pq em +character +.It n +width of rendered +.Sq n +.Pq en +character +.It u +default horizontal span +.It M +mini-em (~1/100 em) +.El +.Pp +Using anything other than +.Sq m , +.Sq n , +.Sq u , +or +.Sq v +is necessarily non-portable across output media. +See +.Sx COMPATIBILITY . +.Pp +If a scaling unit is not provided, the numerical value is interpreted +under the default rules of +.Sq v +for vertical spaces and +.Sq u +for horizontal ones. +.Pp +Examples: +.Bl -tag -width ".Bl -tag -width 2i" -offset indent -compact +.It Li \&.Bl -tag -width 2i +two-inch tagged list indentation in +.Xr mdoc 7 +.It Li \&.HP 2i +two-inch tagged list indentation in +.Xr man 7 +.It Li \&.sp 2v +two vertical spaces +.El +.Ss Sentence Spacing +Each sentence should terminate at the end of an input line. +By doing this, a formatter will be able to apply the proper amount of +spacing after the end of sentence (unescaped) period, exclamation mark, +or question mark followed by zero or more non-sentence closing +delimiters +.Po +.Sq \&) , +.Sq \&] , +.Sq \&' , +.Sq \&" +.Pc . +.Pp +The proper spacing is also intelligently preserved if a sentence ends at +the boundary of a macro line. +.Pp +Examples: +.Bd -literal -offset indent -compact +Do not end sentences mid-line like this. Instead, +end a sentence like this. +A macro would end like this: +\&.Xr mandoc 1 \&. +.Ed .Sh REQUEST SYNTAX A request or macro line consists of: .Pp @@ -83,20 +326,25 @@ Thus, the following request lines are all equivalent: \&. ig end .Ed .Sh MACRO SYNTAX -Macros can be defined by the +Macros are provided by the +.Xr mdoc 7 +and +.Xr man 7 +languages and can be defined by the .Sx \&de request. When called, they follow the same syntax as requests, except that macro arguments may optionally be quoted by enclosing them in double quote characters .Pq Sq \(dq . -To be recognized as the beginning of a quoted argument, the opening -quote character must be preceded by a space character. -.Pp -A quoted argument may contain whitespace, and pairs of double quote -characters +Quoted text, even if it contains whitespace or would cause +a macro invocation when unquoted, is always considered literal text. +Inside quoted text, pairs of double quote characters .Pq Sq Qq resolve to single double quote characters. +.Pp +To be recognised as the beginning of a quoted argument, the opening +quote character must be preceded by a space character. A quoted argument extends to the next double quote character that is not part of a pair, or to the end of the input line, whichever comes earlier. Leaving out the terminating double quote character at the end of the line @@ -114,11 +362,29 @@ In unquoted arguments, space characters can alternatively be included by preceding them with a backslash .Pq Sq \e\~ , but quoting is usually better for clarity. +.Pp +Examples: +.Bl -tag -width Ds -offset indent -compact +.It Li .Fn strlen \(dqconst char *s\(dq +Group arguments +.Qq const char *s +into one function argument. +If unspecified, +.Qq const , +.Qq char , +and +.Qq *s +would be considered separate arguments. +.It Li .Op \(dqFl a\(dq +Consider +.Qq \&Fl a +as literal text instead of a flag macro. +.El .Sh REQUEST REFERENCE The .Xr mandoc 1 .Nm -parser recognizes the following requests. +parser recognises the following requests. Note that the .Nm language defines many more requests not implemented in @@ -126,7 +392,7 @@ language defines many more requests not implemented in .Ss \&ad Set line adjustment mode. This line-scoped request is intended to have one argument to select -normal, left, right, or center adjustment for subsequent text. +normal, left, right, or centre adjustment for subsequent text. Currently, it is ignored including its arguments, and the number of arguments is not checked. .Ss \&am @@ -578,16 +844,43 @@ only accepts relative paths not containing the strings .Qq ../ and .Qq /.. . +.Pp +This request requires +.Xr man 1 +to change to the right directory before calling +.Xr mandoc 1 , +per convention to the root of the manual tree. +Typical usage looks like: +.Pp +.Dl \&.so man3/Xcursor.3 +.Pp +As the whole concept is rather fragile, the use of +.Sx \&so +is discouraged. +Use +.Xr ln 1 +instead. .Ss \&ta Set tab stops. This line-scoped request can take an arbitrary number of arguments. Currently, it is ignored including its arguments. .Ss \&tr Output character translation. -This request is intended to have one argument, -consisting of an even number of characters. -Currently, it is ignored including its arguments, -and the number of arguments is not checked. +Its syntax is as follows: +.Pp +.D1 Pf \. Cm \&tr Ar [ab]+ +.Pp +Pairs of +.Ar ab +characters are replaced +.Ar ( a +for +.Ar b ) . +Replacement (or origin) characters may also be character escapes; thus, +.Pp +.Dl tr \e(xx\e(yy +.Pp +replaces all invocations of \e(xx with \e(yy. .Ss \&T& Re-start a table layout, retaining the options of the prior table invocation. @@ -668,19 +961,29 @@ using the next-line syntax. .%U http://heirloom.sourceforge.net/doctools/troff.pdf .Re .Sh HISTORY -The RUNOFF typesetting system was written in PL/1 for the CTSS -operating system by Jerome ("Jerry") E. Saltzer in 1961. -It was first used as the main documentation tool by Multics since 1963. -Robert ("Bob") H. Morris ported it to the GE-635 and called it +The RUNOFF typesetting system, whose input forms the basis for .Nm , -Doug McIlroy rewrote it in BCPL in 1969, -Joseph F. Ossanna rewrote it in PDP-11 assembly in 1973, -and Brian W. Kernighan rewrote it in C in 1975. +was written in MAD and FAP for the CTSS operating system by Jerome E. +Saltzer in 1964. +Doug McIlroy rewrote it in BCPL in 1969, renaming it +.Nm . +Dennis M. Ritchie rewrote McIlroy's +.Nm +in PDP-11 assembly for +.At v1 , +Joseph F. Ossanna improved roff and renamed it nroff +for +.At v2 , +then ported nroff to C as troff, which Brian W. Kernighan released with +.At v7 . +In 1989, James Clarke re-implemented troff in C++, naming it groff. .Sh AUTHORS .An -nosplit -This partial +This .Nm reference was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv ; and -.An Ingo Schwarze Aq schwarze@openbsd.org . +.An Ingo Schwarze , +.Mt schwarze@openbsd.org . diff --git a/contrib/mdocml/roff.c b/contrib/mdocml/roff.c index 92a4a9b372..b479cc298c 100644 --- a/contrib/mdocml/roff.c +++ b/contrib/mdocml/roff.c @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.142 2011/05/26 11:58:25 kristaps Exp $ */ +/* $Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> @@ -31,6 +31,9 @@ /* Maximum number of nested if-else conditionals. */ #define RSTACK_MAX 128 +/* Maximum number of string expansions per line, to break infinite loops. */ +#define EXPAND_LIMIT 1000 + enum rofft { ROFF_ad, ROFF_am, @@ -71,10 +74,31 @@ enum roffrule { ROFFRULE_DENY }; +/* + * A single register entity. If "set" is zero, the value of the + * register should be the default one, which is per-register. + * Registers are assumed to be unsigned ints for now. + */ +struct reg { + int set; /* whether set or not */ + unsigned int u; /* unsigned integer */ +}; + +/* + * An incredibly-simple string buffer. + */ struct roffstr { - char *name; /* key of symbol */ - char *string; /* current value */ - struct roffstr *next; /* next in list */ + char *p; /* nil-terminated buffer */ + size_t sz; /* saved strlen(p) */ +}; + +/* + * A key-value roffstr pair as part of a singly-linked list. + */ +struct roffkv { + struct roffstr key; + struct roffstr val; + struct roffkv *next; /* next in list */ }; struct roff { @@ -82,8 +106,10 @@ struct roff { struct roffnode *last; /* leaf of stack */ enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ int rstackpos; /* position in rstack */ - struct regset *regs; /* read/writable registers */ - struct roffstr *first_string; /* user-defined strings & macros */ + struct reg regs[REG__MAX]; + struct roffkv *strtab; /* user-defined strings & macros */ + struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ + struct roffstr *xtab; /* single-byte trans table (`tr') */ const char *current_string; /* value of last called user macro */ struct tbl_node *first_tbl; /* first table parsed */ struct tbl_node *last_tbl; /* last table parsed */ @@ -133,6 +159,12 @@ struct predef { #define PREDEF(__name, __str) \ { (__name), (__str) }, +static enum rofft roffhash_find(const char *, size_t); +static void roffhash_init(void); +static void roffnode_cleanscope(struct roff *); +static void roffnode_pop(struct roff *); +static void roffnode_push(struct roff *, enum rofft, + const char *, int, int); static enum rofferr roff_block(ROFF_ARGS); static enum rofferr roff_block_text(ROFF_ARGS); static enum rofferr roff_block_sub(ROFF_ARGS); @@ -143,18 +175,26 @@ static enum rofferr roff_cond_text(ROFF_ARGS); static enum rofferr roff_cond_sub(ROFF_ARGS); static enum rofferr roff_ds(ROFF_ARGS); static enum roffrule roff_evalcond(const char *, int *); -static void roff_freestr(struct roff *); +static void roff_free1(struct roff *); +static void roff_freestr(struct roffkv *); static char *roff_getname(struct roff *, char **, int, int); static const char *roff_getstrn(const struct roff *, const char *, size_t); static enum rofferr roff_line_ignore(ROFF_ARGS); static enum rofferr roff_nr(ROFF_ARGS); -static int roff_res(struct roff *, +static void roff_openeqn(struct roff *, const char *, + int, int, const char *); +static enum rofft roff_parse(struct roff *, const char *, int *); +static enum rofferr roff_parsetext(char *); +static enum rofferr roff_res(struct roff *, char **, size_t *, int, int); static enum rofferr roff_rm(ROFF_ARGS); static void roff_setstr(struct roff *, const char *, const char *, int); +static void roff_setstrn(struct roffkv **, const char *, + size_t, const char *, size_t, int); static enum rofferr roff_so(ROFF_ARGS); +static enum rofferr roff_tr(ROFF_ARGS); static enum rofferr roff_TE(ROFF_ARGS); static enum rofferr roff_TS(ROFF_ARGS); static enum rofferr roff_EQ(ROFF_ARGS); @@ -162,7 +202,7 @@ static enum rofferr roff_EN(ROFF_ARGS); static enum rofferr roff_T_(ROFF_ARGS); static enum rofferr roff_userdef(ROFF_ARGS); -/* See roff_hash_find() */ +/* See roffhash_find() */ #define ASCII_HI 126 #define ASCII_LO 33 @@ -193,7 +233,7 @@ static struct roffmac roffs[ROFF_MAX] = { { "rm", roff_rm, NULL, NULL, 0, NULL }, { "so", roff_so, NULL, NULL, 0, NULL }, { "ta", roff_line_ignore, NULL, NULL, 0, NULL }, - { "tr", roff_line_ignore, NULL, NULL, 0, NULL }, + { "tr", roff_tr, NULL, NULL, 0, NULL }, { "TS", roff_TS, NULL, NULL, 0, NULL }, { "TE", roff_TE, NULL, NULL, 0, NULL }, { "T&", roff_T_, NULL, NULL, 0, NULL }, @@ -210,20 +250,11 @@ static const struct predef predefs[PREDEFS_MAX] = { #include "predefs.in" }; -static void roff_free1(struct roff *); -static enum rofft roff_hash_find(const char *, size_t); -static void roff_hash_init(void); -static void roffnode_cleanscope(struct roff *); -static void roffnode_push(struct roff *, enum rofft, - const char *, int, int); -static void roffnode_pop(struct roff *); -static enum rofft roff_parse(struct roff *, const char *, int *); - -/* See roff_hash_find() */ +/* See roffhash_find() */ #define ROFF_HASH(p) (p[0] - ASCII_LO) static void -roff_hash_init(void) +roffhash_init(void) { struct roffmac *n; int buc, i; @@ -248,7 +279,7 @@ roff_hash_init(void) * the nil-terminated string name could be found. */ static enum rofft -roff_hash_find(const char *p, size_t s) +roffhash_find(const char *p, size_t s) { int buc; struct roffmac *n; @@ -322,6 +353,7 @@ roff_free1(struct roff *r) { struct tbl_node *t; struct eqn_node *e; + int i; while (NULL != (t = r->first_tbl)) { r->first_tbl = t->next; @@ -340,15 +372,30 @@ roff_free1(struct roff *r) while (r->last) roffnode_pop(r); - roff_freestr(r); -} + roff_freestr(r->strtab); + roff_freestr(r->xmbtab); + + r->strtab = r->xmbtab = NULL; + if (r->xtab) + for (i = 0; i < 128; i++) + free(r->xtab[i].p); + + free(r->xtab); + r->xtab = NULL; +} void roff_reset(struct roff *r) { + int i; roff_free1(r); + + memset(&r->regs, 0, sizeof(struct reg) * REG__MAX); + + for (i = 0; i < PREDEFS_MAX; i++) + roff_setstr(r, predefs[i].name, predefs[i].str, 0); } @@ -362,17 +409,16 @@ roff_free(struct roff *r) struct roff * -roff_alloc(struct regset *regs, struct mparse *parse) +roff_alloc(struct mparse *parse) { struct roff *r; int i; r = mandoc_calloc(1, sizeof(struct roff)); - r->regs = regs; r->parse = parse; r->rstackpos = -1; - roff_hash_init(); + roffhash_init(); for (i = 0; i < PREDEFS_MAX; i++) roff_setstr(r, predefs[i].name, predefs[i].str, 0); @@ -380,25 +426,27 @@ roff_alloc(struct regset *regs, struct mparse *parse) return(r); } - /* * Pre-filter each and every line for reserved words (one beginning with * `\*', e.g., `\*(ab'). These must be handled before the actual line * is processed. + * This also checks the syntax of regular escapes. */ -static int +static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { + enum mandoc_esc esc; const char *stesc; /* start of an escape sequence ('\\') */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ - int i, maxl; + int i, maxl, expand_count; size_t nsz; char *n; - /* Search for a leading backslash and save a pointer to it. */ + expand_count = 0; +again: cp = *bufp + pos; while (NULL != (cp = strchr(cp, '\\'))) { stesc = cp++; @@ -410,9 +458,21 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) */ if ('\0' == *cp) - return(1); - if ('*' != *cp++) - continue; + return(ROFF_CONT); + + if ('*' != *cp) { + res = cp; + esc = mandoc_escape(&cp, NULL, NULL); + if (ESCAPE_ERROR != esc) + continue; + cp = res; + mandoc_msg + (MANDOCERR_BADESCAPE, r->parse, + ln, (int)(stesc - *bufp), NULL); + return(ROFF_CONT); + } + + cp++; /* * The third character decides the length @@ -422,7 +482,7 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) switch (*cp) { case ('\0'): - return(1); + return(ROFF_CONT); case ('('): cp++; maxl = 2; @@ -440,8 +500,13 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) /* Advance to the end of the name. */ for (i = 0; 0 == maxl || i < maxl; i++, cp++) { - if ('\0' == *cp) - return(1); /* Error. */ + if ('\0' == *cp) { + mandoc_msg + (MANDOCERR_BADESCAPE, + r->parse, ln, + (int)(stesc - *bufp), NULL); + return(ROFF_CONT); + } if (0 == maxl && ']' == *cp) break; } @@ -454,13 +519,16 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) res = roff_getstrn(r, stnam, (size_t)i); if (NULL == res) { - /* TODO: keep track of the correct position. */ - mandoc_msg(MANDOCERR_BADESCAPE, r->parse, ln, pos, NULL); + mandoc_msg + (MANDOCERR_BADESCAPE, r->parse, + ln, (int)(stesc - *bufp), NULL); res = ""; } /* Replace the escape sequence by the string. */ + pos = stesc - *bufp; + nsz = *szp + strlen(res) + 1; n = mandoc_malloc(nsz); @@ -472,12 +540,57 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) *bufp = n; *szp = nsz; - return(0); - } - return(1); + if (EXPAND_LIMIT >= ++expand_count) + goto again; + + /* Just leave the string unexpanded. */ + mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); + return(ROFF_IGN); + } + return(ROFF_CONT); } +/* + * Process text streams: convert all breakable hyphens into ASCII_HYPH. + */ +static enum rofferr +roff_parsetext(char *p) +{ + size_t sz; + const char *start; + enum mandoc_esc esc; + + start = p; + + while ('\0' != *p) { + sz = strcspn(p, "-\\"); + p += sz; + + if ('\0' == *p) + break; + + if ('\\' == *p) { + /* Skip over escapes. */ + p++; + esc = mandoc_escape + ((const char **)&p, NULL, NULL); + if (ESCAPE_ERROR == esc) + break; + continue; + } else if (p == start) { + p++; + continue; + } + + if (isalpha((unsigned char)p[-1]) && + isalpha((unsigned char)p[1])) + *p = ASCII_HYPH; + p++; + } + + return(ROFF_CONT); +} enum rofferr roff_parseln(struct roff *r, int ln, char **bufp, @@ -492,8 +605,10 @@ roff_parseln(struct roff *r, int ln, char **bufp, * words to fill in. */ - if (r->first_string && ! roff_res(r, bufp, szp, ln, pos)) - return(ROFF_REPARSE); + e = roff_res(r, bufp, szp, ln, pos); + if (ROFF_IGN == e) + return(e); + assert(ROFF_CONT == e); ppos = pos; ctl = mandoc_getcontrol(*bufp, &pos); @@ -515,18 +630,18 @@ roff_parseln(struct roff *r, int ln, char **bufp, if (ROFF_CONT != e) return(e); if (r->eqn) - return(eqn_read(&r->eqn, ln, *bufp, pos)); + return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); if (r->tbl) return(tbl_read(r->tbl, ln, *bufp, pos)); - return(ROFF_CONT); + return(roff_parsetext(*bufp + pos)); } else if ( ! ctl) { if (r->eqn) - return(eqn_read(&r->eqn, ln, *bufp, pos)); + return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); if (r->tbl) return(tbl_read(r->tbl, ln, *bufp, pos)); - return(ROFF_CONT); + return(roff_parsetext(*bufp + pos)); } else if (r->eqn) - return(eqn_read(&r->eqn, ln, *bufp, ppos)); + return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); /* * If a scope is open, go to the child handler for that macro, @@ -568,16 +683,14 @@ roff_endparse(struct roff *r) if (r->eqn) { mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, - r->eqn->eqn.line, r->eqn->eqn.pos, NULL); - eqn_end(r->eqn); - r->eqn = NULL; + r->eqn->eqn.ln, r->eqn->eqn.pos, NULL); + eqn_end(&r->eqn); } if (r->tbl) { mandoc_msg(MANDOCERR_SCOPEEXIT, r->parse, r->tbl->line, r->tbl->pos, NULL); - tbl_end(r->tbl); - r->tbl = NULL; + tbl_end(&r->tbl); } } @@ -592,14 +705,21 @@ roff_parse(struct roff *r, const char *buf, int *pos) size_t maclen; enum rofft t; - if ('\0' == buf[*pos] || '"' == buf[*pos]) + if ('\0' == buf[*pos] || '"' == buf[*pos] || + '\t' == buf[*pos] || ' ' == buf[*pos]) return(ROFF_MAX); + /* + * We stop the macro parse at an escape, tab, space, or nil. + * However, `\}' is also a valid macro, so make sure we don't + * clobber it by seeing the `\' as the end of token. + */ + mac = buf + *pos; - maclen = strcspn(mac, " \\\t\0"); + maclen = strcspn(mac + 1, " \\\t\0") + 1; t = (r->current_string = roff_getstrn(r, mac, maclen)) - ? ROFF_USERDEF : roff_hash_find(mac, maclen); + ? ROFF_USERDEF : roffhash_find(mac, maclen); *pos += (int)maclen; @@ -878,7 +998,22 @@ roff_cond_sub(ROFF_ARGS) ep++; if ('}' != *ep) continue; - *ep = '&'; + + /* + * Make the \} go away. + * This is a little haphazard, as it's not quite + * clear how nroff does this. + * If we're at the end of line, then just chop + * off the \} and resize the buffer. + * If we aren't, then conver it to spaces. + */ + + if ('\0' == *(ep + 1)) { + *--ep = '\0'; + *szp -= 2; + } else + *(ep - 1) = *ep = ' '; + roff_ccond(r, ROFF_ccond, bufp, szp, ln, pos, pos + 2, offs); break; @@ -1078,6 +1213,26 @@ roff_ds(ROFF_ARGS) return(ROFF_IGN); } +int +roff_regisset(const struct roff *r, enum regs reg) +{ + + return(r->regs[(int)reg].set); +} + +unsigned int +roff_regget(const struct roff *r, enum regs reg) +{ + + return(r->regs[(int)reg].u); +} + +void +roff_regunset(struct roff *r, enum regs reg) +{ + + r->regs[(int)reg].set = 0; +} /* ARGSUSED */ static enum rofferr @@ -1086,18 +1241,16 @@ roff_nr(ROFF_ARGS) const char *key; char *val; int iv; - struct reg *rg; val = *bufp + pos; key = roff_getname(r, &val, ln, pos); - rg = r->regs->regs; if (0 == strcmp(key, "nS")) { - rg[(int)REG_nS].set = 1; - if ((iv = mandoc_strntou(val, strlen(val), 10)) >= 0) - rg[REG_nS].v.u = (unsigned)iv; + r->regs[(int)REG_nS].set = 1; + if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0) + r->regs[(int)REG_nS].u = (unsigned)iv; else - rg[(int)REG_nS].v.u = 0u; + r->regs[(int)REG_nS].u = 0u; } return(ROFF_IGN); @@ -1127,9 +1280,8 @@ roff_TE(ROFF_ARGS) if (NULL == r->tbl) mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); else - tbl_end(r->tbl); + tbl_end(&r->tbl); - r->tbl = NULL; return(ROFF_IGN); } @@ -1146,14 +1298,24 @@ roff_T_(ROFF_ARGS) return(ROFF_IGN); } -/* ARGSUSED */ -static enum rofferr -roff_EQ(ROFF_ARGS) +#if 0 +static int +roff_closeeqn(struct roff *r) { - struct eqn_node *e; + + return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0); +} +#endif + +static void +roff_openeqn(struct roff *r, const char *name, int line, + int offs, const char *buf) +{ + struct eqn_node *e; + int poff; assert(NULL == r->eqn); - e = eqn_alloc(ppos, ln); + e = eqn_alloc(name, offs, line, r->parse); if (r->last_eqn) r->last_eqn->next = e; @@ -1161,6 +1323,19 @@ roff_EQ(ROFF_ARGS) r->first_eqn = r->last_eqn = e; r->eqn = r->last_eqn = e; + + if (buf) { + poff = 0; + eqn_read(&r->eqn, line, buf, offs, &poff); + } +} + +/* ARGSUSED */ +static enum rofferr +roff_EQ(ROFF_ARGS) +{ + + roff_openeqn(r, *bufp + pos, ln, ppos, NULL); return(ROFF_IGN); } @@ -1181,7 +1356,7 @@ roff_TS(ROFF_ARGS) if (r->tbl) { mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL); - tbl_end(r->tbl); + tbl_end(&r->tbl); } t = tbl_alloc(ppos, ln, r->parse); @@ -1195,6 +1370,71 @@ roff_TS(ROFF_ARGS) return(ROFF_IGN); } +/* ARGSUSED */ +static enum rofferr +roff_tr(ROFF_ARGS) +{ + const char *p, *first, *second; + size_t fsz, ssz; + enum mandoc_esc esc; + + p = *bufp + pos; + + if ('\0' == *p) { + mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL); + return(ROFF_IGN); + } + + while ('\0' != *p) { + fsz = ssz = 1; + + first = p++; + if ('\\' == *first) { + esc = mandoc_escape(&p, NULL, NULL); + if (ESCAPE_ERROR == esc) { + mandoc_msg + (MANDOCERR_BADESCAPE, r->parse, + ln, (int)(p - *bufp), NULL); + return(ROFF_IGN); + } + fsz = (size_t)(p - first); + } + + second = p++; + if ('\\' == *second) { + esc = mandoc_escape(&p, NULL, NULL); + if (ESCAPE_ERROR == esc) { + mandoc_msg + (MANDOCERR_BADESCAPE, r->parse, + ln, (int)(p - *bufp), NULL); + return(ROFF_IGN); + } + ssz = (size_t)(p - second); + } else if ('\0' == *second) { + mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, + ln, (int)(p - *bufp), NULL); + second = " "; + p--; + } + + if (fsz > 1) { + roff_setstrn(&r->xmbtab, first, + fsz, second, ssz, 0); + continue; + } + + if (NULL == r->xtab) + r->xtab = mandoc_calloc + (128, sizeof(struct roffstr)); + + free(r->xtab[(int)*first].p); + r->xtab[(int)*first].p = mandoc_strndup(second, ssz); + r->xtab[(int)*first].sz = ssz; + } + + return(ROFF_IGN); +} + /* ARGSUSED */ static enum rofferr roff_so(ROFF_ARGS) @@ -1318,26 +1558,40 @@ static void roff_setstr(struct roff *r, const char *name, const char *string, int multiline) { - struct roffstr *n; - char *c; - size_t oldch, newch; + + roff_setstrn(&r->strtab, name, strlen(name), string, + string ? strlen(string) : 0, multiline); +} + +static void +roff_setstrn(struct roffkv **r, const char *name, size_t namesz, + const char *string, size_t stringsz, int multiline) +{ + struct roffkv *n; + char *c; + int i; + size_t oldch, newch; /* Search for an existing string with the same name. */ - n = r->first_string; - while (n && strcmp(name, n->name)) + n = *r; + + while (n && strcmp(name, n->key.p)) n = n->next; if (NULL == n) { /* Create a new string table entry. */ - n = mandoc_malloc(sizeof(struct roffstr)); - n->name = mandoc_strdup(name); - n->string = NULL; - n->next = r->first_string; - r->first_string = n; + n = mandoc_malloc(sizeof(struct roffkv)); + n->key.p = mandoc_strndup(name, namesz); + n->key.sz = namesz; + n->val.p = NULL; + n->val.sz = 0; + n->next = *r; + *r = n; } else if (0 == multiline) { /* In multiline mode, append; else replace. */ - free(n->string); - n->string = NULL; + free(n->val.p); + n->val.p = NULL; + n->val.sz = 0; } if (NULL == string) @@ -1347,61 +1601,64 @@ roff_setstr(struct roff *r, const char *name, const char *string, * One additional byte for the '\n' in multiline mode, * and one for the terminating '\0'. */ - newch = strlen(string) + (multiline ? 2u : 1u); - if (NULL == n->string) { - n->string = mandoc_malloc(newch); - *n->string = '\0'; + newch = stringsz + (multiline ? 2u : 1u); + + if (NULL == n->val.p) { + n->val.p = mandoc_malloc(newch); + *n->val.p = '\0'; oldch = 0; } else { - oldch = strlen(n->string); - n->string = mandoc_realloc(n->string, oldch + newch); + oldch = n->val.sz; + n->val.p = mandoc_realloc(n->val.p, oldch + newch); } /* Skip existing content in the destination buffer. */ - c = n->string + (int)oldch; + c = n->val.p + (int)oldch; /* Append new content to the destination buffer. */ - while (*string) { + i = 0; + while (i < (int)stringsz) { /* * Rudimentary roff copy mode: * Handle escaped backslashes. */ - if ('\\' == *string && '\\' == *(string + 1)) - string++; - *c++ = *string++; + if ('\\' == string[i] && '\\' == string[i + 1]) + i++; + *c++ = string[i++]; } /* Append terminating bytes. */ if (multiline) *c++ = '\n'; + *c = '\0'; + n->val.sz = (int)(c - n->val.p); } static const char * roff_getstrn(const struct roff *r, const char *name, size_t len) { - const struct roffstr *n; + const struct roffkv *n; - n = r->first_string; - while (n && (strncmp(name, n->name, len) || '\0' != n->name[(int)len])) - n = n->next; + for (n = r->strtab; n; n = n->next) + if (0 == strncmp(name, n->key.p, len) && + '\0' == n->key.p[(int)len]) + return(n->val.p); - return(n ? n->string : NULL); + return(NULL); } static void -roff_freestr(struct roff *r) +roff_freestr(struct roffkv *r) { - struct roffstr *n, *nn; + struct roffkv *n, *nn; - for (n = r->first_string; n; n = nn) { - free(n->name); - free(n->string); + for (n = r; n; n = nn) { + free(n->key.p); + free(n->val.p); nn = n->next; free(n); } - - r->first_string = NULL; } const struct tbl_span * @@ -1417,3 +1674,95 @@ roff_eqn(const struct roff *r) return(r->last_eqn ? &r->last_eqn->eqn : NULL); } + +/* + * Duplicate an input string, making the appropriate character + * conversations (as stipulated by `tr') along the way. + * Returns a heap-allocated string with all the replacements made. + */ +char * +roff_strdup(const struct roff *r, const char *p) +{ + const struct roffkv *cp; + char *res; + const char *pp; + size_t ssz, sz; + enum mandoc_esc esc; + + if (NULL == r->xmbtab && NULL == r->xtab) + return(mandoc_strdup(p)); + else if ('\0' == *p) + return(mandoc_strdup("")); + + /* + * Step through each character looking for term matches + * (remember that a `tr' can be invoked with an escape, which is + * a glyph but the escape is multi-character). + * We only do this if the character hash has been initialised + * and the string is >0 length. + */ + + res = NULL; + ssz = 0; + + while ('\0' != *p) { + if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { + sz = r->xtab[(int)*p].sz; + res = mandoc_realloc(res, ssz + sz + 1); + memcpy(res + ssz, r->xtab[(int)*p].p, sz); + ssz += sz; + p++; + continue; + } else if ('\\' != *p) { + res = mandoc_realloc(res, ssz + 2); + res[ssz++] = *p++; + continue; + } + + /* Search for term matches. */ + for (cp = r->xmbtab; cp; cp = cp->next) + if (0 == strncmp(p, cp->key.p, cp->key.sz)) + break; + + if (NULL != cp) { + /* + * A match has been found. + * Append the match to the array and move + * forward by its keysize. + */ + res = mandoc_realloc + (res, ssz + cp->val.sz + 1); + memcpy(res + ssz, cp->val.p, cp->val.sz); + ssz += cp->val.sz; + p += (int)cp->key.sz; + continue; + } + + /* + * Handle escapes carefully: we need to copy + * over just the escape itself, or else we might + * do replacements within the escape itself. + * Make sure to pass along the bogus string. + */ + pp = p++; + esc = mandoc_escape(&p, NULL, NULL); + if (ESCAPE_ERROR == esc) { + sz = strlen(pp); + res = mandoc_realloc(res, ssz + sz + 1); + memcpy(res + ssz, pp, sz); + break; + } + /* + * We bail out on bad escapes. + * No need to warn: we already did so when + * roff_res() was called. + */ + sz = (int)(p - pp); + res = mandoc_realloc(res, ssz + sz + 1); + memcpy(res + ssz, pp, sz); + ssz += sz; + } + + res[(int)ssz] = '\0'; + return(res); +} diff --git a/contrib/mdocml/st.in b/contrib/mdocml/st.in index 888e5e44fb..3ba41dd359 100644 --- a/contrib/mdocml/st.in +++ b/contrib/mdocml/st.in @@ -1,4 +1,4 @@ -/* $Id: st.in,v 1.16 2011/04/24 17:56:44 schwarze Exp $ */ +/* $Id: st.in,v 1.19 2012/02/26 21:47:09 schwarze Exp $ */ /* * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -51,6 +51,7 @@ LINE("-isoC-amd1", "ISO/IEC 9899/AMD1:1995 (\\(lqISO\\~C90, Amendment 1\\(rq)") LINE("-isoC-tcor1", "ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)") LINE("-isoC-tcor2", "ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)") LINE("-isoC-99", "ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)") +LINE("-isoC-2011", "ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(rq)") LINE("-iso9945-1-90", "ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)") LINE("-iso9945-1-96", "ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)") LINE("-iso9945-2-93", "ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)") @@ -59,12 +60,13 @@ LINE("-ansiC-89", "ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)") LINE("-ansiC-99", "ANSI/ISO/IEC 9899-1999 (\\(lqANSI\\~C99\\(rq)") LINE("-ieee754", "IEEE Std 754-1985") LINE("-iso8802-3", "ISO 8802-3: 1989") +LINE("-iso8601", "ISO 8601") LINE("-ieee1275-94", "IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)") LINE("-xpg3", "X/Open Portability Guide Issue\\~3 (\\(lqXPG3\\(rq)") LINE("-xpg4", "X/Open Portability Guide Issue\\~4 (\\(lqXPG4\\(rq)") LINE("-xpg4.2", "X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\(rq)") LINE("-xpg4.3", "X/Open Portability Guide Issue\\~4, Version\\~3 (\\(lqXPG4.3\\(rq)") -LINE("-xbd5", "X/Open System Interface Definitions Issue\\~5 (\\(lqXBD5\\(rq)") +LINE("-xbd5", "X/Open Base Definitions Issue\\~5 (\\(lqXBD5\\(rq)") LINE("-xcu5", "X/Open Commands and Utilities Issue\\~5 (\\(lqXCU5\\(rq)") LINE("-xsh5", "X/Open System Interfaces and Headers Issue\\~5 (\\(lqXSH5\\(rq)") LINE("-xns5", "X/Open Networking Services Issue\\~5 (\\(lqXNS5\\(rq)") diff --git a/contrib/mdocml/style.css b/contrib/mdocml/style.css index 22abf5115b..ee891f41ea 100644 --- a/contrib/mdocml/style.css +++ b/contrib/mdocml/style.css @@ -1,4 +1,4 @@ -/* $Id: style.css,v 1.22 2011/05/14 23:40:49 kristaps Exp $ */ +/* $Id: style.css,v 1.25 2011/08/26 09:03:17 kristaps Exp $ */ /* * This is an example style-sheet provided for mandoc(1) and the -Thtml @@ -9,14 +9,14 @@ * See mdoc(7) and man(7) for macro explanations. */ -html { max-width: 880px; } +html { max-width: 880px; margin-left: 1em; } body { font-size: smaller; font-family: Helvetica,Arial,sans-serif; } h1 { margin-bottom: 1ex; font-size: 110%; margin-left: -4ex; } /* Section header (Sh, SH). */ h2 { margin-bottom: 1ex; font-size: 105%; margin-left: -2ex; } /* Sub-section header (Ss, SS). */ table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */ td { vertical-align: top; } /* All table cells. */ p { } /* Paragraph: Pp, Lp. */ -blockquote { margin-top: 0ex; margin-bottom: 0ex; } /* D1. */ +blockquote { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1. */ div.section { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */ div.subsection { } /* Sub-sections (Ss, SS). */ table.synopsis { } /* SYNOPSIS section table. */ @@ -38,6 +38,7 @@ i { } /* Italic: BI, IB, I, (implicit). */ b { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */ .symb { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */ small { } /* Small: SB, SM. */ +.lit { font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */ /* Block modes. */ @@ -64,7 +65,6 @@ i.ftype { font-weight: normal; } /* Function types (Ft, Fn). */ b.includes { font-style: normal; } /* Header includes (In). */ span.lib { } /* Library (Lb). */ i.link-sec { font-weight: normal; } /* Section links (Sx). */ -code.lit { font-style: normal; font-weight: normal; } /* Literal: Dl, Li, Bf -literal, Bl -literal, Bl -unfilled. */ b.macro { font-style: normal; } /* Macro-ish thing (Fd). */ b.name { font-style: normal; } /* Name of utility (Nm). */ span.opt { } /* Options (Op, Oo/Oc). */ diff --git a/contrib/mdocml/tbl.7 b/contrib/mdocml/tbl.7 index 52039bf0e8..ea3d2ba3a8 100644 --- a/contrib/mdocml/tbl.7 +++ b/contrib/mdocml/tbl.7 @@ -1,4 +1,4 @@ -.\" $Id: tbl.7,v 1.13 2011/03/17 15:12:42 kristaps Exp $ +.\" $Id: tbl.7,v 1.16 2011/09/03 00:29:21 kristaps Exp $ .\" .\" Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: March 17 2011 $ +.Dd $Mdocdate: September 3 2011 $ .Dt TBL 7 .Os .Sh NAME @@ -49,7 +49,7 @@ macro tags, whose precise syntax is documented in Tables consist of a series of options on a single line, followed by the table layout, followed by data. .Pp -For example, the following creates a boxed table with digits centered in +For example, the following creates a boxed table with digits centred in the cells. .Bd -literal -offset indent \&.TS @@ -256,12 +256,21 @@ The following case-insensitive modifier keys are available: .Cm e , .Cm t , .Cm d , -.Cm f , .Cm b , .Cm i , -.Cm b , +.Cm r , and -.Cm i . +.Cm f +.Po +followed by +.Cm b , +.Cm i , +.Cm r , +.Cm 3 , +.Cm 2 , +or +.Cm 1 +.Pc . All of these are ignored by .Xr mandoc 1 . .Pp @@ -332,7 +341,8 @@ This formed the basis of the implementation that is part of the .Xr mandoc 1 utility. .Sh AUTHORS -This partial +This .Nm reference was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/tbl.c b/contrib/mdocml/tbl.c index 6ef2f735b3..b3d651be07 100644 --- a/contrib/mdocml/tbl.c +++ b/contrib/mdocml/tbl.c @@ -1,4 +1,4 @@ -/* $Id: tbl.c,v 1.25 2011/04/04 23:04:38 kristaps Exp $ */ +/* $Id: tbl.c,v 1.26 2011/07/25 15:37:00 kristaps Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> @@ -154,8 +154,12 @@ tbl_span(struct tbl_node *tbl) } void -tbl_end(struct tbl_node *tbl) +tbl_end(struct tbl_node **tblp) { + struct tbl_node *tbl; + + tbl = *tblp; + *tblp = NULL; if (NULL == tbl->first_span || NULL == tbl->first_span->first) mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse, diff --git a/contrib/mdocml/tbl_html.c b/contrib/mdocml/tbl_html.c index 68d3f9c534..8e7dc05de0 100644 --- a/contrib/mdocml/tbl_html.c +++ b/contrib/mdocml/tbl_html.c @@ -1,6 +1,6 @@ -/* $Id: tbl_html.c,v 1.7 2011/01/13 14:30:13 kristaps Exp $ */ +/* $Id: tbl_html.c,v 1.9 2011/09/18 14:14:15 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons <kristaps@kth.se> + * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/contrib/mdocml/tbl_layout.c b/contrib/mdocml/tbl_layout.c index 0aa18dcbeb..7601f146ca 100644 --- a/contrib/mdocml/tbl_layout.c +++ b/contrib/mdocml/tbl_layout.c @@ -1,6 +1,6 @@ -/* $Id: tbl_layout.c,v 1.20 2011/05/17 13:11:40 kristaps Exp $ */ +/* $Id: tbl_layout.c,v 1.22 2011/09/18 14:14:15 schwarze Exp $ */ /* - * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -173,6 +173,8 @@ mod: goto mod; case ('f'): break; + case ('r'): + /* FALLTHROUGH */ case ('b'): /* FALLTHROUGH */ case ('i'): @@ -185,12 +187,20 @@ mod: } switch (tolower((unsigned char)p[(*pos)++])) { + case ('3'): + /* FALLTHROUGH */ case ('b'): cp->flags |= TBL_CELL_BOLD; goto mod; + case ('2'): + /* FALLTHROUGH */ case ('i'): cp->flags |= TBL_CELL_ITALIC; goto mod; + case ('1'): + /* FALLTHROUGH */ + case ('r'): + goto mod; default: break; } diff --git a/contrib/mdocml/tbl_opts.c b/contrib/mdocml/tbl_opts.c index 7b67c13b47..5bd67f80ee 100644 --- a/contrib/mdocml/tbl_opts.c +++ b/contrib/mdocml/tbl_opts.c @@ -1,6 +1,6 @@ -/* $Id: tbl_opts.c,v 1.11 2011/04/04 23:04:38 kristaps Exp $ */ +/* $Id: tbl_opts.c,v 1.12 2011/09/18 14:14:15 schwarze Exp $ */ /* - * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above diff --git a/contrib/mdocml/tbl_term.c b/contrib/mdocml/tbl_term.c index 1e567c690e..f1928f02cb 100644 --- a/contrib/mdocml/tbl_term.c +++ b/contrib/mdocml/tbl_term.c @@ -1,6 +1,6 @@ -/* $Id: tbl_term.c,v 1.19 2011/01/25 12:07:30 schwarze Exp $ */ +/* $Id: tbl_term.c,v 1.21 2011/09/20 23:05:49 schwarze Exp $ */ /* - * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@kth.se> + * Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -34,14 +34,14 @@ static void tbl_char(struct termp *, char, size_t); static void tbl_data(struct termp *, const struct tbl *, const struct tbl_dat *, const struct roffcol *); -static void tbl_hframe(struct termp *, const struct tbl_span *); +static size_t tbl_rulewidth(struct termp *, const struct tbl_head *); +static void tbl_hframe(struct termp *, const struct tbl_span *, int); static void tbl_literal(struct termp *, const struct tbl_dat *, const struct roffcol *); static void tbl_number(struct termp *, const struct tbl *, const struct tbl_dat *, const struct roffcol *); static void tbl_hrule(struct termp *, const struct tbl_span *); -static void tbl_vframe(struct termp *, const struct tbl *); static void tbl_vrule(struct termp *, const struct tbl_head *); @@ -95,12 +95,19 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) /* Horizontal frame at the start of boxed tables. */ - if (TBL_SPAN_FIRST & sp->flags) - tbl_hframe(tp, sp); + if (TBL_SPAN_FIRST & sp->flags) { + if (TBL_OPT_DBOX & sp->tbl->opts) + tbl_hframe(tp, sp, 1); + if (TBL_OPT_DBOX & sp->tbl->opts || + TBL_OPT_BOX & sp->tbl->opts) + tbl_hframe(tp, sp, 0); + } /* Vertical frame at the start of each row. */ - tbl_vframe(tp, sp->tbl); + if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts) + term_word(tp, TBL_SPAN_HORIZ == sp->pos || + TBL_SPAN_DHORIZ == sp->pos ? "+" : "|"); /* * Now print the actual data itself depending on the span type. @@ -138,9 +145,31 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) if (--spans >= 0) continue; + /* + * All cells get a leading blank, except the + * first one and those after double rulers. + */ + + if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos) + tbl_char(tp, ASCII_NBRSP, 1); + col = &tp->tbl.cols[hp->ident]; tbl_data(tp, sp->tbl, dp, col); + /* No trailing blanks. */ + + if (NULL == hp->next) + break; + + /* + * Add another blank between cells, + * or two when there is no vertical ruler. + */ + + tbl_char(tp, ASCII_NBRSP, + TBL_HEAD_VERT == hp->next->pos || + TBL_HEAD_DVERT == hp->next->pos ? 1 : 2); + /* * Go to the next data cell and assign the * number of subsequent spans, if applicable. @@ -154,7 +183,11 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) break; } - tbl_vframe(tp, sp->tbl); + /* Vertical frame at the end of each row. */ + + if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts) + term_word(tp, TBL_SPAN_HORIZ == sp->pos || + TBL_SPAN_DHORIZ == sp->pos ? "+" : " |"); term_flushln(tp); /* @@ -163,7 +196,11 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) */ if (TBL_SPAN_LAST & sp->flags) { - tbl_hframe(tp, sp); + if (TBL_OPT_DBOX & sp->tbl->opts || + TBL_OPT_BOX & sp->tbl->opts) + tbl_hframe(tp, sp, 0); + if (TBL_OPT_DBOX & sp->tbl->opts) + tbl_hframe(tp, sp, 1); assert(tp->tbl.cols); free(tp->tbl.cols); tp->tbl.cols = NULL; @@ -175,86 +212,66 @@ term_tbl(struct termp *tp, const struct tbl_span *sp) } +/* + * Horizontal rules extend across the entire table. + * Calculate the width by iterating over columns. + */ +static size_t +tbl_rulewidth(struct termp *tp, const struct tbl_head *hp) +{ + size_t width; + + width = tp->tbl.cols[hp->ident].width; + if (TBL_HEAD_DATA == hp->pos) { + /* Account for leading blanks. */ + if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos) + width++; + /* Account for trailing blanks. */ + width++; + if (hp->next && + TBL_HEAD_VERT != hp->next->pos && + TBL_HEAD_DVERT != hp->next->pos) + width++; + } + return(width); +} + +/* + * Rules inside the table can be single or double + * and have crossings with vertical rules marked with pluses. + */ static void tbl_hrule(struct termp *tp, const struct tbl_span *sp) { const struct tbl_head *hp; char c; - size_t width; - - /* - * An hrule extends across the entire table and is demarked by a - * standalone `_' or whatnot in lieu of a table row. Spanning - * headers are marked by a `+', as are table boundaries. - */ c = '-'; if (TBL_SPAN_DHORIZ == sp->pos) c = '='; - /* FIXME: don't use `+' between data and a spanner! */ - - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl.cols[hp->ident].width; - switch (hp->pos) { - case (TBL_HEAD_DATA): - if (hp->next) - width += 2; - tbl_char(tp, c, width); - break; - case (TBL_HEAD_DVERT): - tbl_char(tp, '+', width); - /* FALLTHROUGH */ - case (TBL_HEAD_VERT): - tbl_char(tp, '+', width); - break; - default: - abort(); - /* NOTREACHED */ - } - } + for (hp = sp->head; hp; hp = hp->next) + tbl_char(tp, + TBL_HEAD_DATA == hp->pos ? c : '+', + tbl_rulewidth(tp, hp)); } +/* + * Rules above and below the table are always single + * and have an additional plus at the beginning and end. + * For double frames, this function is called twice, + * and the outer one does not have crossings. + */ static void -tbl_hframe(struct termp *tp, const struct tbl_span *sp) +tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer) { const struct tbl_head *hp; - size_t width; - - if ( ! (TBL_OPT_BOX & sp->tbl->opts || - TBL_OPT_DBOX & sp->tbl->opts)) - return; - - /* - * Print out the horizontal part of a frame or double frame. A - * double frame has an unbroken `-' outer line the width of the - * table, bordered by `+'. The frame (or inner frame, in the - * case of the double frame) is a `-' bordered by `+' and broken - * by `+' whenever a span is encountered. - */ - - if (TBL_OPT_DBOX & sp->tbl->opts) { - term_word(tp, "+"); - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl.cols[hp->ident].width; - tbl_char(tp, '-', width); - } - term_word(tp, "+"); - term_flushln(tp); - } term_word(tp, "+"); - for (hp = sp->head; hp; hp = hp->next) { - width = tp->tbl.cols[hp->ident].width; - switch (hp->pos) { - case (TBL_HEAD_DATA): - tbl_char(tp, '-', width); - break; - default: - tbl_char(tp, '+', width); - break; - } - } + for (hp = sp->head; hp; hp = hp->next) + tbl_char(tp, + outer || TBL_HEAD_DATA == hp->pos ? '-' : '+', + tbl_rulewidth(tp, hp)); term_word(tp, "+"); term_flushln(tp); } @@ -333,14 +350,6 @@ tbl_vrule(struct termp *tp, const struct tbl_head *hp) } } -static void -tbl_vframe(struct termp *tp, const struct tbl *tbl) -{ - - if (TBL_OPT_BOX & tbl->opts || TBL_OPT_DBOX & tbl->opts) - term_word(tp, "|"); -} - static void tbl_char(struct termp *tp, char c, size_t len) { @@ -360,37 +369,35 @@ static void tbl_literal(struct termp *tp, const struct tbl_dat *dp, const struct roffcol *col) { - size_t padl, padr, ssz; - - padl = padr = 0; + size_t len, padl, padr; assert(dp->string); - - ssz = term_len(tp, 1); + len = term_strlen(tp, dp->string); + padr = col->width > len ? col->width - len : 0; + padl = 0; switch (dp->layout->pos) { case (TBL_CELL_LONG): - padl = ssz; - padr = col->width - term_strlen(tp, dp->string) - ssz; + padl = term_len(tp, 1); + padr = padr > padl ? padr - padl : 0; break; case (TBL_CELL_CENTRE): - padr = col->width - term_strlen(tp, dp->string); - if (3 > padr) + if (2 > padr) break; - padl = (padr - 1) / 2; + padl = padr / 2; padr -= padl; break; case (TBL_CELL_RIGHT): - padl = col->width - term_strlen(tp, dp->string); + padl = padr; + padr = 0; break; default: - padr = col->width - term_strlen(tp, dp->string); break; } tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, dp->string); - tbl_char(tp, ASCII_NBRSP, padr + 2); + tbl_char(tp, ASCII_NBRSP, padr); } static void @@ -427,13 +434,11 @@ tbl_number(struct termp *tp, const struct tbl *tbl, } else d = sz + psz; - sz += term_len(tp, 2); - d += term_len(tp, 1); - padl = col->decimal - d; tbl_char(tp, ASCII_NBRSP, padl); term_word(tp, dp->string); - tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); + if (col->width > sz + padl) + tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); } diff --git a/contrib/mdocml/term.c b/contrib/mdocml/term.c index 70260bec31..4ca15ed6fa 100644 --- a/contrib/mdocml/term.c +++ b/contrib/mdocml/term.c @@ -1,6 +1,6 @@ -/* $Id: term.c,v 1.197 2011/05/24 21:31:23 kristaps Exp $ */ +/* $Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any @@ -79,22 +79,18 @@ term_end(struct termp *p) * * The following flags may be specified: * - * - TERMP_NOLPAD: when beginning to write the line, don't left-pad the - * offset value. This is useful when doing columnar lists where the - * prior column has right-padded. - * * - TERMP_NOBREAK: this is the most important and is used when making - * columns. In short: don't print a newline and instead pad to the - * right margin. Used in conjunction with TERMP_NOLPAD. + * columns. In short: don't print a newline and instead expect the + * next call to do the padding up to the start of the next column. * - * - TERMP_TWOSPACE: when padding, make sure there are at least two - * space characters of padding. Otherwise, rather break the line. + * - TERMP_TWOSPACE: make sure there is room for at least two space + * characters of padding. Otherwise, rather break the line. * * - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and * the line is overrun, and don't pad-right if it's underrun. * * - TERMP_HANG: like TERMP_DANGLE, but doesn't newline when - * overruning, instead save the position and continue at that point + * overrunning, instead save the position and continue at that point * when the next invocation. * * In-line line breaking: @@ -134,9 +130,10 @@ term_flushln(struct termp *p) bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; /* - * Indent the first line of a paragraph. + * Calculate the required amount of padding. */ - vbl = p->flags & TERMP_NOLPAD ? (size_t)0 : p->offset; + vbl = p->offset + p->overstep > p->viscol ? + p->offset + p->overstep - p->viscol : 0; vis = vend = 0; i = 0; @@ -187,14 +184,12 @@ term_flushln(struct termp *p) if (vend > bp && 0 == jhy && vis > 0) { vend -= vis; (*p->endline)(p); + p->viscol = 0; if (TERMP_NOBREAK & p->flags) { - p->viscol = p->rmargin; - (*p->advance)(p, p->rmargin); + vbl = p->rmargin; vend += p->rmargin - p->offset; - } else { - p->viscol = 0; + } else vbl = p->offset; - } /* Remove the p->overstep width. */ @@ -236,10 +231,14 @@ term_flushln(struct termp *p) if (ASCII_HYPH == p->buf[i]) { (*p->letter)(p, '-'); p->viscol += (*p->width)(p, '-'); - } else { - (*p->letter)(p, p->buf[i]); - p->viscol += (*p->width)(p, p->buf[i]); + continue; } + + (*p->letter)(p, p->buf[i]); + if (8 == p->buf[i]) + p->viscol -= (*p->width)(p, p->buf[i-1]); + else + p->viscol += (*p->width)(p, p->buf[i]); } vis = vend; } @@ -248,7 +247,8 @@ term_flushln(struct termp *p) * If there was trailing white space, it was not printed; * so reset the cursor position accordingly. */ - vis -= vbl; + if (vis) + vis -= vbl; p->col = 0; p->overstep = 0; @@ -273,25 +273,18 @@ term_flushln(struct termp *p) * move it one step LEFT and flag the rest of the line * to be longer. */ - if (p->overstep >= -1) { - assert((int)maxvis + p->overstep >= 0); - maxvis += (size_t)p->overstep; - } else + if (p->overstep < -1) p->overstep = 0; + return; } else if (TERMP_DANGLE & p->flags) return; - /* Right-pad. */ - if (maxvis > vis + + /* If the column was overrun, break the line. */ + if (maxvis <= vis + ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) { - p->viscol += maxvis - vis; - (*p->advance)(p, maxvis - vis); - vis += (maxvis - vis); - } else { /* ...or newline break. */ (*p->endline)(p); - p->viscol = p->rmargin; - (*p->advance)(p, p->rmargin); + p->viscol = 0; } } @@ -306,12 +299,8 @@ term_newln(struct termp *p) { p->flags |= TERMP_NOSPACE; - if (0 == p->col && 0 == p->viscol) { - p->flags &= ~TERMP_NOLPAD; - return; - } - term_flushln(p); - p->flags &= ~TERMP_NOLPAD; + if (p->col || p->viscol) + term_flushln(p); } @@ -576,13 +565,16 @@ encode(struct termp *p, const char *word, size_t sz) adjbuf(p, p->col + 1 + (len * 3)); for (i = 0; i < len; i++) { - if ( ! isgraph((unsigned char)word[i])) { + if (ASCII_HYPH != word[i] && + ! isgraph((unsigned char)word[i])) { p->buf[p->col++] = word[i]; continue; } if (TERMFONT_UNDER == f) p->buf[p->col++] = '_'; + else if (ASCII_HYPH == word[i]) + p->buf[p->col++] = '-'; else p->buf[p->col++] = word[i]; diff --git a/contrib/mdocml/term.h b/contrib/mdocml/term.h index 130024de6c..56d076e54a 100644 --- a/contrib/mdocml/term.h +++ b/contrib/mdocml/term.h @@ -1,6 +1,6 @@ -/* $Id: term.h,v 1.85 2011/05/20 15:48:22 kristaps Exp $ */ +/* $Id: term.h,v 1.90 2011/12/04 23:10:52 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -52,6 +52,8 @@ struct termp_tbl { struct termp { enum termtype type; struct rofftbl tbl; /* table configuration */ + int mdocstyle; /* imitate mdoc(7) output */ + size_t defindent; /* Default indent for text. */ size_t defrmargin; /* Right margin of the device. */ size_t rmargin; /* Current right margin. */ size_t maxrmargin; /* Max right margin. */ @@ -64,7 +66,6 @@ struct termp { int flags; #define TERMP_SENTENCE (1 << 1) /* Space before a sentence. */ #define TERMP_NOSPACE (1 << 2) /* No space before words. */ -#define TERMP_NOLPAD (1 << 3) /* See term_flushln(). */ #define TERMP_NOBREAK (1 << 4) /* See term_flushln(). */ #define TERMP_IGNDELIM (1 << 6) /* Delims like regulars. */ #define TERMP_NONOSPACE (1 << 7) /* No space (no autounset). */ @@ -96,6 +97,7 @@ struct termp { struct termp_ps *ps; }; +void term_eqn(struct termp *, const struct eqn *); void term_tbl(struct termp *, const struct tbl_span *); void term_free(struct termp *); void term_newln(struct termp *); diff --git a/contrib/mdocml/term_ascii.c b/contrib/mdocml/term_ascii.c index e65f590a71..2f114786f6 100644 --- a/contrib/mdocml/term_ascii.c +++ b/contrib/mdocml/term_ascii.c @@ -1,6 +1,6 @@ -/* $Id: term_ascii.c,v 1.17 2011/05/20 15:48:22 kristaps Exp $ */ +/* $Id: term_ascii.c,v 1.20 2011/12/04 23:10:52 schwarze Exp $ */ /* - * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -68,7 +68,7 @@ static size_t locale_width(const struct termp *, int); static struct termp * ascii_init(enum termenc enc, char *outopts) { - const char *toks[2]; + const char *toks[4]; char *v; struct termp *p; @@ -104,14 +104,27 @@ ascii_init(enum termenc enc, char *outopts) } #endif - toks[0] = "width"; - toks[1] = NULL; + toks[0] = "indent"; + toks[1] = "width"; + toks[2] = "mdoc"; + toks[3] = NULL; while (outopts && *outopts) switch (getsubopt(&outopts, UNCONST(toks), &v)) { case (0): + p->defindent = (size_t)atoi(v); + break; + case (1): p->defrmargin = (size_t)atoi(v); break; + case (2): + /* + * Temporary, undocumented mode + * to imitate mdoc(7) output style. + */ + p->mdocstyle = 1; + p->defindent = 5; + break; default: break; } diff --git a/contrib/mdocml/term_ps.c b/contrib/mdocml/term_ps.c index 44e492a2ea..e8a906858a 100644 --- a/contrib/mdocml/term_ps.c +++ b/contrib/mdocml/term_ps.c @@ -1,6 +1,6 @@ -/* $Id: term_ps.c,v 1.51 2011/05/17 14:38:34 kristaps Exp $ */ +/* $Id: term_ps.c,v 1.54 2011/10/16 12:20:34 schwarze Exp $ */ /* - * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -429,7 +429,8 @@ static struct termp * pspdf_alloc(char *outopts) { struct termp *p; - size_t pagex, pagey, marginx, marginy, lineheight; + unsigned int pagex, pagey; + size_t marginx, marginy, lineheight; const char *toks[2]; const char *pp; char *v; @@ -485,10 +486,9 @@ pspdf_alloc(char *outopts) } else if (0 == strcasecmp(pp, "legal")) { pagex = 216; pagey = 356; - } else if (2 != sscanf(pp, "%zux%zu", &pagex, &pagey)) + } else if (2 != sscanf(pp, "%ux%u", &pagex, &pagey)) fprintf(stderr, "%s: Unknown paper\n", pp); - } else if (NULL == pp) - pp = "letter"; + } /* * This MUST be defined before any PNT2AFM or AFM2PNT @@ -513,8 +513,8 @@ pspdf_alloc(char *outopts) lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4)); - p->ps->width = pagex; - p->ps->height = pagey; + p->ps->width = (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); @@ -575,7 +575,7 @@ ps_printf(struct termp *p, const char *fmt, ...) ps_growbuf(p, PS_BUFSLOP); pos = (int)p->ps->psmargcur; - len = vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap); + vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap); va_end(ap); diff --git a/contrib/mdocml/test-fgetln.c b/contrib/mdocml/test-fgetln.c new file mode 100644 index 0000000000..90869cd89f --- /dev/null +++ b/contrib/mdocml/test-fgetln.c @@ -0,0 +1,11 @@ +#include <stdio.h> +#include <stdlib.h> + +int +main(void) +{ + char *cp; + size_t sz; + cp = fgetln(stdin, &sz); + return 0; +} diff --git a/contrib/mdocml/test-getsubopt.c b/contrib/mdocml/test-getsubopt.c new file mode 100644 index 0000000000..25e11246e6 --- /dev/null +++ b/contrib/mdocml/test-getsubopt.c @@ -0,0 +1,12 @@ +#if defined(__linux__) || defined(__MINT__) +# define _GNU_SOURCE /* getsubopt() */ +#endif + +#include <stdlib.h> + +int +main(int argc, char **argv) +{ + getsubopt(argv, argv, argv); + return 0; +} diff --git a/contrib/mdocml/test-strptime.c b/contrib/mdocml/test-strptime.c new file mode 100644 index 0000000000..976e9c8066 --- /dev/null +++ b/contrib/mdocml/test-strptime.c @@ -0,0 +1,13 @@ +#if defined(__linux__) || defined(__MINT__) +# define _GNU_SOURCE /* strptime(), getsubopt() */ +#endif + +#include <time.h> + +int +main(int argc, char **argv) +{ + struct tm tm; + strptime(*argv, "%D", &tm); + return 0; +} diff --git a/contrib/mdocml/tree.c b/contrib/mdocml/tree.c index fecad80ed9..1430c737e0 100644 --- a/contrib/mdocml/tree.c +++ b/contrib/mdocml/tree.c @@ -1,6 +1,6 @@ -/* $Id: tree.c,v 1.37 2011/03/23 12:33:01 kristaps Exp $ */ +/* $Id: tree.c,v 1.47 2011/09/18 14:14:15 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv> + * Copyright (c) 2008, 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,6 +19,7 @@ #endif #include <assert.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <time.h> @@ -28,8 +29,9 @@ #include "man.h" #include "main.h" -static void print_mdoc(const struct mdoc_node *, int); +static void print_box(const struct eqn_box *, int); static void print_man(const struct man_node *, int); +static void print_mdoc(const struct mdoc_node *, int); static void print_span(const struct tbl_span *, int); @@ -63,6 +65,7 @@ print_mdoc(const struct mdoc_node *n, int indent) argv = NULL; argc = sz = 0; params = NULL; + t = p = NULL; switch (n->type) { case (MDOC_ROOT): @@ -90,18 +93,14 @@ print_mdoc(const struct mdoc_node *n, int indent) t = "text"; break; case (MDOC_TBL): - t = "tbl"; - break; + /* FALLTHROUGH */ case (MDOC_EQN): - t = "eqn"; break; default: abort(); /* NOTREACHED */ } - p = NULL; - switch (n->type) { case (MDOC_TEXT): p = n->string; @@ -130,9 +129,8 @@ print_mdoc(const struct mdoc_node *n, int indent) } break; case (MDOC_TBL): - break; + /* FALLTHROUGH */ case (MDOC_EQN): - p = n->eqn->data; break; case (MDOC_ROOT): p = "root"; @@ -143,8 +141,11 @@ print_mdoc(const struct mdoc_node *n, int indent) } if (n->span) { - assert(NULL == p); + assert(NULL == p && NULL == t); print_span(n->span, indent); + } else if (n->eqn) { + assert(NULL == p && NULL == t); + print_box(n->eqn->root, indent); } else { for (i = 0; i < indent; i++) putchar('\t'); @@ -164,11 +165,9 @@ print_mdoc(const struct mdoc_node *n, int indent) for (i = 0; i < (int)sz; i++) printf(" [%s]", params[i]); - printf(" %d:%d", n->line, n->pos); + printf(" %d:%d\n", n->line, n->pos); } - putchar('\n'); - if (n->child) print_mdoc(n->child, indent + 1); if (n->next) @@ -182,6 +181,8 @@ print_man(const struct man_node *n, int indent) const char *p, *t; int i; + t = p = NULL; + switch (n->type) { case (MAN_ROOT): t = "root"; @@ -205,18 +206,14 @@ print_man(const struct man_node *n, int indent) t = "block-tail"; break; case (MAN_TBL): - t = "tbl"; - break; + /* FALLTHROUGH */ case (MAN_EQN): - t = "eqn"; break; default: abort(); /* NOTREACHED */ } - p = NULL; - switch (n->type) { case (MAN_TEXT): p = n->string; @@ -236,9 +233,8 @@ print_man(const struct man_node *n, int indent) p = "root"; break; case (MAN_TBL): - break; + /* FALLTHROUGH */ case (MAN_EQN): - p = n->eqn->data; break; default: abort(); @@ -246,22 +242,65 @@ print_man(const struct man_node *n, int indent) } if (n->span) { - assert(NULL == p); + assert(NULL == p && NULL == t); print_span(n->span, indent); + } else if (n->eqn) { + assert(NULL == p && NULL == t); + print_box(n->eqn->root, indent); } else { for (i = 0; i < indent; i++) putchar('\t'); - printf("%s (%s) %d:%d", p, t, n->line, n->pos); + printf("%s (%s) %d:%d\n", p, t, n->line, n->pos); } - putchar('\n'); - if (n->child) print_man(n->child, indent + 1); if (n->next) print_man(n->next, indent); } +static void +print_box(const struct eqn_box *ep, int indent) +{ + int i; + const char *t; + + if (NULL == ep) + return; + for (i = 0; i < indent; i++) + putchar('\t'); + + t = NULL; + switch (ep->type) { + case (EQN_ROOT): + t = "eqn-root"; + break; + case (EQN_LIST): + t = "eqn-list"; + break; + case (EQN_SUBEXPR): + t = "eqn-expr"; + break; + case (EQN_TEXT): + t = "eqn-text"; + break; + case (EQN_MATRIX): + t = "eqn-matrix"; + break; + } + + assert(t); + printf("%s(%d, %d, %d, %d, %d, \"%s\", \"%s\") %s\n", + t, EQN_DEFSIZE == ep->size ? 0 : ep->size, + ep->pos, ep->font, ep->mark, ep->pile, + ep->left ? ep->left : "", + ep->right ? ep->right : "", + ep->text ? ep->text : ""); + + print_box(ep->first, indent + 1); + print_box(ep->next, indent); +} + static void print_span(const struct tbl_span *sp, int indent) { @@ -306,5 +345,5 @@ print_span(const struct tbl_span *sp, int indent) putchar(' '); } - printf("(tbl) %d:1", sp->line); + printf("(tbl) %d:1\n", sp->line); } diff --git a/contrib/mdocml/whatis.1 b/contrib/mdocml/whatis.1 new file mode 100644 index 0000000000..500b0b6c34 --- /dev/null +++ b/contrib/mdocml/whatis.1 @@ -0,0 +1,190 @@ +.\" $Id: whatis.1,v 1.8 2012/03/24 01:54:43 kristaps Exp $ +.\" +.\" Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: March 24 2012 $ +.Dt WHATIS 1 +.Os +.Sh NAME +.Nm whatis +.Nd search for manual pages by page names +.Sh SYNOPSIS +.Nm +.Op Fl C Ar file +.Op Fl M Ar manpath +.Op Fl m Ar manpath +.Op Fl S Ar arch +.Op Fl s Ar section +.Ar name ... +.Sh DESCRIPTION +The +.Nm +utility searches databases generated by +.Xr mandocdb 8 +for manuals containing the word +.Ar name +in their page name, ignoring case. +It returns the header lines from all matching pages. +You can then use the +.Xr man 1 +command to get more information. +.Pp +By default, +.Nm +searches for +.Xr mandocdb 8 +databases in the default paths stipulated by +.Xr man 1 . +If standard output is a TTY, a result may be selected from a list and +its manual displayed with the pager. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl C Ar file +Specify an alternative configuration +.Ar file +in +.Xr man.conf 5 +format. +.It Fl M Ar manpath +Use the colon-separated +.Ar manpath +instead of the default list of paths searched for +.Xr mandocdb 8 +databases. +Invalid paths, or paths without manual databases, are ignored. +.It Fl m Ar manpath +Prepend the colon-separated +.Ar manpath +to the list of paths searched for +.Xr mandocdb 8 +databases. +Invalid paths, or paths without manual databases, are ignored. +.It Fl S Ar arch +Search only for a particular architecture. +.It Fl s Ar cat +Search only for a manual section. +See +.Xr man 1 +for a listing of manual sections. +.El +.Pp +Results are sorted by manual title, with output formatted as +.Pp +.D1 title(sec) \- description +.Pp +Where +.Qq title +is the manual's title (note multiple manual names may exist for one +title), +.Qq sec +is the manual section, and +.Qq description +is the manual's short description. +If an architecture is specified for the manual, it is displayed as +.Pp +.D1 title(cat/arch) \- description +.Pp +If on a TTY, results are prefixed with a numeric identifier. +.Pp +.D1 [index] title(cat) \- description +.Pp +One may choose a manual be entering the index at the prompt. +Valid choices are displayed using +.Ev MANPAGER , +or failing that , +.Ev PAGER +or just +.Xr more 1 . +Source pages are formatted with +.Xr mandoc 1 ; +preformatted pages with +.Xr cat 1 . +.Pp +.Nm +is identical to running +.Xr apropos 1 +as follows: +.Pp +.Dl $ apropos -- -i 'Nm~[[:<:]]term[[:>:]]' +.Sh ENVIRONMENT +.Bl -tag -width Ds +.It Ev MANPAGER +Default pager for manuals. +If this is unset, falls back to +.Ev Pager . +.It Ev PAGER +The second choice for a manual pager. +If this is unset, use +.Xr more 1 . +.It Ev MANPATH +Colon-separated paths modifying the default list of paths searched for +manual databases. +Invalid paths, or paths without manual databases, are ignored. +Overridden by +.Fl M . +If +.Ev MANPATH +begins with a +.Sq \&: , +it is appended to the default list; +else if it ends with +.Sq \&: , +it is prepended to the default list; else if it contains +.Sq \&:: , +the default list is inserted between the colons. +If none of these conditions are met, it overrides the default list. +.El +.Sh FILES +.Bl -tag -width "/etc/man.conf" -compact +.It Pa whatis.db +name of the +.Xr mandocdb 8 +keyword database +.It Pa whatis.index +name of the +.Xr mandocdb 8 +filename database +.It Pa /etc/man.conf +default +.Xr man 1 +configuration file +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr apropos 1 , +.Xr man 1 , +.Xr man.conf 5 , +.Xr mandocdb 8 +.Sh HISTORY +The +.Nm +utility first appeared in +.Bx 2 . +It was rewritten from scratch as part of the +.Xr mandocdb 8 +project for +.Ox 5.1 . +.Sh AUTHORS +.An -nosplit +.An Bill Joy +wrote the original +.Bx +.Nm +in 1979. +The current version was written by +.An Kristaps Dzonsons , +.Mt kristaps@bsd.lv . -- 2.41.0