diff --git a/.gitignore b/.gitignore index 52fb026e6..d32690bd8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ CVS minix-port.patch *.worldstone.log .worldstone* +usr.bin/mdocml/man/*.7 diff --git a/Makefile b/Makefile index 7256106ac..462f30779 100644 --- a/Makefile +++ b/Makefile @@ -61,12 +61,14 @@ commands: includes libraries $(MAKE) -C commands all $(MAKE) -C bin all $(MAKE) -C usr.bin all + $(MAKE) -C libexec all dep-all: $(MAKE) CC=cc -C boot dependall $(MAKE) -C commands dependall $(MAKE) -C bin dependall $(MAKE) -C usr.bin dependall + $(MAKE) -C libexec dependall $(MAKE) -C kernel dependall $(MAKE) -C servers dependall $(MAKE) -C drivers dependall @@ -82,10 +84,12 @@ all: $(MAKE) -C commands all $(MAKE) -C bin all $(MAKE) -C usr.bin all + $(MAKE) -C libexec all $(MAKE) -C tools all install: $(MAKE) CC=cc -C boot install + $(MAKE) -C libexec install $(MAKE) -C man install makedb $(MAKE) -C commands install $(MAKE) -C bin install @@ -99,6 +103,7 @@ clean: mkfiles $(MAKE) -C commands clean $(MAKE) -C bin clean $(MAKE) -C usr.bin clean + $(MAKE) -C libexec clean $(MAKE) -C tools clean $(MAKE) -C lib clean_all $(MAKE) -C test clean @@ -109,4 +114,5 @@ cleandepend: mkfiles $(MAKE) -C commands cleandepend $(MAKE) -C bin cleandepend $(MAKE) -C usr.bin cleandepend + $(MAKE) -C libexec cleandepend $(MAKE) -C tools cleandepend diff --git a/commands/Makefile b/commands/Makefile index b051cdcb4..65e1c5999 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -16,8 +16,8 @@ SUBDIR= aal add_route adduser arp ash at autil awk \ hostaddr id ifconfig ifdef install \ intr ipcrm ipcs irdpd isoread join kill last leave \ less lex loadkeys loadramdisk logger login look lp \ - lpd ls lspci M mail make MAKEDEV man \ - mdb mdocml mesg mined ackmkdep mkfifo mkfs.mfs mknod \ + lpd ls lspci M mail make MAKEDEV \ + mdb mesg mined ackmkdep mkfifo mkfs.mfs mknod \ mkproto modem mount mt netconf newroot nice acknm nohup \ nonamed od passwd paste patch pax \ ping postinstall poweroff pr prep printf printroot \ diff --git a/commands/man/Makefile b/commands/man/Makefile deleted file mode 100644 index 797d24bd4..000000000 --- a/commands/man/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# $NetBSD: Makefile,v 1.10 2007/10/05 07:38:52 lukem Exp $ -# @(#)Makefile 8.1 (Berkeley) 6/6/93 - -PROG= man -SRCS= man.c manconf.c -MAN= man.1 man.conf.5 - -DPADD+= ${LIBMINIXUTIL} -LDADD+= -lminixutil - -FILES=man.conf -FILESDIR=/etc - -.include diff --git a/commands/man/man.conf b/commands/man/man.conf deleted file mode 100644 index 855ffa934..000000000 --- a/commands/man/man.conf +++ /dev/null @@ -1,6 +0,0 @@ -_version BSD.2 -_subdir man[1-9] -_suffix .0 -_build .[1-9] mandoc %s -_build .tbl tbl %s | mandoc -_default /usr/man /usr/pkg/X11R6/man /usr/pkg/man /usr/pkg/gcc44/man diff --git a/commands/mdocml/ChangeLog.xsl b/commands/mdocml/ChangeLog.xsl deleted file mode 100644 index dccc79dba..000000000 --- a/commands/mdocml/ChangeLog.xsl +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - mdocml - CVS-ChangeLog - - - - -
- Files modified by - -
-
- - Note: - - -
    - -
  • - - - — Rev: - - , Status: - - - , Tag: - - - -
  • -
    -
-
-
- - -
-
diff --git a/commands/mdocml/Makefile b/commands/mdocml/Makefile deleted file mode 100644 index 5d3ec3d94..000000000 --- a/commands/mdocml/Makefile +++ /dev/null @@ -1,36 +0,0 @@ - -VERSION = 1.10.2 -VDATE = 19 June 2010 - -VFLAGS = -DVERSION="\"$(VERSION)\"" - -.if ${CC} == gcc -WFLAGS = -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings -CFLAGS += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H -ARFLAGS = rs -.else -CFLAGS += ${VFLAGS} -DHAVE_CONFIG_H -ARFLAGS = r -.endif - -# 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\"" - -# Unset this if you don't want Xo/Xc allowing split `It' lines, which -# breaks symmetry. -CFLAGS += -DUGLY - -SRCS = main.c mdoc_term.c chars.c term.c tree.c compat.c \ - man_term.c html.c mdoc_html.c man_html.c out.c \ - term_ps.c term_ascii.c man_macro.c man.c man_hash.c \ - man_validate.c man_action.c mandoc.c man_argv.c roff.c \ - mdoc_macro.c mdoc.c mdoc_hash.c mdoc_strings.c mdoc_argv.c \ - mdoc_validate.c mdoc_action.c lib.c att.c arch.c vol.c \ - msec.c st.c - -MAN = mandoc.1 mdoc.3 mdoc.7 mandoc_char.7 man.7 man.3 roff.7 roff.3 - -PROG = mandoc - -.include diff --git a/commands/mdocml/chars.in b/commands/mdocml/chars.in deleted file mode 100644 index ebbbcfaa5..000000000 --- a/commands/mdocml/chars.in +++ /dev/null @@ -1,446 +0,0 @@ -/* $Id: chars.in,v 1.25 2010/06/19 20:46:27 kristaps Exp $ */ -/* - * Copyright (c) 2009 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. - */ - -/* - * The ASCII translation tables. STRING corresponds to predefined - * strings (cf. mdoc_samples.7 and tmac/mdoc/doc-nroff). CHAR - * corresponds to special characters (cf. groff_char.7). BOTH contains - * sequences that are equivalent in both STRING and CHAR. - * - * Either way, the left-hand side corresponds to the input sequence (\x, - * \(xx, \*(xx and so on) whose length is listed second element. The - * right-hand side is what's produced by the front-end, with the fourth - * element being its length. - * - * XXX - C-escape strings! - * XXX - update LINES_MAX if adding more! - */ - -/* Non-breaking, non-collapsing space uses unit separator. */ -static const char ascii_nbrsp[2] = { ASCII_NBRSP, 0 }; - -CHAR_TBL_START - -/* Spacing. */ -CHAR("c", 1, "", 0, "", 0) -CHAR("0", 1, " ", 1, " ", 7) -CHAR(" ", 1, " ", 1, " ", 7) -CHAR("~", 1, ascii_nbrsp, 1, " ", 6) -CHAR("%", 1, "", 0, "", 0) -CHAR("&", 1, "", 0, "", 0) -CHAR("^", 1, "", 0, "", 0) -CHAR("|", 1, "", 0, "", 0) -CHAR("}", 1, "", 0, "", 0) - -/* Accents. */ -CHAR("a\"", 2, "\"", 1, "̋", 6) -CHAR("a-", 2, "-", 1, "¯", 6) -CHAR("a.", 2, ".", 1, "˙", 6) -CHAR("a^", 2, "^", 1, "̂", 6) -BOTH("\'", 1, "\'", 1, "́", 6) -BOTH("aa", 2, "\'", 1, "́", 6) -BOTH("ga", 2, "`", 1, "̀", 6) -BOTH("`", 1, "`", 1, "̀", 6) -CHAR("ab", 2, "`", 1, "̆", 6) -CHAR("ac", 2, ",", 1, "̧", 6) -CHAR("ad", 2, "\"", 1, "̈", 6) -CHAR("ah", 2, "v", 1, "ˇ", 6) -CHAR("ao", 2, "o", 1, "˚", 6) -CHAR("a~", 2, "~", 1, "̃", 6) -CHAR("ho", 2, ",", 1, "̨", 6) -CHAR("ha", 2, "^", 1, "^", 1) -CHAR("ti", 2, "~", 1, "~", 1) - -/* Quotes. */ -CHAR("Bq", 2, ",,", 2, "„", 7) -CHAR("bq", 2, ",", 1, "‚", 7) -BOTH("lq", 2, "``", 2, "“", 7) -BOTH("rq", 2, "\'\'", 2, "”", 7) -CHAR("oq", 2, "`", 1, "‘", 7) -CHAR("cq", 2, "\'", 1, "’", 7) -CHAR("aq", 2, "\'", 1, "\'", 1) -CHAR("dq", 2, "\"", 1, "\"", 1) -CHAR("Fo", 2, "<<", 2, "«", 6) -CHAR("Fc", 2, ">>", 2, "»", 6) -CHAR("fo", 2, "<", 1, "‹", 7) -CHAR("fc", 2, ">", 1, "›", 7) - -/* Brackets. */ -CHAR("lB", 2, "[", 1, "[", 1) -CHAR("rB", 2, "]", 1, "]", 1) -CHAR("lC", 2, "{", 1, "{", 1) -CHAR("rC", 2, "}", 1, "}", 1) -CHAR("la", 2, "<", 1, "⟨", 8) -CHAR("ra", 2, ">", 1, "⟩", 8) -CHAR("bv", 2, "|", 1, "⎪", 7) -CHAR("braceex", 7, "|", 1, "⎪", 7) -CHAR("bracketlefttp", 13, "|", 1, "⎡", 7) -CHAR("bracketleftbp", 13, "|", 1, "⎣", 7) -CHAR("bracketleftex", 13, "|", 1, "⎢", 7) -CHAR("bracketrighttp", 14, "|", 1, "⎤", 7) -CHAR("bracketrightbp", 14, "|", 1, "⎦", 7) -CHAR("bracketrightex", 14, "|", 1, "⎥", 7) -CHAR("lt", 2, ",-", 2, "⎧", 7) -CHAR("bracelefttp", 11, ",-", 2, "⎧", 7) -CHAR("lk", 2, "{", 1, "⎨", 7) -CHAR("braceleftmid", 12, "{", 1, "⎨", 7) -CHAR("lb", 2, ",-", 2, "⎩", 7) -CHAR("braceleftbp", 11, "`-", 2, "⎩", 7) -CHAR("braceleftex", 11, "|", 1, "⎪", 7) -CHAR("rt", 2, "-.", 2, "⎫", 7) -CHAR("bracerighttp", 12, "-.", 2, "⎫", 7) -CHAR("rk", 2, "}", 1, "⎬", 7) -CHAR("bracerightmid", 13, "}", 1, "⎬", 7) -CHAR("rb", 2, "-\'", 2, "⎭", 7) -CHAR("bracerightbp", 12, "-\'", 2, "⎭", 7) -CHAR("bracerightex", 12, "|", 1, "⎪", 7) -CHAR("parenlefttp", 11, "/", 1, "⎛", 7) -CHAR("parenleftbp", 11, "\\", 1, "⎝", 7) -CHAR("parenleftex", 11, "|", 1, "⎜", 7) -CHAR("parenrighttp", 12, "\\", 1, "⎞", 7) -CHAR("parenrightbp", 12, "/", 1, "⎠", 7) -CHAR("parenrightex", 12, "|", 1, "⎟", 7) - -/* Greek characters. */ -CHAR("*A", 2, "A", 1, "Α", 6) -CHAR("*B", 2, "B", 1, "Β", 6) -CHAR("*G", 2, "|", 1, "Γ", 6) -CHAR("*D", 2, "/\\", 2, "Δ", 6) -CHAR("*E", 2, "E", 1, "Ε", 6) -CHAR("*Z", 2, "Z", 1, "Ζ", 6) -CHAR("*Y", 2, "H", 1, "Η", 6) -CHAR("*H", 2, "O", 1, "Θ", 6) -CHAR("*I", 2, "I", 1, "Ι", 6) -CHAR("*K", 2, "K", 1, "Κ", 6) -CHAR("*L", 2, "/\\", 2, "Λ", 6) -CHAR("*M", 2, "M", 1, "Μ", 6) -CHAR("*N", 2, "N", 1, "Ν", 6) -CHAR("*C", 2, "H", 1, "Ξ", 6) -CHAR("*O", 2, "O", 1, "Ο", 6) -CHAR("*P", 2, "TT", 2, "Π", 6) -CHAR("*R", 2, "P", 1, "Ρ", 6) -CHAR("*S", 2, ">", 1, "Σ", 6) -CHAR("*T", 2, "T", 1, "Τ", 6) -CHAR("*U", 2, "Y", 1, "Υ", 6) -CHAR("*F", 2, "O_", 1, "Φ", 6) -CHAR("*X", 2, "X", 1, "Χ", 6) -CHAR("*Q", 2, "Y", 1, "Ψ", 6) -CHAR("*W", 2, "O", 1, "Ω", 6) -CHAR("*a", 2, "a", 1, "α", 6) -CHAR("*b", 2, "B", 1, "β", 6) -CHAR("*g", 2, "y", 1, "γ", 6) -CHAR("*d", 2, "d", 1, "δ", 6) -CHAR("*e", 2, "e", 1, "ε", 6) -CHAR("*z", 2, "C", 1, "ζ", 6) -CHAR("*y", 2, "n", 1, "η", 6) -CHAR("*h", 2, "0", 1, "θ", 6) -CHAR("*i", 2, "i", 1, "ι", 6) -CHAR("*k", 2, "k", 1, "κ", 6) -CHAR("*l", 2, "\\", 1, "λ", 6) -CHAR("*m", 2, "u", 1, "μ", 6) -CHAR("*n", 2, "v", 1, "ν", 6) -CHAR("*c", 2, "E", 1, "ξ", 6) -CHAR("*o", 2, "o", 1, "ο", 6) -CHAR("*p", 2, "n", 1, "π", 6) -CHAR("*r", 2, "p", 1, "ρ", 6) -CHAR("*s", 2, "o", 1, "σ", 6) -CHAR("*t", 2, "t", 1, "τ", 6) -CHAR("*u", 2, "u", 1, "υ", 6) -CHAR("*f", 2, "o", 1, "ϕ", 6) -CHAR("*x", 2, "x", 1, "χ", 6) -CHAR("*q", 2, "u", 1, "ψ", 6) -CHAR("*w", 2, "w", 1, "ω", 6) -CHAR("+h", 2, "0", 1, "ϑ", 6) -CHAR("+f", 2, "o", 1, "φ", 6) -CHAR("+p", 2, "w", 1, "ϖ", 6) -CHAR("+e", 2, "e", 1, "ϵ", 7) -CHAR("ts", 2, "s", 1, "ς", 6) - -/* Accented letters. */ -CHAR(",C", 2, "C", 1, "Ç", 6) -CHAR(",c", 2, "c", 1, "ç", 6) -CHAR("/L", 2, "L", 1, "Ł", 6) -CHAR("/O", 2, "O", 1, "Ø", 6) -CHAR("/l", 2, "l", 1, "ł", 6) -CHAR("/o", 2, "o", 1, "ø", 6) -CHAR("oA", 2, "A", 1, "Å", 6) -CHAR("oa", 2, "a", 1, "å", 6) -CHAR(":A", 2, "A", 1, "Ä", 6) -CHAR(":E", 2, "E", 1, "Ë", 6) -CHAR(":I", 2, "I", 1, "Ï", 6) -CHAR(":O", 2, "O", 1, "Ö", 6) -CHAR(":U", 2, "U", 1, "Ü", 6) -CHAR(":a", 2, "a", 1, "ä", 6) -CHAR(":e", 2, "e", 1, "ë", 6) -CHAR(":i", 2, "i", 1, "ï", 6) -CHAR(":o", 2, "o", 1, "õ", 6) -CHAR(":u", 2, "u", 1, "ü", 6) -CHAR(":y", 2, "y", 1, "ÿ", 6) -CHAR("\'A", 2, "A", 1, "Á", 6) -CHAR("\'E", 2, "E", 1, "É", 6) -CHAR("\'I", 2, "I", 1, "Í", 6) -CHAR("\'O", 2, "O", 1, "Ó", 6) -CHAR("\'U", 2, "U", 1, "Ú", 6) -CHAR("\'a", 2, "a", 1, "á", 6) -CHAR("\'e", 2, "e", 1, "é", 6) -CHAR("\'i", 2, "i", 1, "í", 6) -CHAR("\'o", 2, "o", 1, "ó", 6) -CHAR("\'u", 2, "u", 1, "ú", 6) -CHAR("^A", 2, "A", 1, "Â", 6) -CHAR("^E", 2, "E", 1, "Ê", 6) -CHAR("^I", 2, "I", 1, "Î", 6) -CHAR("^O", 2, "O", 1, "Ô", 6) -CHAR("^U", 2, "U", 1, "Û", 6) -CHAR("^a", 2, "a", 1, "â", 6) -CHAR("^e", 2, "e", 1, "ê", 6) -CHAR("^i", 2, "i", 1, "î", 6) -CHAR("^o", 2, "o", 1, "ô", 6) -CHAR("^u", 2, "u", 1, "û", 6) -CHAR("`A", 2, "A", 1, "À", 6) -CHAR("`E", 2, "E", 1, "È", 6) -CHAR("`I", 2, "I", 1, "Ì", 6) -CHAR("`O", 2, "O", 1, "Ò", 6) -CHAR("`U", 2, "U", 1, "Ù", 6) -CHAR("`a", 2, "a", 1, "à", 6) -CHAR("`e", 2, "e", 1, "è", 6) -CHAR("`i", 2, "i", 1, "ì", 6) -CHAR("`o", 2, "o", 1, "ò", 6) -CHAR("`u", 2, "u", 1, "ù", 6) -CHAR("~A", 2, "A", 1, "Ã", 6) -CHAR("~N", 2, "N", 1, "Ñ", 6) -CHAR("~O", 2, "O", 1, "Õ", 6) -CHAR("~a", 2, "a", 1, "ã", 6) -CHAR("~n", 2, "n", 1, "ñ", 6) -CHAR("~o", 2, "o", 1, "õ", 6) - -/* Arrows and lines. */ -CHAR("<-", 2, "<-", 2, "←", 7) -CHAR("->", 2, "->", 2, "→", 7) -CHAR("<>", 2, "<>", 2, "↔", 7) -CHAR("da", 2, "v", 1, "↓", 7) -BOTH("ua", 2, "^", 1, "↑", 7) -BOTH("va", 2, "^v", 2, "↕", 7) -CHAR("lA", 2, "<=", 2, "⇐", 7) -CHAR("rA", 2, "=>", 2, "⇒", 7) -CHAR("hA", 2, "<=>", 3, "⇔", 7) -CHAR("dA", 2, "v", 1, "⇓", 7) -CHAR("uA", 2, "^", 1, "⇑", 7) -CHAR("vA", 2, "^=v", 3, "⇕", 7) - -/* Logic. */ -CHAR("AN", 2, "^", 1, "∧", 7) -CHAR("OR", 2, "v", 1, "∨", 7) -CHAR("no", 2, "~", 1, "¬", 6) -CHAR("tno", 3, "~", 1, "¬", 6) -CHAR("te", 2, "3", 1, "∃", 7) -CHAR("fa", 2, "V", 1, "∀", 7) -CHAR("st", 2, "-)", 2, "∋", 7) -CHAR("tf", 2, ".:.", 3, "∴", 7) -CHAR("3d", 2, ".:.", 3, "∴", 7) -CHAR("or", 2, "|", 1, "|", 1) - -/* Mathematicals. */ -CHAR("pl", 2, "+", 1, "+", 5) -CHAR("mi", 2, "-", 1, "−", 7) -CHAR("-", 1, "-", 1, "-", 1) -CHAR("-+", 2, "-+", 2, "∓", 7) -CHAR("+-", 2, "+-", 2, "±", 6) -CHAR("t+-", 3, "+-", 2, "±", 6) -CHAR("pc", 2, ".", 1, "·", 6) -CHAR("md", 2, ".", 1, "⋅", 7) -CHAR("mu", 2, "x", 1, "×", 6) -CHAR("tmu", 3, "x", 1, "×", 6) -CHAR("c*", 2, "x", 1, "⊗", 7) -CHAR("c+", 2, "+", 1, "⊕", 7) -CHAR("di", 2, "-:-", 3, "÷", 6) -CHAR("tdi", 3, "-:-", 3, "÷", 6) -CHAR("f/", 2, "/", 1, "⁄", 7) -CHAR("**", 2, "*", 1, "∗", 7) -BOTH("<=", 2, "<=", 2, "≤", 7) -BOTH(">=", 2, ">=", 2, "≥", 7) -CHAR("<<", 2, "<<", 2, "≪", 7) -CHAR(">>", 2, ">>", 2, "≫", 7) -CHAR("eq", 2, "=", 1, "=", 5) -CHAR("!=", 2, "!=", 2, "≠", 7) -CHAR("==", 2, "==", 2, "≡", 7) -CHAR("ne", 2, "!==", 3, "≢", 7) -CHAR("=~", 2, "=~", 2, "≅", 7) -CHAR("-~", 2, "-~", 2, "≃", 7) -CHAR("ap", 2, "~", 1, "∼", 7) -CHAR("~~", 2, "~~", 2, "≈", 7) -CHAR("~=", 2, "~=", 2, "≌", 7) -CHAR("pt", 2, "oc", 2, "∝", 7) -CHAR("es", 2, "{}", 2, "∅", 7) -CHAR("mo", 2, "E", 1, "∈", 7) -CHAR("nm", 2, "!E", 2, "∉", 7) -CHAR("sb", 2, "(=", 2, "⊂", 7) -CHAR("nb", 2, "(!=", 3, "⊄", 7) -CHAR("sp", 2, "=)", 2, "⊃", 7) -CHAR("nc", 2, "!=)", 3, "⊅", 7) -CHAR("ib", 2, "(=", 2, "⊆", 7) -CHAR("ip", 2, "=)", 2, "⊇", 7) -CHAR("ca", 2, "(^)", 3, "∩", 7) -CHAR("cu", 2, "U", 1, "∪", 7) -CHAR("/_", 2, "/_", 2, "∠", 7) -CHAR("pp", 2, "_|_", 3, "⊥", 7) -CHAR("is", 2, "I", 1, "∫", 7) -CHAR("integral", 8, "I", 1, "∫", 7) -CHAR("sum", 3, "E", 1, "∑", 7) -CHAR("product", 7, "TT", 2, "∏", 7) -CHAR("coproduct", 9, "U", 1, "∐", 7) -CHAR("gr", 2, "V", 1, "∇", 7) -CHAR("sr", 2, "\\/", 2, "√", 7) -CHAR("sqrt", 4, "\\/", 2, "√", 7) -CHAR("lc", 2, "|~", 2, "⌈", 7) -CHAR("rc", 2, "~|", 2, "⌉", 7) -CHAR("lf", 2, "|_", 2, "⌊", 7) -CHAR("rf", 2, "_|", 2, "⌋", 7) -CHAR("if", 2, "oo", 2, "∞", 7) -CHAR("Ah", 2, "N", 1, "ℵ", 7) -CHAR("Im", 2, "I", 1, "ℑ", 7) -CHAR("Re", 2, "R", 1, "ℜ", 7) -CHAR("pd", 2, "a", 1, "∂", 7) -CHAR("-h", 2, "/h", 2, "ℏ", 7) - -/* Ligatures. */ -CHAR("ff", 2, "ff", 2, "ff", 8) -CHAR("fi", 2, "fi", 2, "fi", 8) -CHAR("fl", 2, "fl", 2, "fl", 8) -CHAR("Fi", 2, "ffi", 3, "ffi", 8) -CHAR("Fl", 2, "ffl", 3, "ffl", 8) -BOTH("AE", 2, "AE", 2, "Æ", 6) -BOTH("ae", 2, "ae", 2, "æ", 6) -CHAR("OE", 2, "OE", 2, "Œ", 6) -CHAR("oe", 2, "oe", 2, "œ", 6) -CHAR("ss", 2, "ss", 2, "ß", 6) -CHAR("IJ", 2, "IJ", 2, "IJ", 6) -CHAR("ij", 2, "ij", 2, "ij", 6) - -/* Special letters. */ -CHAR("-D", 2, "D", 1, "Ð", 6) -CHAR("Sd", 2, "o", 1, "ð", 6) -CHAR("TP", 2, "b", 1, "Þ", 6) -CHAR("Tp", 2, "b", 1, "þ", 6) -CHAR(".i", 2, "i", 1, "ı", 6) -CHAR(".j", 2, "j", 1, "ȷ", 6) - -/* Currency. */ -CHAR("Do", 2, "$", 1, "$", 1) -CHAR("ct", 2, "c", 1, "¢", 6) -CHAR("Eu", 2, "EUR", 3, "€", 7) -CHAR("eu", 2, "EUR", 3, "€", 7) -CHAR("Ye", 2, "Y", 1, "¥", 6) -CHAR("Po", 2, "L", 1, "£", 6) -CHAR("Cs", 2, "x", 1, "¤", 6) -CHAR("Fn", 2, "f", 1, "ƒ", 6) - -/* pod2man holdovers. */ -STRING("--", 2, "--", 2, "—", 7) -STRING("PI", 2, "pi", 2, "π", 6) -STRING("L\"", 2, "``", 2, "“", 7) -STRING("R\"", 2, "\'\'", 2, "”", 7) -STRING("C+", 2, "C++", 3, "C++", 3) -STRING("C`", 2, "`", 1, "‘", 7) -STRING("C\'", 2, "\'", 1, "’", 7) -STRING("Aq", 2, "\'", 1, "\'", 1) -STRING("^", 1, "^", 1, "^", 1) -STRING(",", 1, ",", 1, ",", 1) -STRING("~", 1, "~", 1, "~", 1) -STRING("/", 1, "/", 1, "/", 1) -STRING(":", 1, "\"", 1, "̈", 6) -STRING("8", 1, "B", 1, "β", 6) -STRING("o", 1, "o", 1, "°", 6) -STRING("D-", 2, "D", 1, "Ð", 6) -STRING("d-", 2, "o", 1, "ð", 6) -STRING("Th", 2, "b", 1, "Þ", 6) -STRING("th", 2, "b", 1, "þ", 6) - -/* Old style. */ -STRING("Am", 2, "&", 1, "&", 5) -STRING("Ba", 2, "|", 1, "|", 1) -STRING("Ge", 2, ">=", 2, "≥", 7) -STRING("Gt", 2, ">", 1, ">", 4) -STRING("If", 2, "infinity", 8, "infinity", 8) -STRING("Le", 2, "<=", 2, "≤", 7) -STRING("Lq", 2, "``", 2, "“", 7) -STRING("Lt", 2, "<", 1, "<", 4) -STRING("Na", 2, "NaN", 3, "NaN", 3) -STRING("Ne", 2, "!=", 2, "≠", 7) -STRING("Pi", 2, "pi", 2, "π", 6) -STRING("Pm", 2, "+-", 2, "±", 6) -STRING("R", 1, "(R)", 3, "®", 6) -STRING("Rq", 2, "\'\'", 2, "”", 7) -STRING("Tm", 2, "tm", 2, "™", 7) -STRING("left-bracket", 12, "[", 1, "[", 1) -STRING("left-parenthesis", 16, "(", 1, "(", 1) -STRING("left-singlequote", 16, "`", 1, "‘", 7) -STRING("lp", 2, "(", 1, "(", 1) -STRING("q", 1, "\"", 1, """, 6) -STRING("quote-left", 10, "`", 1, "‘", 7) -STRING("quote-right", 11, "\'", 1, "’", 7) -STRING("right-bracket", 13, "]", 1, "]", 1) -STRING("right-parenthesis", 17, ")", 1, ")", 1) -STRING("right-singlequote", 17, "\'", 1, "’", 7) -STRING("rp", 2, ")", 1, ")", 1) - -/* Lines. */ -CHAR("ba", 2, "|", 1, "|", 6) -CHAR("br", 2, "|", 1, "│", 7) -CHAR("ul", 2, "_", 1, "_", 5) -CHAR("rl", 2, "-", 1, "‾", 7) -CHAR("bb", 2, "|", 1, "¦", 6) -CHAR("sl", 2, "/", 1, "/", 5) -CHAR("rs", 2, "\\", 1, "\", 5) - -/* Text markers. */ -CHAR("ci", 2, "o", 1, "○", 7) -CHAR("bu", 2, "o", 1, "•", 7) -CHAR("dd", 2, "=", 1, "‡", 7) -CHAR("dg", 2, "-", 1, "†", 7) -CHAR("lz", 2, "<>", 2, "◊", 7) -CHAR("sq", 2, "[]", 2, "□", 7) -CHAR("ps", 2, "9|", 2, "¶", 6) -CHAR("sc", 2, "S", 1, "§", 6) -CHAR("lh", 2, "<=", 2, "☜", 7) -CHAR("rh", 2, "=>", 2, "☞", 7) -CHAR("at", 2, "@", 1, "@", 5) -CHAR("sh", 2, "#", 1, "#", 5) -CHAR("CR", 2, "_|", 2, "↵", 7) -CHAR("OK", 2, "\\/", 2, "✓", 8) - -/* Legal symbols. */ -CHAR("co", 2, "(C)", 3, "©", 6) -CHAR("rg", 2, "(R)", 3, "®", 6) -CHAR("tm", 2, "tm", 2, "™", 7) - -/* Punctuation. */ -CHAR(".", 1, ".", 1, ".", 1) -CHAR("r!", 2, "i", 1, "¡", 6) -CHAR("r?", 2, "c", 1, "¿", 6) -CHAR("em", 2, "--", 2, "—", 7) -CHAR("en", 2, "-", 1, "–", 7) -CHAR("hy", 2, "-", 1, "‐", 7) -CHAR("e", 1, "\\", 1, "\\", 1) - -/* Units. */ -CHAR("de", 2, "o", 1, "°", 6) -CHAR("%0", 2, "%o", 2, "‰", 7) -CHAR("fm", 2, "\'", 1, "′", 7) -CHAR("sd", 2, "\"", 1, "″", 7) -CHAR("mc", 2, "mu", 2, "µ", 6) - -CHAR_TBL_END diff --git a/commands/mdocml/config.h b/commands/mdocml/config.h deleted file mode 100644 index 17072dd3a..000000000 --- a/commands/mdocml/config.h +++ /dev/null @@ -1,20 +0,0 @@ - -#ifndef MANDOC_CONFIG_H -#define MANDOC_CONFIG_H - -#define HAVE_STRLCAT -#define HAVE_STRLCPY - -#include -#include - -#ifndef __GNUC__ -#define inline -#endif - -#ifndef _DIAGASSERT -#define _DIAGASSERT assert -#endif - -#endif /* MANDOC_CONFIG_H */ - diff --git a/commands/mdocml/example.style.css b/commands/mdocml/example.style.css deleted file mode 100644 index fcaf64b60..000000000 --- a/commands/mdocml/example.style.css +++ /dev/null @@ -1,72 +0,0 @@ -/* $Id: example.style.css,v 1.20 2010/04/08 08:17:55 kristaps Exp $ */ - -div.body { font-family: monospace; - min-width: 580px; width: 580px; } /* Top-most div tag. */ - -div.sec-head { font-weight: bold; font-style: normal; } /* Sections (Sh). */ -div.sec-body { } -div.sec-block { } - -div.ssec-head { font-weight: bold; font-style: normal; } /* Sub-sections (Ss). */ -div.ssec-body { } -div.ssec-block { } - -span.addr { } /* Address (Ad). */ -span.arg { font-style: italic; font-weight: normal; } /* Command argument (Ar). */ -span.author { } /* Author name (An). */ -span.bold { font-weight: bold; font-style: normal; } /* Generically bold (SB, BI, IB, BR, RB, B). */ -span.cmd { font-weight: bold; font-style: normal; } /* Command (Cm). */ -span.config { font-weight: bold; font-style: normal; } /* Config statement (Cd). */ -span.define { } /* Defines (Dv). */ -span.desc { } /* Nd. After em-dash. */ -span.diag { font-weight: bold; font-style: normal; } /* Diagnostic (Bl -diag). */ -span.emph { font-style: italic; font-weight: normal; } /* Emphasis (Em). */ -span.env { } /* Environment variables (Ev). */ -span.errno { } /* Error string (Er). */ -span.farg { font-style: italic; font-weight: normal; } /* Function argument (Fa, Fn). */ -span.file { font-style: italic; font-weight: normal; } /* File (Pa). */ -span.flag { font-weight: bold; font-style: normal; } /* Flag (Fl, Cm). */ -span.fname { font-weight: bold; font-style: normal; } /* Function name (Fa, Fn, Rv). */ -span.ftype { font-style: italic; font-weight: normal; } /* Function types (Ft, Fn). */ -span.includes { font-weight: bold; font-style: normal; } /* Header includes (In). */ -span.italic { font-style: italic; font-weight: normal; } /* Generically italic (BI, IB, I). */ -span.lib { } /* Library (Lb). */ -span.lit { } /* Literals (Bf -literal). */ -span.macro { font-weight: bold; font-style: normal; } /* Macro-ish thing (Fd). */ -span.name { font-weight: bold; font-style: normal; } /* Name of utility (Nm). */ -span.opt { } /* Options (Op, Oo/Oc). */ -span.ref { } /* Citations (Rs). */ -span.ref-auth { } /* Reference author (%A). */ -span.ref-book { font-style: italic; font-weight: normal; } /* Reference book (%B). */ -span.ref-city { } /* Reference city (%C). */ -span.ref-date { } /* Reference date (%D). */ -span.ref-issue { font-style: italic; font-weight: normal; } /* Reference issuer/publisher (%I). */ -span.ref-jrnl { font-style: italic; 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.roman { font-style: normal; font-weight: normal; } /* Generic font. */ -span.small { font-size: smaller; } /* Generically small (SB, SM). */ -span.symb { font-weight: bold; font-style: normal; } /* Symbols. */ -span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */ -span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */ -span.utility { font-weight: bold; font-style: normal; } /* Name of utility (Ex). */ -span.var { font-weight: bold; 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). */ - -div.emph { font-style: italic; font-weight: normal; } /* Emphasis (Bl -emphasis). */ -div.lit { } /* Literal (D1, Bd -literal, Dl, Bd -literal). */ -div.symb { font-weight: bold; font-style: normal; } /* Symbols (Bl -symbolic). */ - -table.footer { } /* Document footer. */ -table.header { } /* Document header. */ diff --git a/commands/mdocml/external.png b/commands/mdocml/external.png deleted file mode 100644 index 419c06fb9..000000000 Binary files a/commands/mdocml/external.png and /dev/null differ diff --git a/commands/mdocml/index.css b/commands/mdocml/index.css deleted file mode 100644 index 51025707a..000000000 --- a/commands/mdocml/index.css +++ /dev/null @@ -1,48 +0,0 @@ -body { color: #333333; - font-size: smaller; - font-family: Verdana, Tahoma, Arial, sans-serif; } - -table.frame { max-width: 800px; - padding-left: 10px; } - -table { padding-left: 40px; } - -p { padding-left: 40px; - text-align: justify; } - -h1 { font-weight: bold; - font-size: small; - font-family: Verdana, Tahoma, Arial, sans-serif; } - -h2 { font-weight: bold; - font-size: small; - padding-left: 20px; - margin-bottom: 0px; - font-family: Verdana, Tahoma, Arial, sans-serif; } - -span.nm { color: #000000; font-weight: bold; } - -span.attn { color: #000000; font-weight: bold; } - -span.flag { font-weight: bold; } - -div.head { border-bottom: 1px solid #dddddd; - padding-bottom: 5px; - text-align: right; } - -div.subhead { font-size: smaller; - margin-bottom: 1em; } - -div.foot { border-top: 1px solid #dddddd; - padding-top: 5px; - font-size: smaller; - text-align: right; } - -a.external { background: transparent url(external.png) center right no-repeat; - padding-right: 12px; } - -span.date { color: #000000; } - -div.news { margin-bottom: 2em; } - -div.news ul { margin-left: 4em; } diff --git a/commands/mdocml/index.sgml b/commands/mdocml/index.sgml deleted file mode 100644 index 43caf293a..000000000 --- a/commands/mdocml/index.sgml +++ /dev/null @@ -1,420 +0,0 @@ - - - - - - - mdocml | mdoc macro compiler - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- mdocml – mdoc macro compiler -
- -
-

- DESCRIPTION -

- -

- mdocml is a suite of tools compiling -mdoc, the roff macro package - of choice for BSD manual pages, and -man, the predominant historical package for UNIX - manuals. The mission of mdocml is to deprecate groff, the GNU roff implementation, for - displaying -mdoc pages whilst providing token support for -man. -

- -

- Why? groff amounts to over 5 MB of source code, most of which is C++ and all of which is GPL. It runs - slowly, produces uncertain output, and varies in operation from system to system. mdocml strives to fix - this (respectively small, C, ISC-licensed, fast and regular). -

- -

- The core of mdocml is composed of the libmdoc, libman, and libroff validating compiler libraries. All - are simple, fast libraries operating on memory buffers, so they may be used for a variety of front-ends - (terminal-based, CGI and so on). The front-end is mandoc, which formats - manuals for display. -

- -

- The mdocml suite is a BSD.lv - Project member. -

-
-

- SOURCES -

- -

- Sources correctly build and install on DragonFly BSD, FreeBSD, OpenBSD, NetBSD, GNU/Linux, and many - other operating systems, tested variously on i386, AMD64, alpha, and others. The most current version - is @VERSION@, dated @VDATE@. A full ChangeLog (txt) is written with each release. -

- -

- Current -

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

- Downstream -

- - - - - - - - - - - - - - - - - - - - - - -
DragonFly BSD - usr.bin/mandoc -
FreeBSD - ports/textproc/mdocml -
NetBSD - src/external/bsd/mdocml -
OpenBSD - src/usr.bin/mandoc -
- -

- Historical -

- - - - - - - - - - -
Source archive - /snapshots/ -
-
-

- DOCUMENTATION -

- -

- These manuals are generated automatically and refer to the current snapshot. -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
man(3) - man macro compiler library - -
man(7) - man language reference - -
mandoc(1) - format and display UNIX manuals - -
mandoc_char(7) - mandoc special characters - -
mdoc(3) - mdoc macro compiler library - -
mdoc(7) - mdoc language reference - -
roff(3) - roff macro compiler library - -
roff(7) - roff-mandoc language reference - -
- -

- See Writing UNIX Manual Pages for a general - introduction to manpages and mdoc. -

-
-

- CONTACT -

- -

- For all issues related to mdocml, contact Kristaps Dzonsons, kris...@bsd.lv. -

- -

- You may also subscribe to several mailing lists (these require subscription, which is moderated). An - archive is not yet available on-line, although you may request one once subscribed. -

- - - - - - - - - - - - - - - - - - -
- disc...@mdocml.bsd.lv - high-level discussions and version announcements
- tec...@mdocml.bsd.lv - low-level discussions
- sou...@mdocml.bsd.lv - source commit messages
-
-

- NEWS -

-
-

- 19-06-2010: - version 1.10.2 -

-

- Small release featuring text-decoration in -Tps output, a few - minor relaxations of errors, and some optimisations. -

-
-
-

- 07-06-2010: - version 1.10.1 -

-

- This primarily focusses on the Bl and It macros described in mdoc. Multi-line column support is now fully compatible with - groff, as are implicit list entries for columns. Removed manuals.7 in favour of http://manpages.bsd.lv. The way we - handle the SYNOPSIS section (see the SYNOPSIS documentation in MANUAL STRUCTURE) has also - been considerably simplified compared to groff's method. Furthermore, the -Owidth=width output option has been added to -Tascii (see mandoc). Lastly, initial - PostScript output has been added with the -Tps option to mandoc. It's brutally simple at the moment: fixed-font, with - no font decorations. -

-
-
-

- 29-05-2010: - version 1.10.0 -

-

- Release consisting of the results from the m2k10 hackathon and up-merge from OpenBSD. - This requires a significant note of thanks to Ingo Schwarze (OpenBSD) and Joerg - Sonnenberger (NetBSD) for their hard work, and again to Joerg for hosting m2k10. - Highlights (mostly cribbed from Ingo's m2k10 report) follow in no particular order: -

-
    -
  • a libroff preprocessor in front of libmdoc and libman stripping out - roff instructions;
  • -
  • end-of-sentence (EOS) detection in free-form and macro lines;
  • -
  • correct handling of tab-separated columnar lists in -mdoc;
  • -
  • improved main calling routines to optionally use mmap() for better - performance;
  • -
  • cleaned up exiting when invoked as -Tlint or over - multiple files with -fign-errors;
  • -
  • error and warning message handling re-written to be unified for libroff, libmdoc, and libman;
  • -
  • handling of badly-nested explicit-scoped macros;
  • -
  • improved free-form text parsing in libman and libmdoc;
  • -
  • significant GNU troff compatibility improvements in -Tascii, largely in terms of spacing;
  • -
  • a regression framework for making sure the many fragilities of GNU troff - aren't trampled in subsequent work;
  • -
  • support for -Tascii breaking at hyphens - encountered in free-form text;
  • -
  • and many more minor fixes and improvements (no really, consult cvsweb and see - for yourself!).
  • -
-
-
-

- 13-05-2010: - version 1.9.25 -

-

- Fixed handling of \*(Ba escape. Backed out -fno-ign-chars (pointless complexity). Fixed erroneous - breaking of literal lines. Fixed SYNOPSIS breaking lines before non-initial - macros. Changed default section ordering. Most importantly, the framework for - end-of-sentence double-spacing is in place, now implemented for the - end-of-sentence, end-of-line rule. This is a stable roll-back point - before the mandoc hackathon in Rostock! -

-
-
-

- 09-05-2010: - version 1.9.24 -

-

- Rolled back break-at-hyphen. -DUGLY is now the - default (no feature splits!). Free-form text is not de-chunked any more: lines - are passed whole-sale into the front-end, including whitespace. Added mailing - lists. Lastly, mdocml is the focus of two Google Summer of Code - projects this year: mandoc -Tps - (NetBSD) and BSD-licensed - Text-Processing Tools (FreeBSD). -

-
-

- See cvsweb for - historical notes. -

-
-
- Copyright © 2008–2010 Kristaps Dzonsons, $Date: 2010/06/19 20:43:35 $ -
-
- - diff --git a/commands/mdocml/main.c b/commands/mdocml/main.c deleted file mode 100644 index ae5c8b902..000000000 --- a/commands/mdocml/main.c +++ /dev/null @@ -1,870 +0,0 @@ -/* $Id: main.c,v 1.89 2010/06/19 20:46:27 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "mandoc.h" -#include "mdoc.h" -#include "man.h" -#include "roff.h" -#include "main.h" - -#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) - -/* FIXME: Intel's compiler? LLVM? pcc? */ - -#if !defined(__GNUC__) || (__GNUC__ < 2) -# if !defined(lint) -# define __attribute__(x) -# endif -#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ - -typedef void (*out_mdoc)(void *, const struct mdoc *); -typedef void (*out_man)(void *, const struct man *); -typedef void (*out_free)(void *); - -struct buf { - char *buf; - size_t sz; -}; - -enum intt { - INTT_AUTO, - INTT_MDOC, - INTT_MAN -}; - -enum outt { - OUTT_ASCII = 0, - OUTT_TREE, - OUTT_HTML, - OUTT_XHTML, - OUTT_LINT, - OUTT_PS -}; - -struct curparse { - const char *file; /* Current parse. */ - int fd; /* Current parse. */ - int wflags; - /* FIXME: set by max error */ -#define WARN_WALL (1 << 0) /* All-warnings mask. */ -#define WARN_WERR (1 << 2) /* Warnings->errors. */ - int fflags; -#define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */ -#define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */ -#define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */ -#define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */ -#define FL_STRICT FL_NIGN_ESCAPE | \ - FL_NIGN_MACRO /* ignore nothing */ - enum intt inttype; /* which parser to use */ - struct man *man; /* man parser */ - struct mdoc *mdoc; /* mdoc parser */ - struct roff *roff; /* roff parser (!NULL) */ - enum outt outtype; /* which output to use */ - out_mdoc outmdoc; /* mdoc output ptr */ - out_man outman; /* man output ptr */ - out_free outfree; /* free output ptr */ - void *outdata; /* data for output */ - char outopts[BUFSIZ]; /* buf of output opts */ -}; - -static const char * const mandocerrs[MANDOCERR_MAX] = { - "ok", - "text should be uppercase", - "sections out of conventional order", - "section name repeats", - "out of order prologue", - "repeated prologue entry", - "list type must come first", - "bad standard", - "bad library", - "bad escape sequence", - "unterminated quoted string", - "argument requires the width argument", - "superfluous width argument", - "ignoring argument", - "bad date argument", - "bad width argument", - "unknown manual section", - "section not in conventional manual section", - "end of line whitespace", - "scope open on exit", - "NAME section must come first", - "bad Boolean value", - "child violates parent syntax", - "bad AT&T symbol", - "list type repeated", - "display type repeated", - "argument repeated", - "manual name not yet set", - "obsolete macro ignored", - "empty macro ignored", - "macro not allowed in body", - "macro not allowed in prologue", - "bad character", - "bad NAME section contents", - "no blank lines", - "no text in this context", - "bad comment style", - "unknown macro will be lost", - "line scope broken", - "scope broken", - "argument count wrong", - "request scope close w/none open", - "scope already open", - "macro requires line argument(s)", - "macro requires body argument(s)", - "macro requires argument(s)", - "no title in document", - "missing list type", - "missing display type", - "line argument(s) will be lost", - "body argument(s) will be lost", - "column syntax is inconsistent", - "missing font type", - "displays may not be nested", - "unsupported display type", - "no scope to rewind: syntax violated", - "scope broken, syntax violated", - "line scope broken, syntax violated", - "argument count wrong, violates syntax", - "child violates parent syntax", - "argument count wrong, violates syntax", - "no document body", - "no document prologue", - "utsname system call failed", - "memory exhausted", -}; - -static void fdesc(struct curparse *); -static void ffile(const char *, struct curparse *); -static int foptions(int *, char *); -static struct man *man_init(struct curparse *); -static struct mdoc *mdoc_init(struct curparse *); -static struct roff *roff_init(struct curparse *); -static int moptions(enum intt *, char *); -static int mmsg(enum mandocerr, void *, - int, int, const char *); -static int pset(const char *, int, struct curparse *, - struct man **, struct mdoc **); -static int toptions(struct curparse *, char *); -static void usage(void) __attribute__((noreturn)); -static void version(void) __attribute__((noreturn)); -static int woptions(int *, char *); - -static const char *progname; -static int with_error; -static int with_warning; - -int -main(int argc, char *argv[]) -{ - int c; - struct curparse curp; - - progname = strrchr(argv[0], '/'); - if (progname == NULL) - progname = argv[0]; - else - ++progname; - - memset(&curp, 0, sizeof(struct curparse)); - - curp.inttype = INTT_AUTO; - curp.outtype = OUTT_ASCII; - - /* LINTED */ - while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:"))) - switch (c) { - case ('f'): - if ( ! foptions(&curp.fflags, optarg)) - return(EXIT_FAILURE); - break; - case ('m'): - if ( ! moptions(&curp.inttype, optarg)) - return(EXIT_FAILURE); - break; - case ('O'): - (void)strlcat(curp.outopts, optarg, BUFSIZ); - (void)strlcat(curp.outopts, ",", BUFSIZ); - break; - case ('T'): - if ( ! toptions(&curp, optarg)) - return(EXIT_FAILURE); - break; - case ('W'): - if ( ! woptions(&curp.wflags, optarg)) - return(EXIT_FAILURE); - break; - case ('V'): - version(); - /* NOTREACHED */ - default: - usage(); - /* NOTREACHED */ - } - - argc -= optind; - argv += optind; - - if (NULL == *argv) { - curp.file = ""; - curp.fd = STDIN_FILENO; - - fdesc(&curp); - } - - while (*argv) { - ffile(*argv, &curp); - - if (with_error && !(curp.fflags & FL_IGN_ERRORS)) - break; - ++argv; - } - - if (curp.outfree) - (*curp.outfree)(curp.outdata); - if (curp.mdoc) - mdoc_free(curp.mdoc); - if (curp.man) - man_free(curp.man); - if (curp.roff) - roff_free(curp.roff); - - return((with_warning || with_error) ? - EXIT_FAILURE : EXIT_SUCCESS); -} - - -static void -version(void) -{ - - (void)printf("%s %s\n", progname, VERSION); - exit(EXIT_SUCCESS); -} - - -static void -usage(void) -{ - - (void)fprintf(stderr, "usage: %s [-V] [-foption] " - "[-mformat] [-Ooption] [-Toutput] " - "[-Werr] [file...]\n", progname); - exit(EXIT_FAILURE); -} - - -static struct man * -man_init(struct curparse *curp) -{ - int pflags; - - /* Defaults from mandoc.1. */ - - pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE; - - if (curp->fflags & FL_NIGN_MACRO) - pflags &= ~MAN_IGN_MACRO; - if (curp->fflags & FL_NIGN_ESCAPE) - pflags &= ~MAN_IGN_ESCAPE; - - return(man_alloc(curp, pflags, mmsg)); -} - - -static struct roff * -roff_init(struct curparse *curp) -{ - - return(roff_alloc(mmsg, curp)); -} - - -static struct mdoc * -mdoc_init(struct curparse *curp) -{ - int pflags; - - /* Defaults from mandoc.1. */ - - pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE; - - if (curp->fflags & FL_IGN_SCOPE) - pflags |= MDOC_IGN_SCOPE; - if (curp->fflags & FL_NIGN_ESCAPE) - pflags &= ~MDOC_IGN_ESCAPE; - if (curp->fflags & FL_NIGN_MACRO) - pflags &= ~MDOC_IGN_MACRO; - - return(mdoc_alloc(curp, pflags, mmsg)); -} - - -static void -ffile(const char *file, struct curparse *curp) -{ - - curp->file = file; - if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) { - perror(curp->file); - with_error = 1; - return; - } - - fdesc(curp); - - if (-1 == close(curp->fd)) - perror(curp->file); -} - - -static int -resize_buf(struct buf *buf, size_t initial) -{ - void *tmp; - size_t sz; - - if (buf->sz == 0) - sz = initial; - else - sz = 2 * buf->sz; - tmp = realloc(buf->buf, sz); - if (NULL == tmp) { - perror(NULL); - return(0); - } - buf->buf = tmp; - buf->sz = sz; - return(1); -} - - -static int -read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap) -{ - struct stat st; - size_t off; - ssize_t ssz; - - if (-1 == fstat(curp->fd, &st)) { - perror(curp->file); - with_error = 1; - return(0); - } - -#ifndef __minix - /* - * If we're a regular file, try just reading in the whole entry - * via mmap(). This is faster than reading it into blocks, and - * since each file is only a few bytes to begin with, I'm not - * concerned that this is going to tank any machines. - */ - - if (S_ISREG(st.st_mode)) { - if (st.st_size >= (1U << 31)) { - fprintf(stderr, "%s: input too large\n", - curp->file); - with_error = 1; - return(0); - } - *with_mmap = 1; - fb->sz = (size_t)st.st_size; - fb->buf = mmap(NULL, fb->sz, PROT_READ, - MAP_FILE|MAP_SHARED, curp->fd, 0); - if (fb->buf != MAP_FAILED) - return(1); - } -#endif - - /* - * If this isn't a regular file (like, say, stdin), then we must - * go the old way and just read things in bit by bit. - */ - - *with_mmap = 0; - off = 0; - fb->sz = 0; - fb->buf = NULL; - for (;;) { - if (off == fb->sz) { - if (fb->sz == (1U << 31)) { - fprintf(stderr, "%s: input too large\n", - curp->file); - break; - } - if (! resize_buf(fb, 65536)) - break; - } - ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off); - if (ssz == 0) { - fb->sz = off; - return(1); - } - if (ssz == -1) { - perror(curp->file); - break; - } - off += (size_t)ssz; - } - - free(fb->buf); - fb->buf = NULL; - with_error = 1; - return(0); -} - - -static void -fdesc(struct curparse *curp) -{ - struct buf ln, blk; - int i, pos, lnn, lnn_start, with_mmap, of; - enum rofferr re; - struct man *man; - struct mdoc *mdoc; - struct roff *roff; - - man = NULL; - mdoc = NULL; - roff = NULL; - memset(&ln, 0, sizeof(struct buf)); - - /* - * Two buffers: ln and buf. buf is the input file and may be - * memory mapped. ln is a line buffer and grows on-demand. - */ - - if ( ! read_whole_file(curp, &blk, &with_mmap)) - return; - - if (NULL == curp->roff) - curp->roff = roff_init(curp); - if (NULL == (roff = curp->roff)) - goto bailout; - - for (i = 0, lnn = 1; i < (int)blk.sz;) { - pos = 0; - lnn_start = lnn; - while (i < (int)blk.sz) { - if ('\n' == blk.buf[i]) { - ++i; - ++lnn; - break; - } - /* Trailing backslash is like a plain character. */ - if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) { - if (pos >= (int)ln.sz) - if (! resize_buf(&ln, 256)) - goto bailout; - ln.buf[pos++] = blk.buf[i++]; - continue; - } - /* Found an escape and at least one other character. */ - if ('\n' == blk.buf[i + 1]) { - /* Escaped newlines are skipped over */ - i += 2; - ++lnn; - continue; - } - if ('"' == blk.buf[i + 1]) { - i += 2; - /* Comment, skip to end of line */ - for (; i < (int)blk.sz; ++i) { - if ('\n' == blk.buf[i]) { - ++i; - ++lnn; - break; - } - } - /* Backout trailing whitespaces */ - for (; pos > 0; --pos) { - if (ln.buf[pos - 1] != ' ') - break; - if (pos > 2 && ln.buf[pos - 2] == '\\') - break; - } - break; - } - /* Some other escape sequence, copy and continue. */ - if (pos + 1 >= (int)ln.sz) - if (! resize_buf(&ln, 256)) - goto bailout; - - ln.buf[pos++] = blk.buf[i++]; - ln.buf[pos++] = blk.buf[i++]; - } - - if (pos >= (int)ln.sz) - if (! resize_buf(&ln, 256)) - goto bailout; - ln.buf[pos] = '\0'; - - /* - * A significant amount of complexity is contained by - * the roff preprocessor. It's line-oriented but can be - * expressed on one line, so we need at times to - * readjust our starting point and re-run it. The roff - * preprocessor can also readjust the buffers with new - * data, so we pass them in wholesale. - */ - - of = 0; - do { - re = roff_parseln(roff, lnn_start, - &ln.buf, &ln.sz, of, &of); - } while (ROFF_RERUN == re); - - if (ROFF_IGN == re) - continue; - else if (ROFF_ERR == re) - goto bailout; - - /* - * If input parsers have not been allocated, do so now. - * We keep these instanced betwen parsers, but set them - * locally per parse routine since we can use different - * parsers with each one. - */ - - if ( ! (man || mdoc)) - if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc)) - goto bailout; - - /* Lastly, push down into the parsers themselves. */ - - if (man && ! man_parseln(man, lnn_start, ln.buf, of)) - goto bailout; - if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of)) - goto bailout; - } - - /* NOTE a parser may not have been assigned, yet. */ - - if ( ! (man || mdoc)) { - fprintf(stderr, "%s: Not a manual\n", curp->file); - goto bailout; - } - - /* Clean up the parse routine ASTs. */ - - if (mdoc && ! mdoc_endparse(mdoc)) - goto bailout; - if (man && ! man_endparse(man)) - goto bailout; - if (roff && ! roff_endparse(roff)) - goto bailout; - - /* If unset, allocate output dev now (if applicable). */ - - if ( ! (curp->outman && curp->outmdoc)) { - switch (curp->outtype) { - case (OUTT_XHTML): - curp->outdata = xhtml_alloc(curp->outopts); - break; - case (OUTT_HTML): - curp->outdata = html_alloc(curp->outopts); - break; - case (OUTT_ASCII): - curp->outdata = ascii_alloc(curp->outopts); - curp->outfree = ascii_free; - break; - case (OUTT_PS): - curp->outdata = ps_alloc(); - curp->outfree = ps_free; - break; - default: - break; - } - - switch (curp->outtype) { - case (OUTT_HTML): - /* FALLTHROUGH */ - case (OUTT_XHTML): - curp->outman = html_man; - curp->outmdoc = html_mdoc; - curp->outfree = html_free; - break; - case (OUTT_TREE): - curp->outman = tree_man; - curp->outmdoc = tree_mdoc; - break; - case (OUTT_ASCII): - /* FALLTHROUGH */ - case (OUTT_PS): - curp->outman = terminal_man; - curp->outmdoc = terminal_mdoc; - break; - default: - break; - } - } - - /* Execute the out device, if it exists. */ - - if (man && curp->outman) - (*curp->outman)(curp->outdata, man); - if (mdoc && curp->outmdoc) - (*curp->outmdoc)(curp->outdata, mdoc); - - cleanup: - if (mdoc) - mdoc_reset(mdoc); - if (man) - man_reset(man); - if (roff) - roff_reset(roff); - if (ln.buf) - free(ln.buf); -#ifdef __minix - assert(!with_mmap); -#else - if (with_mmap) - munmap(blk.buf, blk.sz); - else -#endif - free(blk.buf); - - return; - - bailout: - with_error = 1; - goto cleanup; -} - - -static int -pset(const char *buf, int pos, struct curparse *curp, - struct man **man, struct mdoc **mdoc) -{ - int i; - - /* - * Try to intuit which kind of manual parser should be used. If - * passed in by command-line (-man, -mdoc), then use that - * explicitly. If passed as -mandoc, then try to guess from the - * line: either skip dot-lines, use -mdoc when finding `.Dt', or - * default to -man, which is more lenient. - */ - - if ('.' == buf[0] || '\'' == buf[0]) { - for (i = 1; buf[i]; i++) - if (' ' != buf[i] && '\t' != buf[i]) - break; - if (0 == buf[i]) - return(1); - } - - switch (curp->inttype) { - case (INTT_MDOC): - if (NULL == curp->mdoc) - curp->mdoc = mdoc_init(curp); - if (NULL == (*mdoc = curp->mdoc)) - return(0); - return(1); - case (INTT_MAN): - if (NULL == curp->man) - curp->man = man_init(curp); - if (NULL == (*man = curp->man)) - return(0); - return(1); - default: - break; - } - - if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { - if (NULL == curp->mdoc) - curp->mdoc = mdoc_init(curp); - if (NULL == (*mdoc = curp->mdoc)) - return(0); - return(1); - } - - if (NULL == curp->man) - curp->man = man_init(curp); - if (NULL == (*man = curp->man)) - return(0); - return(1); -} - - -static int -moptions(enum intt *tflags, char *arg) -{ - - if (0 == strcmp(arg, "doc")) - *tflags = INTT_MDOC; - else if (0 == strcmp(arg, "andoc")) - *tflags = INTT_AUTO; - else if (0 == strcmp(arg, "an")) - *tflags = INTT_MAN; - else { - fprintf(stderr, "%s: Bad argument\n", arg); - return(0); - } - - return(1); -} - - -static int -toptions(struct curparse *curp, char *arg) -{ - - if (0 == strcmp(arg, "ascii")) - curp->outtype = OUTT_ASCII; - else if (0 == strcmp(arg, "lint")) { - curp->outtype = OUTT_LINT; - curp->wflags |= WARN_WALL; - curp->fflags |= FL_STRICT; - } - else if (0 == strcmp(arg, "tree")) - curp->outtype = OUTT_TREE; - else if (0 == strcmp(arg, "html")) - curp->outtype = OUTT_HTML; - else if (0 == strcmp(arg, "xhtml")) - curp->outtype = OUTT_XHTML; - else if (0 == strcmp(arg, "ps")) - curp->outtype = OUTT_PS; - else { - fprintf(stderr, "%s: Bad argument\n", arg); - return(0); - } - - return(1); -} - - -static int -foptions(int *fflags, char *arg) -{ - char *v, *o; - const char *toks[8]; - - toks[0] = "ign-scope"; - toks[1] = "no-ign-escape"; - toks[2] = "no-ign-macro"; - toks[3] = "ign-errors"; - toks[4] = "strict"; - toks[5] = "ign-escape"; - toks[6] = NULL; - - while (*arg) { - o = arg; - switch (getsubopt(&arg, UNCONST(toks), &v)) { - case (0): - *fflags |= FL_IGN_SCOPE; - break; - case (1): - *fflags |= FL_NIGN_ESCAPE; - break; - case (2): - *fflags |= FL_NIGN_MACRO; - break; - case (3): - *fflags |= FL_IGN_ERRORS; - break; - case (4): - *fflags |= FL_STRICT; - break; - case (5): - *fflags &= ~FL_NIGN_ESCAPE; - break; - default: - fprintf(stderr, "%s: Bad argument\n", o); - return(0); - } - } - - return(1); -} - - -static int -woptions(int *wflags, char *arg) -{ - char *v, *o; - const char *toks[3]; - - toks[0] = "all"; - toks[1] = "error"; - toks[2] = NULL; - - while (*arg) { - o = arg; - switch (getsubopt(&arg, UNCONST(toks), &v)) { - case (0): - *wflags |= WARN_WALL; - break; - case (1): - *wflags |= WARN_WERR; - break; - default: - fprintf(stderr, "%s: Bad argument\n", o); - return(0); - } - } - - return(1); -} - - -static int -mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg) -{ - struct curparse *cp; - - cp = (struct curparse *)arg; - - if (t <= MANDOCERR_ERROR) { - if ( ! (cp->wflags & WARN_WALL)) - return(1); - with_warning = 1; - } else - with_error = 1; - - fprintf(stderr, "%s:%d:%d: %s", cp->file, - ln, col + 1, mandocerrs[t]); - - if (msg) - fprintf(stderr, ": %s", msg); - - fputc('\n', stderr); - - /* This is superfluous, but whatever. */ - if (t > MANDOCERR_ERROR) - return(0); - if (cp->wflags & WARN_WERR) { - with_error = 1; - return(0); - } - return(1); -} diff --git a/commands/mdocml/man_action.c b/commands/mdocml/man_action.c deleted file mode 100644 index e63d342b4..000000000 --- a/commands/mdocml/man_action.c +++ /dev/null @@ -1,280 +0,0 @@ -/* $Id: man_action.c,v 1.39 2010/05/26 14:03:54 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "mandoc.h" -#include "libman.h" -#include "libmandoc.h" - -struct actions { - int (*post)(struct man *); -}; - -static int post_TH(struct man *); -static int post_fi(struct man *); -static int post_nf(struct man *); -static int post_AT(struct man *); -static int post_UC(struct man *); - -const struct actions man_actions[MAN_MAX] = { - { NULL }, /* br */ - { post_TH }, /* TH */ - { NULL }, /* SH */ - { NULL }, /* SS */ - { NULL }, /* TP */ - { NULL }, /* LP */ - { NULL }, /* PP */ - { NULL }, /* P */ - { NULL }, /* IP */ - { NULL }, /* HP */ - { NULL }, /* SM */ - { NULL }, /* SB */ - { NULL }, /* BI */ - { NULL }, /* IB */ - { NULL }, /* BR */ - { NULL }, /* RB */ - { NULL }, /* R */ - { NULL }, /* B */ - { NULL }, /* I */ - { NULL }, /* IR */ - { NULL }, /* RI */ - { NULL }, /* na */ - { NULL }, /* i */ - { NULL }, /* sp */ - { post_nf }, /* nf */ - { post_fi }, /* fi */ - { NULL }, /* r */ - { NULL }, /* RE */ - { NULL }, /* RS */ - { NULL }, /* DT */ - { post_UC }, /* UC */ - { NULL }, /* PD */ - { NULL }, /* Sp */ - { post_nf }, /* Vb */ - { post_fi }, /* Ve */ - { post_AT }, /* AT */ -}; - - -int -man_action_post(struct man *m) -{ - - if (MAN_ACTED & m->last->flags) - return(1); - m->last->flags |= MAN_ACTED; - - switch (m->last->type) { - case (MAN_TEXT): - /* FALLTHROUGH */ - case (MAN_ROOT): - return(1); - default: - break; - } - - if (NULL == man_actions[m->last->tok].post) - return(1); - return((*man_actions[m->last->tok].post)(m)); -} - - -static int -post_fi(struct man *m) -{ - - if ( ! (MAN_LITERAL & m->flags)) - if ( ! man_nmsg(m, m->last, MANDOCERR_NOSCOPE)) - return(0); - m->flags &= ~MAN_LITERAL; - return(1); -} - - -static int -post_nf(struct man *m) -{ - - if (MAN_LITERAL & m->flags) - if ( ! man_nmsg(m, m->last, MANDOCERR_SCOPEREP)) - return(0); - m->flags |= MAN_LITERAL; - return(1); -} - - -static int -post_TH(struct man *m) -{ - struct man_node *n; - - if (m->meta.title) - free(m->meta.title); - if (m->meta.vol) - free(m->meta.vol); - if (m->meta.source) - free(m->meta.source); - if (m->meta.msec) - free(m->meta.msec); - if (m->meta.rawdate) - free(m->meta.rawdate); - - m->meta.title = m->meta.vol = m->meta.rawdate = - m->meta.msec = m->meta.source = NULL; - m->meta.date = 0; - - /* ->TITLE<- MSEC DATE SOURCE VOL */ - - n = m->last->child; - assert(n); - m->meta.title = mandoc_strdup(n->string); - - /* TITLE ->MSEC<- DATE SOURCE VOL */ - - n = n->next; - assert(n); - m->meta.msec = mandoc_strdup(n->string); - - /* TITLE MSEC ->DATE<- SOURCE VOL */ - - /* - * Try to parse the date. If this works, stash the epoch (this - * is optimal because we can reformat it in the canonical form). - * If it doesn't parse, isn't specified at all, or is an empty - * string, then use the current date. - */ - - n = n->next; - if (n && n->string && *n->string) { - m->meta.date = mandoc_a2time - (MTIME_ISO_8601, n->string); - if (0 == m->meta.date) { - if ( ! man_nmsg(m, n, MANDOCERR_BADDATE)) - return(0); - m->meta.rawdate = mandoc_strdup(n->string); - } - } else - m->meta.date = time(NULL); - - /* TITLE MSEC DATE ->SOURCE<- VOL */ - - if (n && (n = n->next)) - m->meta.source = mandoc_strdup(n->string); - - /* TITLE MSEC DATE SOURCE ->VOL<- */ - - if (n && (n = n->next)) - m->meta.vol = mandoc_strdup(n->string); - - /* - * Remove the `TH' node after we've processed it for our - * meta-data. - */ - man_node_delete(m, m->last); - return(1); -} - - -static int -post_AT(struct man *m) -{ - static const char * const unix_versions[] = { - "7th Edition", - "System III", - "System V", - "System V Release 2", - }; - - const char *p, *s; - struct man_node *n, *nn; - - n = m->last->child; - - if (NULL == n || MAN_TEXT != n->type) - p = unix_versions[0]; - else { - s = n->string; - if (0 == strcmp(s, "3")) - p = unix_versions[0]; - else if (0 == strcmp(s, "4")) - p = unix_versions[1]; - else if (0 == strcmp(s, "5")) { - nn = n->next; - if (nn && MAN_TEXT == nn->type && nn->string[0]) - p = unix_versions[3]; - else - p = unix_versions[2]; - } else - p = unix_versions[0]; - } - - if (m->meta.source) - free(m->meta.source); - - m->meta.source = mandoc_strdup(p); - - return(1); -} - - -static int -post_UC(struct man *m) -{ - static const char * const bsd_versions[] = { - "3rd Berkeley Distribution", - "4th Berkeley Distribution", - "4.2 Berkeley Distribution", - "4.3 Berkeley Distribution", - "4.4 Berkeley Distribution", - }; - - const char *p, *s; - struct man_node *n; - - n = m->last->child; - - if (NULL == n || MAN_TEXT != n->type) - p = bsd_versions[0]; - else { - s = n->string; - if (0 == strcmp(s, "3")) - p = bsd_versions[0]; - else if (0 == strcmp(s, "4")) - p = bsd_versions[1]; - else if (0 == strcmp(s, "5")) - p = bsd_versions[2]; - else if (0 == strcmp(s, "6")) - p = bsd_versions[3]; - else if (0 == strcmp(s, "7")) - p = bsd_versions[4]; - else - p = bsd_versions[0]; - } - - if (m->meta.source) - free(m->meta.source); - - m->meta.source = mandoc_strdup(p); - - return(1); -} diff --git a/commands/mdocml/man_argv.c b/commands/mdocml/man_argv.c deleted file mode 100644 index 44b9a25c3..000000000 --- a/commands/mdocml/man_argv.c +++ /dev/null @@ -1,104 +0,0 @@ -/* $Id: man_argv.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "libman.h" - - -int -man_args(struct man *m, int line, int *pos, char *buf, char **v) -{ - - assert(*pos); - assert(' ' != buf[*pos]); - - if (0 == buf[*pos]) - return(ARGS_EOLN); - - *v = &buf[*pos]; - - /* - * Process a quoted literal. A quote begins with a double-quote - * and ends with a double-quote NOT preceded by a double-quote. - * Whitespace is NOT involved in literal termination. - */ - - if ('\"' == buf[*pos]) { - *v = &buf[++(*pos)]; - - for ( ; buf[*pos]; (*pos)++) { - if ('\"' != buf[*pos]) - continue; - if ('\"' != buf[*pos + 1]) - break; - (*pos)++; - } - - if (0 == buf[*pos]) { - if ( ! man_pmsg(m, line, *pos, MANDOCERR_BADQUOTE)) - return(ARGS_ERROR); - return(ARGS_QWORD); - } - - buf[(*pos)++] = 0; - - if (0 == buf[*pos]) - return(ARGS_QWORD); - - while (' ' == buf[*pos]) - (*pos)++; - - if (0 == buf[*pos]) - if ( ! man_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE)) - return(ARGS_ERROR); - - return(ARGS_QWORD); - } - - /* - * A non-quoted term progresses until either the end of line or - * a non-escaped whitespace. - */ - - for ( ; buf[*pos]; (*pos)++) - if (' ' == buf[*pos] && '\\' != buf[*pos - 1]) - break; - - if (0 == buf[*pos]) - return(ARGS_WORD); - - buf[(*pos)++] = 0; - - while (' ' == buf[*pos]) - (*pos)++; - - if (0 == buf[*pos]) - if ( ! man_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE)) - return(ARGS_ERROR); - - return(ARGS_WORD); -} - diff --git a/commands/mdocml/man_validate.c b/commands/mdocml/man_validate.c deleted file mode 100644 index fb7056fe8..000000000 --- a/commands/mdocml/man_validate.c +++ /dev/null @@ -1,328 +0,0 @@ -/* $Id: man_validate.c,v 1.44 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "mandoc.h" -#include "libman.h" -#include "libmandoc.h" - -#define CHKARGS struct man *m, struct man_node *n - -typedef int (*v_check)(CHKARGS); - -struct man_valid { - v_check *pres; - v_check *posts; -}; - -static int check_bline(CHKARGS); -static int check_eq0(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 int check_text(CHKARGS); -static int check_title(CHKARGS); - -static v_check posts_eq0[] = { check_eq0, NULL }; -static v_check posts_th[] = { check_ge2, check_le5, check_title, 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_le1[] = { check_le1, NULL }; -static v_check pres_bline[] = { check_bline, 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, posts_par }, /* TP */ - { pres_bline, posts_par }, /* LP */ - { pres_bline, posts_par }, /* PP */ - { pres_bline, posts_par }, /* P */ - { pres_bline, posts_par }, /* IP */ - { pres_bline, posts_par }, /* HP */ - { NULL, NULL }, /* SM */ - { NULL, NULL }, /* SB */ - { NULL, NULL }, /* BI */ - { NULL, NULL }, /* IB */ - { NULL, NULL }, /* BR */ - { NULL, NULL }, /* RB */ - { NULL, NULL }, /* R */ - { NULL, NULL }, /* B */ - { NULL, NULL }, /* I */ - { NULL, NULL }, /* IR */ - { NULL, NULL }, /* RI */ - { NULL, posts_eq0 }, /* na */ - { NULL, NULL }, /* i */ - { NULL, posts_le1 }, /* sp */ - { pres_bline, posts_eq0 }, /* nf */ - { pres_bline, posts_eq0 }, /* fi */ - { NULL, NULL }, /* r */ - { NULL, NULL }, /* RE */ - { NULL, posts_part }, /* RS */ - { NULL, NULL }, /* DT */ - { NULL, NULL }, /* UC */ - { NULL, NULL }, /* PD */ - { NULL, posts_le1 }, /* Sp */ - { pres_bline, posts_le1 }, /* Vb */ - { pres_bline, posts_eq0 }, /* Ve */ - { NULL, NULL }, /* AT */ -}; - - -int -man_valid_pre(struct man *m, struct man_node *n) -{ - v_check *cp; - - if (MAN_TEXT == n->type) - return(1); - if (MAN_ROOT == n->type) - return(1); - - if (NULL == (cp = man_valids[n->tok].pres)) - return(1); - for ( ; *cp; cp++) - if ( ! (*cp)(m, n)) - return(0); - return(1); -} - - -int -man_valid_post(struct man *m) -{ - v_check *cp; - - if (MAN_VALID & m->last->flags) - return(1); - m->last->flags |= MAN_VALID; - - switch (m->last->type) { - case (MAN_TEXT): - return(check_text(m, m->last)); - case (MAN_ROOT): - return(check_root(m, m->last)); - default: - break; - } - - if (NULL == (cp = man_valids[m->last->tok].posts)) - return(1); - for ( ; *cp; cp++) - if ( ! (*cp)(m, m->last)) - return(0); - - return(1); -} - - -static int -check_root(CHKARGS) -{ - - if (MAN_BLINE & m->flags) - return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT)); - if (MAN_ELINE & m->flags) - return(man_nmsg(m, n, MANDOCERR_SCOPEEXIT)); - - m->flags &= ~MAN_BLINE; - m->flags &= ~MAN_ELINE; - - if (NULL == m->first->child) { - man_nmsg(m, n, MANDOCERR_NODOCBODY); - return(0); - } else if (NULL == m->meta.title) { - if ( ! man_nmsg(m, n, MANDOCERR_NOTITLE)) - return(0); - /* - * If a title hasn't been set, do so now (by - * implication, date and section also aren't set). - * - * FIXME: this should be in man_action.c. - */ - m->meta.title = mandoc_strdup("unknown"); - m->meta.date = time(NULL); - m->meta.msec = mandoc_strdup("1"); - } - - return(1); -} - - -static int -check_title(CHKARGS) -{ - const char *p; - - assert(n->child); - /* FIXME: is this sufficient? */ - if ('\0' == *n->child->string) { - man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT); - return(0); - } - - for (p = n->child->string; '\0' != *p; p++) - if (isalpha((u_char)*p) && ! isupper((u_char)*p)) - if ( ! man_nmsg(m, n, MANDOCERR_UPPERCASE)) - return(0); - - return(1); -} - - -static int -check_text(CHKARGS) -{ - char *p; - int pos, c; - - assert(n->string); - - for (p = n->string, pos = n->pos + 1; *p; p++, pos++) { - if ('\\' == *p) { - c = mandoc_special(p); - if (c) { - p += c - 1; - pos += c - 1; - continue; - } - - c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE); - if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c) - return(c); - } - - if ('\t' == *p || isprint((u_char)*p) || ASCII_HYPH == *p) - continue; - if ( ! man_pmsg(m, n->line, pos, MANDOCERR_BADCHAR)) - return(0); - } - - return(1); -} - - -#define INEQ_DEFINE(x, ineq, name) \ -static int \ -check_##name(CHKARGS) \ -{ \ - if (n->nchild ineq (x)) \ - return(1); \ - man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \ - "line arguments %s %d (have %d)", \ - #ineq, (x), n->nchild); \ - return(0); \ -} - -INEQ_DEFINE(0, ==, eq0) -INEQ_DEFINE(1, <=, le1) -INEQ_DEFINE(2, >=, ge2) -INEQ_DEFINE(5, <=, le5) - - -static int -check_sec(CHKARGS) -{ - - if (MAN_HEAD == n->type && 0 == n->nchild) { - man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT); - return(0); - } else if (MAN_BODY == n->type && 0 == n->nchild) - return(man_nmsg(m, n, MANDOCERR_NOBODY)); - - return(1); -} - - -static int -check_part(CHKARGS) -{ - - if (MAN_BODY == n->type && 0 == n->nchild) - return(man_nmsg(m, n, MANDOCERR_NOBODY)); - return(1); -} - - -static int -check_par(CHKARGS) -{ - - if (MAN_BODY == n->type) - switch (n->tok) { - case (MAN_IP): - /* FALLTHROUGH */ - case (MAN_HP): - /* FALLTHROUGH */ - case (MAN_TP): - /* Body-less lists are ok. */ - break; - default: - if (n->nchild) - break; - return(man_nmsg(m, n, MANDOCERR_NOBODY)); - } - if (MAN_HEAD == n->type) - switch (n->tok) { - case (MAN_PP): - /* FALLTHROUGH */ - case (MAN_P): - /* FALLTHROUGH */ - case (MAN_LP): - if (0 == n->nchild) - break; - return(man_nmsg(m, n, MANDOCERR_ARGSLOST)); - default: - if (n->nchild) - break; - return(man_nmsg(m, n, MANDOCERR_NOARGS)); - } - - return(1); -} - - -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); -} - diff --git a/commands/mdocml/mandoc.c b/commands/mdocml/mandoc.c deleted file mode 100644 index a923a3686..000000000 --- a/commands/mdocml/mandoc.c +++ /dev/null @@ -1,396 +0,0 @@ -/* $Id: mandoc.c,v 1.19 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "mandoc.h" -#include "libmandoc.h" - -static int a2time(time_t *, const char *, const char *); -static int spec_norm(char *, int); - - -/* - * "Normalise" a special string by converting its ASCII_HYPH entries - * into actual hyphens. - */ -static int -spec_norm(char *p, int sz) -{ - int i; - - for (i = 0; i < sz; i++) - if (ASCII_HYPH == p[i]) - p[i] = '-'; - - return(sz); -} - - -int -mandoc_special(char *p) -{ - int terminator; /* Terminator for \s. */ - int lim; /* Limit for N in \s. */ - int c, i; - char *sv; - - sv = p; - - if ('\\' != *p++) - return(spec_norm(sv, 0)); - - switch (*p) { - case ('\''): - /* FALLTHROUGH */ - case ('`'): - /* FALLTHROUGH */ - case ('q'): - /* FALLTHROUGH */ - case (ASCII_HYPH): - /* FALLTHROUGH */ - case ('-'): - /* FALLTHROUGH */ - case ('~'): - /* FALLTHROUGH */ - case ('^'): - /* FALLTHROUGH */ - case ('%'): - /* FALLTHROUGH */ - case ('0'): - /* FALLTHROUGH */ - case (' '): - /* FALLTHROUGH */ - case ('}'): - /* FALLTHROUGH */ - case ('|'): - /* FALLTHROUGH */ - case ('&'): - /* FALLTHROUGH */ - case ('.'): - /* FALLTHROUGH */ - case (':'): - /* FALLTHROUGH */ - case ('c'): - /* FALLTHROUGH */ - case ('e'): - return(spec_norm(sv, 2)); - case ('s'): - if ('\0' == *++p) - return(spec_norm(sv, 2)); - - c = 2; - terminator = 0; - lim = 1; - - if (*p == '\'') { - lim = 0; - terminator = 1; - ++p; - ++c; - } else if (*p == '[') { - lim = 0; - terminator = 2; - ++p; - ++c; - } else if (*p == '(') { - lim = 2; - terminator = 3; - ++p; - ++c; - } - - if (*p == '+' || *p == '-') { - ++p; - ++c; - } - - if (*p == '\'') { - if (terminator) - return(spec_norm(sv, 0)); - lim = 0; - terminator = 1; - ++p; - ++c; - } else if (*p == '[') { - if (terminator) - return(spec_norm(sv, 0)); - lim = 0; - terminator = 2; - ++p; - ++c; - } else if (*p == '(') { - if (terminator) - return(spec_norm(sv, 0)); - lim = 2; - terminator = 3; - ++p; - ++c; - } - - /* TODO: needs to handle floating point. */ - - if ( ! isdigit((u_char)*p)) - return(spec_norm(sv, 0)); - - for (i = 0; isdigit((u_char)*p); i++) { - if (lim && i >= lim) - break; - ++p; - ++c; - } - - if (terminator && terminator < 3) { - if (1 == terminator && *p != '\'') - return(spec_norm(sv, 0)); - if (2 == terminator && *p != ']') - return(spec_norm(sv, 0)); - ++p; - ++c; - } - - return(spec_norm(sv, c)); - case ('f'): - /* FALLTHROUGH */ - case ('F'): - /* FALLTHROUGH */ - case ('*'): - if ('\0' == *++p || isspace((u_char)*p)) - return(spec_norm(sv, 0)); - switch (*p) { - case ('('): - if ('\0' == *++p || isspace((u_char)*p)) - return(spec_norm(sv, 0)); - return(spec_norm(sv, 4)); - case ('['): - for (c = 3, p++; *p && ']' != *p; p++, c++) - if (isspace((u_char)*p)) - break; - return(spec_norm(sv, *p == ']' ? c : 0)); - default: - break; - } - return(spec_norm(sv, 3)); - case ('('): - if ('\0' == *++p || isspace((u_char)*p)) - return(spec_norm(sv, 0)); - if ('\0' == *++p || isspace((u_char)*p)) - return(spec_norm(sv, 0)); - return(spec_norm(sv, 4)); - case ('['): - break; - default: - return(spec_norm(sv, 0)); - } - - for (c = 3, p++; *p && ']' != *p; p++, c++) - if (isspace((u_char)*p)) - break; - - return(spec_norm(sv, *p == ']' ? c : 0)); -} - - -void * -mandoc_calloc(size_t num, size_t size) -{ - void *ptr; - - ptr = calloc(num, size); - if (NULL == ptr) { - perror(NULL); - exit(EXIT_FAILURE); - } - - return(ptr); -} - - -void * -mandoc_malloc(size_t size) -{ - void *ptr; - - ptr = malloc(size); - if (NULL == ptr) { - perror(NULL); - exit(EXIT_FAILURE); - } - - return(ptr); -} - - -void * -mandoc_realloc(void *ptr, size_t size) -{ - - ptr = realloc(ptr, size); - if (NULL == ptr) { - perror(NULL); - exit(EXIT_FAILURE); - } - - return(ptr); -} - - -char * -mandoc_strdup(const char *ptr) -{ - char *p; - - p = strdup(ptr); - if (NULL == p) { - perror(NULL); - exit(EXIT_FAILURE); - } - - return(p); -} - - -static int -a2time(time_t *t, const char *fmt, const char *p) -{ - struct tm tm; - char *pp; - - memset(&tm, 0, sizeof(struct tm)); - - pp = strptime(p, fmt, &tm); - if (NULL != pp && '\0' == *pp) { - *t = mktime(&tm); - return(1); - } - - return(0); -} - - -/* - * Convert from a manual date string (see mdoc(7) and man(7)) into a - * date according to the stipulated date type. - */ -time_t -mandoc_a2time(int flags, const char *p) -{ - time_t t; - - if (MTIME_MDOCDATE & flags) { - if (0 == strcmp(p, "$" "Mdocdate$")) - return(time(NULL)); - if (a2time(&t, "$" "Mdocdate: %b %d %Y $", p)) - return(t); - } - - if (MTIME_CANONICAL & flags || MTIME_REDUCED & flags) - if (a2time(&t, "%b %d, %Y", p)) - return(t); - - if (MTIME_ISO_8601 & flags) - if (a2time(&t, "%Y-%m-%d", p)) - return(t); - - if (MTIME_REDUCED & flags) { - if (a2time(&t, "%d, %Y", p)) - return(t); - if (a2time(&t, "%Y", p)) - return(t); - } - - return(0); -} - - -int -mandoc_eos(const char *p, size_t sz) -{ - - if (0 == sz) - return(0); - - /* - * End-of-sentence recognition must include situations where - * some symbols, such as `)', allow prior EOS punctuation to - * propogate outward. - */ - - for ( ; sz; sz--) { - switch (p[(int)sz - 1]) { - case ('\"'): - /* FALLTHROUGH */ - case ('\''): - /* FALLTHROUGH */ - case (']'): - /* FALLTHROUGH */ - case (')'): - break; - case ('.'): - /* Escaped periods. */ - if (sz > 1 && '\\' == p[(int)sz - 2]) - return(0); - /* FALLTHROUGH */ - case ('!'): - /* FALLTHROUGH */ - case ('?'): - return(1); - default: - return(0); - } - } - - return(0); -} - - -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); -} diff --git a/commands/mdocml/mandoc.h b/commands/mdocml/mandoc.h deleted file mode 100644 index 2fc74676c..000000000 --- a/commands/mdocml/mandoc.h +++ /dev/null @@ -1,110 +0,0 @@ -/* $Id: mandoc.h,v 1.12 2010/06/12 11:41:50 kristaps Exp $ */ -/* - * Copyright (c) 2010 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 MANDOC_H -#define MANDOC_H - -#define ASCII_NBRSP 31 /* non-breaking space */ -#define ASCII_HYPH 30 /* breakable hyphen */ - - -__BEGIN_DECLS - -enum mandocerr { - MANDOCERR_OK, - MANDOCERR_UPPERCASE, /* text should be uppercase */ - MANDOCERR_SECOOO, /* sections out of conventional order */ - MANDOCERR_SECREP, /* section name repeats */ - MANDOCERR_PROLOGOOO, /* out of order prologue */ - MANDOCERR_PROLOGREP, /* repeated prologue entry */ - MANDOCERR_LISTFIRST, /* list type must come first */ - MANDOCERR_BADSTANDARD, /* bad standard */ - MANDOCERR_BADLIB, /* bad library */ - MANDOCERR_BADESCAPE, /* bad escape sequence */ - MANDOCERR_BADQUOTE, /* unterminated quoted string */ - MANDOCERR_NOWIDTHARG, /* argument requires the width argument */ - /* FIXME: merge with MANDOCERR_IGNARGV. */ - MANDOCERR_WIDTHARG, /* superfluous width argument */ - MANDOCERR_IGNARGV, /* macro ignoring argv */ - MANDOCERR_BADDATE, /* bad date argument */ - MANDOCERR_BADWIDTH, /* bad width argument */ - MANDOCERR_BADMSEC, /* unknown manual section */ - MANDOCERR_SECMSEC, /* section not in conventional manual section */ - MANDOCERR_EOLNSPACE, /* end of line whitespace */ - MANDOCERR_SCOPEEXIT, /* scope open on exit */ -#define MANDOCERR_WARNING MANDOCERR_SCOPEEXIT - - MANDOCERR_NAMESECFIRST, /* NAME section must come first */ - MANDOCERR_BADBOOL, /* bad Boolean value */ - MANDOCERR_CHILD, /* child violates parent syntax */ - MANDOCERR_BADATT, /* bad AT&T symbol */ - MANDOCERR_LISTREP, /* list type repeated */ - MANDOCERR_DISPREP, /* display type repeated */ - MANDOCERR_ARGVREP, /* argument repeated */ - MANDOCERR_NONAME, /* manual name not yet set */ - MANDOCERR_MACROOBS, /* obsolete macro ignored */ - MANDOCERR_MACROEMPTY, /* empty macro ignored */ - MANDOCERR_BADBODY, /* macro not allowed in body */ - MANDOCERR_BADPROLOG, /* macro not allowed in prologue */ - MANDOCERR_BADCHAR, /* bad character */ - MANDOCERR_BADNAMESEC, /* bad NAME section contents */ - MANDOCERR_NOBLANKLN, /* no blank lines */ - MANDOCERR_NOTEXT, /* no text in this context */ - MANDOCERR_BADCOMMENT, /* bad comment style */ - MANDOCERR_MACRO, /* unknown macro will be lost */ - MANDOCERR_LINESCOPE, /* line scope broken */ - MANDOCERR_SCOPE, /* scope broken */ - MANDOCERR_ARGCOUNT, /* argument count wrong */ - MANDOCERR_NOSCOPE, /* request scope close w/none open */ - MANDOCERR_SCOPEREP, /* scope already open */ - /* FIXME: merge following with MANDOCERR_ARGCOUNT */ - MANDOCERR_NOARGS, /* macro requires line argument(s) */ - MANDOCERR_NOBODY, /* macro requires body argument(s) */ - MANDOCERR_NOARGV, /* macro requires argument(s) */ - MANDOCERR_NOTITLE, /* no title in document */ - MANDOCERR_LISTTYPE, /* missing list type */ - MANDOCERR_DISPTYPE, /* missing display type */ - MANDOCERR_ARGSLOST, /* line argument(s) will be lost */ - MANDOCERR_BODYLOST, /* body argument(s) will be lost */ -#define MANDOCERR_ERROR MANDOCERR_BODYLOST - - MANDOCERR_COLUMNS, /* column syntax is inconsistent */ - /* FIXME: this should be a MANDOCERR_ERROR */ - MANDOCERR_FONTTYPE, /* missing font type */ - /* FIXME: this should be a MANDOCERR_ERROR */ - MANDOCERR_NESTEDDISP, /* displays may not be nested */ - MANDOCERR_BADDISP, /* unsupported display type */ - MANDOCERR_SYNTNOSCOPE, /* request scope close w/none open */ - MANDOCERR_SYNTSCOPE, /* scope broken, syntax violated */ - 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 */ - MANDOCERR_NODOCBODY, /* no document body */ - MANDOCERR_NODOCPROLOG, /* no document prologue */ - MANDOCERR_UTSNAME, /* utsname() system call failed */ - MANDOCERR_MEM, /* memory exhausted */ -#define MANDOCERR_FATAL MANDOCERR_MEM - - MANDOCERR_MAX -}; - -typedef int (*mandocmsg)(enum mandocerr, - void *, int, int, const char *); - -__END_DECLS - -#endif /*!MANDOC_H*/ diff --git a/commands/mdocml/mdoc_action.c b/commands/mdocml/mdoc_action.c deleted file mode 100644 index 3cc87e418..000000000 --- a/commands/mdocml/mdoc_action.c +++ /dev/null @@ -1,1034 +0,0 @@ -/* $Id: mdoc_action.c,v 1.71 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 - -#ifndef OSNAME -#include -#endif - -#include -#include -#include -#include -#include - -#include "mandoc.h" -#include "libmdoc.h" -#include "libmandoc.h" - -#define POST_ARGS struct mdoc *m, struct mdoc_node *n -#define PRE_ARGS struct mdoc *m, struct mdoc_node *n - -#define NUMSIZ 32 -#define DATESIZ 32 - -struct actions { - int (*pre)(PRE_ARGS); - int (*post)(POST_ARGS); -}; - -static int concat(struct mdoc *, char *, - const struct mdoc_node *, size_t); -static inline int order_rs(enum mdoct); - -static int post_ar(POST_ARGS); -static int post_at(POST_ARGS); -static int post_bl(POST_ARGS); -static int post_bl_head(POST_ARGS); -static int post_bl_tagwidth(POST_ARGS); -static int post_bl_width(POST_ARGS); -static int post_dd(POST_ARGS); -static int post_display(POST_ARGS); -static int post_dt(POST_ARGS); -static int post_lb(POST_ARGS); -static int post_li(POST_ARGS); -static int post_nm(POST_ARGS); -static int post_os(POST_ARGS); -static int post_pa(POST_ARGS); -static int post_prol(POST_ARGS); -static int post_rs(POST_ARGS); -static int post_sh(POST_ARGS); -static int post_st(POST_ARGS); -static int post_std(POST_ARGS); - -static int pre_bd(PRE_ARGS); -static int pre_dl(PRE_ARGS); - -static const struct actions mdoc_actions[MDOC_MAX] = { - { NULL, NULL }, /* Ap */ - { NULL, post_dd }, /* Dd */ - { NULL, post_dt }, /* Dt */ - { NULL, post_os }, /* Os */ - { NULL, post_sh }, /* Sh */ - { NULL, NULL }, /* Ss */ - { NULL, NULL }, /* Pp */ - { NULL, NULL }, /* D1 */ - { pre_dl, post_display }, /* Dl */ - { pre_bd, post_display }, /* Bd */ - { NULL, NULL }, /* Ed */ - { NULL, post_bl }, /* Bl */ - { NULL, NULL }, /* El */ - { NULL, NULL }, /* It */ - { NULL, NULL }, /* Ad */ - { NULL, NULL }, /* An */ - { NULL, post_ar }, /* Ar */ - { NULL, NULL }, /* Cd */ - { NULL, NULL }, /* Cm */ - { NULL, NULL }, /* Dv */ - { NULL, NULL }, /* Er */ - { NULL, NULL }, /* Ev */ - { NULL, post_std }, /* Ex */ - { NULL, NULL }, /* Fa */ - { NULL, NULL }, /* Fd */ - { NULL, NULL }, /* Fl */ - { NULL, NULL }, /* Fn */ - { NULL, NULL }, /* Ft */ - { NULL, NULL }, /* Ic */ - { NULL, NULL }, /* In */ - { NULL, post_li }, /* Li */ - { NULL, NULL }, /* Nd */ - { NULL, post_nm }, /* Nm */ - { NULL, NULL }, /* Op */ - { NULL, NULL }, /* Ot */ - { NULL, post_pa }, /* Pa */ - { NULL, post_std }, /* Rv */ - { NULL, post_st }, /* St */ - { NULL, NULL }, /* Va */ - { NULL, NULL }, /* Vt */ - { NULL, NULL }, /* Xr */ - { NULL, NULL }, /* %A */ - { NULL, NULL }, /* %B */ - { NULL, NULL }, /* %D */ - { NULL, NULL }, /* %I */ - { NULL, NULL }, /* %J */ - { NULL, NULL }, /* %N */ - { NULL, NULL }, /* %O */ - { NULL, NULL }, /* %P */ - { NULL, NULL }, /* %R */ - { NULL, NULL }, /* %T */ - { NULL, NULL }, /* %V */ - { NULL, NULL }, /* Ac */ - { NULL, NULL }, /* Ao */ - { NULL, NULL }, /* Aq */ - { NULL, post_at }, /* At */ - { NULL, NULL }, /* Bc */ - { NULL, NULL }, /* Bf */ - { NULL, NULL }, /* Bo */ - { NULL, NULL }, /* Bq */ - { NULL, NULL }, /* Bsx */ - { NULL, NULL }, /* Bx */ - { NULL, NULL }, /* Db */ - { NULL, NULL }, /* Dc */ - { NULL, NULL }, /* Do */ - { NULL, NULL }, /* Dq */ - { NULL, NULL }, /* Ec */ - { NULL, NULL }, /* Ef */ - { NULL, NULL }, /* Em */ - { NULL, NULL }, /* Eo */ - { NULL, NULL }, /* Fx */ - { NULL, NULL }, /* Ms */ - { NULL, NULL }, /* No */ - { NULL, NULL }, /* Ns */ - { NULL, NULL }, /* Nx */ - { NULL, NULL }, /* Ox */ - { NULL, NULL }, /* Pc */ - { NULL, NULL }, /* Pf */ - { NULL, NULL }, /* Po */ - { NULL, NULL }, /* Pq */ - { NULL, NULL }, /* Qc */ - { NULL, NULL }, /* Ql */ - { NULL, NULL }, /* Qo */ - { NULL, NULL }, /* Qq */ - { NULL, NULL }, /* Re */ - { NULL, post_rs }, /* Rs */ - { NULL, NULL }, /* Sc */ - { NULL, NULL }, /* So */ - { NULL, NULL }, /* Sq */ - { NULL, NULL }, /* Sm */ - { NULL, NULL }, /* Sx */ - { NULL, NULL }, /* Sy */ - { NULL, NULL }, /* Tn */ - { NULL, NULL }, /* Ux */ - { NULL, NULL }, /* Xc */ - { NULL, NULL }, /* Xo */ - { NULL, NULL }, /* Fo */ - { NULL, NULL }, /* Fc */ - { NULL, NULL }, /* Oo */ - { NULL, NULL }, /* Oc */ - { NULL, NULL }, /* Bk */ - { NULL, NULL }, /* Ek */ - { NULL, NULL }, /* Bt */ - { NULL, NULL }, /* Hf */ - { NULL, NULL }, /* Fr */ - { NULL, NULL }, /* Ud */ - { NULL, post_lb }, /* Lb */ - { NULL, NULL }, /* Lp */ - { NULL, NULL }, /* Lk */ - { NULL, NULL }, /* Mt */ - { NULL, NULL }, /* Brq */ - { NULL, NULL }, /* Bro */ - { NULL, NULL }, /* Brc */ - { NULL, NULL }, /* %C */ - { NULL, NULL }, /* Es */ - { NULL, NULL }, /* En */ - { NULL, NULL }, /* Dx */ - { NULL, NULL }, /* %Q */ - { NULL, NULL }, /* br */ - { NULL, NULL }, /* sp */ - { NULL, NULL }, /* %U */ - { NULL, NULL }, /* Ta */ -}; - -#define RSORD_MAX 14 - -static const enum mdoct rsord[RSORD_MAX] = { - MDOC__A, - MDOC__T, - MDOC__B, - MDOC__I, - MDOC__J, - MDOC__R, - MDOC__N, - MDOC__V, - MDOC__P, - MDOC__Q, - MDOC__D, - MDOC__O, - MDOC__C, - MDOC__U -}; - - -int -mdoc_action_pre(struct mdoc *m, struct mdoc_node *n) -{ - - switch (n->type) { - case (MDOC_ROOT): - /* FALLTHROUGH */ - case (MDOC_TEXT): - return(1); - default: - break; - } - - if (NULL == mdoc_actions[n->tok].pre) - return(1); - return((*mdoc_actions[n->tok].pre)(m, n)); -} - - -int -mdoc_action_post(struct mdoc *m) -{ - - if (MDOC_ACTED & m->last->flags) - return(1); - m->last->flags |= MDOC_ACTED; - - switch (m->last->type) { - case (MDOC_TEXT): - /* FALLTHROUGH */ - case (MDOC_ROOT): - return(1); - default: - break; - } - - if (NULL == mdoc_actions[m->last->tok].post) - return(1); - return((*mdoc_actions[m->last->tok].post)(m, m->last)); -} - - -/* - * Concatenate sibling nodes together. All siblings must be of type - * MDOC_TEXT or an assertion is raised. Concatenation is separated by a - * single whitespace. - */ -static int -concat(struct mdoc *m, char *p, const struct mdoc_node *n, size_t sz) -{ - - assert(sz); - p[0] = '\0'; - for ( ; n; n = n->next) { - assert(MDOC_TEXT == n->type); - /* - * XXX: yes, these can technically be resized, but it's - * highly unlikely that we're going to get here, so let - * it slip for now. - */ - 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); - return(0); - } - } - - return(1); -} - - -/* - * Macros accepting `-std' as an argument have the name of the current - * document (`Nm') filled in as the argument if it's not provided. - */ -static int -post_std(POST_ARGS) -{ - struct mdoc_node *nn; - - if (n->child) - return(1); - if (NULL == m->meta.name) - return(1); - - nn = n; - m->next = MDOC_NEXT_CHILD; - - if ( ! mdoc_word_alloc(m, n->line, n->pos, m->meta.name)) - return(0); - m->last = nn; - return(1); -} - - -/* - * The `Nm' macro's first use sets the name of the document. See also - * post_std(), etc. - */ -static int -post_nm(POST_ARGS) -{ - char buf[BUFSIZ]; - - if (m->meta.name) - return(1); - if ( ! concat(m, buf, n->child, BUFSIZ)) - return(0); - m->meta.name = mandoc_strdup(buf); - return(1); -} - - -/* - * Look up the value of `Lb' for matching predefined strings. If it has - * one, then substitute the current value for the formatted value. Note - * that the lookup may fail (we can provide arbitrary strings). - */ -/* ARGSUSED */ -static int -post_lb(POST_ARGS) -{ - const char *p; - char *buf; - size_t sz; - - assert(MDOC_TEXT == n->child->type); - p = mdoc_a2lib(n->child->string); - - if (p) { - free(n->child->string); - n->child->string = mandoc_strdup(p); - return(1); - } - - sz = strlen(n->child->string) + - 2 + strlen("\\(lqlibrary\\(rq"); - buf = mandoc_malloc(sz); - snprintf(buf, sz, "library \\(lq%s\\(rq", n->child->string); - free(n->child->string); - n->child->string = buf; - return(1); -} - - -/* - * Substitute the value of `St' for the corresponding formatted string. - * We're guaranteed that this exists (it's been verified during the - * validation phase). - */ -/* ARGSUSED */ -static int -post_st(POST_ARGS) -{ - const char *p; - - assert(MDOC_TEXT == n->child->type); - p = mdoc_a2st(n->child->string); - if (p != NULL) { - free(n->child->string); - n->child->string = mandoc_strdup(p); - } - return(1); -} - - -/* - * Look up the standard string in a table. We know that it exists from - * the validation phase, so assert on failure. If a standard key wasn't - * supplied, supply the default ``AT&T UNIX''. - */ -static int -post_at(POST_ARGS) -{ - struct mdoc_node *nn; - const char *p, *q; - char *buf; - size_t sz; - - if (n->child) { - assert(MDOC_TEXT == n->child->type); - p = mdoc_a2att(n->child->string); - if (p) { - free(n->child->string); - n->child->string = mandoc_strdup(p); - } else { - p = "AT&T UNIX "; - q = n->child->string; - sz = strlen(p) + strlen(q) + 1; - buf = mandoc_malloc(sz); - strlcpy(buf, p, sz); - strlcat(buf, q, sz); - free(n->child->string); - n->child->string = buf; - } - return(1); - } - - nn = n; - m->next = MDOC_NEXT_CHILD; - if ( ! mdoc_word_alloc(m, nn->line, nn->pos, "AT&T UNIX")) - return(0); - m->last = nn; - return(1); -} - - -/* - * Mark the current section. The ``named'' section (lastnamed) is set - * whenever the current section isn't a custom section--we use this to - * keep track of section ordering. Also check that the section is - * allowed within the document's manual section. - */ -static int -post_sh(POST_ARGS) -{ - enum mdoc_sec sec; - char buf[BUFSIZ]; - - if (MDOC_HEAD != n->type) - return(1); - - if ( ! concat(m, buf, n->child, BUFSIZ)) - return(0); - sec = mdoc_str2sec(buf); - /* - * The first section should always make us move into a non-new - * state. - */ - if (SEC_NONE == m->lastnamed || SEC_CUSTOM != sec) - m->lastnamed = sec; - - /* Some sections only live in certain manual sections. */ - - switch ((m->lastsec = sec)) { - case (SEC_RETURN_VALUES): - /* FALLTHROUGH */ - case (SEC_ERRORS): - assert(m->meta.msec); - if (*m->meta.msec == '2') - break; - if (*m->meta.msec == '3') - break; - if (*m->meta.msec == '9') - break; - return(mdoc_nmsg(m, n, MANDOCERR_SECMSEC)); - default: - break; - } - return(1); -} - - -/* - * Parse out the contents of `Dt'. See in-line documentation for how we - * handle the various fields of this macro. - */ -static int -post_dt(POST_ARGS) -{ - struct mdoc_node *nn; - const char *cp; - - if (m->meta.title) - free(m->meta.title); - if (m->meta.vol) - free(m->meta.vol); - if (m->meta.arch) - free(m->meta.arch); - - m->meta.title = m->meta.vol = m->meta.arch = NULL; - /* Handles: `.Dt' - * --> title = unknown, volume = local, msec = 0, arch = NULL - */ - - if (NULL == (nn = n->child)) { - /* XXX: make these macro values. */ - /* FIXME: warn about missing values. */ - m->meta.title = mandoc_strdup("UNKNOWN"); - m->meta.vol = mandoc_strdup("LOCAL"); - m->meta.msec = mandoc_strdup("1"); - return(post_prol(m, n)); - } - - /* Handles: `.Dt TITLE' - * --> title = TITLE, volume = local, msec = 0, arch = NULL - */ - - m->meta.title = mandoc_strdup - ('\0' == nn->string[0] ? "UNKNOWN" : nn->string); - - if (NULL == (nn = nn->next)) { - /* FIXME: warn about missing msec. */ - /* XXX: make this a macro value. */ - m->meta.vol = mandoc_strdup("LOCAL"); - m->meta.msec = mandoc_strdup("1"); - return(post_prol(m, n)); - } - - /* Handles: `.Dt TITLE SEC' - * --> title = TITLE, volume = SEC is msec ? - * format(msec) : SEC, - * msec = SEC is msec ? atoi(msec) : 0, - * arch = NULL - */ - - cp = mdoc_a2msec(nn->string); - if (cp) { - m->meta.vol = mandoc_strdup(cp); - m->meta.msec = mandoc_strdup(nn->string); - } else if (mdoc_nmsg(m, n, MANDOCERR_BADMSEC)) { - m->meta.vol = mandoc_strdup(nn->string); - m->meta.msec = mandoc_strdup(nn->string); - } else - return(0); - - if (NULL == (nn = nn->next)) - return(post_prol(m, n)); - - /* Handles: `.Dt TITLE SEC VOL' - * --> title = TITLE, volume = VOL is vol ? - * format(VOL) : - * VOL is arch ? format(arch) : - * VOL - */ - - cp = mdoc_a2vol(nn->string); - if (cp) { - free(m->meta.vol); - m->meta.vol = mandoc_strdup(cp); - } else { - /* FIXME: warn about bad arch. */ - cp = mdoc_a2arch(nn->string); - if (NULL == cp) { - free(m->meta.vol); - m->meta.vol = mandoc_strdup(nn->string); - } else - m->meta.arch = mandoc_strdup(cp); - } - - /* Ignore any subsequent parameters... */ - /* FIXME: warn about subsequent parameters. */ - - return(post_prol(m, n)); -} - - -/* - * Set the operating system by way of the `Os' macro. Note that if an - * argument isn't provided and -DOSNAME="\"foo\"" is provided during - * compilation, this value will be used instead of filling in "sysname - * release" from uname(). - */ -static int -post_os(POST_ARGS) -{ - char buf[BUFSIZ]; -#ifndef OSNAME - struct utsname utsname; -#endif - - if (m->meta.os) - free(m->meta.os); - - if ( ! concat(m, buf, n->child, BUFSIZ)) - return(0); - - /* XXX: yes, these can all be dynamically-adjusted buffers, but - * it's really not worth the extra hackery. - */ - - if ('\0' == buf[0]) { -#ifdef OSNAME - if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) { - mdoc_nmsg(m, n, MANDOCERR_MEM); - return(0); - } -#else /*!OSNAME */ - if (-1 == uname(&utsname)) - return(mdoc_nmsg(m, n, MANDOCERR_UTSNAME)); - - if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) { - mdoc_nmsg(m, n, MANDOCERR_MEM); - return(0); - } - if (strlcat(buf, " ", 64) >= BUFSIZ) { - mdoc_nmsg(m, n, MANDOCERR_MEM); - return(0); - } - if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) { - mdoc_nmsg(m, n, MANDOCERR_MEM); - return(0); - } -#endif /*!OSNAME*/ - } - - m->meta.os = mandoc_strdup(buf); - return(post_prol(m, n)); -} - - -/* - * Calculate the -width for a `Bl -tag' list if it hasn't been provided. - * Uses the first head macro. NOTE AGAIN: this is ONLY if the -width - * argument has NOT been provided. See post_bl_width() for converting - * the -width string. - */ -static int -post_bl_tagwidth(POST_ARGS) -{ - struct mdoc_node *nn; - size_t sz, ssz; - int i; - char buf[NUMSIZ]; - - sz = 10; - - for (nn = n->body->child; nn; nn = nn->next) { - if (MDOC_It != nn->tok) - continue; - - assert(MDOC_BLOCK == nn->type); - nn = nn->head->child; - - if (MDOC_TEXT == nn->type) { - sz = strlen(nn->string) + 1; - break; - } - - if (0 != (ssz = mdoc_macro2len(nn->tok))) - sz = ssz; - else if ( ! mdoc_nmsg(m, n, MANDOCERR_NOWIDTHARG)) - return(0); - - break; - } - - /* Defaults to ten ens. */ - - snprintf(buf, NUMSIZ, "%zun", sz); - - /* - * We have to dynamically add this to the macro's argument list. - * We're guaranteed that a MDOC_Width doesn't already exist. - */ - - assert(n->args); - i = (int)(n->args->argc)++; - - n->args->argv = mandoc_realloc(n->args->argv, - n->args->argc * sizeof(struct mdoc_argv)); - - n->args->argv[i].arg = MDOC_Width; - n->args->argv[i].line = n->line; - n->args->argv[i].pos = n->pos; - n->args->argv[i].sz = 1; - n->args->argv[i].value = mandoc_malloc(sizeof(char *)); - n->args->argv[i].value[0] = mandoc_strdup(buf); - - /* Set our width! */ - n->data.Bl.width = n->args->argv[i].value[0]; - return(1); -} - - -/* - * Calculate the real width of a list from the -width string, which may - * contain a macro (with a known default width), a literal string, or a - * scaling width. - */ -static int -post_bl_width(POST_ARGS) -{ - size_t width; - int i; - enum mdoct tok; - char buf[NUMSIZ]; - - /* - * If the value to -width is a macro, then we re-write it to be - * the macro's width as set in share/tmac/mdoc/doc-common. - */ - - if (0 == strcmp(n->data.Bl.width, "Ds")) - width = 6; - else if (MDOC_MAX == (tok = mdoc_hash_find(n->data.Bl.width))) - return(1); - else if (0 == (width = mdoc_macro2len(tok))) - return(mdoc_nmsg(m, n, MANDOCERR_BADWIDTH)); - - /* The value already exists: free and reallocate it. */ - - assert(n->args); - - for (i = 0; i < (int)n->args->argc; i++) - if (MDOC_Width == n->args->argv[i].arg) - break; - - assert(i < (int)n->args->argc); - - snprintf(buf, NUMSIZ, "%zun", width); - free(n->args->argv[i].value[0]); - n->args->argv[i].value[0] = mandoc_strdup(buf); - - /* Set our width! */ - n->data.Bl.width = n->args->argv[i].value[0]; - return(1); -} - - -/* - * Do processing for -column lists, which can have two distinct styles - * of invocation. Merge this two styles into a consistent form. - */ -/* ARGSUSED */ -static int -post_bl_head(POST_ARGS) -{ - int i, c; - struct mdoc_node *np, *nn, *nnp; - - if (LIST_column != n->data.Bl.type) - return(1); - else if (NULL == n->child) - return(1); - - np = n->parent; - assert(np->args); - - for (c = 0; c < (int)np->args->argc; c++) - if (MDOC_Column == np->args->argv[c].arg) - break; - - assert(c < (int)np->args->argc); - assert(0 == np->args->argv[c].sz); - - /* - * Accomodate for new-style groff column syntax. Shuffle the - * child nodes, all of which must be TEXT, as arguments for the - * column field. Then, delete the head children. - */ - - np->args->argv[c].sz = (size_t)n->nchild; - np->args->argv[c].value = mandoc_malloc - ((size_t)n->nchild * sizeof(char *)); - - for (i = 0, nn = n->child; nn; i++) { - np->args->argv[c].value[i] = nn->string; - nn->string = NULL; - nnp = nn; - nn = nn->next; - mdoc_node_delete(NULL, nnp); - } - - n->nchild = 0; - n->child = NULL; - return(1); -} - - -static int -post_bl(POST_ARGS) -{ - struct mdoc_node *nn; - const char *ww; - - if (MDOC_HEAD == n->type) - return(post_bl_head(m, n)); - if (MDOC_BLOCK != n->type) - return(1); - - /* - * These are fairly complicated, so we've broken them into two - * functions. post_bl_tagwidth() is called when a -tag is - * specified, but no -width (it must be guessed). The second - * when a -width is specified (macro indicators must be - * rewritten into real lengths). - */ - - ww = n->data.Bl.width; - - if (LIST_tag == n->data.Bl.type && NULL == n->data.Bl.width) { - if ( ! post_bl_tagwidth(m, n)) - return(0); - } else if (NULL != n->data.Bl.width) { - if ( ! post_bl_width(m, n)) - return(0); - } else - return(1); - - assert(n->data.Bl.width); - - /* If it has changed, propogate new width to children. */ - - if (ww == n->data.Bl.width) - return(1); - - for (nn = n->child; nn; nn = nn->next) - if (MDOC_Bl == nn->tok) - nn->data.Bl.width = n->data.Bl.width; - - return(1); -} - - -/* - * The `Pa' macro defaults to a tilde if no value is provided as an - * argument. - */ -static int -post_pa(POST_ARGS) -{ - struct mdoc_node *np; - - if (n->child) - return(1); - - np = n; - m->next = MDOC_NEXT_CHILD; - if ( ! mdoc_word_alloc(m, n->line, n->pos, "~")) - return(0); - m->last = np; - return(1); -} - - -/* - * Empty `Li' macros get an empty string to make front-ends add an extra - * space. - */ -static int -post_li(POST_ARGS) -{ - struct mdoc_node *np; - - if (n->child) - return(1); - - np = n; - m->next = MDOC_NEXT_CHILD; - if ( ! mdoc_word_alloc(m, n->line, n->pos, "")) - return(0); - m->last = np; - return(1); -} - - -/* - * The `Ar' macro defaults to two strings "file ..." if no value is - * provided as an argument. - */ -static int -post_ar(POST_ARGS) -{ - struct mdoc_node *np; - - if (n->child) - return(1); - - np = n; - m->next = MDOC_NEXT_CHILD; - /* XXX: make into macro values. */ - if ( ! mdoc_word_alloc(m, n->line, n->pos, "file")) - return(0); - if ( ! mdoc_word_alloc(m, n->line, n->pos, "...")) - return(0); - m->last = np; - return(1); -} - - -/* - * Parse the date field in `Dd'. - */ -static int -post_dd(POST_ARGS) -{ - char buf[DATESIZ]; - - if ( ! concat(m, buf, n->child, DATESIZ)) - return(0); - - m->meta.date = mandoc_a2time - (MTIME_MDOCDATE | MTIME_CANONICAL, buf); - - if (0 == m->meta.date) { - if ( ! mdoc_nmsg(m, n, MANDOCERR_BADDATE)) - return(0); - m->meta.date = time(NULL); - } - - return(post_prol(m, n)); -} - - -/* - * Remove prologue macros from the document after they're processed. - * The final document uses mdoc_meta for these values and discards the - * originals. - */ -static int -post_prol(POST_ARGS) -{ - - mdoc_node_delete(m, n); - if (m->meta.title && m->meta.date && m->meta.os) - m->flags |= MDOC_PBODY; - return(1); -} - - -/* - * Trigger a literal context. - */ -static int -pre_dl(PRE_ARGS) -{ - - if (MDOC_BODY == n->type) - m->flags |= MDOC_LITERAL; - return(1); -} - - -static int -pre_bd(PRE_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - - if (DISP_literal == n->data.Bd.type) - m->flags |= MDOC_LITERAL; - if (DISP_unfilled == n->data.Bd.type) - m->flags |= MDOC_LITERAL; - - return(1); -} - - -static int -post_display(POST_ARGS) -{ - - if (MDOC_BODY == n->type) - m->flags &= ~MDOC_LITERAL; - return(1); -} - - -static inline int -order_rs(enum mdoct t) -{ - int i; - - for (i = 0; i < (int)RSORD_MAX; i++) - if (rsord[i] == t) - return(i); - - abort(); - /* NOTREACHED */ -} - - -/* ARGSUSED */ -static int -post_rs(POST_ARGS) -{ - struct mdoc_node *nn, *next, *prev; - int o; - - if (MDOC_BLOCK != n->type) - return(1); - - assert(n->body->child); - for (next = NULL, nn = n->body->child->next; nn; nn = next) { - o = order_rs(nn->tok); - - /* Remove `nn' from the chain. */ - next = nn->next; - if (next) - next->prev = nn->prev; - - prev = nn->prev; - if (prev) - prev->next = nn->next; - - nn->prev = nn->next = NULL; - - /* - * Scan back until we reach a node that's ordered before - * us, then set ourselves as being the next. - */ - for ( ; prev; prev = prev->prev) - if (order_rs(prev->tok) <= o) - break; - - nn->prev = prev; - if (prev) { - if (prev->next) - prev->next->prev = nn; - nn->next = prev->next; - prev->next = nn; - continue; - } - - n->body->child->prev = nn; - nn->next = n->body->child; - n->body->child = nn; - } - return(1); -} diff --git a/commands/mdocml/mdoc_validate.c b/commands/mdocml/mdoc_validate.c deleted file mode 100644 index de1a7cc1d..000000000 --- a/commands/mdocml/mdoc_validate.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* $Id: mdoc_validate.c,v 1.99 2010/06/13 21:02:49 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "mandoc.h" -#include "libmdoc.h" -#include "libmandoc.h" - -/* FIXME: .Bl -diag can't have non-text children in HEAD. */ -/* TODO: ignoring Pp (it's superfluous in some invocations). */ - -#define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n -#define POST_ARGS struct mdoc *mdoc - -typedef int (*v_pre)(PRE_ARGS); -typedef int (*v_post)(POST_ARGS); - -struct valids { - v_pre *pre; - v_post *post; -}; - -static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type); -static int check_stdarg(PRE_ARGS); -static int check_text(struct mdoc *, int, int, char *); -static int check_argv(struct mdoc *, - struct mdoc_node *, struct mdoc_argv *); -static int check_args(struct mdoc *, struct mdoc_node *); -static int err_child_lt(struct mdoc *, const char *, int); -static int warn_child_lt(struct mdoc *, const char *, int); -static int err_child_gt(struct mdoc *, const char *, int); -static int warn_child_gt(struct mdoc *, const char *, int); -static int err_child_eq(struct mdoc *, const char *, int); -static int warn_child_eq(struct mdoc *, const char *, int); -static int warn_count(struct mdoc *, const char *, - int, const char *, int); -static int err_count(struct mdoc *, const char *, - int, const char *, int); - -static int berr_ge1(POST_ARGS); -static int bwarn_ge1(POST_ARGS); -static int ebool(POST_ARGS); -static int eerr_eq0(POST_ARGS); -static int eerr_eq1(POST_ARGS); -static int eerr_ge1(POST_ARGS); -static int eerr_le1(POST_ARGS); -static int ewarn_ge1(POST_ARGS); -static int herr_eq0(POST_ARGS); -static int herr_ge1(POST_ARGS); -static int hwarn_eq1(POST_ARGS); -static int hwarn_eq0(POST_ARGS); -static int hwarn_le1(POST_ARGS); - -static int post_an(POST_ARGS); -static int post_at(POST_ARGS); -static int post_bf(POST_ARGS); -static int post_bl(POST_ARGS); -static int post_bl_head(POST_ARGS); -static int post_dt(POST_ARGS); -static int post_it(POST_ARGS); -static int post_lb(POST_ARGS); -static int post_nm(POST_ARGS); -static int post_root(POST_ARGS); -static int post_rs(POST_ARGS); -static int post_sh(POST_ARGS); -static int post_sh_body(POST_ARGS); -static int post_sh_head(POST_ARGS); -static int post_st(POST_ARGS); -static int post_eoln(POST_ARGS); -static int post_vt(POST_ARGS); -static int pre_an(PRE_ARGS); -static int pre_bd(PRE_ARGS); -static int pre_bl(PRE_ARGS); -static int pre_dd(PRE_ARGS); -static int pre_display(PRE_ARGS); -static int pre_dt(PRE_ARGS); -static int pre_it(PRE_ARGS); -static int pre_os(PRE_ARGS); -static int pre_rv(PRE_ARGS); -static int pre_sh(PRE_ARGS); -static int pre_ss(PRE_ARGS); - -static v_post posts_an[] = { post_an, NULL }; -static v_post posts_at[] = { post_at, NULL }; -static v_post posts_bd[] = { hwarn_eq0, bwarn_ge1, NULL }; -static v_post posts_bf[] = { hwarn_le1, post_bf, NULL }; -static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; -static v_post posts_bool[] = { eerr_eq1, ebool, NULL }; -static v_post posts_eoln[] = { post_eoln, NULL }; -static v_post posts_dt[] = { post_dt, NULL }; -static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; -static v_post posts_it[] = { post_it, NULL }; -static v_post posts_lb[] = { eerr_eq1, post_lb, NULL }; -static v_post posts_nd[] = { berr_ge1, NULL }; -static v_post posts_nm[] = { post_nm, NULL }; -static v_post posts_notext[] = { eerr_eq0, NULL }; -static v_post posts_rs[] = { berr_ge1, herr_eq0, post_rs, NULL }; -static v_post posts_sh[] = { herr_ge1, bwarn_ge1, post_sh, NULL }; -static v_post posts_sp[] = { eerr_le1, NULL }; -static v_post posts_ss[] = { herr_ge1, NULL }; -static v_post posts_st[] = { eerr_eq1, post_st, NULL }; -static v_post posts_text[] = { eerr_ge1, NULL }; -static v_post posts_text1[] = { eerr_eq1, NULL }; -static v_post posts_vt[] = { post_vt, NULL }; -static v_post posts_wline[] = { bwarn_ge1, herr_eq0, NULL }; -static v_post posts_wtext[] = { ewarn_ge1, NULL }; -static v_pre pres_an[] = { pre_an, NULL }; -static v_pre pres_bd[] = { pre_display, pre_bd, NULL }; -static v_pre pres_bl[] = { pre_bl, NULL }; -static v_pre pres_d1[] = { pre_display, NULL }; -static v_pre pres_dd[] = { pre_dd, NULL }; -static v_pre pres_dt[] = { pre_dt, NULL }; -static v_pre pres_er[] = { NULL, NULL }; -static v_pre pres_ex[] = { NULL, NULL }; -static v_pre pres_fd[] = { NULL, NULL }; -static v_pre pres_it[] = { pre_it, NULL }; -static v_pre pres_os[] = { pre_os, NULL }; -static v_pre pres_rv[] = { pre_rv, NULL }; -static v_pre pres_sh[] = { pre_sh, NULL }; -static v_pre pres_ss[] = { pre_ss, NULL }; - -const struct valids mdoc_valids[MDOC_MAX] = { - { NULL, NULL }, /* Ap */ - { pres_dd, posts_text }, /* Dd */ - { pres_dt, posts_dt }, /* Dt */ - { pres_os, NULL }, /* Os */ - { pres_sh, posts_sh }, /* Sh */ - { pres_ss, posts_ss }, /* Ss */ - { NULL, posts_notext }, /* Pp */ - { pres_d1, posts_wline }, /* D1 */ - { pres_d1, posts_wline }, /* Dl */ - { pres_bd, posts_bd }, /* Bd */ - { NULL, NULL }, /* Ed */ - { pres_bl, posts_bl }, /* Bl */ - { NULL, NULL }, /* El */ - { pres_it, posts_it }, /* It */ - { NULL, posts_text }, /* Ad */ - { pres_an, posts_an }, /* An */ - { NULL, NULL }, /* Ar */ - { NULL, posts_text }, /* Cd */ - { NULL, NULL }, /* Cm */ - { NULL, NULL }, /* Dv */ - { pres_er, posts_text }, /* Er */ - { NULL, NULL }, /* Ev */ - { pres_ex, NULL }, /* Ex */ - { NULL, NULL }, /* Fa */ - { pres_fd, posts_wtext }, /* Fd */ - { NULL, NULL }, /* Fl */ - { NULL, posts_text }, /* Fn */ - { NULL, posts_wtext }, /* Ft */ - { NULL, posts_text }, /* Ic */ - { NULL, posts_text1 }, /* In */ - { NULL, NULL }, /* Li */ - { NULL, posts_nd }, /* Nd */ - { NULL, posts_nm }, /* Nm */ - { NULL, posts_wline }, /* Op */ - { NULL, NULL }, /* Ot */ - { NULL, NULL }, /* Pa */ - { pres_rv, NULL }, /* Rv */ - { NULL, posts_st }, /* St */ - { NULL, NULL }, /* Va */ - { NULL, posts_vt }, /* Vt */ - { NULL, posts_wtext }, /* Xr */ - { NULL, posts_text }, /* %A */ - { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */ - { NULL, posts_text }, /* %D */ /* FIXME: check date with mandoc_a2time(). */ - { NULL, posts_text }, /* %I */ - { NULL, posts_text }, /* %J */ - { NULL, posts_text }, /* %N */ - { NULL, posts_text }, /* %O */ - { NULL, posts_text }, /* %P */ - { NULL, posts_text }, /* %R */ - { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */ - { NULL, posts_text }, /* %V */ - { NULL, NULL }, /* Ac */ - { NULL, NULL }, /* Ao */ - { NULL, posts_wline }, /* Aq */ - { NULL, posts_at }, /* At */ - { NULL, NULL }, /* Bc */ - { NULL, posts_bf }, /* Bf */ - { NULL, NULL }, /* Bo */ - { NULL, posts_wline }, /* Bq */ - { NULL, NULL }, /* Bsx */ - { NULL, NULL }, /* Bx */ - { NULL, posts_bool }, /* Db */ - { NULL, NULL }, /* Dc */ - { NULL, NULL }, /* Do */ - { NULL, posts_wline }, /* Dq */ - { NULL, NULL }, /* Ec */ - { NULL, NULL }, /* Ef */ - { NULL, NULL }, /* Em */ - { NULL, NULL }, /* Eo */ - { NULL, NULL }, /* Fx */ - { NULL, posts_text }, /* Ms */ - { NULL, posts_notext }, /* No */ - { NULL, posts_notext }, /* Ns */ - { NULL, NULL }, /* Nx */ - { NULL, NULL }, /* Ox */ - { NULL, NULL }, /* Pc */ - { NULL, posts_text1 }, /* Pf */ - { NULL, NULL }, /* Po */ - { NULL, posts_wline }, /* Pq */ - { NULL, NULL }, /* Qc */ - { NULL, posts_wline }, /* Ql */ - { NULL, NULL }, /* Qo */ - { NULL, posts_wline }, /* Qq */ - { NULL, NULL }, /* Re */ - { NULL, posts_rs }, /* Rs */ - { NULL, NULL }, /* Sc */ - { NULL, NULL }, /* So */ - { NULL, posts_wline }, /* Sq */ - { NULL, posts_bool }, /* Sm */ - { NULL, posts_text }, /* Sx */ - { NULL, posts_text }, /* Sy */ - { NULL, posts_text }, /* Tn */ - { NULL, NULL }, /* Ux */ - { NULL, NULL }, /* Xc */ - { NULL, NULL }, /* Xo */ - { NULL, posts_fo }, /* Fo */ - { NULL, NULL }, /* Fc */ - { NULL, NULL }, /* Oo */ - { NULL, NULL }, /* Oc */ - { NULL, posts_wline }, /* Bk */ - { NULL, NULL }, /* Ek */ - { NULL, posts_eoln }, /* Bt */ - { NULL, NULL }, /* Hf */ - { NULL, NULL }, /* Fr */ - { NULL, posts_eoln }, /* Ud */ - { NULL, posts_lb }, /* Lb */ - { NULL, posts_notext }, /* Lp */ - { NULL, posts_text }, /* Lk */ - { NULL, posts_text }, /* Mt */ - { NULL, posts_wline }, /* Brq */ - { NULL, NULL }, /* Bro */ - { NULL, NULL }, /* Brc */ - { NULL, posts_text }, /* %C */ - { NULL, NULL }, /* Es */ - { NULL, NULL }, /* En */ - { NULL, NULL }, /* Dx */ - { NULL, posts_text }, /* %Q */ - { NULL, posts_notext }, /* br */ - { NULL, posts_sp }, /* sp */ - { NULL, posts_text1 }, /* %U */ - { NULL, NULL }, /* Ta */ -}; - - -int -mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) -{ - v_pre *p; - int line, pos; - char *tp; - - if (MDOC_TEXT == n->type) { - tp = n->string; - line = n->line; - pos = n->pos; - return(check_text(mdoc, line, pos, tp)); - } - - if ( ! check_args(mdoc, n)) - return(0); - if (NULL == mdoc_valids[n->tok].pre) - return(1); - for (p = mdoc_valids[n->tok].pre; *p; p++) - if ( ! (*p)(mdoc, n)) - return(0); - return(1); -} - - -int -mdoc_valid_post(struct mdoc *mdoc) -{ - v_post *p; - - if (MDOC_VALID & mdoc->last->flags) - return(1); - mdoc->last->flags |= MDOC_VALID; - - if (MDOC_TEXT == mdoc->last->type) - return(1); - if (MDOC_ROOT == mdoc->last->type) - return(post_root(mdoc)); - - if (NULL == mdoc_valids[mdoc->last->tok].post) - return(1); - for (p = mdoc_valids[mdoc->last->tok].post; *p; p++) - if ( ! (*p)(mdoc)) - return(0); - - return(1); -} - - -static inline int -warn_count(struct mdoc *m, const char *k, - int want, const char *v, int has) -{ - - return(mdoc_vmsg(m, MANDOCERR_ARGCOUNT, - m->last->line, m->last->pos, - "%s %s %d (have %d)", v, k, want, has)); -} - - -static inline int -err_count(struct mdoc *m, const char *k, - int want, const char *v, int has) -{ - - mdoc_vmsg(m, MANDOCERR_SYNTARGCOUNT, - m->last->line, m->last->pos, - "%s %s %d (have %d)", - v, k, want, has); - return(0); -} - - -/* - * Build these up with macros because they're basically the same check - * for different inequalities. Yes, this could be done with functions, - * but this is reasonable for now. - */ - -#define CHECK_CHILD_DEFN(lvl, name, ineq) \ -static int \ -lvl##_child_##name(struct mdoc *mdoc, const char *p, int sz) \ -{ \ - if (mdoc->last->nchild ineq sz) \ - return(1); \ - return(lvl##_count(mdoc, #ineq, sz, p, mdoc->last->nchild)); \ -} - -#define CHECK_BODY_DEFN(name, lvl, func, num) \ -static int \ -b##lvl##_##name(POST_ARGS) \ -{ \ - if (MDOC_BODY != mdoc->last->type) \ - return(1); \ - return(func(mdoc, "multi-line arguments", (num))); \ -} - -#define CHECK_ELEM_DEFN(name, lvl, func, num) \ -static int \ -e##lvl##_##name(POST_ARGS) \ -{ \ - assert(MDOC_ELEM == mdoc->last->type); \ - return(func(mdoc, "line arguments", (num))); \ -} - -#define CHECK_HEAD_DEFN(name, lvl, func, num) \ -static int \ -h##lvl##_##name(POST_ARGS) \ -{ \ - if (MDOC_HEAD != mdoc->last->type) \ - return(1); \ - return(func(mdoc, "line arguments", (num))); \ -} - - -CHECK_CHILD_DEFN(warn, gt, >) /* warn_child_gt() */ -CHECK_CHILD_DEFN(err, gt, >) /* err_child_gt() */ -CHECK_CHILD_DEFN(warn, eq, ==) /* warn_child_eq() */ -CHECK_CHILD_DEFN(err, eq, ==) /* err_child_eq() */ -CHECK_CHILD_DEFN(err, lt, <) /* err_child_lt() */ -CHECK_CHILD_DEFN(warn, lt, <) /* warn_child_lt() */ -CHECK_BODY_DEFN(ge1, warn, warn_child_gt, 0) /* bwarn_ge1() */ -CHECK_BODY_DEFN(ge1, err, err_child_gt, 0) /* berr_ge1() */ -CHECK_ELEM_DEFN(ge1, warn, warn_child_gt, 0) /* ewarn_ge1() */ -CHECK_ELEM_DEFN(eq1, err, err_child_eq, 1) /* eerr_eq1() */ -CHECK_ELEM_DEFN(le1, err, err_child_lt, 2) /* eerr_le1() */ -CHECK_ELEM_DEFN(eq0, err, err_child_eq, 0) /* eerr_eq0() */ -CHECK_ELEM_DEFN(ge1, err, err_child_gt, 0) /* eerr_ge1() */ -CHECK_HEAD_DEFN(eq0, err, err_child_eq, 0) /* herr_eq0() */ -CHECK_HEAD_DEFN(le1, warn, warn_child_lt, 2) /* hwarn_le1() */ -CHECK_HEAD_DEFN(ge1, err, err_child_gt, 0) /* herr_ge1() */ -CHECK_HEAD_DEFN(eq1, warn, warn_child_eq, 1) /* hwarn_eq1() */ -CHECK_HEAD_DEFN(eq0, warn, warn_child_eq, 0) /* hwarn_eq0() */ - - -static int -check_stdarg(PRE_ARGS) -{ - - if (n->args && 1 == n->args->argc) - if (MDOC_Std == n->args->argv[0].arg) - return(1); - return(mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV)); -} - - -static int -check_args(struct mdoc *m, struct mdoc_node *n) -{ - int i; - - if (NULL == n->args) - return(1); - - assert(n->args->argc); - for (i = 0; i < (int)n->args->argc; i++) - if ( ! check_argv(m, n, &n->args->argv[i])) - return(0); - - return(1); -} - - -static int -check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v) -{ - int i; - - for (i = 0; i < (int)v->sz; i++) - if ( ! check_text(m, v->line, v->pos, v->value[i])) - return(0); - - if (MDOC_Std == v->arg) { - if (v->sz || m->meta.name) - return(1); - if ( ! mdoc_nmsg(m, n, MANDOCERR_NONAME)) - return(0); - } - - return(1); -} - - -static int -check_text(struct mdoc *mdoc, int line, int pos, char *p) -{ - int c; - - for ( ; *p; p++, pos++) { - if ('\t' == *p) { - if ( ! (MDOC_LITERAL & mdoc->flags)) - if ( ! mdoc_pmsg(mdoc, line, pos, MANDOCERR_BADCHAR)) - return(0); - } else if ( ! isprint((u_char)*p) && ASCII_HYPH != *p) - if ( ! mdoc_pmsg(mdoc, line, pos, MANDOCERR_BADCHAR)) - return(0); - - if ('\\' != *p) - continue; - - c = mandoc_special(p); - if (c) { - p += c - 1; - pos += c - 1; - continue; - } - - c = mdoc_pmsg(mdoc, line, pos, MANDOCERR_BADESCAPE); - if ( ! (MDOC_IGN_ESCAPE & mdoc->pflags) && ! c) - return(c); - } - - return(1); -} - - - - -static int -check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t) -{ - - assert(n->parent); - if ((MDOC_ROOT == t || tok == n->parent->tok) && - (t == n->parent->type)) - return(1); - - mdoc_vmsg(mdoc, MANDOCERR_SYNTCHILD, - n->line, n->pos, "want parent %s", - MDOC_ROOT == t ? "" : - mdoc_macronames[tok]); - return(0); -} - - - -static int -pre_display(PRE_ARGS) -{ - struct mdoc_node *node; - - /* Display elements (`Bd', `D1'...) cannot be nested. */ - - if (MDOC_BLOCK != n->type) - return(1); - - /* LINTED */ - for (node = mdoc->last->parent; node; node = node->parent) - if (MDOC_BLOCK == node->type) - if (MDOC_Bd == node->tok) - break; - if (NULL == node) - return(1); - - mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP); - return(0); -} - - -static int -pre_bl(PRE_ARGS) -{ - int i, comp, dup; - const char *offs, *width; - enum mdoc_list lt; - - if (MDOC_BLOCK != n->type) { - assert(n->parent); - assert(MDOC_BLOCK == n->parent->type); - assert(MDOC_Bl == n->parent->tok); - assert(LIST__NONE != n->parent->data.Bl.type); - memcpy(&n->data.Bl, &n->parent->data.Bl, - sizeof(struct mdoc_bl)); - return(1); - } - - /* - * First figure out which kind of list to use: bind ourselves to - * the first mentioned list type and warn about any remaining - * ones. If we find no list type, we default to LIST_item. - */ - - assert(LIST__NONE == n->data.Bl.type); - - /* LINTED */ - for (i = 0; n->args && i < (int)n->args->argc; i++) { - lt = LIST__NONE; - dup = comp = 0; - width = offs = NULL; - switch (n->args->argv[i].arg) { - /* Set list types. */ - case (MDOC_Bullet): - lt = LIST_bullet; - break; - case (MDOC_Dash): - lt = LIST_dash; - break; - case (MDOC_Enum): - lt = LIST_enum; - break; - case (MDOC_Hyphen): - lt = LIST_hyphen; - break; - case (MDOC_Item): - lt = LIST_item; - break; - case (MDOC_Tag): - lt = LIST_tag; - break; - case (MDOC_Diag): - lt = LIST_diag; - break; - case (MDOC_Hang): - lt = LIST_hang; - break; - case (MDOC_Ohang): - lt = LIST_ohang; - break; - case (MDOC_Inset): - lt = LIST_inset; - break; - case (MDOC_Column): - lt = LIST_column; - break; - /* Set list arguments. */ - case (MDOC_Compact): - dup = n->data.Bl.comp; - comp = 1; - break; - case (MDOC_Width): - dup = (NULL != n->data.Bl.width); - width = n->args->argv[i].value[0]; - break; - case (MDOC_Offset): - /* NB: this can be empty! */ - if (n->args->argv[i].sz) { - offs = n->args->argv[i].value[0]; - dup = (NULL != n->data.Bl.offs); - break; - } - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV)) - return(0); - break; - } - - /* Check: duplicate auxiliary arguments. */ - - if (dup && ! mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP)) - return(0); - - if (comp && ! dup) - n->data.Bl.comp = comp; - if (offs && ! dup) - n->data.Bl.offs = offs; - if (width && ! dup) - n->data.Bl.width = width; - - /* Check: multiple list types. */ - - if (LIST__NONE != lt && n->data.Bl.type != LIST__NONE) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP)) - return(0); - - /* Assign list type. */ - - if (LIST__NONE != lt && n->data.Bl.type == LIST__NONE) - n->data.Bl.type = lt; - - /* The list type should come first. */ - - if (n->data.Bl.type == LIST__NONE) - if (n->data.Bl.width || - n->data.Bl.offs || - n->data.Bl.comp) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST)) - return(0); - - continue; - } - - /* Allow lists to default to LIST_item. */ - - if (LIST__NONE == n->data.Bl.type) { - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE)) - return(0); - n->data.Bl.type = LIST_item; - } - - /* - * Validate the width field. Some list types don't need width - * types and should be warned about them. Others should have it - * and must also be warned. - */ - - switch (n->data.Bl.type) { - case (LIST_tag): - if (n->data.Bl.width) - break; - if (mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG)) - break; - return(0); - case (LIST_column): - /* FALLTHROUGH */ - case (LIST_diag): - /* FALLTHROUGH */ - case (LIST_ohang): - /* FALLTHROUGH */ - case (LIST_inset): - /* FALLTHROUGH */ - case (LIST_item): - if (NULL == n->data.Bl.width) - break; - if (mdoc_nmsg(mdoc, n, MANDOCERR_WIDTHARG)) - break; - return(0); - default: - break; - } - - return(1); -} - - -static int -pre_bd(PRE_ARGS) -{ - int i, dup, comp; - enum mdoc_disp dt; - const char *offs; - - if (MDOC_BLOCK != n->type) { - assert(n->parent); - assert(MDOC_BLOCK == n->parent->type); - assert(MDOC_Bd == n->parent->tok); - assert(DISP__NONE != n->parent->data.Bd.type); - memcpy(&n->data.Bd, &n->parent->data.Bd, - sizeof(struct mdoc_bd)); - return(1); - } - - assert(DISP__NONE == n->data.Bd.type); - - /* LINTED */ - for (i = 0; n->args && i < (int)n->args->argc; i++) { - dt = DISP__NONE; - dup = comp = 0; - offs = NULL; - - switch (n->args->argv[i].arg) { - case (MDOC_Centred): - dt = DISP_centred; - break; - case (MDOC_Ragged): - dt = DISP_ragged; - break; - case (MDOC_Unfilled): - dt = DISP_unfilled; - break; - case (MDOC_Filled): - dt = DISP_filled; - break; - case (MDOC_Literal): - dt = DISP_literal; - break; - case (MDOC_File): - mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP); - return(0); - case (MDOC_Offset): - /* NB: this can be empty! */ - if (n->args->argv[i].sz) { - offs = n->args->argv[i].value[0]; - dup = (NULL != n->data.Bd.offs); - break; - } - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV)) - return(0); - break; - case (MDOC_Compact): - comp = 1; - dup = n->data.Bd.comp; - break; - default: - abort(); - /* NOTREACHED */ - } - - /* Check whether we have duplicates. */ - - if (dup && ! mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP)) - return(0); - - /* Make our auxiliary assignments. */ - - if (offs && ! dup) - n->data.Bd.offs = offs; - if (comp && ! dup) - n->data.Bd.comp = comp; - - /* Check whether a type has already been assigned. */ - - if (DISP__NONE != dt && n->data.Bd.type != DISP__NONE) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP)) - return(0); - - /* Make our type assignment. */ - - if (DISP__NONE != dt && n->data.Bd.type == DISP__NONE) - n->data.Bd.type = dt; - } - - if (DISP__NONE == n->data.Bd.type) { - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE)) - return(0); - n->data.Bd.type = DISP_ragged; - } - - return(1); -} - - -static int -pre_ss(PRE_ARGS) -{ - - if (MDOC_BLOCK != n->type) - return(1); - return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY)); -} - - -static int -pre_sh(PRE_ARGS) -{ - - if (MDOC_BLOCK != n->type) - return(1); - return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); -} - - -static int -pre_it(PRE_ARGS) -{ - - if (MDOC_BLOCK != n->type) - return(1); - /* - * FIXME: this can probably be lifted if we make the It into - * something else on-the-fly? - */ - return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY)); -} - - -static int -pre_an(PRE_ARGS) -{ - - if (NULL == n->args || 1 == n->args->argc) - return(1); - mdoc_vmsg(mdoc, MANDOCERR_SYNTARGCOUNT, - n->line, n->pos, - "line arguments == 1 (have %d)", - n->args->argc); - return(0); -} - - -static int -pre_rv(PRE_ARGS) -{ - - return(check_stdarg(mdoc, n)); -} - - -static int -post_dt(POST_ARGS) -{ - const struct mdoc_node *nn; - const char *p; - - if (NULL != (nn = mdoc->last->child)) - for (p = nn->string; *p; p++) { - if (toupper((u_char)*p) == *p) - continue; - if ( ! mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE)) - return(0); - break; - } - - return(1); -} - - -static int -pre_dt(PRE_ARGS) -{ - - if (0 == mdoc->meta.date || mdoc->meta.os) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO)) - return(0); - if (mdoc->meta.title) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP)) - return(0); - return(1); -} - - -static int -pre_os(PRE_ARGS) -{ - - if (NULL == mdoc->meta.title || 0 == mdoc->meta.date) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO)) - return(0); - if (mdoc->meta.os) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP)) - return(0); - return(1); -} - - -static int -pre_dd(PRE_ARGS) -{ - - if (mdoc->meta.title || mdoc->meta.os) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO)) - return(0); - if (mdoc->meta.date) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP)) - return(0); - return(1); -} - - -static int -post_bf(POST_ARGS) -{ - char *p; - struct mdoc_node *head; - - if (MDOC_BLOCK != mdoc->last->type) - return(1); - - head = mdoc->last->head; - - if (mdoc->last->args && head->child) { - /* FIXME: this should provide a default. */ - mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SYNTARGVCOUNT); - return(0); - } else if (mdoc->last->args) - return(1); - - if (NULL == head->child || MDOC_TEXT != head->child->type) { - /* FIXME: this should provide a default. */ - mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SYNTARGVCOUNT); - return(0); - } - - p = head->child->string; - - if (0 == strcmp(p, "Em")) - return(1); - else if (0 == strcmp(p, "Li")) - return(1); - else if (0 == strcmp(p, "Sy")) - return(1); - - mdoc_nmsg(mdoc, head, MANDOCERR_FONTTYPE); - return(0); -} - - -static int -post_lb(POST_ARGS) -{ - - if (mdoc_a2lib(mdoc->last->child->string)) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADLIB)); -} - - -static int -post_eoln(POST_ARGS) -{ - - if (NULL == mdoc->last->child) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST)); -} - - -static int -post_vt(POST_ARGS) -{ - const struct mdoc_node *n; - - /* - * The Vt macro comes in both ELEM and BLOCK form, both of which - * have different syntaxes (yet more context-sensitive - * behaviour). ELEM types must have a child; BLOCK types, - * specifically the BODY, should only have TEXT children. - */ - - if (MDOC_ELEM == mdoc->last->type) - return(eerr_ge1(mdoc)); - if (MDOC_BODY != mdoc->last->type) - return(1); - - for (n = mdoc->last->child; n; n = n->next) - if (MDOC_TEXT != n->type) - if ( ! mdoc_nmsg(mdoc, n, MANDOCERR_CHILD)) - return(0); - - return(1); -} - - -static int -post_nm(POST_ARGS) -{ - - if (mdoc->last->child) - return(1); - if (mdoc->meta.name) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME)); -} - - -static int -post_at(POST_ARGS) -{ - - if (NULL == mdoc->last->child) - return(1); - assert(MDOC_TEXT == mdoc->last->child->type); - if (mdoc_a2att(mdoc->last->child->string)) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT)); -} - - -static int -post_an(POST_ARGS) -{ - - if (mdoc->last->args) { - if (NULL == mdoc->last->child) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGCOUNT)); - } - - if (mdoc->last->child) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS)); -} - - -static int -post_it(POST_ARGS) -{ - int i, cols, rc; - enum mdoc_list lt; - struct mdoc_node *n, *c; - enum mandocerr er; - - if (MDOC_BLOCK != mdoc->last->type) - return(1); - - n = mdoc->last->parent->parent; - lt = n->data.Bl.type; - - if (LIST__NONE == lt) { - mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE); - return(0); - } - - switch (lt) { - case (LIST_tag): - if (mdoc->last->head->child) - break; - /* FIXME: give this a dummy value. */ - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS)) - return(0); - break; - case (LIST_hang): - /* FALLTHROUGH */ - case (LIST_ohang): - /* FALLTHROUGH */ - case (LIST_inset): - /* FALLTHROUGH */ - case (LIST_diag): - if (NULL == mdoc->last->head->child) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS)) - return(0); - if (NULL == mdoc->last->body->child) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY)) - return(0); - break; - case (LIST_bullet): - /* FALLTHROUGH */ - case (LIST_dash): - /* FALLTHROUGH */ - case (LIST_enum): - /* FALLTHROUGH */ - case (LIST_hyphen): - /* FALLTHROUGH */ - case (LIST_item): - if (mdoc->last->head->child) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST)) - return(0); - if (NULL == mdoc->last->body->child) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY)) - return(0); - break; - case (LIST_column): - cols = -1; - for (i = 0; i < (int)n->args->argc; i++) - if (MDOC_Column == n->args->argv[i].arg) { - cols = (int)n->args->argv[i].sz; - break; - } - - assert(-1 != cols); - assert(NULL == mdoc->last->head->child); - - if (NULL == mdoc->last->body->child) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY)) - return(0); - - for (i = 0, c = mdoc->last->child; c; c = c->next) - if (MDOC_BODY == c->type) - i++; - - if (i < cols) - er = MANDOCERR_ARGCOUNT; - else if (i == cols || i == cols + 1) - break; - else - er = MANDOCERR_SYNTARGCOUNT; - - rc = mdoc_vmsg(mdoc, er, - mdoc->last->line, mdoc->last->pos, - "columns == %d (have %d)", cols, i); - return(rc); - default: - break; - } - - return(1); -} - - -static int -post_bl_head(POST_ARGS) -{ - int i; - struct mdoc_node *n; - - assert(mdoc->last->parent); - n = mdoc->last->parent; - - if (LIST_column == n->data.Bl.type) { - for (i = 0; i < (int)n->args->argc; i++) - if (MDOC_Column == n->args->argv[i].arg) - break; - assert(i < (int)n->args->argc); - - if (n->args->argv[i].sz && mdoc->last->nchild) { - mdoc_nmsg(mdoc, n, MANDOCERR_COLUMNS); - return(0); - } - return(1); - } - - if (0 == (i = mdoc->last->nchild)) - return(1); - return(warn_count(mdoc, "==", 0, "line arguments", i)); -} - - -static int -post_bl(POST_ARGS) -{ - struct mdoc_node *n; - - if (MDOC_HEAD == mdoc->last->type) - return(post_bl_head(mdoc)); - if (MDOC_BODY != mdoc->last->type) - return(1); - if (NULL == mdoc->last->child) - return(1); - - /* - * We only allow certain children of `Bl'. This is usually on - * `It', but apparently `Sm' occurs here and there, so we let - * that one through, too. - */ - - /* LINTED */ - for (n = mdoc->last->child; n; n = n->next) { - if (MDOC_BLOCK == n->type && MDOC_It == n->tok) - continue; - if (MDOC_Sm == n->tok) - continue; - mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD); - return(0); - } - - return(1); -} - - -static int -ebool(struct mdoc *mdoc) -{ - struct mdoc_node *n; - - /* LINTED */ - for (n = mdoc->last->child; n; n = n->next) { - if (MDOC_TEXT != n->type) - break; - if (0 == strcmp(n->string, "on")) - continue; - if (0 == strcmp(n->string, "off")) - continue; - break; - } - - if (NULL == n) - return(1); - return(mdoc_nmsg(mdoc, n, MANDOCERR_BADBOOL)); -} - - -static int -post_root(POST_ARGS) -{ - - if (NULL == mdoc->first->child) - mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCBODY); - else if ( ! (MDOC_PBODY & mdoc->flags)) - mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG); - else if (MDOC_BLOCK != mdoc->first->child->type) - mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCBODY); - else if (MDOC_Sh != mdoc->first->child->tok) - mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCBODY); - else - return(1); - - return(0); -} - - -static int -post_st(POST_ARGS) -{ - - if (mdoc_a2st(mdoc->last->child->string)) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD)); -} - - -static int -post_rs(POST_ARGS) -{ - struct mdoc_node *nn; - - if (MDOC_BODY != mdoc->last->type) - return(1); - - for (nn = mdoc->last->child; nn; nn = nn->next) - switch (nn->tok) { - case(MDOC__U): - /* FALLTHROUGH */ - case(MDOC__Q): - /* FALLTHROUGH */ - case(MDOC__C): - /* FALLTHROUGH */ - case(MDOC__A): - /* FALLTHROUGH */ - case(MDOC__B): - /* FALLTHROUGH */ - case(MDOC__D): - /* FALLTHROUGH */ - case(MDOC__I): - /* FALLTHROUGH */ - case(MDOC__J): - /* FALLTHROUGH */ - case(MDOC__N): - /* FALLTHROUGH */ - case(MDOC__O): - /* FALLTHROUGH */ - case(MDOC__P): - /* FALLTHROUGH */ - case(MDOC__R): - /* FALLTHROUGH */ - case(MDOC__T): - /* FALLTHROUGH */ - case(MDOC__V): - break; - default: - mdoc_nmsg(mdoc, nn, MANDOCERR_SYNTCHILD); - return(0); - } - - return(1); -} - - -static int -post_sh(POST_ARGS) -{ - - if (MDOC_HEAD == mdoc->last->type) - return(post_sh_head(mdoc)); - if (MDOC_BODY == mdoc->last->type) - return(post_sh_body(mdoc)); - - return(1); -} - - -static int -post_sh_body(POST_ARGS) -{ - struct mdoc_node *n; - - if (SEC_NAME != mdoc->lastsec) - return(1); - - /* - * Warn if the NAME section doesn't contain the `Nm' and `Nd' - * macros (can have multiple `Nm' and one `Nd'). Note that the - * children of the BODY declaration can also be "text". - */ - - if (NULL == (n = mdoc->last->child)) - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC)); - - for ( ; n && n->next; n = n->next) { - if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) - continue; - if (MDOC_TEXT == n->type) - continue; - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC)) - return(0); - } - - assert(n); - if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) - return(1); - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC)); -} - - -static int -post_sh_head(POST_ARGS) -{ - char buf[BUFSIZ]; - enum mdoc_sec sec; - const struct mdoc_node *n; - - /* - * Process a new section. Sections are either "named" or - * "custom"; custom sections are user-defined, while named ones - * usually follow a conventional order and may only appear in - * certain manual sections. - */ - - buf[0] = '\0'; - - /* - * FIXME: yes, these can use a dynamic buffer, but I don't do so - * in the interests of simplicity. - */ - - for (n = mdoc->last->child; n; n = n->next) { - /* XXX - copied from compact(). */ - assert(MDOC_TEXT == n->type); - - if (strlcat(buf, n->string, BUFSIZ) >= BUFSIZ) { - mdoc_nmsg(mdoc, n, MANDOCERR_MEM); - return(0); - } - if (NULL == n->next) - continue; - if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) { - mdoc_nmsg(mdoc, n, MANDOCERR_MEM); - return(0); - } - } - - sec = mdoc_str2sec(buf); - - /* - * Check: NAME should always be first, CUSTOM has no roles, - * non-CUSTOM has a conventional order to be followed. - */ - - if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST)) - return(0); - - if (SEC_CUSTOM == sec) - return(1); - - if (sec == mdoc->lastnamed) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP)) - return(0); - - if (sec < mdoc->lastnamed) - if ( ! mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO)) - return(0); - - /* - * Check particular section/manual conventions. LIBRARY can - * only occur in manual section 2, 3, and 9. - */ - - switch (sec) { - case (SEC_LIBRARY): - assert(mdoc->meta.msec); - if (*mdoc->meta.msec == '2') - break; - if (*mdoc->meta.msec == '3') - break; - if (*mdoc->meta.msec == '9') - break; - return(mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC)); - default: - break; - } - - return(1); -} diff --git a/commands/mdocml/out.c b/commands/mdocml/out.c deleted file mode 100644 index 6a26f95a3..000000000 --- a/commands/mdocml/out.c +++ /dev/null @@ -1,399 +0,0 @@ -/* $Id: out.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2009 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 "out.h" - -/* See a2roffdeco(). */ -#define C2LIM(c, l) do { \ - (l) = 1; \ - if ('[' == (c) || '\'' == (c)) \ - (l) = 0; \ - else if ('(' == (c)) \ - (l) = 2; } \ - while (/* CONSTCOND */ 0) - -/* See a2roffdeco(). */ -#define C2TERM(c, t) do { \ - (t) = 0; \ - if ('\'' == (c)) \ - (t) = 1; \ - else if ('[' == (c)) \ - (t) = 2; \ - else if ('(' == (c)) \ - (t) = 3; } \ - while (/* CONSTCOND */ 0) - -/* - * Convert a `scaling unit' to a consistent form, or fail. Scaling - * units are documented in groff.7, mdoc.7, man.7. - */ -int -a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) -{ - char buf[BUFSIZ], hasd; - int i; - enum roffscale unit; - - if ('\0' == *src) - return(0); - - i = hasd = 0; - - switch (*src) { - case ('+'): - src++; - break; - case ('-'): - buf[i++] = *src++; - break; - default: - break; - } - - if ('\0' == *src) - return(0); - - while (i < BUFSIZ) { - if ( ! isdigit((u_char)*src)) { - if ('.' != *src) - break; - else if (hasd) - break; - else - hasd = 1; - } - buf[i++] = *src++; - } - - if (BUFSIZ == i || (*src && *(src + 1))) - return(0); - - buf[i] = '\0'; - - switch (*src) { - case ('c'): - unit = SCALE_CM; - break; - case ('i'): - unit = SCALE_IN; - break; - case ('P'): - unit = SCALE_PC; - break; - case ('p'): - unit = SCALE_PT; - break; - case ('f'): - unit = SCALE_FS; - break; - case ('v'): - unit = SCALE_VS; - break; - case ('m'): - unit = SCALE_EM; - break; - case ('\0'): - if (SCALE_MAX == def) - return(0); - unit = SCALE_BU; - break; - case ('u'): - unit = SCALE_BU; - break; - case ('M'): - unit = SCALE_MM; - break; - case ('n'): - unit = SCALE_EN; - break; - default: - return(0); - } - - if ((dst->scale = atof(buf)) < 0) - dst->scale = 0; - dst->unit = unit; - dst->pt = hasd; - - 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); -} - - -/* - * Returns length of parsed string (the leading "\" should NOT be - * included). This can be zero if the current character is the nil - * terminator. "d" is set to the type of parsed decorator, which may - * have an adjoining "word" of size "sz" (e.g., "(ab" -> "ab", 2). - */ -int -a2roffdeco(enum roffdeco *d, - const char **word, size_t *sz) -{ - int j, term, lim; - char set; - const char *wp, *sp; - - *d = DECO_NONE; - wp = *word; - - switch ((set = *wp)) { - case ('\0'): - return(0); - - case ('('): - if ('\0' == *(++wp)) - return(1); - if ('\0' == *(wp + 1)) - return(2); - - *d = DECO_SPECIAL; - *sz = 2; - *word = wp; - return(3); - - case ('F'): - /* FALLTHROUGH */ - case ('f'): - /* - * FIXME: this needs work and consolidation (it should - * follow the sequence that special characters do, for - * one), but isn't a priority at the moment. Note, for - * one, that in reality \fB != \FB, although here we let - * these slip by. - */ - switch (*(++wp)) { - case ('\0'): - return(1); - case ('3'): - /* FALLTHROUGH */ - case ('B'): - *d = DECO_BOLD; - return(2); - case ('2'): - /* FALLTHROUGH */ - case ('I'): - *d = DECO_ITALIC; - return(2); - case ('P'): - *d = DECO_PREVIOUS; - return(2); - case ('1'): - /* FALLTHROUGH */ - case ('R'): - *d = DECO_ROMAN; - return(2); - case ('('): - if ('\0' == *(++wp)) - return(2); - if ('\0' == *(wp + 1)) - return(3); - - *d = 'F' == set ? DECO_FFONT : DECO_FONT; - *sz = 2; - *word = wp; - return(4); - case ('['): - *word = ++wp; - for (j = 0; *wp && ']' != *wp; wp++, j++) - /* Loop... */ ; - - if ('\0' == *wp) - return(j + 2); - - *d = 'F' == set ? DECO_FFONT : DECO_FONT; - *sz = (size_t)j; - return(j + 3); - default: - break; - } - - *d = 'F' == set ? DECO_FFONT : DECO_FONT; - *sz = 1; - *word = wp; - return(2); - - case ('*'): - switch (*(++wp)) { - case ('\0'): - return(1); - - case ('('): - if ('\0' == *(++wp)) - return(2); - if ('\0' == *(wp + 1)) - return(3); - - *d = DECO_RESERVED; - *sz = 2; - *word = wp; - return(4); - - case ('['): - *word = ++wp; - for (j = 0; *wp && ']' != *wp; wp++, j++) - /* Loop... */ ; - - if ('\0' == *wp) - return(j + 2); - - *d = DECO_RESERVED; - *sz = (size_t)j; - return(j + 3); - - default: - break; - } - - *d = DECO_RESERVED; - *sz = 1; - *word = wp; - return(2); - - case ('s'): - sp = wp; - if ('\0' == *(++wp)) - return(1); - - C2LIM(*wp, lim); - C2TERM(*wp, term); - - if (term) - wp++; - - *word = wp; - - if (*wp == '+' || *wp == '-') - ++wp; - - switch (*wp) { - case ('\''): - /* FALLTHROUGH */ - case ('['): - /* FALLTHROUGH */ - case ('('): - if (term) - return((int)(wp - sp)); - - C2LIM(*wp, lim); - C2TERM(*wp, term); - wp++; - break; - default: - break; - } - - if ( ! isdigit((u_char)*wp)) - return((int)(wp - sp)); - - for (j = 0; isdigit((u_char)*wp); j++) { - if (lim && j >= lim) - break; - ++wp; - } - - if (term && term < 3) { - if (1 == term && *wp != '\'') - return((int)(wp - sp)); - if (2 == term && *wp != ']') - return((int)(wp - sp)); - ++wp; - } - - *d = DECO_SIZE; - return((int)(wp - sp)); - - case ('['): - *word = ++wp; - - for (j = 0; *wp && ']' != *wp; wp++, j++) - /* Loop... */ ; - - if ('\0' == *wp) - return(j + 1); - - *d = DECO_SPECIAL; - *sz = (size_t)j; - return(j + 2); - - case ('c'): - *d = DECO_NOSPACE; - *sz = 1; - return(1); - - default: - break; - } - - *d = DECO_SPECIAL; - *word = wp; - *sz = 1; - return(1); -} diff --git a/commands/mdocml/roff.7 b/commands/mdocml/roff.7 deleted file mode 100644 index 12ccfc353..000000000 --- a/commands/mdocml/roff.7 +++ /dev/null @@ -1,304 +0,0 @@ -.\" $Id: roff.7,v 1.9 2010/06/10 21:42:02 kristaps Exp $ -.\" -.\" Copyright (c) 2010 Kristaps Dzonsons -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: June 10 2010 $ -.Dt ROFF 7 -.Os -.Sh NAME -.Nm roff -.Nd roff language reference -.Sh DESCRIPTION -The -.Nm roff -language is a general-purpose text-formatting language. The purpose of -this document is to consistently describe those language constructs -accepted by the -.Xr mandoc 1 -utility. It is a work in progress. -.Pp -An -.Nm -document follows simple rules: lines beginning with the control -characters -.Sq \. -or -.Sq \(aq -are parsed for macros. Other lines are interpreted within the scope of -prior macros: -.Bd -literal -offset indent -\&.xx Macro lines change control state. -Other 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. All -manuals must have -.Ux -line terminators. -.Sh MACRO SYNTAX -Macros are arbitrary in length and begin with a control character , -.Sq \. -or -.Sq \(aq , -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 -\&.if -\&.\ \ \ \&if -.Ed -.Sh REFERENCE -This section is a canonical reference of all macros, arranged -alphabetically. -.Ss \&am -The syntax of this macro is the same as that of -.Sx \&ig , -except that a leading argument must be specified. -It is ignored, as are its children. -.Ss \&ami -The syntax of this macro is the same as that of -.Sx \&ig , -except that a leading argument must be specified. -It is ignored, as are its children. -.Ss \&am1 -The syntax of this macro is the same as that of -.Sx \&ig , -except that a leading argument must be specified. -It is ignored, as are its children. -.Ss \&de -The syntax of this macro is the same as that of -.Sx \&ig , -except that a leading argument must be specified. -It is ignored, as are its children. -.Ss \&dei -The syntax of this macro is the same as that of -.Sx \&ig , -except that a leading argument must be specified. -It is ignored, as are its children. -.Ss \&ds -Define a string. -This macro is intended to have two arguments, -the name of the string to define and its content. -Currently, it is ignored including its arguments, -and the number of arguments is not checked. -.Ss \&de1 -The syntax of this macro is the same as that of -.Sx \&ig , -except that a leading argument must be specified. -It is ignored, as are its children. -.Ss \&el -The -.Qq else -half of an if/else conditional. -Pops a result off the stack of conditional evaluations pushed by -.Sx \&ie -and uses it as its conditional. -If no stack entries are present (e.g., due to no prior -.Sx \&ie -calls) -then false is assumed. -The syntax of this macro is similar to -.Sx \&if -except that the conditional is missing. -.Ss \&ie -The -.Qq if -half of an if/else conditional. -The result of the conditional is pushed into a stack used by subsequent -invocations of -.Sx \&el , -which may be separated by any intervening input (or not exist at all). -Its syntax is equivalent to -.Sx \&if . -.Ss \&if -Begins a conditional. -Right now, the conditional evaluates to true -if and only if it starts with the letter -.Sy n , -indicating processing in -.Xr nroff 1 -style as opposed to -.Xr troff 1 -style. -If a conditional is false, its children are not processed, but are -syntactically interpreted to preserve the integrity of the input -document. -Thus, -.Pp -.D1 \&.if t \e .ig -.Pp -will discard the -.Sq \&.ig , -which may lead to interesting results, but -.Pp -.D1 \&.if t \e .if t \e{\e -.Pp -will continue to syntactically interpret to the block close of the final -conditional. -Sub-conditionals, in this case, obviously inherit the truth value of -the parent. -This macro has the following syntax: -.Pp -.Bd -literal -offset indent -compact -\&.if COND \e{\e -BODY... -\&.\e} -.Ed -.Bd -literal -offset indent -compact -\&.if COND \e{ BODY -BODY... \e} -.Ed -.Bd -literal -offset indent -compact -\&.if COND \e{ BODY -BODY... -\&.\e} -.Ed -.Bd -literal -offset indent -compact -\&.if COND \e -BODY -.Ed -.Pp -COND is a conditional statement. -roff allows for complicated conditionals; mandoc is much simpler. -At this time, mandoc supports only -.Sq n , -evaluating to true; -and -.Sq t , -.Sq e , -and -.Sq o , -evaluating to false. -All other invocations are read up to the next end of line or space and -evaluate as false. -.Pp -If the BODY section is begun by an escaped brace -.Sq \e{ , -scope continues until a closing-brace macro -.Sq \.\e} . -If the BODY is not enclosed in braces, scope continues until the next -macro or word. -If the COND is followed by a BODY on the same line, whether after a -brace or not, then macros -.Em must -begin with a control character. -It is generally more intuitive, in this case, to write -.Bd -literal -offset indent -\&.if COND \e{\e -\&.foo -bar -\&.\e} -.Ed -.Pp -than having the macro follow as -.Pp -.D1 \&.if COND \e{ .foo -.Pp -The scope of a conditional is always parsed, but only executed if the -conditional evaluates to true. -.Pp -Note that text subsequent a -.Sq \&.\e} -macro is discarded. -Furthermore, if an explicit closing sequence -.Sq \e} -is specified in a free-form line, the entire line is accepted within the -scope of the prior macro, not only the text preceding the close, with the -.Sq \e} -collapsing into a zero-width space. -.Ss \&ig -Ignore input. -Accepts the following syntax: -.Pp -.Bd -literal -offset indent -compact -\&.ig -BODY... -\&.. -.Ed -.Bd -literal -offset indent -compact -\&.ig END -BODY... -\&.END -.Ed -.Pp -In the first case, input is ignored until a -.Sq \&.. -macro is encountered on its own line. -In the second case, input is ignored until a -.Sq \&.END -is encountered. -Text subsequent the -.Sq \&.END -or -.Sq \&.. -is discarded. -.Pp -Do not use the escape -.Sq \e -anywhere in the definition of END. -It causes very strange behaviour. -Furthermore, if you redefine a -.Nm -macro, such as -.Pp -.D1 \&.ig if -.Pp -the subsequent invocation of -.Sx \&if -will first signify the end of comment, then be invoked as a macro. -This behaviour really shouldn't be counted upon. -.Ss \&rm -Remove a request, macro or string. -This macro is intended to have one argument, -the name of the request, macro or string to be undefined. -Currently, it is ignored including its arguments, -and the number of arguments is not checked. -.Ss \&tr -Output character translation. -This macro 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. -.Sh COMPATIBILITY -This section documents compatibility between mandoc and other other -troff implementations, at this time limited to GNU troff -.Pq Qq groff . -The term -.Qq historic groff -refers to groff versions before the -.Pa doc.tmac -file re-write -.Pq somewhere between 1.15 and 1.19 . -.Pp -.Bl -dash -compact -.It -Historic groff did not accept white-space buffering the custom END tag -for the -.Sx \&ig -macro. -.It -The -.Sx \&if -and family would print funny white-spaces with historic groff when -depending on next-line syntax. -.El -.Sh AUTHORS -The -.Nm -reference was written by -.An Kristaps Dzonsons Aq kristaps@bsd.lv . diff --git a/commands/mdocml/roff.c b/commands/mdocml/roff.c deleted file mode 100644 index a22697d1c..000000000 --- a/commands/mdocml/roff.c +++ /dev/null @@ -1,857 +0,0 @@ -/* $Id: roff.c,v 1.88 2010/06/10 21:42:02 kristaps Exp $ */ -/* - * Copyright (c) 2010 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 "mandoc.h" -#include "roff.h" - -#define RSTACK_MAX 128 - -#define ROFF_CTL(c) \ - ('.' == (c) || '\'' == (c)) - -#ifndef __minix -#if 1 -#define ROFF_DEBUG(fmt, args...) \ - do { /* Nothing. */ } while (/*CONSTCOND*/ 0) -#else -#define ROFF_DEBUG(fmt, args...) \ - do { fprintf(stderr, fmt , ##args); } while (/*CONSTCOND*/ 0) -#endif -#else -void do_nothing(char *fmt, ...) -{ -} -#if 1 -#define ROFF_DEBUG do_nothing -#else -#define ROFF_DEBUG fprintf -#endif -#endif - -enum rofft { - ROFF_am, - ROFF_ami, - ROFF_am1, - ROFF_de, - ROFF_dei, - ROFF_de1, - ROFF_ds, - ROFF_el, - ROFF_ie, - ROFF_if, - ROFF_ig, - ROFF_rm, - ROFF_tr, - ROFF_cblock, - ROFF_ccond, - ROFF_MAX -}; - -enum roffrule { - ROFFRULE_ALLOW, - ROFFRULE_DENY -}; - -struct roff { - struct roffnode *last; /* leaf of stack */ - mandocmsg msg; /* err/warn/fatal messages */ - void *data; /* privdata for messages */ - enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ - int rstackpos; /* position in rstack */ -}; - -struct roffnode { - enum rofft tok; /* type of node */ - struct roffnode *parent; /* up one in stack */ - int line; /* parse line */ - int col; /* parse col */ - char *end; /* end-rules: custom token */ - int endspan; /* end-rules: next-line or infty */ - enum roffrule rule; /* current evaluation rule */ -}; - -#define ROFF_ARGS struct roff *r, /* parse ctx */ \ - enum rofft tok, /* tok of macro */ \ - char **bufp, /* input buffer */ \ - size_t *szp, /* size of input buffer */ \ - int ln, /* parse line */ \ - int ppos, /* original pos in buffer */ \ - int pos, /* current pos in buffer */ \ - int *offs /* reset offset of buffer data */ - -typedef enum rofferr (*roffproc)(ROFF_ARGS); - -struct roffmac { - const char *name; /* macro name */ - roffproc proc; /* process new macro */ - roffproc text; /* process as child text of macro */ - roffproc sub; /* process as child of macro */ - int flags; -#define ROFFMAC_STRUCT (1 << 0) /* always interpret */ - struct roffmac *next; -}; - -static enum rofferr roff_block(ROFF_ARGS); -static enum rofferr roff_block_text(ROFF_ARGS); -static enum rofferr roff_block_sub(ROFF_ARGS); -static enum rofferr roff_cblock(ROFF_ARGS); -static enum rofferr roff_ccond(ROFF_ARGS); -static enum rofferr roff_cond(ROFF_ARGS); -static enum rofferr roff_cond_text(ROFF_ARGS); -static enum rofferr roff_cond_sub(ROFF_ARGS); -static enum roffrule roff_evalcond(const char *, int *); -static enum rofferr roff_line(ROFF_ARGS); - -/* See roff_hash_find() */ - -#define ASCII_HI 126 -#define ASCII_LO 33 -#define HASHWIDTH (ASCII_HI - ASCII_LO + 1) - -static struct roffmac *hash[HASHWIDTH]; - -static struct roffmac roffs[ROFF_MAX] = { - { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "ds", roff_line, NULL, NULL, 0, NULL }, - { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, - { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, - { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, - { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, - { "rm", roff_line, NULL, NULL, 0, NULL }, - { "tr", roff_line, NULL, NULL, 0, NULL }, - { ".", roff_cblock, NULL, NULL, 0, NULL }, - { "\\}", roff_ccond, NULL, NULL, 0, NULL }, -}; - -static void roff_free1(struct roff *); -static enum rofft roff_hash_find(const char *); -static void roff_hash_init(void); -static void roffnode_cleanscope(struct roff *); -static int roffnode_push(struct roff *, - enum rofft, int, int); -static void roffnode_pop(struct roff *); -static enum rofft roff_parse(const char *, int *); - -/* See roff_hash_find() */ -#define ROFF_HASH(p) (p[0] - ASCII_LO) - -static void -roff_hash_init(void) -{ - struct roffmac *n; - int buc, i; - - for (i = 0; i < (int)ROFF_MAX; i++) { - assert(roffs[i].name[0] >= ASCII_LO); - assert(roffs[i].name[0] <= ASCII_HI); - - buc = ROFF_HASH(roffs[i].name); - - if (NULL != (n = hash[buc])) { - for ( ; n->next; n = n->next) - /* Do nothing. */ ; - n->next = &roffs[i]; - } else - hash[buc] = &roffs[i]; - } -} - - -/* - * Look up a roff token by its name. Returns ROFF_MAX if no macro by - * the nil-terminated string name could be found. - */ -static enum rofft -roff_hash_find(const char *p) -{ - int buc; - struct roffmac *n; - - /* - * libroff has an extremely simple hashtable, for the time - * being, which simply keys on the first character, which must - * be printable, then walks a chain. It works well enough until - * optimised. - */ - - if (p[0] < ASCII_LO || p[0] > ASCII_HI) - return(ROFF_MAX); - - buc = ROFF_HASH(p); - - if (NULL == (n = hash[buc])) - return(ROFF_MAX); - for ( ; n; n = n->next) - if (0 == strcmp(n->name, p)) - return((enum rofft)(n - roffs)); - - return(ROFF_MAX); -} - - -/* - * Pop the current node off of the stack of roff instructions currently - * pending. - */ -static void -roffnode_pop(struct roff *r) -{ - struct roffnode *p; - - assert(r->last); - p = r->last; - - if (ROFF_el == p->tok) - if (r->rstackpos > -1) - r->rstackpos--; - - r->last = r->last->parent; - if (p->end) - free(p->end); - free(p); -} - - -/* - * Push a roff node onto the instruction stack. This must later be - * removed with roffnode_pop(). - */ -static int -roffnode_push(struct roff *r, enum rofft tok, int line, int col) -{ - struct roffnode *p; - - if (NULL == (p = calloc(1, sizeof(struct roffnode)))) { - (*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL); - return(0); - } - - p->tok = tok; - p->parent = r->last; - p->line = line; - p->col = col; - p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY; - - r->last = p; - return(1); -} - - -static void -roff_free1(struct roff *r) -{ - - while (r->last) - roffnode_pop(r); -} - - -void -roff_reset(struct roff *r) -{ - - roff_free1(r); -} - - -void -roff_free(struct roff *r) -{ - - roff_free1(r); - free(r); -} - - -struct roff * -roff_alloc(const mandocmsg msg, void *data) -{ - struct roff *r; - - if (NULL == (r = calloc(1, sizeof(struct roff)))) { - (*msg)(MANDOCERR_MEM, data, 0, 0, NULL); - return(0); - } - - r->msg = msg; - r->data = data; - r->rstackpos = -1; - - roff_hash_init(); - return(r); -} - - -enum rofferr -roff_parseln(struct roff *r, int ln, - char **bufp, size_t *szp, int pos, int *offs) -{ - enum rofft t; - int ppos; - - /* - * First, if a scope is open and we're not a macro, pass the - * text through the macro's filter. If a scope isn't open and - * we're not a macro, just let it through. - */ - - if (r->last && ! ROFF_CTL((*bufp)[pos])) { - t = r->last->tok; - assert(roffs[t].text); - - ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n", - roffs[t].name, &(*bufp)[pos]); - return((*roffs[t].text) - (r, t, bufp, szp, ln, pos, pos, offs)); - } else if ( ! ROFF_CTL((*bufp)[pos])) { - ROFF_DEBUG("roff: pass non-scoped text: [%s]\n", - &(*bufp)[pos]); - return(ROFF_CONT); - } - - /* - * If a scope is open, go to the child handler for that macro, - * as it may want to preprocess before doing anything with it. - */ - - if (r->last) { - t = r->last->tok; - assert(roffs[t].sub); - ROFF_DEBUG("roff: intercept scoped context: %s\n", - roffs[t].name); - return((*roffs[t].sub) - (r, t, bufp, szp, ln, pos, pos, offs)); - } - - /* - * Lastly, as we've no scope open, try to look up and execute - * the new macro. If no macro is found, simply return and let - * the compilers handle it. - */ - - ppos = pos; - if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) { - ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n", - &(*bufp)[pos]); - return(ROFF_CONT); - } - - ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n", - roffs[t].name, &(*bufp)[pos]); - assert(roffs[t].proc); - return((*roffs[t].proc) - (r, t, bufp, szp, ln, ppos, pos, offs)); -} - - -int -roff_endparse(struct roff *r) -{ - - if (NULL == r->last) - return(1); - return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line, - r->last->col, NULL)); -} - - -/* - * Parse a roff node's type from the input buffer. This must be in the - * form of ".foo xxx" in the usual way. - */ -static enum rofft -roff_parse(const char *buf, int *pos) -{ - int j; - char mac[5]; - enum rofft t; - - assert(ROFF_CTL(buf[*pos])); - (*pos)++; - - while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos])) - (*pos)++; - - if ('\0' == buf[*pos]) - return(ROFF_MAX); - - for (j = 0; j < 4; j++, (*pos)++) - if ('\0' == (mac[j] = buf[*pos])) - break; - else if (' ' == buf[*pos] || (j && '\\' == buf[*pos])) - break; - - if (j == 4 || j < 1) - return(ROFF_MAX); - - mac[j] = '\0'; - - if (ROFF_MAX == (t = roff_hash_find(mac))) - return(t); - - while (buf[*pos] && ' ' == buf[*pos]) - (*pos)++; - - return(t); -} - - -/* ARGSUSED */ -static enum rofferr -roff_cblock(ROFF_ARGS) -{ - - /* - * A block-close `..' should only be invoked as a child of an - * ignore macro, otherwise raise a warning and just ignore it. - */ - - if (NULL == r->last) { - if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL)) - return(ROFF_ERR); - return(ROFF_IGN); - } - - switch (r->last->tok) { - case (ROFF_am): - /* FALLTHROUGH */ - case (ROFF_ami): - /* FALLTHROUGH */ - case (ROFF_am1): - /* FALLTHROUGH */ - case (ROFF_de): - /* FALLTHROUGH */ - case (ROFF_dei): - /* FALLTHROUGH */ - case (ROFF_de1): - /* FALLTHROUGH */ - case (ROFF_ig): - break; - default: - if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL)) - return(ROFF_ERR); - return(ROFF_IGN); - } - - if ((*bufp)[pos]) - if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL)) - return(ROFF_ERR); - - roffnode_pop(r); - roffnode_cleanscope(r); - return(ROFF_IGN); - -} - - -static void -roffnode_cleanscope(struct roff *r) -{ - - while (r->last) { - if (--r->last->endspan < 0) - break; - roffnode_pop(r); - } -} - - -/* ARGSUSED */ -static enum rofferr -roff_ccond(ROFF_ARGS) -{ - - if (NULL == r->last) { - if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL)) - return(ROFF_ERR); - return(ROFF_IGN); - } - - switch (r->last->tok) { - case (ROFF_el): - /* FALLTHROUGH */ - case (ROFF_ie): - /* FALLTHROUGH */ - case (ROFF_if): - break; - default: - if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL)) - return(ROFF_ERR); - return(ROFF_IGN); - } - - if (r->last->endspan > -1) { - if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL)) - return(ROFF_ERR); - return(ROFF_IGN); - } - - if ((*bufp)[pos]) - if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL)) - return(ROFF_ERR); - - roffnode_pop(r); - roffnode_cleanscope(r); - return(ROFF_IGN); -} - - -/* ARGSUSED */ -static enum rofferr -roff_block(ROFF_ARGS) -{ - int sv; - size_t sz; - - if (ROFF_ig != tok && '\0' == (*bufp)[pos]) { - if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL)) - return(ROFF_ERR); - return(ROFF_IGN); - } else if (ROFF_ig != tok) { - while ((*bufp)[pos] && ' ' != (*bufp)[pos]) - pos++; - while (' ' == (*bufp)[pos]) - pos++; - } - - if ( ! roffnode_push(r, tok, ln, ppos)) - return(ROFF_ERR); - - if ('\0' == (*bufp)[pos]) - return(ROFF_IGN); - - sv = pos; - while ((*bufp)[pos] && ' ' != (*bufp)[pos] && - '\t' != (*bufp)[pos]) - pos++; - - /* - * Note: groff does NOT like escape characters in the input. - * Instead of detecting this, we're just going to let it fly and - * to hell with it. - */ - - assert(pos > sv); - sz = (size_t)(pos - sv); - - if (1 == sz && '.' == (*bufp)[sv]) - return(ROFF_IGN); - - r->last->end = malloc(sz + 1); - - if (NULL == r->last->end) { - (*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL); - return(ROFF_ERR); - } - - memcpy(r->last->end, *bufp + sv, sz); - r->last->end[(int)sz] = '\0'; - - if ((*bufp)[pos]) - if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL)) - return(ROFF_ERR); - - return(ROFF_IGN); -} - - -/* ARGSUSED */ -static enum rofferr -roff_block_sub(ROFF_ARGS) -{ - enum rofft t; - int i, j; - - /* - * First check whether a custom macro exists at this level. If - * it does, then check against it. This is some of groff's - * stranger behaviours. If we encountered a custom end-scope - * tag and that tag also happens to be a "real" macro, then we - * need to try interpreting it again as a real macro. If it's - * not, then return ignore. Else continue. - */ - - if (r->last->end) { - i = pos + 1; - while (' ' == (*bufp)[i] || '\t' == (*bufp)[i]) - i++; - - for (j = 0; r->last->end[j]; j++, i++) - if ((*bufp)[i] != r->last->end[j]) - break; - - if ('\0' == r->last->end[j] && - ('\0' == (*bufp)[i] || - ' ' == (*bufp)[i] || - '\t' == (*bufp)[i])) { - roffnode_pop(r); - roffnode_cleanscope(r); - - if (ROFF_MAX != roff_parse(*bufp, &pos)) - return(ROFF_RERUN); - return(ROFF_IGN); - } - } - - /* - * If we have no custom end-query or lookup failed, then try - * pulling it out of the hashtable. - */ - - ppos = pos; - t = roff_parse(*bufp, &pos); - - /* If we're not a comment-end, then throw it away. */ - if (ROFF_cblock != t) - return(ROFF_IGN); - - assert(roffs[t].proc); - return((*roffs[t].proc)(r, t, bufp, - szp, ln, ppos, pos, offs)); -} - - -/* ARGSUSED */ -static enum rofferr -roff_block_text(ROFF_ARGS) -{ - - return(ROFF_IGN); -} - - -/* ARGSUSED */ -static enum rofferr -roff_cond_sub(ROFF_ARGS) -{ - enum rofft t; - enum roffrule rr; - struct roffnode *l; - - ppos = pos; - rr = r->last->rule; - - /* - * Clean out scope. If we've closed ourselves, then don't - * continue. - */ - - l = r->last; - roffnode_cleanscope(r); - - if (l != r->last) - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); - - if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); - - /* - * A denied conditional must evaluate its children if and only - * if they're either structurally required (such as loops and - * conditionals) or a closing macro. - */ - if (ROFFRULE_DENY == rr) - if ( ! (ROFFMAC_STRUCT & roffs[t].flags)) - if (ROFF_ccond != t) - return(ROFF_IGN); - - assert(roffs[t].proc); - return((*roffs[t].proc) - (r, t, bufp, szp, ln, ppos, pos, offs)); -} - - -/* ARGSUSED */ -static enum rofferr -roff_cond_text(ROFF_ARGS) -{ - char *ep, *st; - enum roffrule rr; - - rr = r->last->rule; - - /* - * We display the value of the text if out current evaluation - * scope permits us to do so. - */ - - st = &(*bufp)[pos]; - if (NULL == (ep = strstr(st, "\\}"))) { - roffnode_cleanscope(r); - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); - } - - if (ep == st || (ep > st && '\\' != *(ep - 1))) - roffnode_pop(r); - - roffnode_cleanscope(r); - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); -} - - -static enum roffrule -roff_evalcond(const char *v, int *pos) -{ - - switch (v[*pos]) { - case ('n'): - (*pos)++; - return(ROFFRULE_ALLOW); - case ('e'): - /* FALLTHROUGH */ - case ('o'): - /* FALLTHROUGH */ - case ('t'): - (*pos)++; - return(ROFFRULE_DENY); - default: - break; - } - - while (v[*pos] && ' ' != v[*pos]) - (*pos)++; - return(ROFFRULE_DENY); -} - - -/* ARGSUSED */ -static enum rofferr -roff_cond(ROFF_ARGS) -{ - int sv; - enum roffrule rule; - - /* Stack overflow! */ - - if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) { - (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL); - return(ROFF_ERR); - } - - /* First, evaluate the conditional. */ - - if (ROFF_el == tok) { - /* - * An `.el' will get the value of the current rstack - * entry set in prior `ie' calls or defaults to DENY. - */ - if (r->rstackpos < 0) - rule = ROFFRULE_DENY; - else - rule = r->rstack[r->rstackpos]; - } else - rule = roff_evalcond(*bufp, &pos); - - sv = pos; - - while (' ' == (*bufp)[pos]) - pos++; - - /* - * Roff is weird. If we have just white-space after the - * conditional, it's considered the BODY and we exit without - * really doing anything. Warn about this. It's probably - * wrong. - */ - - if ('\0' == (*bufp)[pos] && sv != pos) { - if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL)) - return(ROFF_IGN); - return(ROFF_ERR); - } - - if ( ! roffnode_push(r, tok, ln, ppos)) - return(ROFF_ERR); - - r->last->rule = rule; - - ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name, - ROFFRULE_ALLOW == rule ? "allow" : "deny"); - - if (ROFF_ie == tok) { - /* - * An if-else will put the NEGATION of the current - * evaluated conditional into the stack. - */ - r->rstackpos++; - if (ROFFRULE_DENY == r->last->rule) - r->rstack[r->rstackpos] = ROFFRULE_ALLOW; - else - r->rstack[r->rstackpos] = ROFFRULE_DENY; - } - - /* If the parent has false as its rule, then so do we. */ - - if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) { - r->last->rule = ROFFRULE_DENY; - ROFF_DEBUG("roff: cond override: %s -> deny\n", - roffs[tok].name); - } - - /* - * Determine scope. If we're invoked with "\{" trailing the - * conditional, then we're in a multiline scope. Else our scope - * expires on the next line. - */ - - r->last->endspan = 1; - - if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) { - r->last->endspan = -1; - pos += 2; - ROFF_DEBUG("roff: cond-scope: %s, multi-line\n", - roffs[tok].name); - } else - ROFF_DEBUG("roff: cond-scope: %s, one-line\n", - roffs[tok].name); - - /* - * If there are no arguments on the line, the next-line scope is - * assumed. - */ - - if ('\0' == (*bufp)[pos]) - return(ROFF_IGN); - - /* Otherwise re-run the roff parser after recalculating. */ - - *offs = pos; - return(ROFF_RERUN); -} - - -/* ARGSUSED */ -static enum rofferr -roff_line(ROFF_ARGS) -{ - - return(ROFF_IGN); -} diff --git a/commands/mdocml/style.css b/commands/mdocml/style.css deleted file mode 100644 index 090c7331c..000000000 --- a/commands/mdocml/style.css +++ /dev/null @@ -1,77 +0,0 @@ -div.body { color: #333333; - max-width: 800px; - padding-left: 10px; - font-size: smaller; - font-family: Verdana, Tahoma, Arial, sans-serif; } - -div.sec-head { color: #000000; - font-weight: bold; } -div.sec-body { } -div.sec-block { padding-bottom: 1em; } -div.ssec-head { color: #000000; - font-weight: bold; } -div.ssec-body { } -div.ssec-block { } - -span.addr { } /* Address (Ad). */ -span.arg { font-style: italic; } /* Command argument (Ar). */ -span.author { } /* Author name (An). */ -span.cmd { font-weight: bold; } /* Command (Cm). */ -span.config { font-weight: bold; } /* Config statement (Cd). */ -span.define { } /* Defines (Dv). */ -span.desc { } /* Nd. After em-dash. */ -span.diag { font-weight: bold; } /* Diagnostic (Bl -diag). */ -span.emph { font-style: italic; } /* Emphasis (Em). */ -span.env { } /* Environment variables (Ev). */ -span.errno { } /* Error string (Er). */ -span.farg { font-style: italic; } /* Function argument (Fa, Fn). */ -span.file { font-style: italic; } /* File (Pa). */ -span.flag { font-weight: bold; } /* Flag (Fl, Cm). */ -span.fname { font-weight: bold; } /* Function name (Fa, Fn, Rv). */ -span.ftype { font-style: italic; } /* Function types (Ft, Fn). */ -span.includes { font-weight: bold; } /* Header includes (In). */ -span.lib { } /* Library (Lb). */ -span.lit { font-family: monospace; } /* Literals (Bf -literal). */ -span.macro { font-weight: bold; } /* Macro-ish thing (Fd). */ -span.name { color: #003333; font-weight: bold; } /* Name of utility (Nm). */ -span.opt { } /* Options (Op, Oo/Oc). */ -span.ref { } /* Citations (Rs). */ -span.ref-auth { } /* Reference author (%A). */ -span.ref-book { font-style: italic; } /* Reference book (%B). */ -span.ref-city { } /* Reference city (%C). */ -span.ref-date { } /* Reference date (%D). */ -span.ref-issue { font-style: italic; } /* Reference issuer/publisher (%I). */ -span.ref-jrnl { font-style: italic; } /* 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 { } /* Reference title (%T). */ -span.ref-vol { } /* Reference volume (%V). */ -span.symb { font-weight: bold; } /* Symbols. */ -span.type { font-style: italic; } /* Variable types (Vt). */ -span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */ -span.utility { font-weight: bold; } /* Name of utility (Ex). */ -span.var { font-weight: bold; } /* Variables (Rv). */ - -a.link-ext { background: transparent url(external.png) center right no-repeat; padding-right: 12px; }/* Off-site link (Lk). */ -a.link-includes { } /* Include-file link (In). */ -a.link-mail { background: transparent url(external.png) center right no-repeat; padding-right: 12px; }/* Mailto links (Mt). */ -a.link-man { } /* Manual links (Xr). */ -a.link-sec { text-decoration: none; border-bottom: 1px dotted #339999; } /* Section links (Sx). */ - -div.emph { font-style: italic; } /* Emphasis (Bl -emphasis). */ -div.lit { margin: 3px; - padding: 3px; - background-color: #EEEEEE; - border: 1px solid #339999; - color: #000000; - font-family: monospace; } -div.symb { font-weight: bold; } /* Symbols (Bl -symbolic). */ - -table.header { border-bottom: 1px dotted #dddddd; - color: #999999; } -table.footer { border-top: 1px dotted #dddddd; - color: #999999; } - diff --git a/commands/mdocml/term_ps.c b/commands/mdocml/term_ps.c deleted file mode 100644 index 56d1be240..000000000 --- a/commands/mdocml/term_ps.c +++ /dev/null @@ -1,427 +0,0 @@ -/* $Id: term_ps.c,v 1.10 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2008, 2009 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 "out.h" -#include "main.h" -#include "term.h" - -#define PS_CHAR_WIDTH 6 -#define PS_CHAR_HEIGHT 12 -#define PS_CHAR_TOPMARG (792 - 24) -#define PS_CHAR_TOP (PS_CHAR_TOPMARG - 36) -#define PS_CHAR_LEFT 36 -#define PS_CHAR_BOTMARG 24 -#define PS_CHAR_BOT (PS_CHAR_BOTMARG + 36) - -#define PS_BUFSLOP 128 -#define PS_GROWBUF(p, sz) \ - do if ((p)->engine.ps.psmargcur + (sz) > \ - (p)->engine.ps.psmargsz) { \ - (p)->engine.ps.psmargsz += /* CONSTCOND */ \ - MAX(PS_BUFSLOP, (sz)); \ - (p)->engine.ps.psmarg = realloc \ - ((p)->engine.ps.psmarg, \ - (p)->engine.ps.psmargsz); \ - if (NULL == (p)->engine.ps.psmarg) { \ - perror(NULL); \ - exit(EXIT_FAILURE); \ - } \ - } while (/* CONSTCOND */ 0) - - -static void ps_letter(struct termp *, char); -static void ps_begin(struct termp *); -static void ps_end(struct termp *); -static void ps_advance(struct termp *, size_t); -static void ps_endline(struct termp *); -static void ps_fclose(struct termp *); -static void ps_pclose(struct termp *); -static void ps_pletter(struct termp *, char); -static void ps_printf(struct termp *, const char *, ...); -static void ps_putchar(struct termp *, char); -static void ps_setfont(struct termp *, enum termfont); - - -void * -ps_alloc(void) -{ - struct termp *p; - - if (NULL == (p = term_alloc(TERMENC_ASCII))) - return(NULL); - - p->type = TERMTYPE_PS; - p->letter = ps_letter; - p->begin = ps_begin; - p->end = ps_end; - p->advance = ps_advance; - p->endline = ps_endline; - return(p); -} - - -void -ps_free(void *arg) -{ - struct termp *p; - - p = (struct termp *)arg; - - if (p->engine.ps.psmarg) - free(p->engine.ps.psmarg); - - term_free(p); -} - - -static void -ps_printf(struct termp *p, const char *fmt, ...) -{ - va_list ap; - int pos; - - va_start(ap, fmt); - - /* - * If we're running in regular mode, then pipe directly into - * vprintf(). If we're processing margins, then push the data - * into our growable margin buffer. - */ - - if ( ! (PS_MARGINS & p->engine.ps.psstate)) { - vprintf(fmt, ap); - va_end(ap); - return; - } - - /* - * XXX: I assume that the in-margin print won't exceed - * PS_BUFSLOP (128 bytes), which is reasonable but still an - * assumption that will cause pukeage if it's not the case. - */ - - PS_GROWBUF(p, PS_BUFSLOP); - - pos = (int)p->engine.ps.psmargcur; - vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap); - p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg); - - va_end(ap); -} - - -static void -ps_putchar(struct termp *p, char c) -{ - int pos; - - /* See ps_printf(). */ - - if ( ! (PS_MARGINS & p->engine.ps.psstate)) { - putchar(c); - return; - } - - PS_GROWBUF(p, 2); - - pos = (int)p->engine.ps.psmargcur++; - p->engine.ps.psmarg[pos++] = c; - p->engine.ps.psmarg[pos] = '\0'; -} - - -/* ARGSUSED */ -static void -ps_end(struct termp *p) -{ - - /* - * At the end of the file, do one last showpage. This is the - * same behaviour as groff(1) and works for multiple pages as - * well as just one. - */ - - assert(0 == p->engine.ps.psstate); - assert('\0' == p->engine.ps.last); - assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]); - printf("%s", p->engine.ps.psmarg); - printf("showpage\n"); - printf("%s\n", "%%EOF"); -} - - -static void -ps_begin(struct termp *p) -{ - - /* - * Print margins into margin buffer. Nothing gets output to the - * screen yet, so we don't need to initialise the primary state. - */ - - if (p->engine.ps.psmarg) { - assert(p->engine.ps.psmargsz); - p->engine.ps.psmarg[0] = '\0'; - } - - p->engine.ps.psmargcur = 0; - p->engine.ps.psstate = PS_MARGINS; - p->engine.ps.pscol = PS_CHAR_LEFT; - p->engine.ps.psrow = PS_CHAR_TOPMARG; - - ps_setfont(p, TERMFONT_NONE); - - (*p->headf)(p, p->argf); - (*p->endline)(p); - - p->engine.ps.pscol = PS_CHAR_LEFT; - p->engine.ps.psrow = PS_CHAR_BOTMARG; - - (*p->footf)(p, p->argf); - (*p->endline)(p); - - p->engine.ps.psstate &= ~PS_MARGINS; - - assert(0 == p->engine.ps.psstate); - assert(p->engine.ps.psmarg); - assert('\0' != p->engine.ps.psmarg[0]); - - /* - * Print header and initialise page state. Following this, - * stuff gets printed to the screen, so make sure we're sane. - */ - - printf("%s\n", "%!PS"); - ps_setfont(p, TERMFONT_NONE); - p->engine.ps.pscol = PS_CHAR_LEFT; - p->engine.ps.psrow = PS_CHAR_TOP; -} - - -static void -ps_pletter(struct termp *p, char c) -{ - - /* - * If we're not in a PostScript "word" context, then open one - * now at the current cursor. - */ - - if ( ! (PS_INLINE & p->engine.ps.psstate)) { - ps_printf(p, "%zu %zu moveto\n(", - p->engine.ps.pscol, - p->engine.ps.psrow); - p->engine.ps.psstate |= PS_INLINE; - } - - /* - * We need to escape these characters as per the PostScript - * specification. We would also escape non-graphable characters - * (like tabs), but none of them would get to this point and - * it's superfluous to abort() on them. - */ - - switch (c) { - case ('('): - /* FALLTHROUGH */ - case (')'): - /* FALLTHROUGH */ - case ('\\'): - ps_putchar(p, '\\'); - break; - default: - break; - } - - /* Write the character and adjust where we are on the page. */ - - ps_putchar(p, c); - p->engine.ps.pscol += PS_CHAR_WIDTH; -} - - -static void -ps_pclose(struct termp *p) -{ - - /* - * Spit out that we're exiting a word context (this is a - * "partial close" because we don't check the last-char buffer - * or anything). - */ - - if ( ! (PS_INLINE & p->engine.ps.psstate)) - return; - - ps_printf(p, ") show\n"); - p->engine.ps.psstate &= ~PS_INLINE; -} - - -static void -ps_fclose(struct termp *p) -{ - - /* - * Strong closure: if we have a last-char, spit it out after - * checking that we're in the right font mode. This will of - * course open a new scope, if applicable. - * - * Following this, close out any scope that's open. - */ - - if ('\0' != p->engine.ps.last) { - if (p->engine.ps.lastf != TERMFONT_NONE) { - ps_pclose(p); - ps_setfont(p, TERMFONT_NONE); - } - ps_pletter(p, p->engine.ps.last); - p->engine.ps.last = '\0'; - } - - if ( ! (PS_INLINE & p->engine.ps.psstate)) - return; - - ps_pclose(p); -} - - -static void -ps_letter(struct termp *p, char c) -{ - char cc; - - /* - * State machine dictates whether to buffer the last character - * or not. Basically, encoded words are detected by checking if - * we're an "8" and switching on the buffer. Then we put "8" in - * our buffer, and on the next charater, flush both character - * and buffer. Thus, "regular" words are detected by having a - * regular character and a regular buffer character. - */ - - if ('\0' == p->engine.ps.last) { - assert(8 != c); - p->engine.ps.last = c; - return; - } else if (8 == p->engine.ps.last) { - assert(8 != c); - p->engine.ps.last = '\0'; - } else if (8 == c) { - assert(8 != p->engine.ps.last); - if ('_' == p->engine.ps.last) { - if (p->engine.ps.lastf != TERMFONT_UNDER) { - ps_pclose(p); - ps_setfont(p, TERMFONT_UNDER); - } - } else if (p->engine.ps.lastf != TERMFONT_BOLD) { - ps_pclose(p); - ps_setfont(p, TERMFONT_BOLD); - } - p->engine.ps.last = c; - return; - } else { - if (p->engine.ps.lastf != TERMFONT_NONE) { - ps_pclose(p); - ps_setfont(p, TERMFONT_NONE); - } - cc = p->engine.ps.last; - p->engine.ps.last = c; - c = cc; - } - - ps_pletter(p, c); -} - - -static void -ps_advance(struct termp *p, size_t len) -{ - - /* - * Advance some spaces. This can probably be made smarter, - * i.e., to have multiple space-separated words in the same - * scope, but this is easier: just close out the current scope - * and readjust our column settings. - */ - - ps_fclose(p); - p->engine.ps.pscol += len ? len * PS_CHAR_WIDTH : 0; -} - - -static void -ps_endline(struct termp *p) -{ - - /* Close out any scopes we have open: we're at eoln. */ - - ps_fclose(p); - - /* - * If we're in the margin, don't try to recalculate our current - * row. XXX: if the column tries to be fancy with multiple - * lines, we'll do nasty stuff. - */ - - if (PS_MARGINS & p->engine.ps.psstate) - return; - - /* - * Put us down a line. If we're at the page bottom, spit out a - * showpage and restart our row. - */ - - p->engine.ps.pscol = PS_CHAR_LEFT; - if (p->engine.ps.psrow >= PS_CHAR_HEIGHT + PS_CHAR_BOT) { - p->engine.ps.psrow -= PS_CHAR_HEIGHT; - return; - } - - assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]); - printf("%s", p->engine.ps.psmarg); - printf("showpage\n"); - p->engine.ps.psrow = PS_CHAR_TOP; -} - - -static void -ps_setfont(struct termp *p, enum termfont f) -{ - - if (TERMFONT_BOLD == f) - ps_printf(p, "/Courier-Bold\n"); - else if (TERMFONT_UNDER == f) - ps_printf(p, "/Courier-Oblique\n"); - else - ps_printf(p, "/Courier\n"); - - ps_printf(p, "10 selectfont\n"); - p->engine.ps.lastf = f; -} - diff --git a/common/lib/libutil/Makefile.inc b/common/lib/libutil/Makefile.inc new file mode 100644 index 000000000..9848c3e96 --- /dev/null +++ b/common/lib/libutil/Makefile.inc @@ -0,0 +1,3 @@ +# $NetBSD: Makefile.inc,v 1.1 2008/12/16 22:33:11 christos Exp $ +.PATH.c: ${.PARSEDIR} +SRCS+= snprintb.c diff --git a/common/lib/libutil/snprintb.c b/common/lib/libutil/snprintb.c new file mode 100644 index 000000000..1ad02dc81 --- /dev/null +++ b/common/lib/libutil/snprintb.c @@ -0,0 +1,282 @@ +/* $NetBSD: snprintb.c,v 1.5 2009/05/13 02:50:31 pgoyette Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * 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. + * + * 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. + */ + +/* + * snprintb: print an interpreted bitmask to a buffer + * + * => returns the length of the buffer that would be required to print the + * string minus the terminating NUL. + */ +#ifndef _STANDALONE +# ifndef _KERNEL + +# if HAVE_NBTOOL_CONFIG_H +# include "nbtool_config.h" +# endif + +# include +# if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: snprintb.c,v 1.5 2009/05/13 02:50:31 pgoyette Exp $"); +# endif + +# include +# include +# include +# include +# include +# else +# include +__KERNEL_RCSID(0, "$NetBSD: snprintb.c,v 1.5 2009/05/13 02:50:31 pgoyette Exp $"); +# include +# include +# include +# include +# endif + +int +snprintb_m(char *buf, size_t buflen, const char *bitfmt, uint64_t val, + size_t l_max) +{ + char *bp = buf, *s_bp = NULL; + const char *c_fmt, *s_fmt = NULL, *cur_fmt; + const char *sbase; + int bit, ch, t_len, s_len = 0, l_len, f_len, v_len, sep; + int restart = 0; + uint64_t field; + +#ifdef _KERNEL + /* + * For safety; no other *s*printf() do this, but in the kernel + * we don't usually check the return value + */ + (void)memset(buf, 0, buflen); +#endif /* _KERNEL */ + + ch = *bitfmt++; + switch (ch != '\177' ? ch : *bitfmt++) { + case 8: + sbase = "0%" PRIo64; + break; + case 10: + sbase = "%" PRId64; + break; + case 16: + sbase = "0x%" PRIx64; + break; + default: + goto internal; + } + + /* Reserve space for trailing blank line if needed */ + if (l_max > 0) + buflen--; + + t_len = snprintf(bp, buflen, sbase, val); + if (t_len < 0) + goto internal; + + v_len = l_len = t_len; + + if ((size_t)t_len < buflen) + bp += t_len; + else + bp += buflen - 1; + + /* + * If the value we printed was 0 and we're using the old-style format, + * we're done. + */ + if ((val == 0) && (ch != '\177')) + goto terminate; + +#define STORE(c) { l_len++; \ + if ((size_t)(++t_len) < buflen) \ + *bp++ = (c); \ + } while ( /* CONSTCOND */ 0) + +#define BACKUP { if (s_bp != NULL) { \ + bp = s_bp; s_bp = NULL; \ + t_len -= l_len - s_len; \ + restart = 1; \ + bitfmt = s_fmt; \ + } \ + STORE('>'); STORE('\0'); \ + if ((size_t)t_len < buflen) \ + snprintf(bp, buflen - t_len, sbase, val); \ + t_len += v_len; l_len = v_len; bp += v_len; \ + } while ( /* CONSTCOND */ 0) + +#define PUTSEP \ + if (l_max > 0 && (size_t)l_len >= l_max) { \ + BACKUP; \ + STORE('<'); \ + } else { \ + /* Remember separator location */ \ + if ( l_max > 0 && sep != '<') { \ + s_len = l_len; \ + s_bp = bp; \ + s_fmt = cur_fmt; \ + } \ + STORE(sep); \ + restart = 0; \ + } \ + +#define PUTCHR(c) \ + if (l_max > 0 && (size_t)l_len >= (l_max - 1)) { \ + BACKUP; \ + if (restart == 0) { \ + STORE(c); \ + } else \ + sep = '<'; \ + } else { \ + STORE(c); \ + restart = 0; \ + } \ + +#define PUTS(s) while ((ch = *(s)++) != 0) { \ + PUTCHR(ch); \ + if (restart) \ + break; \ + } + + /* + * Chris Torek's new bitmask format is identified by a leading \177 + */ + sep = '<'; + if (ch != '\177') { + /* old (standard) format. */ + for (;(bit = *bitfmt) != 0;) { + cur_fmt = bitfmt++; + if (val & (1 << (bit - 1))) { + PUTSEP; + if (restart) + continue; + sep = ','; + for (; (ch = *bitfmt) > ' '; ++bitfmt) { + PUTCHR(ch); + if (restart) + break; + } + } else + for (; *bitfmt > ' '; ++bitfmt) + continue; + } + } else { + /* new quad-capable format; also does fields. */ + field = val; + while (c_fmt = bitfmt, (ch = *bitfmt++) != '\0') { + bit = *bitfmt++; /* now 0-origin */ + switch (ch) { + case 'b': + if (((u_int)(val >> bit) & 1) == 0) + goto skip; + cur_fmt = c_fmt; + PUTSEP; + if (restart) + break; + PUTS(bitfmt); + if (restart == 0) + sep = ','; + break; + case 'f': + case 'F': + cur_fmt = c_fmt; + f_len = *bitfmt++; /* field length */ + field = (val >> bit) & + (((uint64_t)1 << f_len) - 1); + if (ch == 'F') /* just extract */ + break; + PUTSEP; + if (restart == 0) { + sep = ','; + PUTS(bitfmt); + } + if (restart == 0) { + PUTCHR('='); + } + if (restart == 0) { + f_len = snprintf(bp, buflen - t_len, + sbase, field); + if (f_len < 0) + goto internal; + t_len += f_len; + l_len += f_len; + if ((size_t)t_len < buflen) + bp += f_len; + if (l_max > 0 && + (size_t)l_len > l_max) { + PUTCHR('#'); + } + } + break; + case '=': + case ':': + /* + * Here "bit" is actually a value instead, + * to be compared against the last field. + * This only works for values in [0..255], + * of course. + */ + if ((int)field != bit) + goto skip; + if (ch == '=') { + PUTCHR('='); + } + PUTS(bitfmt); + break; + default: + skip: + while (*bitfmt++ != '\0') + continue; + break; + } + } + } + l_len++; + if ((size_t)(++t_len) < buflen) + *bp++ = '>'; +terminate: + *bp++ = '\0'; + if (l_max != 0) { + t_len++; + *bp = '\0'; + } + return t_len; +internal: +#ifndef _KERNEL + errno = EINVAL; +#endif + return -1; +} + +int +snprintb(char *buf, size_t buflen, const char *bitfmt, uint64_t val) +{ + return snprintb_m(buf, buflen, bitfmt, val, 0); +} +#endif diff --git a/docs/UPDATING b/docs/UPDATING index 975cd4d2f..a21898b91 100644 --- a/docs/UPDATING +++ b/docs/UPDATING @@ -1,3 +1,7 @@ +20110928: + Update your /usr/etc/daily and /etc/man.conf if you + want to fully enjoy the manpage fixes. + 20110817: To use the new asynchronous version of VFS do: # BUILDAVFS=yes make cleandepend world diff --git a/etc/Makefile b/etc/Makefile index fa269a1e3..5cdf674d1 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -5,7 +5,7 @@ FILES1=fstab group hostname.file inet.conf motd.install mtab passwd profile \ protocols rc services termcap ttytab utmp rc.cd binary_sizes \ binary_sizes.big binary_sizes.xxl syslog.conf rc.daemons.dist \ rs.inet rs.single make.conf system.conf ttys resolv.conf rc.conf \ - rc.subr + rc.subr man.conf FILES2=shadow FILES3=daily dhcptags.conf rc diff --git a/etc/man.conf b/etc/man.conf new file mode 100644 index 000000000..abef91197 --- /dev/null +++ b/etc/man.conf @@ -0,0 +1,16 @@ +_version BSD.2 +_subdir man{[1-9],1x} +_suffix .0 +_build .[1-9] mandoc %s +_build .tbl tbl %s | mandoc +_default /usr/{,pkg/{,gcc44/}}man +_whatdb /usr/{,pkg/{,gcc44/}}man/whatis.db +1 man1 +2 man2 +3 man3 +4 man4 +5 man5 +6 man6 +7 man7 +8 man8 +9 man9 diff --git a/etc/usr/daily b/etc/usr/daily index 6552aa535..f7387145a 100755 --- a/etc/usr/daily +++ b/etc/usr/daily @@ -66,6 +66,9 @@ do fi done +# Update manpage index +/usr/libexec/makewhatis + # Continue with a local script if present. test -f /usr/local/etc/daily && sh /usr/local/etc/daily $caller exit 0 diff --git a/lib/Makefile b/lib/Makefile index 9cdb5f778..3e9dc86d9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,7 +21,7 @@ SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \ libddekit libminixfs .if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no") -SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert +SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert libutil .endif .if ${COMPILER_TYPE} == "ack" @@ -31,7 +31,6 @@ SUBDIR+= ack/libd ack/libe ack/libfp ack/liby .if ${OBJECT_FMT} == "a.out" SUBDIR+= libend .endif - .include build_ack: diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile new file mode 100644 index 000000000..d26e03857 --- /dev/null +++ b/lib/libutil/Makefile @@ -0,0 +1,92 @@ +# $NetBSD: Makefile,v 1.63 2010/01/27 19:10:31 drochner Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +USE_SHLIBDIR= yes + +.include + +.include "${NETBSDSRCDIR}/common/lib/libutil/Makefile.inc" + +WARNS= 4 +LIB= util +CPPFLAGS+=-DLIBC_SCCS -I${.CURDIR} +SRCS= efun.c getbootfile.c \ + getmntopts.c \ + login.c loginx.c login_tty.c logout.c logoutx.c \ + logwtmp.c logwtmpx.c opendisk.c \ + passwd.c pw_scan.c pidfile.c pidlock.c pty.c \ + raise_default_signal.c \ + secure_path.c snprintb.c \ + ttyaction.c + #disklabel_dkcksum.c disklabel_scan.c \ + #if_media.c \ + #sockaddr_snprintf.c + #getlabelsector.c + #getmaxpartitions.c + #stat_flags.c + #getrawpartition.c + #login_cap.c + #ttymsg.c + #parsedate.y + +MAN= efun.3 getbootfile.3 getlabelsector.3 getmaxpartitions.3 \ + getmntopts.3 \ + getrawpartition.3 \ + login.3 login_cap.3 loginx.3 \ + disklabel_dkcksum.3 disklabel_scan.3 \ + opendisk.3 openpty.3 parsedate.3 pidfile.3 pidlock.3 \ + pw_getconf.3 pw_init.3 pw_lock.3 secure_path.3 \ + raise_default_signal.3 \ + snprintb.3 sockaddr_snprintf.3 stat_flags.3 ttyaction.3 \ + ttymsg.3 util.3 + +YPREFIX=__pd +#.PATH: ${NETBSDSRCDIR}/lib/libc/gen +.PATH: ${NETBSDSRCDIR}/lib/nbsd_libc/gen + +#.include "compat/Makefile.inc" + +MLINKS+=getlabelsector.3 getlabeloffset.3 +MLINKS+=login.3 logout.3 +MLINKS+=login.3 logwtmp.3 +MLINKS+=login_cap.3 login_getclass.3 +MLINKS+=login_cap.3 login_getcapbool.3 +MLINKS+=login_cap.3 login_getcapnum.3 +MLINKS+=login_cap.3 login_getcapsize.3 +MLINKS+=login_cap.3 login_getcapstr.3 +MLINKS+=login_cap.3 login_getcaptime.3 +MLINKS+=login_cap.3 login_close.3 +MLINKS+=login_cap.3 setclasscontext.3 +MLINKS+=login_cap.3 setusercontext.3 +MLINKS+=loginx.3 logoutx.3 loginx.3 logwtmpx.3 +MLINKS+=openpty.3 login_tty.3 +MLINKS+=openpty.3 forkpty.3 +MLINKS+=pw_getconf.3 pw_getpwconf.3 +MLINKS+=pw_init.3 pw_edit.3 +MLINKS+=pw_init.3 pw_prompt.3 +MLINKS+=pw_init.3 pw_copy.3 +MLINKS+=pw_init.3 pw_copyx.3 +MLINKS+=pw_init.3 pw_scan.3 +MLINKS+=pw_init.3 pw_error.3 +MLINKS+=pw_lock.3 pw_mkdb.3 +MLINKS+=pw_lock.3 pw_abort.3 +MLINKS+=pw_lock.3 pw_getprefix.3 +MLINKS+=pw_lock.3 pw_setprefix.3 +MLINKS+=pidlock.3 ttylock.3 +MLINKS+=pidlock.3 ttyunlock.3 +MLINKS+=efun.3 esetfunc.3 +MLINKS+=efun.3 easprintf.3 +MLINKS+=efun.3 estrlcpy.3 +MLINKS+=efun.3 estrlcat.3 +MLINKS+=efun.3 estrdup.3 +MLINKS+=efun.3 estrndup.3 +MLINKS+=efun.3 emalloc.3 +MLINKS+=efun.3 ecalloc.3 +MLINKS+=efun.3 erealloc.3 +MLINKS+=efun.3 efopen.3 +MLINKS+=efun.3 evasprintf.3 +MLINKS+=stat_flags.3 string_to_flags.3 +MLINKS+=stat_flags.3 flags_to_string.3 +MLINKS+=snprintb.3 snprintb_m.3 + +.include diff --git a/lib/libutil/compat/Makefile.inc b/lib/libutil/compat/Makefile.inc new file mode 100644 index 000000000..9e0f550a1 --- /dev/null +++ b/lib/libutil/compat/Makefile.inc @@ -0,0 +1,7 @@ +# $NetBSD: Makefile.inc,v 1.2 2009/01/11 02:57:18 christos Exp $ + +.PATH: ${.CURDIR}/compat + +CPPFLAGS+=-I${.CURDIR}/../libc -I${.CURDIR}/../../sys +SRCS+=compat_passwd.c compat_loginx.c compat_login.c compat_parsedate.c \ + compat_login_cap.c diff --git a/lib/libutil/compat/compat_gepwconf.c b/lib/libutil/compat/compat_gepwconf.c new file mode 100644 index 000000000..770df54ca --- /dev/null +++ b/lib/libutil/compat/compat_gepwconf.c @@ -0,0 +1,63 @@ +/* $NetBSD: compat_gepwconf.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/*- + * Copyright (c) 2008 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: compat_gepwconf.c,v 1.2 2009/01/11 02:57:18 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#define __LIBC12_SOURCE__ + +#include +#include +#include +#include +#include +#include +#include + +__warn_references(pw_getpwconf, + "warning: reference to compatibility pw_getpwconf(); " + "include to generate correct reference") + +void +pw_getpwconf(char *buf, size_t len, const struct passwd50 *p, const char *opt) +{ + struct passwd px; + passwd50_to_passwd(p, &px); + __pw_getpwconf50(buf, len, &px, opt); +} diff --git a/lib/libutil/compat/compat_login.c b/lib/libutil/compat/compat_login.c new file mode 100644 index 000000000..25876e99c --- /dev/null +++ b/lib/libutil/compat/compat_login.c @@ -0,0 +1,78 @@ +/* $NetBSD: compat_login.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)login.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: compat_login.c,v 1.2 2009/01/11 02:57:18 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#define __LIBC12_SOURCE__ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +__warn_references(login, + "warning: reference to compatibility login(); include to generate correct reference") + +void +login(const struct utmp50 *ut50) +{ + int fd; + int tty; + struct utmp ut; + + utmp50_to_utmp(ut50, &ut); + + tty = ttyslot(); + if (tty > 0 && (fd = open(_PATH_UTMP, O_WRONLY|O_CREAT, 0644)) >= 0) { + (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); + (void)write(fd, &ut, sizeof(ut)); + (void)close(fd); + } + if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) >= 0) { + (void)write(fd, &ut, sizeof(ut)); + (void)close(fd); + } +} diff --git a/lib/libutil/compat/compat_login_cap.c b/lib/libutil/compat/compat_login_cap.c new file mode 100644 index 000000000..c0ed68a08 --- /dev/null +++ b/lib/libutil/compat/compat_login_cap.c @@ -0,0 +1,70 @@ +/* $NetBSD: compat_login_cap.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ +/*- + * Copyright (c) 2008 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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 + +#define __LIBC12_SOURCE__ +#include +#include +#include +#include +#include + +__warn_references(login_getpwclass, + "warning: reference to compatibility login_getpwclass();" + " include for correct reference") +__warn_references(setusercontext, + "warning: reference to compatibility setusercontext();" + " include for correct reference") + +login_cap_t * +login_getpwclass(const struct passwd50 *pw50) +{ + struct passwd pw; + + passwd50_to_passwd(pw50, &pw); + return __login_getpwclass50(&pw); +} + +int +setusercontext(login_cap_t *lc, struct passwd50 *pw50, uid_t uid, u_int flags) +{ + struct passwd pw; + + passwd50_to_passwd(pw50, &pw); + return __setusercontext50(lc, &pw, uid, flags); +} diff --git a/lib/libutil/compat/compat_loginx.c b/lib/libutil/compat/compat_loginx.c new file mode 100644 index 000000000..b8f1414e1 --- /dev/null +++ b/lib/libutil/compat/compat_loginx.c @@ -0,0 +1,64 @@ +/* $NetBSD: compat_loginx.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: compat_loginx.c,v 1.2 2009/01/11 02:57:18 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include + +#define __LIBC12_SOURCE__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +__warn_references(loginx, + "warning: reference to compatibility loginx(); include to generate correct reference") + +void +loginx(const struct utmpx50 *ut50) +{ + struct utmpx ut; + utmpx50_to_utmpx(ut50, &ut); + (void)__pututxline50(&ut); + (void)__updwtmpx50(_PATH_WTMPX, &ut); +} diff --git a/lib/libutil/compat/compat_logoutx.c b/lib/libutil/compat/compat_logoutx.c new file mode 100644 index 000000000..ce2e35a5f --- /dev/null +++ b/lib/libutil/compat/compat_logoutx.c @@ -0,0 +1,73 @@ +/* $NetBSD: compat_logoutx.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)logout.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: compat_logoutx.c,v 1.2 2009/01/11 02:57:18 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +logoutx(const char *line, int status, int type) +{ + struct utmpx *utp, ut; + (void)strlcpy(ut.ut_line, line, sizeof(ut.ut_line)); + if ((utp = getutxline(&ut)) == NULL) { + endutxent(); + return 0; + } + utp->ut_type = type; + if (WIFEXITED(status)) + utp->ut_exit.e_exit = (uint16_t)WEXITSTATUS(status); + if (WIFSIGNALED(status)) + utp->ut_exit.e_termination = (uint16_t)WTERMSIG(status); + (void)gettimeofday(&utp->ut_tv, NULL); + (void)pututxline(utp); + endutxent(); + return 1; +} diff --git a/lib/libutil/compat/compat_parsedate.c b/lib/libutil/compat/compat_parsedate.c new file mode 100644 index 000000000..ed076ea62 --- /dev/null +++ b/lib/libutil/compat/compat_parsedate.c @@ -0,0 +1,60 @@ +/* $NetBSD: compat_parsedate.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/*- + * Copyright (c) 2008 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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 + +#define __LIBC12_SOURCE__ +#include +#include +#include +#include +#include +#include +#include + +__warn_references(parsedate, + "warning: reference to compatibility parsedate();" + " include for correct reference") + +int32_t +parsedate(const char *str, const int32_t *t50, const int *tzoff) +{ + time_t t; + if (t50) + t = *t50; + return (int32_t)__parsedate50(str, t50 ? &t : NULL, tzoff); +} diff --git a/lib/libutil/compat/compat_passwd.c b/lib/libutil/compat/compat_passwd.c new file mode 100644 index 000000000..572d0ebb5 --- /dev/null +++ b/lib/libutil/compat/compat_passwd.c @@ -0,0 +1,103 @@ +/* $NetBSD: compat_passwd.c,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/*- + * Copyright (c) 2008 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: compat_passwd.c,v 1.2 2009/01/11 02:57:18 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#define __LIBC12_SOURCE__ + +#include +#include +#include +#include +#include +#include +#include + +__warn_references(pw_scan, + "warning: reference to compatibility pw_scan(); " + "include to generate correct reference") +__warn_references(pw_copy, + "warning: reference to compatibility pw_copy(); " + "include to generate correct reference") +__warn_references(pw_copyx, + "warning: reference to compatibility pw_copyx(); " + "include to generate correct reference") +__warn_references(pw_getpwconf, + "warning: reference to compatibility pw_getpwconf(); " + "include to generate correct reference") + +int +pw_scan(char *buf, struct passwd50 *p, int *flags) +{ + struct passwd px; + int rv = __pw_scan50(buf, &px, flags); + passwd_to_passwd50(&px, p); + return rv; +} + +void +pw_copy(int ffd, int tfd, struct passwd50 *pw50, struct passwd50 *opw50) +{ + struct passwd pw, opw; + passwd50_to_passwd(pw50, &pw); + if (opw50) + passwd50_to_passwd(opw50, &opw); + __pw_copy50(ffd, tfd, &pw, opw50 ? &opw : NULL); +} + +int +pw_copyx(int ffd, int tfd, struct passwd50 *pw50, struct passwd50 *opw50, + char *errbuf, size_t errbufsiz) +{ + struct passwd pw, opw; + passwd50_to_passwd(pw50, &pw); + if (opw50) + passwd50_to_passwd(opw50, &opw); + return __pw_copyx50(ffd, tfd, &pw, opw50 ? &opw : NULL, errbuf, + errbufsiz); +} + +void +pw_getpwconf(char *buf, size_t len, const struct passwd50 *p, const char *opt) +{ + struct passwd px; + passwd50_to_passwd(p, &px); + __pw_getpwconf50(buf, len, &px, opt); +} diff --git a/lib/libutil/compat/login_cap.h b/lib/libutil/compat/login_cap.h new file mode 100644 index 000000000..d2d48ac2e --- /dev/null +++ b/lib/libutil/compat/login_cap.h @@ -0,0 +1,51 @@ +/* $NetBSD: login_cap.h,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/*- + * Copyright (c) 2008 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. 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. + */ +#ifndef _COMPAT_LOGIN_CAP_H_ +#define _COMPAT_LOGIN_CAP_H_ + +struct passwd; +struct passwd50; + +__BEGIN_DECLS +login_cap_t *login_getpwclass(const struct passwd50 *); +login_cap_t *__login_getpwclass50(const struct passwd *); +int setusercontext(login_cap_t *, struct passwd50 *, uid_t, u_int); +int __setusercontext50(login_cap_t *, struct passwd *, uid_t, u_int); +__END_DECLS + +#endif /* !_COMPAT_LOGIN_CAP_H_ */ diff --git a/lib/libutil/compat/util.h b/lib/libutil/compat/util.h new file mode 100644 index 000000000..5c2394196 --- /dev/null +++ b/lib/libutil/compat/util.h @@ -0,0 +1,63 @@ +/* $NetBSD: util.h,v 1.2 2009/01/11 02:57:18 christos Exp $ */ + +/*- + * Copyright (c) 1995 + * 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. + */ + +#ifndef _COMPAT_UTIL_H_ +#define _COMPAT_UTIL_H_ + +#include +#include +#include +#include +#include +#include + +void login(const struct utmp50 *); +void loginx(const struct utmpx50 *); + +int32_t parsedate(const char *, const int32_t *, const int *); + +void pw_copy(int, int, struct passwd50 *, struct passwd50 *); +int pw_copyx(int, int, struct passwd50 *, struct passwd50 *, + char *, size_t); +void pw_getpwconf(char *, size_t, const struct passwd50 *, + const char *); + +void __login50(const struct utmp *); +void __loginx50(const struct utmpx *); + +time_t __parsedate50(const char *, const time_t *, const int *); + +void __pw_copy50(int, int, struct passwd *, struct passwd *); +int __pw_copyx50(int, int, struct passwd *, struct passwd *, + char *, size_t); +void __pw_getpwconf50(char *, size_t, const struct passwd *, + const char *); +#endif /* !_COMPAT_UTIL_H_ */ diff --git a/lib/libutil/disklabel_dkcksum.3 b/lib/libutil/disklabel_dkcksum.3 new file mode 100644 index 000000000..de9c48169 --- /dev/null +++ b/lib/libutil/disklabel_dkcksum.3 @@ -0,0 +1,56 @@ +.\" $NetBSD: disklabel_dkcksum.3,v 1.8 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Roland C. Dowdeswell. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt DISKLABEL_DKCKSUM 3 +.Os +.Sh NAME +.Nm disklabel_dkcksum +.Nd compute the checksum for a disklabel +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft uint16_t +.Fo disklabel_dkcksum +.Fa "struct disklabel *lp" +.Fc +.Sh DESCRIPTION +.Fn disklabel_dkcksum +computes the checksum for the disklabel passed in as +.Fa lp . +.Sh RETURN VALUES +The +.Fn disklabel_dkcksum +returns the computed checksum. +.Sh HISTORY +The +.Fn disklabel_dkcksum +function call appeared in +.Nx 2.0 . diff --git a/lib/libutil/disklabel_dkcksum.c b/lib/libutil/disklabel_dkcksum.c new file mode 100644 index 000000000..d6883dea0 --- /dev/null +++ b/lib/libutil/disklabel_dkcksum.c @@ -0,0 +1,58 @@ +/* $NetBSD: disklabel_dkcksum.c,v 1.4 2005/05/15 21:01:34 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 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 +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dkcksum.c 8.1 (Berkeley) 6/5/93"; +#else +__RCSID("$NetBSD: disklabel_dkcksum.c,v 1.4 2005/05/15 21:01:34 thorpej Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include + +uint16_t +disklabel_dkcksum(struct disklabel *lp) +{ + uint16_t *start, *end; + uint16_t sum; + + sum = 0; + start = (uint16_t *)(void *)lp; + end = (uint16_t *)(void *)&lp->d_partitions[lp->d_npartitions]; + while (start < end) + sum ^= *start++; + return (sum); +} diff --git a/lib/libutil/disklabel_scan.3 b/lib/libutil/disklabel_scan.3 new file mode 100644 index 000000000..53c08885c --- /dev/null +++ b/lib/libutil/disklabel_scan.3 @@ -0,0 +1,59 @@ +.\" $NetBSD: disklabel_scan.3,v 1.6 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Roland C. Dowdeswell. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt DISKLABEL_SCAN 3 +.Os +.Sh NAME +.Nm disklabel_scan +.Nd scan a buffer for a valid disklabel +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn disklabel_scan "struct disklabel *lp" "char *buf" "size_t buflen" +.Sh DESCRIPTION +.Fn disklabel_scan +scans the memory region specified by +.Fa buf +and +.Fa buflen +for a valid disklabel. +If such a label is found, it is copied into +.Fa lp . +.Sh RETURN VALUES +The +.Fn disklabel_scan +function returns 0 if a valid disklabel was found and 1 if not. +.Sh HISTORY +The +.Fn disklabel_scan +function call appeared in +.Nx 2.0 . diff --git a/lib/libutil/disklabel_scan.c b/lib/libutil/disklabel_scan.c new file mode 100644 index 000000000..953e14891 --- /dev/null +++ b/lib/libutil/disklabel_scan.c @@ -0,0 +1,69 @@ +/* $NetBSD: disklabel_scan.c,v 1.3 2009/01/18 12:13:03 lukem Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roland C. Dowdeswell. + * + * 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. + * + * 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 +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 2002\ + The NetBSD Foundation, Inc. All rights reserved."); +__RCSID("$NetBSD: disklabel_scan.c,v 1.3 2009/01/18 12:13:03 lukem Exp $"); +#endif + +#include +#include +#include + +#include + +#define SCAN_INCR 4 + +int +disklabel_scan(struct disklabel *lp, char *buf, size_t buflen) +{ + size_t i; + + /* scan for the correct magic numbers. */ + + for (i=0; i <= buflen - sizeof(*lp); i += SCAN_INCR) { + memcpy(lp, buf + i, sizeof(*lp)); + if (lp->d_magic == DISKMAGIC && lp->d_magic2 == DISKMAGIC) + goto sanity; + } + + return 1; + +sanity: + /* we've found something, let's sanity check it */ + if (lp->d_npartitions > MAXPARTITIONS || disklabel_dkcksum(lp)) + return 1; + + return 0; +} diff --git a/lib/libutil/efun.3 b/lib/libutil/efun.3 new file mode 100644 index 000000000..259a6f384 --- /dev/null +++ b/lib/libutil/efun.3 @@ -0,0 +1,117 @@ +.\" $NetBSD: efun.3,v 1.10 2010/05/03 05:40:37 jruoho Exp $ +.\" +.\" Copyright (c) 2006 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. +.\" +.\" 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. +.\" +.Dd May 3, 2010 +.Dt EFUN 3 +.Os +.Sh NAME +.Nm esetfunc , +.Nm easprintf , +.Nm efopen , +.Nm emalloc , +.Nm ecalloc , +.Nm erealloc , +.Nm estrdup , +.Nm estrndup , +.Nm estrlcat , +.Nm estrlcpy , +.Nm evasprintf +.Nd error-checked utility functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft void (*)(int, const char *, ...) +.Fn esetfunc "void (*)(int, const char *, ...)" +.Ft int +.Fn easprintf "char ** restrict str" "const char * restrict fmt" "..." +.Ft FILE * +.Fn efopen "const char *p" "const char *m" +.Ft void * +.Fn ecalloc "size_t n" "size_t c" +.Ft void * +.Fn emalloc "size_t n" +.Ft void * +.Fn erealloc "void *p" "size_t n" +.Ft char * +.Fn estrdup "const char *s" +.Ft char * +.Fn estrndup "const char *s" "size_t len" +.Ft size_t +.Fn estrlcat "char *dst" "const char *src" "size_t len" +.Ft size_t +.Fn estrlcpy "char *dst" "const char *src" "size_t len" +.Ft int +.Fn evasprintf "char ** restrict str" "const char * restrict fmt" "..." +.Sh DESCRIPTION +The +.Fn easprintf , +.Fn efopen , +.Fn ecalloc , +.Fn emalloc , +.Fn erealloc , +.Fn estrdup , +.Fn estrndup , +.Fn estrlcat , +.Fn estrlcpy , +and +.Fn evasprintf +functions +operate exactly as the corresponding functions that do not start with an +.Sq e +except that in case of an error, they call +the installed error handler that can be configured with +.Fn esetfunc . +.Pp +For the string handling functions, it is an error when the destination +buffer is not large enough to hold the complete string. +For functions that allocate memory or open a file, it is an error when +they would return a null pointer. +The default error handler is +.Xr err 3 . +The function +.Fn esetfunc +returns the previous error handler function. +A +.Dv NULL +error handler will just call +.Xr exit 3 . +.Sh SEE ALSO +.Xr asprintf 3 , +.Xr calloc 3 , +.Xr err 3 , +.Xr exit 3 , +.Xr fopen 3 , +.Xr malloc 3 , +.Xr realloc 3 , +.Xr strdup 3 , +.Xr strlcat 3 , +.Xr strlcpy 3 , +.Xr strndup 3 , +.Xr vasprintf 3 diff --git a/lib/libutil/efun.c b/lib/libutil/efun.c new file mode 100644 index 000000000..0f9019fc7 --- /dev/null +++ b/lib/libutil/efun.c @@ -0,0 +1,158 @@ +/* $NetBSD: efun.c,v 1.6 2008/04/28 20:23:02 martin Exp $ */ + +/*- + * Copyright (c) 2006 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. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifdef __RCSID +__RCSID("$NetBSD: efun.c,v 1.6 2008/04/28 20:23:02 martin Exp $"); +#endif + +#include +#include +#include +#include +#include +#include +#include + +static void (*efunc)(int, const char *, ...) = err; + +void (* +esetfunc(void (*ef)(int, const char *, ...)))(int, const char *, ...) +{ + void (*of)(int, const char *, ...) = efunc; + efunc = ef == NULL ? (void (*)(int, const char *, ...))exit : ef; + return of; +} + +size_t +estrlcpy(char *dst, const char *src, size_t len) +{ + size_t rv; + if ((rv = strlcpy(dst, src, len)) >= len) { + errno = ENAMETOOLONG; + (*efunc)(1, + "Cannot copy string; %zu chars needed %zu provided", + rv, len); + } + return rv; +} + +size_t +estrlcat(char *dst, const char *src, size_t len) +{ + size_t rv; + if ((rv = strlcat(dst, src, len)) >= len) { + errno = ENAMETOOLONG; + (*efunc)(1, + "Cannot append to string; %zu chars needed %zu provided", + rv, len); + } + return rv; +} + +char * +estrdup(const char *s) +{ + char *d = strdup(s); + if (d == NULL) + (*efunc)(1, "Cannot copy string"); + return d; +} + +char * +estrndup(const char *s, size_t len) +{ + char *d = strndup(s, len); + if (d == NULL) + (*efunc)(1, "Cannot copy string"); + return d; +} + +void * +emalloc(size_t n) +{ + void *p = malloc(n); + if (p == NULL) + (*efunc)(1, "Cannot allocate %zu bytes", n); + return p; +} + +void * +ecalloc(size_t n, size_t s) +{ + void *p = calloc(n, s); + if (p == NULL) + (*efunc)(1, "Cannot allocate %zu bytes", n); + return p; +} + +void * +erealloc(void *p, size_t n) +{ + void *q = realloc(p, n); + if (q == NULL) + (*efunc)(1, "Cannot re-allocate %zu bytes", n); + return q; +} + +FILE * +efopen(const char *p, const char *m) +{ + FILE *fp = fopen(p, m); + if (fp == NULL) + (*efunc)(1, "Cannot open `%s'", p); + return fp; +} + +int +easprintf(char ** __restrict ret, const char * __restrict format, ...) +{ + int rv; + va_list ap; + va_start(ap, format); + if ((rv = vasprintf(ret, format, ap)) == -1) + (*efunc)(1, "Cannot format string"); + va_end(ap); + return rv; +} + +int +evasprintf(char ** __restrict ret, const char * __restrict format, va_list ap) +{ + int rv; + if ((rv = vasprintf(ret, format, ap)) == -1) + (*efunc)(1, "Cannot format string"); + return rv; +} diff --git a/lib/libutil/getbootfile.3 b/lib/libutil/getbootfile.3 new file mode 100644 index 000000000..a47149767 --- /dev/null +++ b/lib/libutil/getbootfile.3 @@ -0,0 +1,61 @@ +.\" $NetBSD: getbootfile.3,v 1.9 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 2001 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Thomas Klausner. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt GETBOOTFILE 3 +.Os +.Sh NAME +.Nm getbootfile +.Nd get the name of the booted kernel file +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft const char * +.Fn getbootfile void +.Sh DESCRIPTION +.Fn getbootfile +returns a static pointer to the full path name of the file from which +the current kernel was loaded. +If it can not be determined, or the file is not ``secure'' (see +.Xr secure_path 3 ) , +.Dv _PATH_UNIX +from +.In paths.h +is returned instead. +.Sh SEE ALSO +.Xr secure_path 3 , +.Xr sysctl 3 +.Sh HISTORY +The +.Fn getbootfile +function call appeared in +.Fx 2.0 +and +.Nx 1.6 . diff --git a/lib/libutil/getbootfile.c b/lib/libutil/getbootfile.c new file mode 100644 index 000000000..8b296f3e0 --- /dev/null +++ b/lib/libutil/getbootfile.c @@ -0,0 +1,84 @@ +/* $NetBSD: getbootfile.c,v 1.5 2008/04/28 20:23:02 martin Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Thomas Klausner. + * + * 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getbootfile.c,v 1.5 2008/04/28 20:23:02 martin Exp $"); +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef CPU_BOOTED_KERNEL +static char name[MAXPATHLEN]; +#endif + +const char * +getbootfile(void) +{ +#ifdef CPU_BOOTED_KERNEL + int mib[2]; + size_t size; +#endif + const char *kernel; + + kernel = _PATH_UNIX; +#ifdef CPU_BOOTED_KERNEL + /* find real boot-kernel name */ + mib[0] = CTL_MACHDEP; + mib[1] = CPU_BOOTED_KERNEL; + size = sizeof(name) - 1; + if (sysctl(mib, 2, name + 1, &size, NULL, 0) == 0) { + /* + * traditionally, this sysctl returns the relative + * path of the kernel with the leading slash stripped + * -- could be empty, though (e.g. when netbooting). + */ + if (name[1] != '\0') { + name[0] = '/'; + kernel = name; + } + + /* check if we got a valid and 'secure' filename */ + if (strcmp(kernel, _PATH_UNIX) != 0 && + secure_path(kernel) != 0) { + /* doesn't seems so, fall back to default */ + kernel = _PATH_UNIX; + } + } +#endif + + return (kernel); +} diff --git a/lib/libutil/getlabelsector.3 b/lib/libutil/getlabelsector.3 new file mode 100644 index 000000000..5db5aff6e --- /dev/null +++ b/lib/libutil/getlabelsector.3 @@ -0,0 +1,75 @@ +.\" $NetBSD: getlabelsector.3,v 1.5 2009/06/24 22:31:58 zafer Exp $ +.\" +.\" +.\" Copyright 2002 Wasabi Systems, Inc. +.\" All rights reserved. +.\" +.\" Written by Steve C. Woodford for Wasabi Systems, Inc. +.\" +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed for the NetBSD Project by +.\" Wasabi Systems, Inc. +.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC +.\" 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. +.\" +.Dd December 11, 2002 +.Dt GETLABELSECTOR 3 +.Os +.Sh NAME +.Nm getlabelsector , +.Nm getlabeloffset +.Nd get the sector number and offset of the disklabel +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft daddr_t +.Fn getlabelsector void +.Ft off_t +.Fn getlabeloffset void +.Sh DESCRIPTION +The +.Fn getlabelsector +and +.Fn getlabeloffset +functions return values which describe the exact on-disk location of the +.Xr disklabel 5 +on the current system, or \-1 on error. +These functions supersede the hardcoded +.Dv LABELSECTOR +and +.Dv LABELOFFSET +definitions previously used to derive the location of the +.Xr disklabel 5 . +.Sh SEE ALSO +.Xr sysctl 3 , +.Xr disklabel 5 +.Sh HISTORY +The +.Fn getlabelsector +and +.Fn getlabeloffset +functions appeared in +.Nx 2.0 . diff --git a/lib/libutil/getlabelsector.c b/lib/libutil/getlabelsector.c new file mode 100644 index 000000000..7badd029a --- /dev/null +++ b/lib/libutil/getlabelsector.c @@ -0,0 +1,75 @@ +/* $NetBSD: getlabelsector.c,v 1.3 2005/09/17 01:51:21 elad Exp $ */ + +/* + * Copyright 2002 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Steve C. Woodford for Wasabi Systems, Inc. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getlabelsector.c,v 1.3 2005/09/17 01:51:21 elad Exp $"); +#endif + +#include +#include +#include + +int +getlabelsector(void) +{ + int sector, mib[2]; + size_t varlen; + + mib[0] = CTL_KERN; + mib[1] = KERN_LABELSECTOR; + varlen = sizeof(sector); + if (sysctl(mib, 2, §or, &varlen, NULL, (size_t)0) < 0) + return (-1); + + return sector; +} + +off_t +getlabeloffset(void) +{ + int offset, mib[2]; + size_t varlen; + + mib[0] = CTL_KERN; + mib[1] = KERN_LABELOFFSET; + varlen = sizeof(offset); + if (sysctl(mib, 2, &offset, &varlen, NULL, (size_t)0) < 0) + return (-1); + + return ((off_t)offset); +} diff --git a/lib/libutil/getmaxpartitions.3 b/lib/libutil/getmaxpartitions.3 new file mode 100644 index 000000000..9a439e259 --- /dev/null +++ b/lib/libutil/getmaxpartitions.3 @@ -0,0 +1,59 @@ +.\" $NetBSD: getmaxpartitions.3,v 1.10 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 1996 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt GETMAXPARTITIONS 3 +.Os +.Sh NAME +.Nm getmaxpartitions +.Nd get the maximum number of partitions allowed per disk +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn getmaxpartitions void +.Sh DESCRIPTION +.Fn getmaxpartitions +returns the number of partitions that are allowed per disk on the +system, or \-1 in case of an error, setting the global +.Va errno +variable. +The possible values for +.Va errno +are the same as in +.Xr sysctl 3 . +.Sh SEE ALSO +.Xr getrawpartition 3 , +.Xr sysctl 3 +.Sh HISTORY +The +.Fn getmaxpartitions +function call appeared in +.Nx 1.2 . diff --git a/lib/libutil/getmaxpartitions.c b/lib/libutil/getmaxpartitions.c new file mode 100644 index 000000000..ceb49c7ec --- /dev/null +++ b/lib/libutil/getmaxpartitions.c @@ -0,0 +1,54 @@ +/* $NetBSD: getmaxpartitions.c,v 1.6 2008/04/28 20:23:02 martin Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getmaxpartitions.c,v 1.6 2008/04/28 20:23:02 martin Exp $"); +#endif + +#include +#include +#include + +int +getmaxpartitions(void) +{ + int maxpart, mib[2]; + size_t varlen; + + mib[0] = CTL_KERN; + mib[1] = KERN_MAXPARTITIONS; + varlen = sizeof(maxpart); + if (sysctl(mib, 2, &maxpart, &varlen, NULL, (size_t)0) < 0) + return (-1); + + return (maxpart); +} diff --git a/lib/libutil/getmntopts.3 b/lib/libutil/getmntopts.3 new file mode 100644 index 000000000..6ad3ab6b7 --- /dev/null +++ b/lib/libutil/getmntopts.3 @@ -0,0 +1,277 @@ +.\" $NetBSD: getmntopts.3,v 1.12 2010/08/24 12:05:01 christos Exp $ +.\" +.\" Copyright (c) 1994 +.\" 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. +.\" +.\" @(#)getmntopts.3 8.3 (Berkeley) 3/30/95 +.\" +.Dd May 4, 2010 +.Dt GETMNTOPTS 3 +.Os +.Sh NAME +.Nm getmntopts +.Nd scan mount options +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In mntopts.h +.Ft mntoptparse_t +.Fn getmntopts "const char *options" "const struct mntopt *mopts" "int *flagp" "int *altflagp" +.Ft const char * +.Fn getmntoptstr "mntoptparse_t mp" "const char *opt" +.Ft long +.Fn getmntoptnum "mntoptparse_t mp" "const char *opt" +.Ft void +.Fn freemntopts "mntoptparse_t mp" +.Sh DESCRIPTION +The +.Fn getmntopts +function takes a comma separated option list and a list +of valid option names, and computes the bitmasks +corresponding to the requested set of options. +.Pp +The string +.Ar options +is broken down into a sequence of comma separated tokens. +Each token is looked up in the table described by +.Ar mopts +and the bits in +the word referenced by either +.Ar flagp +or +.Ar altflagp +(depending on the +.Dv m_altloc +field of the option's table entry) +are updated. +The flag words are not initialized by +.Fn getmntopts . +The table, +.Ar mopts , +has the following format: +.Bd -literal +struct mntopt { + const char *m_option; /* option name */ + int m_inverse; /* negative option, e.g., "dev" */ + int m_flag; /* bit to set, e.g., MNT_RDONLY */ + int m_altloc; /* use altflagp rather than flagp */ +}; +.Ed +.Pp +The members of this structure are: +.Bl -tag -width m_inverse +.It Fa m_option +the option name, +for example +.Dq suid . +.It Fa m_inverse +tells +.Fn getmntopts +that the name has the inverse meaning of the bit. +For example, +.Dq suid +is the string, whereas the mount flag is +.Dv MNT_NOSUID . +In this case, the sense of the string and the flag +are inverted, so the +.Fa m_inverse +flag should be set. +.It Fa m_flag +the value of the bit to be set or cleared in +the flag word when the option is recognized. +The bit is set when the option is discovered, +but cleared if the option name was preceded +by the letters +.Dq no . +The +.Fa m_inverse +flag causes these two operations to be reversed. +.It Fa m_altloc +the bit should be set or cleared in +.Ar altflagp +rather than +.Ar flagp . +.El +.Pp +Each of the user visible +.Dv MNT_ +flags has a corresponding +.Dv MOPT_ +macro which defines an appropriate +.Li "struct mntopt" +entry. +To simplify the program interface and ensure consistency across all +programs, a general purpose macro, +.Dv MOPT_STDOPTS , +is defined which contains an entry for all the generic VFS options. +In addition, the macros +.Dv MOPT_FORCE +and +.Dv MOPT_UPDATE +exist to enable the +.Dv MNT_FORCE +and +.Dv MNT_UPDATE +flags to be set. +Finally, the table must be terminated by an entry with a +.Dv NULL +first element. +.Pp +.Fn getmntopts +returns a +.Li "mntoptparse_t" +handle that can be used in subsequent +.Fn getmntoptstr +and +.Fn getmntoptnum +calls to fetch a value for an option and that must be freed with a call +to +.Fn freemntopts . +If an error occurred, then if the external integer value +.Va getmnt_silent +is zero then +.Fn getmntopts +prints an error message and exits; +if +.Va getmnt_silent +is non-zero then +.Fn getmntopts +returns +.Dv NULL . +.Pp +The +.Fn getmntoptstr +function returns the string value of the named option, if such a value +was set in the option string. +If the value was not set, then if the external integer value +.Va getmnt_silent +is zero then +.Fn getmntoptstr +prints an error message and exits; +if +.Va getmnt_silent +is non-zero then +.Fn getmntoptstr +returns +.Dv NULL . +.Pp +The +.Fn getmntoptnum +returns the long value of the named option, if such a value was set in the +option string. +If the value was not set, or could not be converted from a string to a +long, then if the external integer value +.Va getmnt_silent +is zero then +.Fn getmntoptnum +prints an error message and exits; +if +.Va getmnt_silent +is non-zero then +.Fn getmntoptnum +returns \-1. +.Pp +The +.Fn freemntopts +frees the storage used by +.Fn getmntopts . +.Sh RETURN VALUES +.Fn getmntopts +returns +.Dv NULL +if an error occurred. +Note that some bits may already have been set in +.Va flagp +and +.Va altflagp +even if +.Dv NULL +is returned. +.Fn getmntoptstr +returns +.Dv NULL +if an error occurred. +.Fn getmntoptnum +returns \-1 if an error occurred. +.Sh EXAMPLES +Most commands will use the standard option set. +Local filesystems which support the +.Dv MNT_UPDATE +flag, would also have an +.Dv MOPT_UPDATE +entry. +This can be declared and used as follows: +.Bd -literal -offset indent +#include \*[Lt]mntopts.h\*[Gt] + +static const struct mntopt mopts[] = { + MOPT_STDOPTS, + MOPT_UPDATE, + { NULL } +}; + +\&... + +long val; +mntoptparse_t mp; +mntflags = mntaltflags = 0; + +\&... + +mp = getmntopts(options, mopts, \*[Am]mntflags, \*[Am]mntaltflags); + +if (mp == NULL) + err(EXIT_FAILURE, "getmntopts"); + +\&... + +val = getmntoptnum(mp, "rsize"); +freemntopts(mp); +.Ed +.Sh DIAGNOSTICS +If the external integer variable +.Va getmnt_silent +is zero then the +.Fn getmntopts , +.Fn getmntoptstr , +and +.Fn getmntoptnum +functions display an error message and exit if an error occurred. +By default +.Va getmnt_silent +is zero. +.Sh SEE ALSO +.Xr err 3 , +.Xr mount 8 +.Sh HISTORY +The +.Fn getmntopts +function appeared in +.Bx 4.4 . +It was moved to the utilities library and enhanced to retrieve option +values in +.Nx 2.0 . diff --git a/lib/libutil/getmntopts.c b/lib/libutil/getmntopts.c new file mode 100644 index 000000000..9c948306f --- /dev/null +++ b/lib/libutil/getmntopts.c @@ -0,0 +1,194 @@ +/* $NetBSD: getmntopts.c,v 1.4 2007/08/26 22:46:15 pooka Exp $ */ + +/*- + * Copyright (c) 1994 + * 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 +#ifndef lint +#if 0 +static char sccsid[] = "@(#)getmntopts.c 8.3 (Berkeley) 3/29/95"; +#else +__RCSID("$NetBSD: getmntopts.c,v 1.4 2007/08/26 22:46:15 pooka Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +int getmnt_silent = 0; + +static const char errmsg[] = "-o %s: option not supported"; + +struct mntoptparse { + const char *options; + const struct mntopt *mopts; + char *optbuf; + char **optarg; +}; + +const char * +getmntoptstr(mntoptparse_t mp, const char *opt) +{ + const struct mntopt *m; + + for (m = mp->mopts; m->m_option != NULL; m++) + if (strcasecmp(opt, m->m_option) == 0) + break; + + if (m->m_option == NULL) { + if (getmnt_silent == 0) + errx(1, errmsg, opt); + else + return NULL; + } + + return mp->optarg[m - mp->mopts]; +} + +long +getmntoptnum(mntoptparse_t mp, const char *opt) +{ + char *ep; + long rv; + void (*fun)(int, const char *, ...) = NULL; + const char *val = getmntoptstr(mp, opt); + + if (val == NULL) { + if (getmnt_silent == 0) + errx(1, "Missing %s argument", opt); + else + return -1; + } + + errno = 0; + rv = strtol(val, &ep, 0); + + if (*ep) + fun = errx; + + if (errno == ERANGE && (rv == LONG_MAX || rv == LONG_MIN)) + fun = err; + + if (fun) { + if (getmnt_silent != 0) + return -1; + (*fun)(1, "Invalid %s argument `%s'", opt, val); + } + return rv; +} + +void +freemntopts(mntoptparse_t mp) +{ + free(mp->optbuf); + free(mp->optarg); + free(mp); +} + +mntoptparse_t +getmntopts(const char *options, const struct mntopt *m0, int *flagp, + int *altflagp) +{ + const struct mntopt *m; + int negative; + char *opt, *p; + int *thisflagp; + size_t nopts; + mntoptparse_t mp; + + for (nopts = 0, m = m0; m->m_option != NULL; ++m, nopts++) + continue; + + if ((mp = malloc(sizeof(struct mntoptparse))) == NULL) + return NULL; + + /* Copy option string, since it is about to be torn asunder... */ + if ((mp->optbuf = strdup(options)) == NULL) { + free(mp); + return NULL; + } + + if ((mp->optarg = calloc(nopts, sizeof(char *))) == NULL) { + free(mp->optbuf); + free(mp); + return NULL; + } + + mp->mopts = m0; + mp->options = options; + + for (opt = mp->optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { + /* Check for "no" prefix. */ + if (opt[0] == 'n' && opt[1] == 'o') { + negative = 1; + opt += 2; + } else + negative = 0; + + /* + * for options with assignments in them (ie. quotas) + * ignore the assignment as it's handled elsewhere + */ + p = strchr(opt, '='); + if (p) { + *p++ = '\0'; + } + + /* Scan option table. */ + for (m = m0; m->m_option != NULL; ++m) + if (strcasecmp(opt, m->m_option) == 0) + break; + + /* Save flag, or fail if option is not recognised. */ + if (m->m_option) { + mp->optarg[m - m0] = p; + thisflagp = m->m_altloc ? altflagp : flagp; + if (negative == m->m_inverse) + *thisflagp |= m->m_flag; + else + *thisflagp &= ~m->m_flag; + } else if (!getmnt_silent) { + errx(1, errmsg, opt); + } else { + free(mp->optbuf); + free(mp); + + return NULL; + } + } + return mp; +} diff --git a/lib/libutil/getrawpartition.3 b/lib/libutil/getrawpartition.3 new file mode 100644 index 000000000..78790cff2 --- /dev/null +++ b/lib/libutil/getrawpartition.3 @@ -0,0 +1,71 @@ +.\" $NetBSD: getrawpartition.3,v 1.11 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 1996 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt GETRAWPARTITION 3 +.Os +.Sh NAME +.Nm getrawpartition +.Nd get the system +.Dq raw +partition +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn getrawpartition void +.Sh DESCRIPTION +.Fn getrawpartition +returns the partition number ( +.Sq a +== 0, +.Sq b +== 1, ...) of the +.Dq raw +partition of the system's disks, +or \-1 in case of an error, setting the global +.Va errno +variable. +The possible values for +.Va errno +are the same as in +.Xr sysctl 3 . +The +.Dq raw +partition is defined as the partition which provides access to the entire +disk, regardless of the disk's partition map. +.Sh SEE ALSO +.Xr getmaxpartitions 3 , +.Xr sysctl 3 +.Sh HISTORY +The +.Fn getrawpartition +function call appeared in +.Nx 1.2 . diff --git a/lib/libutil/getrawpartition.c b/lib/libutil/getrawpartition.c new file mode 100644 index 000000000..159cc8824 --- /dev/null +++ b/lib/libutil/getrawpartition.c @@ -0,0 +1,54 @@ +/* $NetBSD: getrawpartition.c,v 1.6 2008/04/28 20:23:03 martin Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: getrawpartition.c,v 1.6 2008/04/28 20:23:03 martin Exp $"); +#endif + +#include +#include +#include + +int +getrawpartition() +{ + int rawpart, mib[2]; + size_t varlen; + + mib[0] = CTL_KERN; + mib[1] = KERN_RAWPARTITION; + varlen = sizeof(rawpart); + if (sysctl(mib, 2, &rawpart, &varlen, NULL, (size_t)0) < 0) + return (-1); + + return (rawpart); +} diff --git a/lib/libutil/if_media.c b/lib/libutil/if_media.c new file mode 100644 index 000000000..a68782624 --- /dev/null +++ b/lib/libutil/if_media.c @@ -0,0 +1,180 @@ +/* $NetBSD: if_media.c,v 1.2 2008/04/28 20:23:03 martin Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: if_media.c,v 1.2 2008/04/28 20:23:03 martin Exp $"); +#endif + +#include +#include +#include +#include +#include + +struct ifmedia_description ifm_mode_descriptions[] = + IFM_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_descriptions[] = + IFM_SUBTYPE_DESCRIPTIONS; + +struct ifmedia_description ifm_option_descriptions[] = + IFM_OPTION_DESCRIPTIONS; + +const char * +get_media_type_string(int mword) +{ + struct ifmedia_description *desc; + + for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++) { + if (IFM_TYPE(mword) == desc->ifmt_word) + return (desc->ifmt_string); + } + return ""; +} + +const char * +get_media_subtype_string(int mword) +{ + struct ifmedia_description *desc; + + for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL; + desc++) { + if (IFM_TYPE_MATCH(desc->ifmt_word, mword) && + IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(mword)) + return desc->ifmt_string; + } + return ""; +} + +const char * +get_media_mode_string(int mword) +{ + struct ifmedia_description *desc; + + for (desc = ifm_mode_descriptions; desc->ifmt_string != NULL; desc++) { + if (IFM_TYPE_MATCH(desc->ifmt_word, mword) && + IFM_MODE(mword) == IFM_MODE(desc->ifmt_word)) + return desc->ifmt_string; + } + return NULL; +} + +const char * +get_media_option_string(int *mwordp) +{ + struct ifmedia_description *desc; + int mword = *mwordp; + + for (desc = ifm_option_descriptions; desc->ifmt_string != NULL; + desc++) { + if (!IFM_TYPE_MATCH(desc->ifmt_word, mword)) + continue; + if (mword & IFM_OPTIONS(desc->ifmt_word)) { + *mwordp = mword & ~IFM_OPTIONS(desc->ifmt_word); + return desc->ifmt_string; + } + } + + /* Historical behaviour is to ignore unknown option bits! */ + *mwordp = mword & ~IFM_OPTIONS(~0); + return NULL; +} + +int +lookup_media_word(struct ifmedia_description *desc, int type, const char *val) +{ + + for (; desc->ifmt_string != NULL; desc++) { + if (IFM_TYPE_MATCH(desc->ifmt_word, type) && + strcasecmp(desc->ifmt_string, val) == 0) + return (desc->ifmt_word); + } + return -1; +} + +int +get_media_mode(int type, const char *val) +{ + + return lookup_media_word(ifm_mode_descriptions, type, val); +} + +int +get_media_subtype(int type, const char *val) +{ + + return lookup_media_word(ifm_subtype_descriptions, type, val); +} + +int +get_media_options(int type, const char *val, char **invalid) +{ + char *optlist, *str; + int option, rval = 0; + + /* We muck with the string, so copy it. */ + optlist = strdup(val); + if (optlist == NULL) { + if (invalid != NULL) + *invalid = NULL; + return -1; + } + str = optlist; + + /* + * Look up the options in the user-provided comma-separated list. + */ + type = IFM_TYPE(type); + for (; (str = strtok(str, ",")) != NULL; str = NULL) { + option = lookup_media_word(ifm_option_descriptions, type, str); + if (option != -1) { + rval |= IFM_OPTIONS(option); + continue; + } + rval = -1; + if (invalid == NULL) + break; + /* Pass invalid option at start of malloced buffer */ + if (str != optlist) + memmove(optlist, str, strlen(str) + 1); + /* Caller should free() or exit() */ + *invalid = optlist; + return rval; + } + + free(optlist); + return (rval); +} diff --git a/lib/libutil/login.3 b/lib/libutil/login.3 new file mode 100644 index 000000000..0bcd42e1e --- /dev/null +++ b/lib/libutil/login.3 @@ -0,0 +1,107 @@ +.\" $NetBSD: login.3,v 1.6 2003/08/07 16:44:58 agc Exp $ +.\" +.\" Copyright (c) 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software developed by the Computer Systems +.\" Engineering group at Lawrence Berkeley Laboratory under DARPA contract +.\" BG 91-66 and contributed to Berkeley. +.\" +.\" 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. +.\" +.Dd December 14, 1995 +.Dt LOGIN 3 +.Os +.Sh NAME +.Nm login , +.Nm logout , +.Nm logwtmp +.Nd login utility functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft void +.Fn login "struct utmp *ut" +.Ft int +.Fn logout "const char *line" +.Ft void +.Fn logwtmp "const char *line" "const char *name" "const char *host" +.Sh DESCRIPTION +The +.Fn login , +.Fn logout , +and +.Fn logwtmp +functions operate on the database of current users in +.Pa /var/run/utmp +and on the logfile +.Pa /var/log/wtmp +of logins and logouts. +.Pp +The +.Fn login +function updates the +.Pa /var/run/utmp +and +.Pa /var/log/wtmp +files with user information contained in +.Fa ut . +.Pp +The +.Fn logout +function removes the entry from +.Pa /var/run/utmp +corresponding to the device +.Fa line . +.Pp +The +.Fn logwtmp +function adds an entry to +.Pa /var/log/wtmp . +Since +.Fn login +will add the appropriate entry for +.Pa /var/log/wtmp +during a login, +.Fn logwtmp +is usually used for logouts. +.Sh RETURN VALUES +.Fn logout +returns non-zero if it was able to find and delete an entry for +.Fa line , +and zero if there is no entry for +.Fa line +in +.Pa /var/run/utmp . +.Sh FILES +.Bl -tag -width /var/run/wtmp -compact +.It Pa /dev/\(** +.It Pa /etc/ttys +.It Pa /var/run/utmp +.It Pa /var/log/wtmp +.El +.Sh SEE ALSO +.Xr utmp 5 diff --git a/lib/libutil/login.c b/lib/libutil/login.c new file mode 100644 index 000000000..5068a808f --- /dev/null +++ b/lib/libutil/login.c @@ -0,0 +1,70 @@ +/* $NetBSD: login.c,v 1.16 2003/08/07 16:44:58 agc Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)login.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: login.c,v 1.16 2003/08/07 16:44:58 agc Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void +login(const struct utmp *ut) +{ + int fd; + int tty; + + _DIAGASSERT(ut != NULL); + + tty = ttyslot(); + if (tty > 0 && (fd = open(_PATH_UTMP, O_WRONLY|O_CREAT, 0644)) >= 0) { + (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); + (void)write(fd, ut, sizeof(struct utmp)); + (void)close(fd); + } + if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) >= 0) { + (void)write(fd, ut, sizeof(struct utmp)); + (void)close(fd); + } +} diff --git a/lib/libutil/login_cap.3 b/lib/libutil/login_cap.3 new file mode 100644 index 000000000..57190e9e5 --- /dev/null +++ b/lib/libutil/login_cap.3 @@ -0,0 +1,289 @@ +.\" $NetBSD: login_cap.3,v 1.18 2010/05/05 22:05:31 wiz Exp $ +.\" +.\" Copyright (c) 1996,1997 Berkeley Software Design, Inc. 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Berkeley Software Design, +.\" Inc. +.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. +.\" +.\" BSDI login_cap.3,v 1.4 1997/11/07 16:22:27 jch Exp +.\" +.Dd October 6, 2007 +.Dt LOGIN_CAP 3 +.Os +.Sh NAME +.Nm login_getclass , +.Nm login_getcapbool , +.Nm login_getcapnum , +.Nm login_getcapsize , +.Nm login_getcapstr , +.Nm login_getcaptime , +.Nm login_close , +.Nm setclasscontext , +.Nm setusercontext +.Nd query login.conf database about a user class +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In sys/types.h +.In login_cap.h +.Ft login_cap_t * +.Fn login_getclass "char *class" +.Ft int +.Fn login_getcapbool "login_cap_t *lc" "const char *cap" "u_int def" +.Ft quad_t +.Fn login_getcapnum "login_cap_t *lc" "const char *cap" "quad_t def" "quad_t err" +.Ft quad_t +.Fn login_getcapsize "login_cap_t *lc" "const char *cap" "quad_t def" "quad_t err" +.Ft char * +.Fn login_getcapstr "login_cap_t *lc" "const char *cap" "char *def" "char *err" +.Ft quad_t +.Fn login_getcaptime "login_cap_t *lc" "const char *cap" "quad_t def" "quad_t err" +.Ft void +.Fn login_close "login_cap_t *lc" +.Ft int +.Fn setclasscontext "const char *class" "u_int flags" +.Ft int +.Fn setusercontext "login_cap_t *lc" "const struct passwd *pwd" "uid_t uid" "u_int flags" +.Sh DESCRIPTION +The +.Fn login_getclass +function extracts the entry specified by +.Ar class +(or +.Li default +if +.Ar class +is +.Dv NULL +or the empty string) +from +.Pa /etc/login.conf +(see +.Xr login.conf 5 ) . +If the entry is found, a +.Li login_cap_t +pointer is returned. +.Dv NULL +is returned if the user class is not found. +When the +.Li login_cap_t +structure is no longer needed, it should be freed by the +.Fn login_close +function. +.Pp +Once +.Ar lc +has been returned by +.Fn login_getclass , +any of the other +.Fn login_* +functions may be called. +.Pp +The +.Fn login_getcapnum , +.Fn login_getcapsize , +.Fn login_getcapstr , +and +.Fn login_getcaptime +functions all query the database entry for a field named +.Ar cap . +If the field is found, its value is returned. +If the field is not found, the value specified by +.Ar def +is returned. +If an error is encountered while trying to find the field, +.Ar err +is returned. +See +.Xr login.conf 5 +for a discussion of the various textual forms the value may take. +The +.Fn login_getcapbool +function is slightly different. +It returns +.Ar def +if no capabilities were found for this class (typically meaning that +the default class was used and the +.Li /etc/login.conf +file is missing). +It returns a non-zero value if +.Ar cap , +with no value, was found, +zero otherwise. +.Pp +The +.Fn setclasscontext +function takes +.Ar class , +the name of a user class, +and sets the resources defined by that class according to +.Ar flags . +Only the +.Dv LOGIN_SETPATH , +.Dv LOGIN_SETPRIORITY , +.Dv LOGIN_SETRESOURCES , +and +.Dv LOGIN_SETUMASK +bits are used. +(See +.Fn setusercontext +below). +It returns 0 on success and -1 on failure. +.Pp +The +.Fn setusercontext +function +sets the resources according to +.Ar flags . +The +.Ar lc +argument, if not +.Dv NULL , +contains the class information that should +be used. +The +.Ar pwd +argument, if not +.Dv NULL , +provides information about the user. +Both +.Ar lc +and +.Ar pwd +cannot be +.Dv NULL . +The +.Ar uid +argument is used in place of the user id contained in the +.Ar pwd +structure when calling +.Xr setuid 2 . +The various bits available to be or-ed together to make up +.Ar flags +are: +.Bl -tag -width LOGIN_SETRESOURCESXX +.It LOGIN_SETGID +Set the group id. +Requires the +.Ar pwd +field be specified. +.It LOGIN_SETGROUPS +Set the group membership list by calling +.Xr initgroups 3 . +Requires the +.Ar pwd +field be specified. +.It LOGIN_SETGROUP +Set the group id and call +.Xr initgroups 3 . +Requires the +.Ar pwd +field be specified. +.It LOGIN_SETLOGIN +Sets the login name by +.Xr setlogin 2 . +Requires the +.Ar pwd +field be specified. +.It LOGIN_SETPATH +Sets the +.Ev PATH +environment variable. +.It LOGIN_SETPRIORITY +Sets the priority by +.Xr setpriority 2 . +.It LOGIN_SETRESOURCES +Sets the various system resources by +.Xr setrlimit 2 . +.It LOGIN_SETUMASK +Sets the umask by +.Xr umask 2 . +.It LOGIN_SETUSER +Sets the user id to +.Ar uid +by +.Xr setuid 2 . +.It LOGIN_SETENV +Sets the environment variables as defined by the setenv keyword, by +.Xr setenv 3 . +.It LOGIN_SETALL +Sets all of the above. +.El +.Sh SEE ALSO +.Xr setlogin 2 , +.Xr setpriority 2 , +.Xr setrlimit 2 , +.Xr setuid 2 , +.Xr umask 2 , +.Xr initgroups 3 , +.Xr secure_path 3 , +.Xr login.conf 5 +.Sh HISTORY +The +.Nm +family of functions are largely based on the +.Bsx +implementation of same, and appeared in +.Nx 1.5 +by kind permission. +.Sh CAVEATS +The string returned by +.Fn login_getcapstr +is allocated via +.Xr malloc 3 +when the specified capability is present and thus it is the responsibility +of the caller to +.Fn free +this space. +However, if the capability was not found or an error occurred and +.Fa def +or +.Fa err +(whichever is relevant) are +.Pf non- Dv NULL +the returned value is simply what was passed in to +.Fn login_getcapstr . +Therefore it is not possible to blindly +.Fn free +the return value without first checking it against +.Fa def +and +.Fa err . +.Pp +The same warnings set forth in +.Xr setlogin 2 +apply to +.Fn setusercontext +when the +.Dv LOGIN_SETLOGIN +flag is used. +Specifically, changing the login name affects all processes in the current +session, not just the current process. +See +.Xr setlogin 2 +for more information. diff --git a/lib/libutil/login_cap.c b/lib/libutil/login_cap.c new file mode 100644 index 000000000..729235f65 --- /dev/null +++ b/lib/libutil/login_cap.c @@ -0,0 +1,987 @@ +/* $NetBSD: login_cap.c,v 1.29 2007/12/04 22:09:02 mjf Exp $ */ + +/*- + * Copyright (c) 1995,1997 Berkeley Software Design, Inc. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Berkeley Software Design, + * Inc. + * 4. The name of Berkeley Software Design, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. + * + * BSDI login_cap.c,v 2.13 1998/02/07 03:17:05 prb Exp + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: login_cap.c,v 1.29 2007/12/04 22:09:02 mjf Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u_quad_t multiply(u_quad_t, u_quad_t); +static u_quad_t strtolimit(const char *, char **, int); +static u_quad_t strtosize(const char *, char **, int); +static int gsetrl(login_cap_t *, int, const char *, int type); +static int isinfinite(const char *); +static int envset(void *, const char *, const char *, int); + +login_cap_t * +login_getclass(const char *class) +{ + const char *classfiles[2]; + login_cap_t *lc; + int res; + + /* class may be NULL */ + + if (secure_path(_PATH_LOGIN_CONF) == 0) { + classfiles[0] = _PATH_LOGIN_CONF; + classfiles[1] = NULL; + } else { + classfiles[0] = NULL; + } + + if ((lc = malloc(sizeof(login_cap_t))) == NULL) { + syslog(LOG_ERR, "%s:%d malloc: %m", __FILE__, __LINE__); + return (0); + } + + lc->lc_cap = 0; + lc->lc_style = 0; + + if (class == NULL || class[0] == '\0') + class = LOGIN_DEFCLASS; + + if ((lc->lc_class = strdup(class)) == NULL) { + syslog(LOG_ERR, "%s:%d strdup: %m", __FILE__, __LINE__); + free(lc); + return (0); + } + + /* + * Not having a login.conf file is not an error condition. + * The individual routines deal reasonably with missing + * capabilities and use default values. + */ + if (classfiles[0] == NULL) + return(lc); + + if ((res = cgetent(&lc->lc_cap, classfiles, lc->lc_class)) != 0) { + lc->lc_cap = 0; + switch (res) { + case 1: + syslog(LOG_ERR, "%s: couldn't resolve 'tc'", + lc->lc_class); + break; + case -1: + if (strcmp(lc->lc_class, LOGIN_DEFCLASS) == 0) + return (lc); + syslog(LOG_ERR, "%s: unknown class", lc->lc_class); + break; + case -2: + syslog(LOG_ERR, "%s: getting class information: %m", + lc->lc_class); + break; + case -3: + syslog(LOG_ERR, "%s: 'tc' reference loop", + lc->lc_class); + break; + default: + syslog(LOG_ERR, "%s: unexpected cgetent error", + lc->lc_class); + break; + } + free(lc->lc_class); + free(lc); + return (0); + } + return (lc); +} + +login_cap_t * +login_getpwclass(const struct passwd *pwd) +{ + + /* pwd may be NULL */ + + return login_getclass(pwd ? pwd->pw_class : NULL); +} + +char * +login_getcapstr(login_cap_t *lc, const char *cap, char *def, char *e) +{ + char *res = NULL; + int status; + + errno = 0; + + _DIAGASSERT(cap != NULL); + + if (!lc || !lc->lc_cap) + return (def); + + switch (status = cgetstr(lc->lc_cap, cap, &res)) { + case -1: + if (res) + free(res); + return (def); + case -2: + syslog(LOG_ERR, "%s: getting capability %s: %m", + lc->lc_class, cap); + if (res) + free(res); + return (e); + default: + if (status >= 0) + return (res); + syslog(LOG_ERR, "%s: unexpected error with capability %s", + lc->lc_class, cap); + if (res) + free(res); + return (e); + } +} + +quad_t +login_getcaptime(login_cap_t *lc, const char *cap, quad_t def, quad_t e) +{ + char *ep; + char *res = NULL, *sres; + int status; + quad_t q, r; + + _DIAGASSERT(cap != NULL); + + errno = 0; + if (!lc || !lc->lc_cap) + return (def); + + switch (status = cgetstr(lc->lc_cap, cap, &res)) { + case -1: + if (res) + free(res); + return (def); + case -2: + syslog(LOG_ERR, "%s: getting capability %s: %m", + lc->lc_class, cap); + errno = ERANGE; + if (res) + free(res); + return (e); + default: + if (status >= 0) + break; + syslog(LOG_ERR, "%s: unexpected error with capability %s", + lc->lc_class, cap); + errno = ERANGE; + if (res) + free(res); + return (e); + } + + if (isinfinite(res)) + return (RLIM_INFINITY); + + errno = 0; + + q = 0; + sres = res; + while (*res) { + r = strtoq(res, &ep, 0); + if (!ep || ep == res || + ((r == QUAD_MIN || r == QUAD_MAX) && errno == ERANGE)) { +invalid: + syslog(LOG_ERR, "%s:%s=%s: invalid time", + lc->lc_class, cap, sres); + errno = ERANGE; + free(sres); + return (e); + } + switch (*ep++) { + case '\0': + --ep; + break; + case 's': case 'S': + break; + case 'm': case 'M': + r *= 60; + break; + case 'h': case 'H': + r *= 60 * 60; + break; + case 'd': case 'D': + r *= 60 * 60 * 24; + break; + case 'w': case 'W': + r *= 60 * 60 * 24 * 7; + break; + case 'y': case 'Y': /* Pretty absurd */ + r *= 60 * 60 * 24 * 365; + break; + default: + goto invalid; + } + res = ep; + q += r; + } + free(sres); + return (q); +} + +quad_t +login_getcapnum(login_cap_t *lc, const char *cap, quad_t def, quad_t e) +{ + char *ep; + char *res = NULL; + int status; + quad_t q; + + _DIAGASSERT(cap != NULL); + + errno = 0; + if (!lc || !lc->lc_cap) + return (def); + + switch (status = cgetstr(lc->lc_cap, cap, &res)) { + case -1: + if (res) + free(res); + return (def); + case -2: + syslog(LOG_ERR, "%s: getting capability %s: %m", + lc->lc_class, cap); + errno = ERANGE; + if (res) + free(res); + return (e); + default: + if (status >= 0) + break; + syslog(LOG_ERR, "%s: unexpected error with capability %s", + lc->lc_class, cap); + errno = ERANGE; + if (res) + free(res); + return (e); + } + + if (isinfinite(res)) + return (RLIM_INFINITY); + + errno = 0; + q = strtoq(res, &ep, 0); + if (!ep || ep == res || ep[0] || + ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) { + syslog(LOG_ERR, "%s:%s=%s: invalid number", + lc->lc_class, cap, res); + errno = ERANGE; + free(res); + return (e); + } + free(res); + return (q); +} + +quad_t +login_getcapsize(login_cap_t *lc, const char *cap, quad_t def, quad_t e) +{ + char *ep; + char *res = NULL; + int status; + quad_t q; + + _DIAGASSERT(cap != NULL); + + errno = 0; + + if (!lc || !lc->lc_cap) + return (def); + + switch (status = cgetstr(lc->lc_cap, cap, &res)) { + case -1: + if (res) + free(res); + return (def); + case -2: + syslog(LOG_ERR, "%s: getting capability %s: %m", + lc->lc_class, cap); + errno = ERANGE; + if (res) + free(res); + return (e); + default: + if (status >= 0) + break; + syslog(LOG_ERR, "%s: unexpected error with capability %s", + lc->lc_class, cap); + errno = ERANGE; + if (res) + free(res); + return (e); + } + + errno = 0; + q = strtolimit(res, &ep, 0); + if (!ep || ep == res || (ep[0] && ep[1]) || + ((q == QUAD_MIN || q == QUAD_MAX) && errno == ERANGE)) { + syslog(LOG_ERR, "%s:%s=%s: invalid size", + lc->lc_class, cap, res); + errno = ERANGE; + free(res); + return (e); + } + free(res); + return (q); +} + +int +login_getcapbool(login_cap_t *lc, const char *cap, u_int def) +{ + + _DIAGASSERT(cap != NULL); + + if (!lc || !lc->lc_cap) + return (def); + + return (cgetcap(lc->lc_cap, cap, ':') != NULL); +} + +void +login_close(login_cap_t *lc) +{ + + if (lc) { + if (lc->lc_class) + free(lc->lc_class); + if (lc->lc_cap) + free(lc->lc_cap); + if (lc->lc_style) + free(lc->lc_style); + free(lc); + } +} + +#define R_CTIME 1 +#define R_CSIZE 2 +#define R_CNUMB 3 + +static struct { + int what; + int type; + const char *name; +} r_list[] = { + { RLIMIT_CPU, R_CTIME, "cputime", }, + { RLIMIT_FSIZE, R_CSIZE, "filesize", }, + { RLIMIT_DATA, R_CSIZE, "datasize", }, + { RLIMIT_STACK, R_CSIZE, "stacksize", }, + { RLIMIT_RSS, R_CSIZE, "memoryuse", }, + { RLIMIT_MEMLOCK, R_CSIZE, "memorylocked", }, + { RLIMIT_NPROC, R_CNUMB, "maxproc", }, + { RLIMIT_NOFILE, R_CNUMB, "openfiles", }, + { RLIMIT_CORE, R_CSIZE, "coredumpsize", }, + { RLIMIT_SBSIZE, R_CSIZE, "sbsize", }, + { -1, 0, 0 } +}; + +static int +gsetrl(login_cap_t *lc, int what, const char *name, int type) +{ + struct rlimit rl; + struct rlimit r; + char name_cur[32]; + char name_max[32]; + + _DIAGASSERT(name != NULL); + + (void)snprintf(name_cur, sizeof(name_cur), "%s-cur", name); + (void)snprintf(name_max, sizeof(name_max), "%s-max", name); + + if (getrlimit(what, &r)) { + syslog(LOG_ERR, "getting resource limit: %m"); + return (-1); + } + +#define RCUR r.rlim_cur +#define RMAX r.rlim_max + + switch (type) { + case R_CTIME: + RCUR = login_getcaptime(lc, name, RCUR, RCUR); + RMAX = login_getcaptime(lc, name, RMAX, RMAX); + rl.rlim_cur = login_getcaptime(lc, name_cur, RCUR, RCUR); + rl.rlim_max = login_getcaptime(lc, name_max, RMAX, RMAX); + break; + case R_CSIZE: + RCUR = login_getcapsize(lc, name, RCUR, RCUR); + RMAX = login_getcapsize(lc, name, RMAX, RMAX); + rl.rlim_cur = login_getcapsize(lc, name_cur, RCUR, RCUR); + rl.rlim_max = login_getcapsize(lc, name_max, RMAX, RMAX); + break; + case R_CNUMB: + RCUR = login_getcapnum(lc, name, RCUR, RCUR); + RMAX = login_getcapnum(lc, name, RMAX, RMAX); + rl.rlim_cur = login_getcapnum(lc, name_cur, RCUR, RCUR); + rl.rlim_max = login_getcapnum(lc, name_max, RMAX, RMAX); + break; + default: + syslog(LOG_ERR, "%s: invalid type %d setting resource limit %s", + lc->lc_class, type, name); + return (-1); + } + + if (setrlimit(what, &rl)) { + syslog(LOG_ERR, "%s: setting resource limit %s: %m", + lc->lc_class, name); + return (-1); + } +#undef RCUR +#undef RMAX + return (0); +} + +static int +/*ARGSUSED*/ +envset(void *envp __unused, const char *name, const char *value, int overwrite) +{ + return setenv(name, value, overwrite); +} + +int +setuserenv(login_cap_t *lc, envfunc_t senv, void *envp) +{ + const char *stop = ", \t"; + size_t i, count; + char *ptr; + char **res; + char *str = login_getcapstr(lc, "setenv", NULL, NULL); + + if (str == NULL || *str == '\0') + return 0; + + /* + * count the sub-strings, this may over-count since we don't + * account for escaped delimiters. + */ + for (i = 1, ptr = str; *ptr; i++) { + ptr += strcspn(ptr, stop); + if (*ptr) + ptr++; + } + + /* allocate ptr array and string */ + count = i; + res = malloc(count * sizeof(char *) + strlen(str) + 1); + + if (!res) + return -1; + + ptr = (char *)(void *)&res[count]; + (void)strcpy(ptr, str); + + /* split string */ + for (i = 0; (res[i] = stresep(&ptr, stop, '\\')) != NULL; ) + if (*res[i]) + i++; + + count = i; + + for (i = 0; i < count; i++) { + if ((ptr = strchr(res[i], '=')) != NULL) + *ptr++ = '\0'; + else + ptr = NULL; + (void)(*senv)(envp, res[i], ptr ? ptr : "", 1); + } + + free(res); + return 0; +} + +int +setclasscontext(const char *class, u_int flags) +{ + int ret; + login_cap_t *lc; + + flags &= LOGIN_SETRESOURCES | LOGIN_SETPRIORITY | LOGIN_SETUMASK | + LOGIN_SETPATH; + + lc = login_getclass(class); + ret = lc ? setusercontext(lc, NULL, 0, flags) : -1; + login_close(lc); + return (ret); +} + +int +setusercontext(login_cap_t *lc, struct passwd *pwd, uid_t uid, u_int flags) +{ + char per_user_tmp[MAXPATHLEN + 1]; + const char *component_name; + login_cap_t *flc; + quad_t p; + int i; + ssize_t len; + + flc = NULL; + + if (!lc) + flc = lc = login_getclass(pwd ? pwd->pw_class : NULL); + + /* + * Without the pwd entry being passed we cannot set either + * the group or the login. We could complain about it. + */ + if (pwd == NULL) + flags &= ~(LOGIN_SETGROUP|LOGIN_SETLOGIN); + +#ifdef LOGIN_OSETGROUP + if (pwd == NULL) + flags &= ~LOGIN_OSETGROUP; + if (flags & LOGIN_OSETGROUP) + flags = (flags & ~LOGIN_OSETGROUP) | LOGIN_SETGROUP; +#endif + if (flags & LOGIN_SETRESOURCES) + for (i = 0; r_list[i].name; ++i) + (void)gsetrl(lc, r_list[i].what, r_list[i].name, + r_list[i].type); + + if (flags & LOGIN_SETPRIORITY) { + p = login_getcapnum(lc, "priority", (quad_t)0, (quad_t)0); + + if (setpriority(PRIO_PROCESS, 0, (int)p) == -1) + syslog(LOG_ERR, "%s: setpriority: %m", lc->lc_class); + } + + if (flags & LOGIN_SETUMASK) { + p = login_getcapnum(lc, "umask", (quad_t) LOGIN_DEFUMASK, + (quad_t)LOGIN_DEFUMASK); + umask((mode_t)p); + } + + if (flags & LOGIN_SETGID) { + if (setgid(pwd->pw_gid) == -1) { + syslog(LOG_ERR, "setgid(%d): %m", pwd->pw_gid); + login_close(flc); + return (-1); + } + } + + if (flags & LOGIN_SETGROUPS) { + if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { + syslog(LOG_ERR, "initgroups(%s,%d): %m", + pwd->pw_name, pwd->pw_gid); + login_close(flc); + return (-1); + } + } + + /* Create per-user temporary directories if needed. */ + if ((len = readlink("/tmp", per_user_tmp, + sizeof(per_user_tmp) - 6)) != -1) { + + static const char atuid[] = "/@ruid"; + char *lp; + + /* readlink does not nul-terminate the string */ + per_user_tmp[len] = '\0'; + + /* Check if it's magic symlink. */ + lp = strstr(per_user_tmp, atuid); + if (lp != NULL && *(lp + (sizeof(atuid) - 1)) == '\0') { + lp++; + + if (snprintf(lp, 11, "/%u", pwd->pw_uid) > 10) { + syslog(LOG_ERR, "real temporary path too long"); + login_close(flc); + return (-1); + } + if (mkdir(per_user_tmp, S_IRWXU) != -1) { + if (chown(per_user_tmp, pwd->pw_uid, + pwd->pw_gid)) { + component_name = "chown"; + goto out; + } + + /* + * Must set sticky bit for tmp directory, some + * programs rely on this. + */ + if(chmod(per_user_tmp, S_IRWXU | S_ISVTX)) { + component_name = "chmod"; + goto out; + } + } else { + if (errno != EEXIST) { + component_name = "mkdir"; + goto out; + } else { + /* + * We must ensure that we own the + * directory and that is has the correct + * permissions, otherwise a DOS attack + * is possible. + */ + struct stat sb; + if (stat(per_user_tmp, &sb) == -1) { + component_name = "stat"; + goto out; + } + + if (sb.st_uid != pwd->pw_uid) { + if (chown(per_user_tmp, + pwd->pw_uid, pwd->pw_gid)) { + component_name = "chown"; + goto out; + } + } + + if (sb.st_mode != (S_IRWXU | S_ISVTX)) { + if (chmod(per_user_tmp, + S_IRWXU | S_ISVTX)) { + component_name = "chmod"; + goto out; + } + } + } + } + } + } + errno = 0; + + if (flags & LOGIN_SETLOGIN) + if (setlogin(pwd->pw_name) == -1) { + syslog(LOG_ERR, "setlogin(%s) failure: %m", + pwd->pw_name); + login_close(flc); + return (-1); + } + + if (flags & LOGIN_SETUSER) + if (setuid(uid) == -1) { + syslog(LOG_ERR, "setuid(%d): %m", uid); + login_close(flc); + return (-1); + } + + if (flags & LOGIN_SETENV) + setuserenv(lc, envset, NULL); + + if (flags & LOGIN_SETPATH) + setuserpath(lc, pwd ? pwd->pw_dir : "", envset, NULL); + + login_close(flc); + return (0); + +out: + if (component_name != NULL) { + syslog(LOG_ERR, "%s %s: %m", component_name, per_user_tmp); + login_close(flc); + return (-1); + } else { + syslog(LOG_ERR, "%s: %m", per_user_tmp); + login_close(flc); + return (-1); + } +} + +void +setuserpath(login_cap_t *lc, const char *home, envfunc_t senv, void *envp) +{ + size_t hlen, plen; + int cnt = 0; + char *path; + const char *cpath; + char *p, *q; + + _DIAGASSERT(home != NULL); + + hlen = strlen(home); + + p = path = login_getcapstr(lc, "path", NULL, NULL); + if (p) { + while (*p) + if (*p++ == '~') + ++cnt; + plen = (p - path) + cnt * (hlen + 1) + 1; + p = path; + q = path = malloc(plen); + if (q) { + while (*p) { + p += strspn(p, " \t"); + if (*p == '\0') + break; + plen = strcspn(p, " \t"); + if (hlen == 0 && *p == '~') { + p += plen; + continue; + } + if (q != path) + *q++ = ':'; + if (*p == '~') { + strcpy(q, home); + q += hlen; + ++p; + --plen; + } + memcpy(q, p, plen); + p += plen; + q += plen; + } + *q = '\0'; + cpath = path; + } else + cpath = _PATH_DEFPATH; + } else + cpath = _PATH_DEFPATH; + if ((*senv)(envp, "PATH", cpath, 1)) + warn("could not set PATH"); +} + +/* + * Convert an expression of the following forms + * 1) A number. + * 2) A number followed by a b (mult by 512). + * 3) A number followed by a k (mult by 1024). + * 5) A number followed by a m (mult by 1024 * 1024). + * 6) A number followed by a g (mult by 1024 * 1024 * 1024). + * 7) A number followed by a t (mult by 1024 * 1024 * 1024 * 1024). + * 8) Two or more numbers (with/without k,b,m,g, or t). + * separated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + */ +static u_quad_t +strtosize(const char *str, char **endptr, int radix) +{ + u_quad_t num, num2; + char *expr, *expr2; + + _DIAGASSERT(str != NULL); + /* endptr may be NULL */ + + errno = 0; + num = strtouq(str, &expr, radix); + if (errno || expr == str) { + if (endptr) + *endptr = expr; + return (num); + } + + switch(*expr) { + case 'b': case 'B': + num = multiply(num, (u_quad_t)512); + ++expr; + break; + case 'k': case 'K': + num = multiply(num, (u_quad_t)1024); + ++expr; + break; + case 'm': case 'M': + num = multiply(num, (u_quad_t)1024 * 1024); + ++expr; + break; + case 'g': case 'G': + num = multiply(num, (u_quad_t)1024 * 1024 * 1024); + ++expr; + break; + case 't': case 'T': + num = multiply(num, (u_quad_t)1024 * 1024); + num = multiply(num, (u_quad_t)1024 * 1024); + ++expr; + break; + } + + if (errno) + goto erange; + + switch(*expr) { + case '*': /* Backward compatible. */ + case 'x': + num2 = strtosize(expr+1, &expr2, radix); + if (errno) { + expr = expr2; + goto erange; + } + + if (expr2 == expr + 1) { + if (endptr) + *endptr = expr; + return (num); + } + expr = expr2; + num = multiply(num, num2); + if (errno) + goto erange; + break; + } + if (endptr) + *endptr = expr; + return (num); +erange: + if (endptr) + *endptr = expr; + errno = ERANGE; + return (UQUAD_MAX); +} + +static u_quad_t +strtolimit(const char *str, char **endptr, int radix) +{ + + _DIAGASSERT(str != NULL); + /* endptr may be NULL */ + + if (isinfinite(str)) { + if (endptr) + *endptr = (char *)__UNCONST(str) + strlen(str); + return ((u_quad_t)RLIM_INFINITY); + } + return (strtosize(str, endptr, radix)); +} + +static int +isinfinite(const char *s) +{ + static const char *infs[] = { + "infinity", + "inf", + "unlimited", + "unlimit", + NULL + }; + const char **i; + + _DIAGASSERT(s != NULL); + + for (i = infs; *i; i++) { + if (!strcasecmp(s, *i)) + return 1; + } + return 0; +} + +static u_quad_t +multiply(u_quad_t n1, u_quad_t n2) +{ + static int bpw = 0; + u_quad_t m; + u_quad_t r; + int b1, b2; + + /* + * Get rid of the simple cases + */ + if (n1 == 0 || n2 == 0) + return (0); + if (n1 == 1) + return (n2); + if (n2 == 1) + return (n1); + + /* + * sizeof() returns number of bytes needed for storage. + * This may be different from the actual number of useful bits. + */ + if (!bpw) { + bpw = sizeof(u_quad_t) * 8; + while (((u_quad_t)1 << (bpw-1)) == 0) + --bpw; + } + + /* + * First check the magnitude of each number. If the sum of the + * magnatude is way to high, reject the number. (If this test + * is not done then the first multiply below may overflow.) + */ + for (b1 = bpw; (((u_quad_t)1 << (b1-1)) & n1) == 0; --b1) + ; + for (b2 = bpw; (((u_quad_t)1 << (b2-1)) & n2) == 0; --b2) + ; + if (b1 + b2 - 2 > bpw) { + errno = ERANGE; + return (UQUAD_MAX); + } + + /* + * Decompose the multiplication to be: + * h1 = n1 & ~1 + * h2 = n2 & ~1 + * l1 = n1 & 1 + * l2 = n2 & 1 + * (h1 + l1) * (h2 + l2) + * (h1 * h2) + (h1 * l2) + (l1 * h2) + (l1 * l2) + * + * Since h1 && h2 do not have the low bit set, we can then say: + * + * (h1>>1 * h2>>1 * 4) + ... + * + * So if (h1>>1 * h2>>1) > (1<<(bpw - 2)) then the result will + * overflow. + * + * Finally, if MAX - ((h1 * l2) + (l1 * h2) + (l1 * l2)) < (h1*h2) + * then adding in residual amout will cause an overflow. + */ + + m = (n1 >> 1) * (n2 >> 1); + + if (m >= ((u_quad_t)1 << (bpw-2))) { + errno = ERANGE; + return (UQUAD_MAX); + } + + m *= 4; + + r = (n1 & n2 & 1) + + (n2 & 1) * (n1 & ~(u_quad_t)1) + + (n1 & 1) * (n2 & ~(u_quad_t)1); + + if ((u_quad_t)(m + r) < m) { + errno = ERANGE; + return (UQUAD_MAX); + } + m += r; + + return (m); +} diff --git a/lib/libutil/login_tty.c b/lib/libutil/login_tty.c new file mode 100644 index 000000000..66a4148cc --- /dev/null +++ b/lib/libutil/login_tty.c @@ -0,0 +1,66 @@ +/* $NetBSD: login_tty.c,v 1.12 2008/02/09 05:07:26 dholland 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)login_tty.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: login_tty.c,v 1.12 2008/02/09 05:07:26 dholland Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include + +int +login_tty(int fd) +{ + + _DIAGASSERT(fd != -1); + + (void) setsid(); +#ifdef TIOCSCTTY + if (ioctl(fd, TIOCSCTTY, (char *)NULL) == -1) + return (-1); +#endif + (void) dup2(fd, STDIN_FILENO); + (void) dup2(fd, STDOUT_FILENO); + (void) dup2(fd, STDERR_FILENO); + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + (void) close(fd); + return (0); +} diff --git a/lib/libutil/loginx.3 b/lib/libutil/loginx.3 new file mode 100644 index 000000000..5924bdec6 --- /dev/null +++ b/lib/libutil/loginx.3 @@ -0,0 +1,93 @@ +.\" $NetBSD: loginx.3,v 1.3 2008/04/30 13:10:52 martin Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Thomas Klausner. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd September 26, 2002 +.Dt LOGINX 3 +.Os +.Sh NAME +.Nm loginx , +.Nm logoutx , +.Nm logwtmpx +.Nd login utility functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft void +.Fn loginx "const struct utmpx *ut" +.Ft int +.Fn logoutx "const char *line" "int status" "int type" +.Ft void +.Fn logwtmpx "const char *line" "const char *name" "const char *host" "int status" "int type" +.Sh DESCRIPTION +The +.Fn loginx , +.Fn logoutx , +and +.Fn logwtmpx +operate on the +.Xr utmpx 5 +database of currently logged in users, and the +.Xr wtmpx 5 +database of logins and logouts. +.Pp +The +.Fn loginx +function updates the +.Pa /var/run/utmpx +and +.Pa /var/log/wtmpx +databases with the information from +.Fa ut . +.Pp +.Fn logoutx +updates the entry corresponding to +.Fa line +with the type and status from +.Fa type +and +.Fa status . +.Pp +.Fn logwtmpx +writes an entry filled with data from +.Fa line , +.Fa name , +.Fa host , +.Fa status , +and +.Fa type +to the +.Xr wtmpx 5 +database. +.Sh RETURN VALUES +.Fn logoutx +returns 1 on success, and 0 if no corresponding entry was found. +.Sh SEE ALSO +.Xr endutxent 3 , +.Xr utmpx 5 diff --git a/lib/libutil/loginx.c b/lib/libutil/loginx.c new file mode 100644 index 000000000..3e1cc12b1 --- /dev/null +++ b/lib/libutil/loginx.c @@ -0,0 +1,54 @@ +/* $NetBSD: loginx.c,v 1.2 2003/08/07 16:44:59 agc Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: loginx.c,v 1.2 2003/08/07 16:44:59 agc Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +loginx(const struct utmpx *ut) +{ + (void)pututxline(ut); + (void)updwtmpx(_PATH_WTMPX, ut); +} diff --git a/lib/libutil/logout.c b/lib/libutil/logout.c new file mode 100644 index 000000000..f324b1005 --- /dev/null +++ b/lib/libutil/logout.c @@ -0,0 +1,77 @@ +/* $NetBSD: logout.c,v 1.16 2005/08/27 17:07:17 elad Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)logout.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: logout.c,v 1.16 2005/08/27 17:07:17 elad Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int +logout(const char *line) +{ + int fd, rval; + struct utmp ut; + + _DIAGASSERT(line != NULL); + + if ((fd = open(_PATH_UTMP, O_RDWR, 0)) < 0) + return(0); + rval = 0; + while (read(fd, &ut, sizeof(ut)) == sizeof(ut)) { + if (!ut.ut_name[0] || strncmp(ut.ut_line, line, + (size_t)UT_LINESIZE)) + continue; + memset(ut.ut_name, 0, (size_t)UT_NAMESIZE); + memset(ut.ut_host, 0, (size_t)UT_HOSTSIZE); + (void)time(&ut.ut_time); + (void)lseek(fd, -(off_t)sizeof(ut), SEEK_CUR); + (void)write(fd, &ut, sizeof(ut)); + rval = 1; + } + (void)close(fd); + return(rval); +} diff --git a/lib/libutil/logoutx.c b/lib/libutil/logoutx.c new file mode 100644 index 000000000..5160a00cd --- /dev/null +++ b/lib/libutil/logoutx.c @@ -0,0 +1,73 @@ +/* $NetBSD: logoutx.c,v 1.2 2003/08/07 16:44:59 agc Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)logout.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: logoutx.c,v 1.2 2003/08/07 16:44:59 agc Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +logoutx(const char *line, int status, int type) +{ + struct utmpx *utp, ut; + (void)strlcpy(ut.ut_line, line, sizeof(ut.ut_line)); + if ((utp = getutxline(&ut)) == NULL) { + endutxent(); + return 0; + } + utp->ut_type = type; + if (WIFEXITED(status)) + utp->ut_exit.e_exit = (uint16_t)WEXITSTATUS(status); + if (WIFSIGNALED(status)) + utp->ut_exit.e_termination = (uint16_t)WTERMSIG(status); + (void)gettimeofday(&utp->ut_tv, NULL); + (void)pututxline(utp); + endutxent(); + return 1; +} diff --git a/lib/libutil/logwtmp.c b/lib/libutil/logwtmp.c new file mode 100644 index 000000000..3e9137828 --- /dev/null +++ b/lib/libutil/logwtmp.c @@ -0,0 +1,75 @@ +/* $NetBSD: logwtmp.c,v 1.14 2003/08/07 16:44:59 agc Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: logwtmp.c,v 1.14 2003/08/07 16:44:59 agc Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void +logwtmp(const char *line, const char *name, const char *host) +{ + struct utmp ut; + struct stat buf; + int fd; + + _DIAGASSERT(line != NULL); + _DIAGASSERT(name != NULL); + _DIAGASSERT(host != NULL); + + if ((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (fstat(fd, &buf) == 0) { + (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void) strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void) strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void) time(&ut.ut_time); + if (write(fd, &ut, sizeof(struct utmp)) != sizeof(struct utmp)) + (void) ftruncate(fd, buf.st_size); + } + (void) close(fd); +} diff --git a/lib/libutil/logwtmpx.c b/lib/libutil/logwtmpx.c new file mode 100644 index 000000000..57d3ede1d --- /dev/null +++ b/lib/libutil/logwtmpx.c @@ -0,0 +1,76 @@ +/* $NetBSD: logwtmpx.c,v 1.2 2003/08/07 16:44:59 agc Exp $ */ + +/* + * Copyright (c) 1988, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93"; +#else +__RCSID("$NetBSD: logwtmpx.c,v 1.2 2003/08/07 16:44:59 agc Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +void +logwtmpx(const char *line, const char *name, const char *host, int status, + int type) +{ + struct utmpx ut; + + _DIAGASSERT(line != NULL); + _DIAGASSERT(name != NULL); + _DIAGASSERT(host != NULL); + + (void)memset(&ut, 0, sizeof(ut)); + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + ut.ut_type = type; + if (WIFEXITED(status)) + ut.ut_exit.e_exit = (uint16_t)WEXITSTATUS(status); + if (WIFSIGNALED(status)) + ut.ut_exit.e_termination = (uint16_t)WTERMSIG(status); + (void)gettimeofday(&ut.ut_tv, NULL); + (void)updwtmpx(_PATH_WTMPX, &ut); +} diff --git a/lib/libutil/opendisk.3 b/lib/libutil/opendisk.3 new file mode 100644 index 000000000..63c3dc34f --- /dev/null +++ b/lib/libutil/opendisk.3 @@ -0,0 +1,172 @@ +.\" $NetBSD: opendisk.3,v 1.11 2008/04/30 13:10:52 martin Exp $ +.\" +.\" Copyright (c) 1997, 2001 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd December 11, 2001 +.Dt OPENDISK 3 +.Os +.Sh NAME +.Nm opendisk +.Nd open a disk partition +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fo opendisk +.Fa "const char *path" +.Fa "int flags" +.Fa "char *buf" +.Fa "size_t buflen" +.Fa "int iscooked" +.Fc +.Sh DESCRIPTION +.Fn opendisk +opens +.Fa path , +for reading and/or writing as specified by the argument +.Fa flags +using +.Xr open 2 , +and the file descriptor is returned to the caller. +.Fa buf +is used to store the resultant filename. +.Fa buflen +is the size, in bytes, of the array referenced by +.Fa buf +(usually +.Dv MAXPATHLEN +bytes). +.Fa iscooked +controls which paths in +.Pa /dev +are tried. +.Pp +.Fn opendisk +attempts to open the following variations of +.Fa path , +in order: +.Bl -tag -width "/dev/rpathX" +.It Pa path +The pathname as given. +.It Pa path Ns Em X +.Fa path +with a suffix of +.Sq Em X , +where +.Sq Em X +represents the raw partition of the device, as determined by +.Xr getrawpartition 3 , +usually +.Dq c . +.El +.Pp +If +.Fa path +does not contain a +slash +.Pq Dq / , +the following variations are attempted: +.Pp +.Bl -dash -offset indent +.It +If +.Fa iscooked +is zero: +.Pp +.Bl -tag -width "/dev/rpathX" +.It Pa /dev/rpath +.Fa path +with a prefix of +.Dq Pa /dev/r . +.It Pa /dev/rpath Ns Em X +.Fa path +with a prefix of +.Dq Pa /dev/r +and a suffix of +.Sq Em X +(q.v.). +.El +.It +If +.Fa iscooked +is non-zero: +.Bl -tag -width "/dev/rpathX" +.It Pa /dev/path +.Fa path +with a prefix of +.Dq Pa /dev/ . +.It Pa /dev/path Ns Em X +.Fa path +with a prefix of +.Dq Pa /dev/ +and a suffix of +.Sq Em X +(q.v.). +.El +.El +.Sh RETURN VALUES +An open file descriptor, or -1 if the +.Xr open 2 +failed. +.Sh ERRORS +.Fn opendisk +may set +.Va errno +to one of the following values: +.Bl -tag -width Er +.It Bq Er EINVAL +.Dv O_CREAT +was set in +.Fa flags , +or +.Xr getrawpartition 3 +didn't return a valid partition. +.It Bq Er EFAULT +.Fa buf +was the +.Dv NULL +pointer. +.El +.Pp +The +.Fn opendisk +function +may also set +.Va errno +to any value specified by the +.Xr open 2 +function. +.Sh SEE ALSO +.Xr open 2 , +.Xr getrawpartition 3 +.Sh HISTORY +The +.Fn opendisk +function first appeared in +.Nx 1.3 . diff --git a/lib/libutil/opendisk.c b/lib/libutil/opendisk.c new file mode 100644 index 000000000..f4f4b9141 --- /dev/null +++ b/lib/libutil/opendisk.c @@ -0,0 +1,104 @@ +/* $NetBSD: opendisk.c,v 1.12 2009/10/13 22:00:31 pooka Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: opendisk.c,v 1.12 2009/10/13 22:00:31 pooka Exp $"); +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +static int +__opendisk(const char *path, int flags, char *buf, size_t buflen, int iscooked, + int (*ofn)(const char *, int, ...)) +{ + int f, rawpart; + + if (buf == NULL) { + errno = EFAULT; + return (-1); + } + snprintf(buf, buflen, "%s", path); + + if ((flags & O_CREAT) != 0) { + errno = EINVAL; + return (-1); + } + + rawpart = getrawpartition(); + if (rawpart < 0) + return (-1); /* sysctl(3) in getrawpartition sets errno */ + + f = ofn(buf, flags, 0); + if (f != -1 || errno != ENOENT) + return (f); + + snprintf(buf, buflen, "%s%c", path, 'a' + rawpart); + f = ofn(buf, flags, 0); + if (f != -1 || errno != ENOENT) + return (f); + + if (strchr(path, '/') != NULL) + return (-1); + + snprintf(buf, buflen, "%s%s%s", _PATH_DEV, iscooked ? "" : "r", path); + f = ofn(buf, flags, 0); + if (f != -1 || errno != ENOENT) + return (f); + + snprintf(buf, buflen, "%s%s%s%c", _PATH_DEV, iscooked ? "" : "r", path, + 'a' + rawpart); + f = ofn(buf, flags, 0); + return (f); +} + +int +opendisk(const char *path, int flags, char *buf, size_t buflen, int iscooked) +{ + + return __opendisk(path, flags, buf, buflen, iscooked, open); +} + +int +opendisk1(const char *path, int flags, char *buf, size_t buflen, int iscooked, + int (*ofn)(const char *, int, ...)) +{ + + return __opendisk(path, flags, buf, buflen, iscooked, ofn); +} diff --git a/lib/libutil/openpty.3 b/lib/libutil/openpty.3 new file mode 100644 index 000000000..f6f453dd7 --- /dev/null +++ b/lib/libutil/openpty.3 @@ -0,0 +1,165 @@ +.\" $NetBSD: openpty.3,v 1.14 2008/11/28 07:17:17 dholland Exp $ +.\" +.\" Copyright (c) 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software developed by the Computer Systems +.\" Engineering group at Lawrence Berkeley Laboratory under DARPA contract +.\" BG 91-66 and contributed to Berkeley. +.\" +.\" 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. +.\" +.Dd November 28, 2008 +.Dt OPENPTY 3 +.Os +.Sh NAME +.Nm openpty , +.Nm login_tty , +.Nm forkpty +.Nd tty utility functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn openpty "int *amaster" "int *aslave" "char *name" "struct termios *termp" "struct winsize *winp" +.Ft int +.Fn login_tty "int fd" +.Ft pid_t +.Fn forkpty "int *amaster" "char *name" "struct termios *termp" "struct winsize *winp" +.Sh DESCRIPTION +The +.Fn openpty , +.Fn login_tty , +and +.Fn forkpty +functions perform manipulations on ttys and pseudo-ttys. +.Pp +The +.Fn openpty +function finds an available pseudo-tty and returns file descriptors +for the master and slave in +.Fa amaster +and +.Fa aslave . +If +.Fa name +is non-null, the filename of the slave is returned in +.Fa name . +If +.Fa termp +is non-null, the terminal parameters of the slave will be set to the +values in +.Fa termp . +If +.Fa winp +is non-null, the window size of the slave will be set to the values in +.Fa winp . +.Pp +The +.Fn login_tty +function prepares for a login on the tty +.Fa fd +(which may be a real tty device, or the slave of a pseudo-tty as +returned by +.Fn openpty ) +by creating a new session, making +.Fa fd +the controlling terminal for the current process, setting +.Fa fd +to be the standard input, output, and error streams of the current +process, and closing +.Fa fd . +.Pp +The +.Fn forkpty +function combines +.Fn openpty , +.Fn fork , +and +.Fn login_tty +to create a new process operating in a pseudo-tty. +The file descriptor of the master side of the pseudo-tty is returned +(to the parent process only) in +.Fa amaster . +The filename of the slave is returned (to both the parent and child +processes) in +.Fa name +if +.Fa name +is non-null. +The +.Fa termp +and +.Fa winp +parameters, if non-null, will determine the terminal attributes and +window size of the slave side of the pseudo-tty. +.Sh RETURN VALUES +If a call to +.Fn openpty , +.Fn login_tty , +or +.Fn forkpty +is not successful, -1 is returned and +.Va errno +is set to indicate the error. +Otherwise, +.Fn openpty , +.Fn login_tty , +and the child process of +.Fn forkpty +return 0, and the parent process of +.Fn forkpty +returns the process ID of the child process. +.Sh FILES +.Bl -tag -width /dev/[pt]ty[p-zP-T][0-9a-zA-Z] -compact +.It Pa /dev/[pt]ty[p-zP-T][0-9a-zA-Z] +.El +.Sh ERRORS +.Fn openpty +will fail if: +.Bl -tag -width Er +.It Bq Er ENOENT +There are no available ttys. +.It Bq Er EPERM +The caller was not the superuser and the +.Xr ptm 4 +device is missing or not configured. +.El +.Pp +.Fn login_tty +will fail if +.Fn ioctl +fails to set +.Fa fd +to the controlling terminal of the current process. +.Fn forkpty +will fail if either +.Fn openpty +or +.Fn fork +fails. +.Sh SEE ALSO +.Xr fork 2 diff --git a/lib/libutil/parsedate.3 b/lib/libutil/parsedate.3 new file mode 100644 index 000000000..024b0936d --- /dev/null +++ b/lib/libutil/parsedate.3 @@ -0,0 +1,278 @@ +.\" $NetBSD: parsedate.3,v 1.10 2010/12/22 09:12:28 wiz Exp $ +.\" +.\" Copyright (c) 2006 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. +.\" +.\" 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. +.\" +.Dd December 20, 2010 +.Dt PARSEDATE 3 +.Os +.Sh NAME +.Nm parsedate +.Nd date parsing function +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft time_t +.Fn parsedate "const char *datestr" "const time_t *time" "const int *tzoff" +.Sh DESCRIPTION +The +.Fn parsedate +function parses a datetime from +.Ar datestr +described in english relative to an optional +.Ar time +point and an optional timezone offset in seconds specified in +.Ar tzoff . +If either +.Ar time +or +.Ar tzoff +are +.Dv NULL , +then the current time and timezone offset are used. +.Pp +The +.Ar datestr +is a sequence of white-space separated items. +The white-space is optional the concatenated items are not ambiguous. +An empty +.Ar datestr +is equivalent to midnight today (the beginning of this day). +.Pp +The following words have the indicated numeric meanings: +.Dv last = +\-1, +.Dv this = +0, +.Dv first, next, or one = +1, +.Dv second +is unused so that it is not confused with +.Dq seconds , +.Dv two = +2, +.Dv third or three = +3, +.Dv fourth or four = +4, +.Dv fifth or five = +5, +.Dv sixth or six = +6, +.Dv seventh or seven = +7, +.Dv eighth or eight = +8, +.Dv ninth or nine = +9, +.Dv tenth or ten = +10, +.Dv eleventh or eleven = +11, +.Dv twelfth or twoelve = +12. +.Pp +The following words are recognized in English only: +.Dv AM , +.Dv PM , +.Dv a.m. , +.Dv p.m. +.Pp +The months: +.Dv january , +.Dv february , +.Dv march , +.Dv april , +.Dv may , +.Dv june , +.Dv july , +.Dv august , +.Dv september , +.Dv sept , +.Dv october , +.Dv november , +.Dv december , +.Pp +The days of the week: +.Dv sunday , +.Dv monday , +.Dv tuesday , +.Dv tues , +.Dv wednesday , +.Dv wednes , +.Dv thursday , +.Dv thur , +.Dv thurs , +.Dv friday , +.Dv saturday . +.Pp +Time units: +.Dv year , +.Dv month , +.Dv fortnight , +.Dv week , +.Dv day , +.Dv hour , +.Dv minute , +.Dv min , +.Dv second , +.Dv sec , +.Dv tomorrow , +.Dv yesterday . +.Pp +Timezone names: +.Dv gmt , +.Dv ut , +.Dv utc , +.Dv wet , +.Dv bst , +.Dv wat , +.Dv at , +.Dv ast , +.Dv adt , +.Dv est , +.Dv edt , +.Dv cst , +.Dv cdt , +.Dv mst , +.Dv mdt , +.Dv pst , +.Dv pdt , +.Dv yst , +.Dv ydt , +.Dv hst , +.Dv hdt , +.Dv cat , +.Dv ahst , +.Dv nt , +.Dv idlw , +.Dv cet , +.Dv met , +.Dv mewt , +.Dv mest , +.Dv swt , +.Dv sst , +.Dv fwt , +.Dv fst , +.Dv eet , +.Dv bt , +.Dv zp4 , +.Dv zp5 , +.Dv zp6 , +.Dv wast , +.Dv wadt , +.Dv cct , +.Dv jst , +.Dv east , +.Dv eadt , +.Dv gst , +.Dv nzt , +.Dv nzst , +.Dv nzdt , +.Dv idle . +.Pp +A variety of unambiguous dates are recognized: +.Bl -tag -compact -width "20 Jun 1994" +.It 69-09-10 +For years between 69-99 we assume 1900+ and for years between 0-68 +we assume 2000+. +.It 2006-11-17 +An ISO-8601 date. +.It 10/1/2000 +October 10, 2000; the common US format. +.It 20 Jun 1994 +.It 23jun2001 +.It 1-sep-06 +Other common abbreviations. +.It 1/11 +the year can be omitted +.El +.Pp +As well as times: +.Bl -tag -compact -width 12:11:01.000012 +.It 10:01 +.It 10:12pm +.It 12:11:01.000012 +.It 12:21-0500 +.El +.Pp +Relative items are also supported: +.Bl -tag -compact -width "this thursday" +.It -1 month +.It last friday +.It one week ago +.It this thursday +.It next sunday +.It +2 years +.El +.Pp +Seconds since epoch (also known as UNIX time) are also supported: +.Bl -tag -compact -width "@735275209" +.It @735275209 +Tue Apr 20 03:06:49 UTC 1993 +.El +.Sh RETURN VALUES +.Fn parsedate +returns the number of seconds passed since the Epoch, or +.Dv \-1 +if the date could not be parsed properly. +.Sh SEE ALSO +.Xr date 1 , +.Xr eeprom 8 +.Sh HISTORY +The parser used in +.Fn parsedate +was originally written by Steven M. Bellovin while at the University +of North Carolina at Chapel Hill. +It was later tweaked by a couple of people on Usenet. +Completely overhauled by Rich $alz and Jim Berets in August, 1990. +.Pp +The +.Fn parsedate +function first appeared in +.Nx 4.0 . +.Sh BUGS +.Bl -tag -compact -width 1 +.It 1 +The +.Fn parsedate +function is not re-entrant or thread-safe. +.It 2 +The +.Fn parsedate +function cannot compute days before the unix epoch (19700101). +.It 3 +The +.Fn parsedate +function assumes years less than 0 mean - +.Fa year , +years less than 70 mean 2000 + +.Fa year , +years less than 100 mean 1900 + +.Fa year . +.El diff --git a/lib/libutil/parsedate.y b/lib/libutil/parsedate.y new file mode 100644 index 000000000..bc592ce17 --- /dev/null +++ b/lib/libutil/parsedate.y @@ -0,0 +1,1060 @@ +%{ +/* +** Originally written by Steven M. Bellovin while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** and Jim Berets in August, 1990; +** +** This grammar has 10 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#include +#include +#include +#include +#include +#include + +/* NOTES on rebuilding parsedate.c (particularly for inclusion in CVS + releases): + + We don't want to mess with all the portability hassles of alloca. + In particular, most (all?) versions of bison will use alloca in + their parser. If bison works on your system (e.g. it should work + with gcc), then go ahead and use it, but the more general solution + is to use byacc instead of bison, which should generate a portable + parser. I played with adding "#define alloca dont_use_alloca", to + give an error if the parser generator uses alloca (and thus detect + unportable parsedate.c's), but that seems to cause as many problems + as it solves. */ + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +struct dateinfo { + DSTMODE yyDSTmode; + time_t yyDayOrdinal; + time_t yyDayNumber; + int yyHaveDate; + int yyHaveDay; + int yyHaveRel; + int yyHaveTime; + int yyHaveZone; + time_t yyTimezone; + time_t yyDay; + time_t yyHour; + time_t yyMinutes; + time_t yyMonth; + time_t yySeconds; + time_t yyYear; + MERIDIAN yyMeridian; + time_t yyRelMonth; + time_t yyRelSeconds; +}; +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST AT_SIGN + +%type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type tMERIDIAN o_merid + +%parse-param { struct dateinfo *param } +%parse-param { const char **yyInput } +%lex-param { const char **yyInput } +%pure-parser + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + param->yyHaveTime++; + } + | zone { + param->yyHaveZone++; + } + | date { + param->yyHaveDate++; + } + | day { + param->yyHaveDay++; + } + | rel { + param->yyHaveRel++; + } + | cvsstamp { + param->yyHaveTime++; + param->yyHaveDate++; + param->yyHaveZone++; + } + | epochdate { + param->yyHaveTime++; + param->yyHaveDate++; + param->yyHaveZone++; + } + | number + ; + +cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER { + param->yyYear = $1; + if (param->yyYear < 100) param->yyYear += 1900; + param->yyMonth = $3; + param->yyDay = $5; + param->yyHour = $7; + param->yyMinutes = $9; + param->yySeconds = $11; + param->yyDSTmode = DSToff; + param->yyTimezone = 0; + } + ; + +epochdate: AT_SIGN tUNUMBER { + time_t when = $2; + struct tm tmbuf; + if (gmtime_r(&when, &tmbuf) != NULL) { + param->yyYear = tmbuf.tm_year + 1900; + param->yyMonth = tmbuf.tm_mon + 1; + param->yyDay = tmbuf.tm_mday; + + param->yyHour = tmbuf.tm_hour; + param->yyMinutes = tmbuf.tm_min; + param->yySeconds = tmbuf.tm_sec; + } else { + param->yyYear = EPOCH; + param->yyMonth = 1; + param->yyDay = 1; + + param->yyHour = 0; + param->yyMinutes = 0; + param->yySeconds = 0; + } + param->yyDSTmode = DSToff; + param->yyTimezone = 0; + } + ; + +time : tUNUMBER tMERIDIAN { + param->yyHour = $1; + param->yyMinutes = 0; + param->yySeconds = 0; + param->yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + param->yyHour = $1; + param->yyMinutes = $3; + param->yySeconds = 0; + param->yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + param->yyHour = $1; + param->yyMinutes = $3; + param->yyMeridian = MER24; + param->yyDSTmode = DSToff; + param->yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + param->yyHour = $1; + param->yyMinutes = $3; + param->yySeconds = $5; + param->yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + param->yyHour = $1; + param->yyMinutes = $3; + param->yySeconds = $5; + param->yyMeridian = MER24; + param->yyDSTmode = DSToff; + param->yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER '.' tUNUMBER { + param->yyHour = $1; + param->yyMinutes = $3; + param->yySeconds = $5; + param->yyMeridian = MER24; + param->yyDSTmode = DSToff; +/* XXX: Do nothing with millis */ +/* param->yyTimezone = ($7 % 100 + ($7 / 100) * 60); */ + } + ; + +zone : tZONE { + param->yyTimezone = $1; + param->yyDSTmode = DSToff; + } + | tDAYZONE { + param->yyTimezone = $1; + param->yyDSTmode = DSTon; + } + | + tZONE tDST { + param->yyTimezone = $1; + param->yyDSTmode = DSTon; + } + ; + +day : tDAY { + param->yyDayOrdinal = 1; + param->yyDayNumber = $1; + } + | tDAY ',' { + param->yyDayOrdinal = 1; + param->yyDayNumber = $1; + } + | tUNUMBER tDAY { + param->yyDayOrdinal = $1; + param->yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + param->yyMonth = $1; + param->yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + if ($1 >= 100) { + param->yyYear = $1; + param->yyMonth = $3; + param->yyDay = $5; + } else { + param->yyMonth = $1; + param->yyDay = $3; + param->yyYear = $5; + } + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + param->yyYear = $1; + param->yyMonth = -$2; + param->yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + param->yyDay = $1; + param->yyMonth = $2; + param->yyYear = -$3; + } + | tMONTH tUNUMBER { + param->yyMonth = $1; + param->yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + param->yyMonth = $1; + param->yyDay = $2; + param->yyYear = $4; + } + | tUNUMBER tMONTH { + param->yyMonth = $2; + param->yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + param->yyMonth = $2; + param->yyDay = $1; + param->yyYear = $3; + } + ; + +rel : relunit tAGO { + param->yyRelSeconds = -param->yyRelSeconds; + param->yyRelMonth = -param->yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + param->yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + param->yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + param->yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + param->yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + param->yyRelSeconds += $1; + } + | tSEC_UNIT { + param->yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + param->yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + param->yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + param->yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (param->yyHaveTime && param->yyHaveDate && !param->yyHaveRel) + param->yyYear = $1; + else { + if($1>10000) { + param->yyHaveDate++; + param->yyDay= ($1)%100; + param->yyMonth= ($1/100)%100; + param->yyYear = $1/10000; + } + else { + param->yyHaveTime++; + if ($1 < 100) { + param->yyHour = $1; + param->yyMinutes = 0; + } + else { + param->yyHour = $1 / 100; + param->yyMinutes = $1 % 100; + } + param->yySeconds = 0; + param->yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static const TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static const TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static const TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, + { "one", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "two", tUNUMBER, 2 }, + { "third", tUNUMBER, 3 }, + { "three", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "four", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "five", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "six", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "seven", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "eight", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "nine", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "ten", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "eleven", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "twelve", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static const TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static const TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL, 0, 0 } +}; + + + + +/* ARGSUSED */ +static int +yyerror(struct dateinfo *param, const char **inp, const char *s __unused) +{ + return 0; +} + + +static time_t +ToSeconds( + time_t Hours, + time_t Minutes, + time_t Seconds, + MERIDIAN Meridian +) +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + +static int +isLeap(int year) +{ + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); +} + + +/* Year is either + * A negative number, which means to use its absolute value (why?) + * A number from 0 to 99, which means a year from 1900 to 1999, or + * The actual year (>=100). */ +static time_t +Convert( + time_t Month, + time_t Day, + time_t Year, + time_t Hours, + time_t Minutes, + time_t Seconds, + time_t Timezone, + MERIDIAN Meridian, + DSTMODE DSTmode +) +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian, oJulian; + int i; + + /* XXX Y2K */ + if (Year < 0) + Year = -Year; + if (Year < 70) + Year += 2000; + else if (Year < 100) + Year += 1900; + DaysInMonth[1] = isLeap(Year) ? 29 : 28; + if (Year < EPOCH || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + /* FIXME: + * It would be nice to set a global error string here. + * "February 30 is not a valid date" is much more informative than + * "Can't parse date/time: 100 months" when the user input was + * "100 months" and addition resolved that to February 30, for + * example. See rcs2-7 in src/sanity.sh for more. */ + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + + oJulian = Julian; + for (i = EPOCH; i < Year; i++) { + Julian += 365 + isLeap(i); + if (oJulian > Julian) + return -1; + oJulian = Julian; + } + + Julian *= SECSPERDAY; + if (oJulian > Julian) + return -1; + oJulian = Julian; + Julian += Timezone * 60L; + if (Timezone > 0 && oJulian > Julian) + return -1; + oJulian = Julian; + + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + + Julian += tod; + if (oJulian > Julian) + return -1; + + if (DSTmode == DSTon || (DSTmode == DSTmaybe)) { + struct tm *tm; + if ((tm = localtime(&Julian)) == NULL) + return -1; + if (tm->tm_isdst) + Julian -= 60 * 60; + } + return Julian; +} + + +static time_t +DSTcorrect( + time_t Start, + time_t Future +) +{ + time_t StartDay; + time_t FutureDay; + struct tm *tm; + + if ((tm = localtime(&Start)) == NULL) + return -1; + StartDay = (tm->tm_hour + 1) % 24; + + if ((tm = localtime(&Future)) == NULL) + return -1; + FutureDay = (tm->tm_hour + 1) % 24; + + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate( + time_t Start, + time_t DayOrdinal, + time_t DayNumber +) +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth( + time_t Start, + time_t RelMonth, + time_t Timezone +) +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + if (tm == NULL) + return -1; + Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + Timezone, MER24, DSTmaybe)); +} + + +static int +LookupWord(YYSTYPE *yylval, char *buff) +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper((unsigned char)*p)) + *p = tolower((unsigned char)*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval->Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval->Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval->Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha((unsigned char)*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval->Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex(YYSTYPE *yylval, const char **yyInput) +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + const char *inp = *yyInput; + + for ( ; ; ) { + while (isspace((unsigned char)*inp)) + inp++; + + if (isdigit((unsigned char)(c = *inp)) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit((unsigned char)*++inp)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval->Number = 0; isdigit((unsigned char)(c = *inp++)); ) + yylval->Number = 10 * yylval->Number + c - '0'; + if (sign < 0) + yylval->Number = -yylval->Number; + *yyInput = --inp; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha((unsigned char)c)) { + for (p = buff; isalpha((unsigned char)(c = *inp++)) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + *yyInput = --inp; + return LookupWord(yylval, buff); + } + if (c == '@') { + *yyInput = ++inp; + return AT_SIGN; + } + if (c != '(') { + *yyInput = ++inp; + return c; + } + Count = 0; + do { + c = *inp++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static time_t +difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (long)(ay-by) * 365 + ); + return ((time_t)60*(60*(24*days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +parsedate(const char *p, const time_t *now, const int *zone) +{ + struct tm gmt, local, *gmt_ptr, *tm; + time_t nowt; + int zonet; + time_t Start; + time_t tod, rm; + struct dateinfo param; + + if (now == NULL || zone == NULL) { + now = &nowt; + zone = &zonet; + (void)time(&nowt); + + gmt_ptr = gmtime_r(now, &gmt); + if ((tm = localtime_r(now, &local)) == NULL) + return -1; + + if (gmt_ptr != NULL) + zonet = difftm(&gmt, &local) / 60; + else + /* We are on a system like VMS, where the system clock is + in local time and the system has no concept of timezones. + Hopefully we can fake this out (for the case in which the + user specifies no timezone) by just saying the timezone + is zero. */ + zonet = 0; + + if (local.tm_isdst) + zonet += 60; + } else { + if ((tm = localtime_r(now, &local)) == NULL) + return -1; + } + param.yyYear = tm->tm_year + 1900; + param.yyMonth = tm->tm_mon + 1; + param.yyDay = tm->tm_mday; + param.yyTimezone = *zone; + param.yyDSTmode = DSTmaybe; + param.yyHour = 0; + param.yyMinutes = 0; + param.yySeconds = 0; + param.yyMeridian = MER24; + param.yyRelSeconds = 0; + param.yyRelMonth = 0; + param.yyHaveDate = 0; + param.yyHaveDay = 0; + param.yyHaveRel = 0; + param.yyHaveTime = 0; + param.yyHaveZone = 0; + + if (yyparse(¶m, &p) || param.yyHaveTime > 1 || param.yyHaveZone > 1 || + param.yyHaveDate > 1 || param.yyHaveDay > 1) + return -1; + + if (param.yyHaveDate || param.yyHaveTime || param.yyHaveDay) { + Start = Convert(param.yyMonth, param.yyDay, param.yyYear, param.yyHour, + param.yyMinutes, param.yySeconds, param.yyTimezone, + param.yyMeridian, param.yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = *now; + if (!param.yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += param.yyRelSeconds; + rm = RelativeMonth(Start, param.yyRelMonth, param.yyTimezone); + if (rm == -1) + return -1; + Start += rm; + + if (param.yyHaveDay && !param.yyHaveDate) { + tod = RelativeDate(Start, param.yyDayOrdinal, param.yyDayNumber); + Start += tod; + } + + return Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +int +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = parsedate(buff, NULL, NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/lib/libutil/passwd.c b/lib/libutil/passwd.c new file mode 100644 index 000000000..972ea03fc --- /dev/null +++ b/lib/libutil/passwd.c @@ -0,0 +1,665 @@ +/* $NetBSD: passwd.c,v 1.50 2010/08/18 08:32:02 christos Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994, 1995 + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: passwd.c,v 1.50 2010/08/18 08:32:02 christos Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *pw_filename(const char *filename); +static void pw_cont(int sig); +static const char * pw_equal(char *buf, struct passwd *old_pw); +static const char *pw_default(const char *option); +static int read_line(FILE *fp, char *line, int max); +static void trim_whitespace(char *line); + +static char pw_prefix[MAXPATHLEN]; + +const char * +pw_getprefix(void) +{ + + return(pw_prefix); +} + +int +pw_setprefix(const char *new_prefix) +{ + size_t length; + + _DIAGASSERT(new_prefix != NULL); + + length = strlen(new_prefix); + if (length < sizeof(pw_prefix)) { + (void)strcpy(pw_prefix, new_prefix); + while (length > 0 && pw_prefix[length - 1] == '/') + pw_prefix[--length] = '\0'; + return(0); + } + errno = ENAMETOOLONG; + return(-1); +} + +static const char * +pw_filename(const char *filename) +{ + static char newfilename[MAXPATHLEN]; + + _DIAGASSERT(filename != NULL); + + if (pw_prefix[0] == '\0') + return filename; + + if (strlen(pw_prefix) + strlen(filename) < sizeof(newfilename)) + return strcat(strcpy(newfilename, pw_prefix), filename); + + errno = ENAMETOOLONG; + return(NULL); +} + +int +pw_lock(int retries) +{ + const char *filename; + int i, fd; + mode_t old_mode; + int oerrno; + + /* Acquire the lock file. */ + filename = pw_filename(_PATH_MASTERPASSWD_LOCK); + if (filename == NULL) + return(-1); + old_mode = umask(0); + fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, 0600); + for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) { + sleep(1); + fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, + 0600); + } + oerrno = errno; + (void)umask(old_mode); + errno = oerrno; + return(fd); +} + +int +pw_mkdb(username, secureonly) + const char *username; + int secureonly; +{ + const char *args[9]; + int pstat, i; + pid_t pid; + + pid = vfork(); + if (pid == -1) + return -1; + + if (pid == 0) { + args[0] = "pwd_mkdb"; + args[1] = "-d"; + args[2] = pw_prefix; + args[3] = "-pl"; + i = 4; + + if (secureonly) + args[i++] = "-s"; + if (username != NULL) { + args[i++] = "-u"; + args[i++] = username; + } + + args[i++] = pw_filename(_PATH_MASTERPASSWD_LOCK); + args[i] = NULL; + execv(_PATH_PWD_MKDB, (char * const *)__UNCONST(args)); + _exit(1); + } + pid = waitpid(pid, &pstat, 0); + if (pid == -1) { + warn("error waiting for pid %lu", (unsigned long)pid); + return -1; + } + if (WIFEXITED(pstat)) { + if (WEXITSTATUS(pstat) != 0) { + warnx("pwd_mkdb exited with static %d", + WEXITSTATUS(pstat)); + return -1; + } + } else if (WIFSIGNALED(pstat)) { + warnx("pwd_mkdb exited with signal %d", WTERMSIG(pstat)); + return -1; + } + return 0; +} + +int +pw_abort(void) +{ + const char *filename; + + filename = pw_filename(_PATH_MASTERPASSWD_LOCK); + return((filename == NULL) ? -1 : unlink(filename)); +} + +/* Everything below this point is intended for the convenience of programs + * which allow a user to interactively edit the passwd file. Errors in the + * routines below will cause the process to abort. */ + +static pid_t editpid = -1; + +static void +pw_cont(int sig) +{ + + if (editpid != -1) + kill(editpid, sig); +} + +void +pw_init(void) +{ + struct rlimit rlim; + +#ifndef __minix + /* Unlimited resource limits. */ + rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; + (void)setrlimit(RLIMIT_CPU, &rlim); + (void)setrlimit(RLIMIT_FSIZE, &rlim); + (void)setrlimit(RLIMIT_STACK, &rlim); + (void)setrlimit(RLIMIT_DATA, &rlim); + (void)setrlimit(RLIMIT_RSS, &rlim); + + /* Don't drop core (not really necessary, but GP's). */ + rlim.rlim_cur = rlim.rlim_max = 0; + (void)setrlimit(RLIMIT_CORE, &rlim); +#endif + + /* Turn off signals. */ + (void)signal(SIGALRM, SIG_IGN); + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGPIPE, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGTERM, SIG_IGN); + (void)signal(SIGCONT, pw_cont); +} + +void +pw_edit(int notsetuid, const char *filename) +{ + int pstat; + char *p; + const char * volatile editor; + const char *argp[] = { "sh", "-c", NULL, NULL }; + + if (filename == NULL) + filename = _PATH_MASTERPASSWD_LOCK; + + filename = pw_filename(filename); + if (filename == NULL) + return; + + if ((editor = getenv("EDITOR")) == NULL) + editor = _PATH_VI; + + p = malloc(strlen(editor) + 1 + strlen(filename) + 1); + if (p == NULL) + return; + + sprintf(p, "%s %s", editor, filename); + argp[2] = p; + + switch(editpid = vfork()) { + case -1: + free(p); + return; + case 0: + if (notsetuid) { + setgid(getgid()); + setuid(getuid()); + } + execvp(_PATH_BSHELL, (char *const *)__UNCONST(argp)); + _exit(1); + } + + free(p); + + for (;;) { + editpid = waitpid(editpid, (int *)&pstat, WUNTRACED); + if (editpid == -1) + pw_error(editor, 1, 1); + else if (WIFSTOPPED(pstat)) + raise(WSTOPSIG(pstat)); + else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0) + break; + else + pw_error(editor, 1, 1); + } + editpid = -1; +} + +void +pw_prompt(void) +{ + int c; + + (void)printf("re-edit the password file? [y]: "); + (void)fflush(stdout); + c = getchar(); + if (c != EOF && c != '\n') + while (getchar() != '\n'); + if (c == 'n') + pw_error(NULL, 0, 0); +} + +/* for use in pw_copy(). Compare a pw entry to a pw struct. */ +/* returns a character string labelling the miscompared field or 0 */ +static const char * +pw_equal(char *buf, struct passwd *pw) +{ + struct passwd buf_pw; + size_t len; + + _DIAGASSERT(buf != NULL); + _DIAGASSERT(pw != NULL); + + len = strlen (buf); + if (buf[len-1] == '\n') + buf[len-1] = '\0'; + if (!pw_scan(buf, &buf_pw, NULL)) + return "corrupt line"; + if (strcmp(pw->pw_name, buf_pw.pw_name) != 0) + return "name"; + if (pw->pw_uid != buf_pw.pw_uid) + return "uid"; + if (pw->pw_gid != buf_pw.pw_gid) + return "gid"; + if (strcmp( pw->pw_class, buf_pw.pw_class) != 0) + return "class"; + if (pw->pw_change != buf_pw.pw_change) + return "change"; + if (pw->pw_expire != buf_pw.pw_expire) + return "expire"; + if (strcmp( pw->pw_gecos, buf_pw.pw_gecos) != 0) + return "gecos"; + if (strcmp( pw->pw_dir, buf_pw.pw_dir) != 0) + return "dir"; + if (strcmp( pw->pw_shell, buf_pw.pw_shell) != 0) + return "shell"; + return (char *)0; +} + +void +pw_copy(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw) +{ + char errbuf[200]; + int rv; + + rv = pw_copyx(ffd, tfd, pw, old_pw, errbuf, sizeof(errbuf)); + if (rv == 0) { + warnx("%s", errbuf); + pw_error(NULL, 0, 1); + } +} + +static void +pw_print(FILE *to, const struct passwd *pw) +{ + (void)fprintf(to, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n", + pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid, + pw->pw_class, (long long)pw->pw_change, + (long long)pw->pw_expire, + pw->pw_gecos, pw->pw_dir, pw->pw_shell); +} + +int +pw_copyx(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw, + char *errbuf, size_t errbufsz) +{ + const char *filename; + char mpwd[MAXPATHLEN], mpwdl[MAXPATHLEN], *p, buf[8192]; + FILE *from, *to; + int done; + + _DIAGASSERT(pw != NULL); + _DIAGASSERT(errbuf != NULL); + /* old_pw may be NULL */ + + if ((filename = pw_filename(_PATH_MASTERPASSWD)) == NULL) { + snprintf(errbuf, errbufsz, "%s: %s", pw_prefix, + strerror(errno)); + return (0); + } + (void)strcpy(mpwd, filename); + if ((filename = pw_filename(_PATH_MASTERPASSWD_LOCK)) == NULL) { + snprintf(errbuf, errbufsz, "%s: %s", pw_prefix, + strerror(errno)); + return (0); + } + (void)strcpy(mpwdl, filename); + + if (!(from = fdopen(ffd, "r"))) { + snprintf(errbuf, errbufsz, "%s: %s", mpwd, strerror(errno)); + return (0); + } + if (!(to = fdopen(tfd, "w"))) { + snprintf(errbuf, errbufsz, "%s: %s", mpwdl, strerror(errno)); + (void)fclose(from); + return (0); + } + + for (done = 0; fgets(buf, (int)sizeof(buf), from);) { + const char *neq; + if (!strchr(buf, '\n')) { + snprintf(errbuf, errbufsz, "%s: line too long", mpwd); + (void)fclose(from); + (void)fclose(to); + return (0); + } + if (done) { + (void)fprintf(to, "%s", buf); + if (ferror(to)) { + snprintf(errbuf, errbufsz, "%s", + strerror(errno)); + (void)fclose(from); + (void)fclose(to); + return (0); + } + continue; + } + if (!(p = strchr(buf, ':'))) { + snprintf(errbuf, errbufsz, "%s: corrupted entry", mpwd); + (void)fclose(from); + (void)fclose(to); + return (0); + } + *p = '\0'; + if (strcmp(buf, pw->pw_name)) { + *p = ':'; + (void)fprintf(to, "%s", buf); + if (ferror(to)) { + snprintf(errbuf, errbufsz, "%s", + strerror(errno)); + (void)fclose(from); + (void)fclose(to); + return (0); + } + continue; + } + *p = ':'; + if (old_pw && (neq = pw_equal(buf, old_pw)) != NULL) { + if (strcmp(neq, "corrupt line") == 0) + (void)snprintf(errbuf, errbufsz, + "%s: entry %s corrupted", mpwd, + pw->pw_name); + else + (void)snprintf(errbuf, errbufsz, + "%s: entry %s inconsistent %s", + mpwd, pw->pw_name, neq); + (void)fclose(from); + (void)fclose(to); + return (0); + } + pw_print(to, pw); + done = 1; + if (ferror(to)) { + snprintf(errbuf, errbufsz, "%s", strerror(errno)); + (void)fclose(from); + (void)fclose(to); + return (0); + } + } + /* Only append a new entry if real uid is root! */ + if (!done) { + if (getuid() == 0) { + pw_print(to, pw); + done = 1; + } else { + snprintf(errbuf, errbufsz, + "%s: changes not made, no such entry", mpwd); + } + } + + if (ferror(to)) { + snprintf(errbuf, errbufsz, "%s", strerror(errno)); + (void)fclose(from); + (void)fclose(to); + return (0); + } + (void)fclose(from); + (void)fclose(to); + + return (done); +} + +void +pw_error(const char *name, int error, int eval) +{ + + if (error) { + if (name) + warn("%s", name); + else + warn(NULL); + } + + warnx("%s%s: unchanged", pw_prefix, _PATH_MASTERPASSWD); + pw_abort(); + exit(eval); +} + +/* Removes head and/or tail spaces. */ +static void +trim_whitespace(char *line) +{ + char *p; + + _DIAGASSERT(line != NULL); + + /* Remove leading spaces */ + p = line; + while (isspace((unsigned char) *p)) + p++; + memmove(line, p, strlen(p) + 1); + + /* Remove trailing spaces */ + p = line + strlen(line) - 1; + while (isspace((unsigned char) *p)) + p--; + *(p + 1) = '\0'; +} + + +/* Get one line, remove spaces from front and tail */ +static int +read_line(FILE *fp, char *line, int max) +{ + char *p; + + _DIAGASSERT(fp != NULL); + _DIAGASSERT(line != NULL); + + /* Read one line of config */ + if (fgets(line, max, fp) == NULL) + return (0); + + if ((p = strchr(line, '\n')) == NULL) { + warnx("line too long"); + return (0); + } + *p = '\0'; + + /* Remove comments */ + if ((p = strchr(line, '#')) != NULL) + *p = '\0'; + + trim_whitespace(line); + return (1); +} + +static const char * +pw_default(const char *option) +{ + static const char *options[][2] = { + { "localcipher", "old" }, + { "ypcipher", "old" }, + }; + size_t i; + + _DIAGASSERT(option != NULL); + for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) + if (strcmp(options[i][0], option) == 0) + return (options[i][1]); + + return (NULL); +} + +/* + * Retrieve password information from the /etc/passwd.conf file, at the + * moment this is only for choosing the cipher to use. It could easily be + * used for other authentication methods as well. + */ +void +pw_getconf(char *data, size_t max, const char *key, const char *option) +{ + FILE *fp; + char line[LINE_MAX], *p, *p2; + static char result[LINE_MAX]; + int got, found; + const char *cp; + + _DIAGASSERT(data != NULL); + _DIAGASSERT(key != NULL); + _DIAGASSERT(option != NULL); + + got = 0; + found = 0; + result[0] = '\0'; + + if ((fp = fopen(_PATH_PASSWD_CONF, "r")) == NULL) { + if ((cp = pw_default(option)) != NULL) + strlcpy(data, cp, max); + else + data[0] = '\0'; + return; + } + + while (!found && (got || read_line(fp, line, LINE_MAX))) { + got = 0; + + if (strncmp(key, line, strlen(key)) != 0 || + line[strlen(key)] != ':') + continue; + + /* Now we found our specified key */ + while (read_line(fp, line, LINE_MAX)) { + /* Leaving key field */ + if (line[0] != '\0' && strchr(line + 1, ':') != NULL) { + got = 1; + break; + } + p2 = line; + if ((p = strsep(&p2, "=")) == NULL || p2 == NULL) + continue; + trim_whitespace(p); + + if (!strncmp(p, option, strlen(option))) { + trim_whitespace(p2); + strcpy(result, p2); + found = 1; + break; + } + } + } + fclose(fp); + + if (!found) + errno = ENOENT; + if (!got) + errno = ENOTDIR; + + /* + * If we got no result and were looking for a default + * value, try hard coded defaults. + */ + + if (strlen(result) == 0 && strcmp(key, "default") == 0 && + (cp = pw_default(option)) != NULL) + strlcpy(data, cp, max); + else + strlcpy(data, result, max); +} + +void +pw_getpwconf(char *data, size_t max, const struct passwd *pwd, + const char *option) +{ + char grpkey[LINE_MAX]; + struct group grs, *grp; + char grbuf[1024]; + + pw_getconf(data, max, pwd->pw_name, option); + + /* Try to find an entry for the group */ + if (*data == '\0') { + (void)getgrgid_r(pwd->pw_gid, &grs, grbuf, sizeof(grbuf), &grp); + if (grp != NULL) { + (void)snprintf(grpkey, sizeof(grpkey), ":%s", + grp->gr_name); + pw_getconf(data, max, grpkey, option); + } + if (*data == '\0') + pw_getconf(data, max, "default", option); + } +} diff --git a/lib/libutil/pidfile.3 b/lib/libutil/pidfile.3 new file mode 100644 index 000000000..8ad13328b --- /dev/null +++ b/lib/libutil/pidfile.3 @@ -0,0 +1,89 @@ +.\" $NetBSD: pidfile.3,v 1.12 2010/05/05 22:05:31 wiz Exp $ +.\" +.\" Copyright (c) 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jason R. Thorpe. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt PIDFILE 3 +.Os +.Sh NAME +.Nm pidfile +.Nd write a daemon pid file +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn pidfile "const char *basename" +.Sh DESCRIPTION +.Fn pidfile +writes a file containing the process ID of the program to the +.Pa /var/run +directory. +The file name has the form +.Pa /var/run/basename.pid . +If the +.Ar basename +argument is +.Dv NULL , +.Fn pidfile +will determine the program name and use that instead. +.Pp +The pid file can be used as a quick reference if +the process needs to be sent a signal. +When the program exits, the pid file will be removed automatically, unless +the program receives a fatal signal. +.Pp +Note that only the first invocation of +.Fn pidfile +causes a pid file to be written; subsequent invocations have no effect +unless a new +.Ar basename +is supplied. +If called with a new +.Ar basename , +.Fn pidfile +will remove the old pid file and write the new one. +.Sh RETURN VALUES +.Fn pidfile +returns 0 on success and -1 on failure. +.Sh SEE ALSO +.Xr atexit 3 +.Sh HISTORY +The +.Fn pidfile +function call appeared in +.Nx 1.5 . +.Sh BUGS +.Fn pidfile +uses +.Xr atexit 3 +to ensure the pidfile is unlinked at program exit. +However, programs that use the +.Xr _exit 2 +function (for example, in signal handlers) +will not trigger this behaviour. diff --git a/lib/libutil/pidfile.c b/lib/libutil/pidfile.c new file mode 100644 index 000000000..90a1bdc69 --- /dev/null +++ b/lib/libutil/pidfile.c @@ -0,0 +1,120 @@ +/* $NetBSD: pidfile.c,v 1.8 2008/04/28 20:23:03 martin Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe and Matthias Scheler. + * + * 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: pidfile.c,v 1.8 2008/04/28 20:23:03 martin Exp $"); +#endif + +#include +#include +#include +#include +#include +#include +#include + +static int pidfile_atexit_done; +static pid_t pidfile_pid; +static char *pidfile_basename; +static char *pidfile_path; + +static void pidfile_cleanup(void); + +int +pidfile(const char *basename) +{ + FILE *f; + + /* + * Register handler which will remove the pidfile later. + */ + if (!pidfile_atexit_done) { + if (atexit(pidfile_cleanup) < 0) + return -1; + pidfile_atexit_done = 1; + } + + if (basename == NULL) + basename = getprogname(); + + /* + * If pidfile has already been created for the supplied basename + * we don't need to create a pidfile again. + */ + if (pidfile_path != NULL) { + if (strcmp(pidfile_basename, basename) == 0) + return 0; + /* + * Remove existing pidfile if it was created by this process. + */ + pidfile_cleanup(); + + free(pidfile_path); + pidfile_path = NULL; + free(pidfile_basename); + pidfile_basename = NULL; + } + + pidfile_pid = getpid(); + + pidfile_basename = strdup(basename); + if (pidfile_basename == NULL) + return -1; + + /* _PATH_VARRUN includes trailing / */ + (void) asprintf(&pidfile_path, "%s%s.pid", _PATH_VARRUN, basename); + if (pidfile_path == NULL) { + free(pidfile_basename); + pidfile_basename = NULL; + return -1; + } + + if ((f = fopen(pidfile_path, "w")) == NULL) { + free(pidfile_path); + pidfile_path = NULL; + free(pidfile_basename); + pidfile_basename = NULL; + return -1; + } + + (void) fprintf(f, "%d\n", pidfile_pid); + (void) fclose(f); + return 0; +} + +static void +pidfile_cleanup(void) +{ + /* Only remove the pidfile if it was created by this process. */ + if ((pidfile_path != NULL) && (pidfile_pid == getpid())) + (void) unlink(pidfile_path); +} diff --git a/lib/libutil/pidlock.3 b/lib/libutil/pidlock.3 new file mode 100644 index 000000000..192173490 --- /dev/null +++ b/lib/libutil/pidlock.3 @@ -0,0 +1,184 @@ +.\" $NetBSD: pidlock.3,v 1.12 2009/03/09 19:24:27 joerg Exp $ +.\" +.\" Copyright 1996, 1997 by Curt Sampson +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd March 19, 2006 +.Dt PIDLOCK 3 +.Os +.Sh NAME +.Nm pidlock , +.Nm ttylock , +.Nm ttyunlock +.Nd locks based on files containing PIDs +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn pidlock "const char *lockfile" "int flags" "pid_t *locker" "const char *info" +.Ft int +.Fn ttylock "const char *tty" "int flags" "pid_t *locker" +.Ft int +.Fn ttyunlock "const char *tty" +.Sh DESCRIPTION +The +.Fn pidlock +.Fn ttylock , +and +.Fn ttyunlock +functions attempt to create a lockfile for an arbitrary resource that +only one program may hold at a time. +(In the case of +.Fn ttylock , +this is access to a tty device.) +If the +function succeeds in creating the lockfile, it will succeed for +no other program calling it with the same lockfile until the original +calling program has removed the lockfile or exited. +The +.Fn ttyunlock +function will remove the lockfile created by +.Fn ttylock . +.Pp +These functions use the method of creating a lockfile traditionally +used by UUCP software. +This is described as follows in the documentation for Taylor UUCP: +.Bd -filled -offset indent +The lock file normally contains the process ID of the locking process. +This makes it easy to determine whether a lock is still valid. +The algorithm is to create a temporary file and then link +it to the name that must be locked. +If the link fails because a file with that name already exists, +the existing file is read to get the process ID. +If the process still exists, the lock attempt fails. +Otherwise the lock file is deleted and the locking algorithm +is retried. +.Ed +.Pp +The PID is stored in ASCII format, with leading spaces to pad it +out to ten characters, and a terminating newline. +This implementation has been extended to put the hostname +on the second line of the file, terminated with a newline, and +optionally an arbitrary comment on the third line of the file, also +terminated with a newline. +If a comment is given, but +.Dv PIDLOCK_NONBLOCK +is not, a blank line will be written as the second line of the file. +.Pp +The +.Fn pidlock +function will attempt to create the file +.Fa lockfile +and put the current process's pid in it. +The +.Fn ttylock +function will do the same, but should be passed only the base name +(with no leading directory prefix) of the +.Fa tty +to be locked; it will test that the tty exists in +.Pa /dev +and is a character device, and then create +the file in the +.Pa /var/spool/lock +directory and prefix the filename with +.Pa LCK.. . +Use the +.Fn ttyunlock +function to remove this lock. +.Pp +The following flags may be passed in +.Pa flags : +.Bl -tag -width Dv -offset indent +.It Dv PIDLOCK_NONBLOCK +The function should return immediately when a lock is held by another +active process. +Otherwise the function will wait (forever, if necessary) +for the lock to be freed. +.It Dv PIDLOCK_USEHOSTNAME +The hostname should be compared against the hostname in the second +line of the file (if present), and if they differ, no attempt at +checking for a living process holding the lock will be made, and +the lockfile will never be deleted. +(The process is assumed to be alive.) +This is used for locking on NFS or other remote filesystems. +(The function will never create a lock if +.Dv PIDLOCK_USEHOSTNAME +is specified and no hostname is present.) +.El +.Pp +If +.Pa locker +is non-null, it will contain the PID of the locking process, if there +is one, on return. +.Pp +If +.Pa info +is non-null and the lock succeeds, the string it points to will be +written as the third line of the lock file. +.Sh RETURN VALUES +Zero is returned if the operation was successful; on an error a -1 +is returned and a standard error code is left in the global location +.Va errno . +.Sh ERRORS +In addition to the errors that are returned from +.Xr stat 2 , +.Xr open 2 , +.Xr read 2 , +.Xr write 2 , +and +.Xr link 2 , +.Fn pidlock +or +.Fn ttylock +can set +.Va errno +to the following values on failure: +.Bl -tag -width Er +.It Bq Er EWOULDBLOCK +Another running process has a lock and the +.Dv PIDLOCK_NONBLOCK +flag was specified. +.It Bq Er EFTYPE +The +.Fa tty +specified in +.Fn ttylock +is not a character special device. +.El +.\" .Sh SEE ALSO +.Sh HISTORY +The +.Fn pidlock +and +.Fn ttylock +functions appeared in +.Nx 1.3 . +.Sh AUTHORS +.An Curt Sampson +.Aq cjs@NetBSD.org . +.Sh BUGS +The lockfile format breaks if a pid is longer than ten digits when +printed in decimal form. +.Pp +The PID returned will be the pid of the locker on the remote machine if +.Dv PIDLOCK_USEHOSTNAME +is specified, but there is no indication that this is not on the local +machine. diff --git a/lib/libutil/pidlock.c b/lib/libutil/pidlock.c new file mode 100644 index 000000000..62e67ed74 --- /dev/null +++ b/lib/libutil/pidlock.c @@ -0,0 +1,232 @@ +/* $NetBSD: pidlock.c,v 1.15 2009/01/18 12:13:04 lukem Exp $ */ + +/* + * Copyright 1996, 1997 by Curt Sampson . + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: pidlock.c,v 1.15 2009/01/18 12:13:04 lukem Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Create a lockfile. Return 0 when locked, -1 on error. + */ +int +pidlock(const char *lockfile, int flags, pid_t *locker, const char *info) +{ + char tempfile[MAXPATHLEN]; + char hostname[MAXHOSTNAMELEN + 1]; + pid_t pid2 = -1; + struct stat st; + int err; + int f = -1; + char s[256]; + char *p; + size_t len; + + _DIAGASSERT(lockfile != NULL); + /* locker may be NULL */ + /* info may be NULL */ + + + if (gethostname(hostname, sizeof(hostname))) + return -1; + hostname[sizeof(hostname) - 1] = '\0'; + + /* + * Build a path to the temporary file. + * We use the path with the PID and hostname appended. + * XXX This is not thread safe. + */ + if (snprintf(tempfile, sizeof(tempfile), "%s.%d.%s", lockfile, + (int) getpid(), hostname) >= (int)sizeof(tempfile)) { + errno = ENAMETOOLONG; + return -1; + } + + /* Open it, write pid, hostname, info. */ + if ((f = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) + goto out; + + (void)snprintf(s, sizeof(s), "%10d\n", getpid()); /* pid */ + if (write(f, s, (size_t)11) != 11) + goto out; + + if ((flags & PIDLOCK_USEHOSTNAME)) { /* hostname */ + len = strlen(hostname); + if ((size_t)write(f, hostname, len) != len + || write(f, "\n", (size_t)1) != 1) + goto out; + } + if (info) { /* info */ + if (!(flags & PIDLOCK_USEHOSTNAME)) { + /* write blank line because there's no hostname */ + if (write(f, "\n", (size_t)1) != 1) + goto out; + } + len = strlen(info); + if ((size_t)write(f, info, len) != len || + write(f, "\n", (size_t)1) != 1) + goto out; + } + (void)close(f); + f = -1; + + /* Hard link the temporary file to the real lock file. */ + /* This is an atomic operation. */ +lockfailed: + while (link(tempfile, lockfile) == -1) { + if (errno != EEXIST) + goto out; + /* Find out who has this lockfile. */ + if ((f = open(lockfile, O_RDONLY, 0)) != -1) { + if ((err = read(f, s, (size_t)11)) == -1) + goto out; + if (err == 0) { + errno = EINVAL; + goto out; + } + pid2 = atoi(s); + if ((err = read(f, s, sizeof(s) - 2)) == -1) + goto out; + if (err == 0) + *s = '\0'; + s[sizeof(s) - 1] = '\0'; + if ((p = strchr(s, '\n')) != NULL) + *p = '\0'; + (void)close(f); + f = -1; + + if ((flags & PIDLOCK_USEHOSTNAME) == 0 || + strcmp(s, hostname) == 0) { + if (kill(pid2, 0) == -1 && errno == ESRCH) { + /* process doesn't exist */ + (void)unlink(lockfile); + continue; + } + } + } + if (flags & PIDLOCK_NONBLOCK) { + if (locker) + *locker = pid2; + errno = EWOULDBLOCK; + goto out; + } else + sleep(5); + } + /* + * Check to see that we really were successful (in case we're + * using NFS) by making sure that something really is linked + * to our tempfile (reference count is two). + */ + if (stat(tempfile, &st) == -1) + goto out; + if (st.st_nlink != 2) + goto lockfailed; + + (void)unlink(tempfile); + if (locker) + *locker = getpid(); /* return this process's PID on lock */ + errno = 0; + return 0; +out: + err = errno; + if (f != -1) + (void)close(f); + (void)unlink(tempfile); + errno = err; + return -1; +} + +static int +checktty(const char *tty) +{ + char ttyfile[MAXPATHLEN]; + struct stat sb; + + (void)strlcpy(ttyfile, _PATH_DEV, sizeof(ttyfile)); + (void)strlcat(ttyfile, tty, sizeof(ttyfile)); + + /* make sure the tty exists */ + if (stat(ttyfile, &sb) == -1) + return -1; + if (!S_ISCHR(sb.st_mode)) { + errno = EFTYPE; + return -1; + } + return 0; +} + +#define LOCKPATH "/var/spool/lock/LCK.." + +static char * +makelock(char *buf, size_t bufsiz, const char *tty) +{ + (void)strlcpy(buf, LOCKPATH, bufsiz); + (void)strlcat(buf, tty, bufsiz); + return buf; +} + +/*ARGSUSED*/ +int +ttylock(const char *tty, int flags, pid_t *locker) +{ + char lockfile[MAXPATHLEN]; + + _DIAGASSERT(tty != NULL); + + if (checktty(tty) != 0) + return -1; + + /* do the lock */ + return pidlock(makelock(lockfile, sizeof(lockfile), tty), + flags, locker, 0); +} + +int +ttyunlock(const char *tty) +{ + char lockfile[MAXPATHLEN]; + + _DIAGASSERT(tty != NULL); + + if (checktty(tty) != 0) + return -1; + + /* remove the lock */ + return unlink(makelock(lockfile, sizeof(lockfile), tty)); +} diff --git a/lib/libutil/pty.c b/lib/libutil/pty.c new file mode 100644 index 000000000..cd03b4b47 --- /dev/null +++ b/lib/libutil/pty.c @@ -0,0 +1,170 @@ +/* $NetBSD: pty.c,v 1.31 2009/02/20 16:44:06 christos Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)pty.c 8.3 (Berkeley) 5/16/94"; +#else +__RCSID("$NetBSD: pty.c,v 1.31 2009/02/20 16:44:06 christos Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * XXX: `v' removed until no ports are using console devices which use ttyv0 + */ +#define TTY_LETTERS "pqrstuwxyzPQRST" +#define TTY_OLD_SUFFIX "0123456789abcdef" +#define TTY_NEW_SUFFIX "ghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + +int +openpty(int *amaster, int *aslave, char *name, struct termios *term, + struct winsize *winp) +{ + char line[] = "/dev/XtyXX"; + const char *cp1, *cp2, *cp, *linep; + int master, slave; + gid_t ttygid; + mode_t mode; + struct group grs, *grp; + char grbuf[1024]; + + _DIAGASSERT(amaster != NULL); + _DIAGASSERT(aslave != NULL); + /* name may be NULL */ + /* term may be NULL */ + /* winp may be NULL */ + +#ifndef __minix + if ((master = open("/dev/ptm", O_RDWR)) != -1) { + struct ptmget pt; + if (ioctl(master, TIOCPTMGET, &pt) != -1) { + (void)close(master); + master = pt.cfd; + slave = pt.sfd; + linep = pt.sn; + goto gotit; + } + (void)close(master); + } +#endif + + (void)getgrnam_r("tty", &grs, grbuf, sizeof(grbuf), &grp); + if (grp != NULL) { + ttygid = grp->gr_gid; + mode = S_IRUSR|S_IWUSR|S_IWGRP; + } else { + ttygid = getgid(); + mode = S_IRUSR|S_IWUSR; + } + + for (cp1 = TTY_LETTERS; *cp1; cp1++) { + line[8] = *cp1; + for (cp = cp2 = TTY_OLD_SUFFIX TTY_NEW_SUFFIX; *cp2; cp2++) { + line[5] = 'p'; + line[9] = *cp2; + if ((master = open(line, O_RDWR, 0)) == -1) { + if (errno != ENOENT) + continue; /* busy */ + if ((size_t)(cp2 - cp + 1) < sizeof(TTY_OLD_SUFFIX)) + return -1; /* out of ptys */ + else + break; /* out of ptys in this group */ + } + line[5] = 't'; + linep = line; + if (chown(line, getuid(), ttygid) == 0 && + chmod(line, mode) == 0 && + revoke(line) == 0 && + (slave = open(line, O_RDWR, 0)) != -1) { +gotit: + *amaster = master; + *aslave = slave; + if (name) + (void)strcpy(name, linep); + if (term) + (void)tcsetattr(slave, TCSAFLUSH, term); + if (winp) + (void)ioctl(slave, TIOCSWINSZ, winp); + return 0; + } + (void)close(master); + } + } + errno = ENOENT; /* out of ptys */ + return -1; +} + +pid_t +forkpty(int *amaster, char *name, struct termios *term, struct winsize *winp) +{ + int master, slave; + pid_t pid; + + _DIAGASSERT(amaster != NULL); + /* name may be NULL */ + /* term may be NULL */ + /* winp may be NULL */ + + if (openpty(&master, &slave, name, term, winp) == -1) + return -1; + switch (pid = fork()) { + case -1: + return -1; + case 0: + /* + * child + */ + (void)close(master); + login_tty(slave); + return 0; + } + /* + * parent + */ + *amaster = master; + (void)close(slave); + return pid; +} diff --git a/lib/libutil/pw_getconf.3 b/lib/libutil/pw_getconf.3 new file mode 100644 index 000000000..c0b7cd3d9 --- /dev/null +++ b/lib/libutil/pw_getconf.3 @@ -0,0 +1,114 @@ +.\" $NetBSD: pw_getconf.3,v 1.12 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright 1997 Niels Provos +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Niels Provos. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" from OpenBSD: pw_getconf.3,v 1.5 1999/09/21 04:52:46 csapuntz Exp +.\" +.Dd May 4, 2010 +.Dt PW_GETCONF 3 +.Os +.Sh NAME +.Nm pw_getconf , +.Nm pw_getpwconf +.Nd password encryption configuration access function +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft void +.Fn pw_getconf "char *data" "size_t len" "const char *key" "const char *option" +.Ft void +.Fn pw_getpwconf "char *data" "size_t len" "const struct passwd *pwd" "const char *option" +.Sh DESCRIPTION +The +.Fn pw_getconf +function reads +.Pa /etc/passwd.conf +and retrieves the value of the option specified +by +.Pa option +from the section given by +.Pa key . +If no suitable entry is found +for the +.Pa key +an empty string will be returned in data. +.Pp +To retrieve default values the key +.Pa default +can be used. +In this case, if +.Pa /etc/passwd.conf +does not exist or does not contain a +.Pa default +section, the built-in defaults will be returned. +They are as follows: +.Bl -column localcipher data -offset indent +.It Sy option data +.It ypcipher old +.It localcipher old +.El +.Pp +An empty string is returned for all errors. +.Pp +.Fn pw_getpwconf +returns the value for the option specified for the particular user +specified in +.Ar pwd . +If that option is not found, then it tries to find the option in +the primary group of that user, and if that fails, then it returns +the default entry. +.Sh FILES +.Bl -tag -width /etc/passwd.conf -compact +.It Pa /etc/passwd.conf +.El +.Sh ERRORS +.Fn pw_getconf +and +.Fn pw_getpwconf +will fail if: +.Bl -tag -width Er +.It Bq Er ENOENT +There is no option named +.Pa option +in the specified key. +.It Bq Er ENOTDIR +There is no key in +.Pa /etc/passwd.conf +named +.Pa key . +.El +.Sh SEE ALSO +.Xr passwd 5 , +.Xr passwd.conf 5 +.Sh HISTORY +The +.Fn pw_getconf +function first appeared in +.Nx 1.6 . diff --git a/lib/libutil/pw_init.3 b/lib/libutil/pw_init.3 new file mode 100644 index 000000000..d7e3099c5 --- /dev/null +++ b/lib/libutil/pw_init.3 @@ -0,0 +1,229 @@ +.\" $NetBSD: pw_init.3,v 1.15 2010/05/05 22:05:31 wiz Exp $ +.\" +.\" Copyright (c) 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software developed by the Computer Systems +.\" Engineering group at Lawrence Berkeley Laboratory under DARPA contract +.\" BG 91-66 and contributed to Berkeley. +.\" +.\" 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. +.\" +.Dd August 1, 2004 +.Dt PW_INIT 3 +.Os +.Sh NAME +.Nm pw_init , +.Nm pw_edit , +.Nm pw_prompt , +.Nm pw_copy , +.Nm pw_copyx , +.Nm pw_scan , +.Nm pw_error +.Nd utility functions for interactive passwd file updates +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In pwd.h +.In util.h +.Ft void +.Fn pw_init "void" +.Ft void +.Fn pw_edit "int notsetuid" "const char *filename" +.Ft void +.Fn pw_prompt "void" +.Ft void +.Fn pw_copy "int ffd" "int tfd" "struct passwd *pw" "struct passwd *old_pw" +.Ft int +.Fn pw_copyx "int ffd" "int tfd" "struct passwd *pw" "struct passwd *old_pw" \ + "char *errbuf" "size_t errbufsz" +.Ft int +.Fn pw_scan "char *bp" "struct passwd *pw" "int *flags" +.Ft void +.Fn pw_error "const char *name" "int err" "int eval" +.Sh DESCRIPTION +These functions are designed as conveniences for interactive programs +which update the passwd file and do nothing else. +They generally handle errors by printing out a message to the standard error +stream and possibly aborting the process. +.Pp +The +.Fn pw_init +function prepares for a passwd update by unlimiting all resource +constraints, disabling core dumps (thus preventing dumping the +contents of the passwd database into a world-readable file), and +disabling most signals. +.Pp +The +.Fn pw_edit +function runs an editor (named by the environment variable EDITOR, or +.Pa /usr/bin/vi +if EDITOR is not set) on the file +.Fa filename +(or +.Pa /etc/ptmp +if +.Fa filename +is +.Dv NULL ) . +If +.Fa notsetuid +is nonzero, +.Fn pw_edit +will set the effective user and group ID to the real user and group ID +before running the editor. +.Pp +The +.Fn pw_prompt +function asks the user whether he or she wants to re-edit the password +file; if the answer is no, +.Fn pw_prompt +deletes the lock file and exits the process. +.Pp +The +.Fn pw_copy +function reads a passwd file from +.Fa ffd +and writes it to +.Fa tfd , +updating the entry corresponding to pw-\*[Gt]pw_name with the information +in +.Fa pw . +If +.Fa old_pw +is not +.Dv NULL , +it checks to make sure the old entry is the same as +the one described in +.Fa old_pw +or the process is aborted. +If an entry is not found to match +.Fa pw , +a new entry is appended to the passwd file only if the real user +ID is 0. +If an error occurs, +.Fn pw_copy +will display a message on +.Dv stderr +and call +.Fn pw_error . +.Pp +The +.Fn pw_copyx +function performs the same operation as +.Fn pw_copy +with the exception of error handling. +Upon an error, +.Fn pw_copyx +will write an error message into the buffer pointed to by +.Fa errbuf +which has the size +.Fa errbufsz . +.Pp +The +.Fn pw_scan +function accepts in +.Fa bp +a passwd entry as it would be represented in +.Pa /etc/master.passwd +and fills in +.Fa pw +with corresponding values; string fields in +.Fa pw +will be pointers into +.Fa bp . +Some characters in +.Fa bp +will be overwritten with 0s in order to terminate the strings pointed +to by +.Fa pw . +If +.Fa flags +is non-null, it should be cleared and the following options +enabled if required: +.Bl -tag -offset indent -width _PASSWORD_OLDFMT +.It Dv _PASSWORD_NOWARN +Don't print warnings. +.It Dv _PASSWORD_OLDFMT +Parse +.Fa bp +as an old format entry as found in +.Pa /etc/passwd . +.El +.Pp +Upon return it is cleared, and filled in with the following flags: +.Bl -tag -offset indent -width _PASSWORD_NOGID +.It Dv _PASSWORD_NOUID +The uid field of +.Fa bp +is empty. +.It Dv _PASSWORD_NOGID +The gid field of +.Fa bp +is empty. +.It Dv _PASSWORD_NOCHG +The change field of +.Fa bp +is empty. +.It Dv _PASSWORD_NOEXP +The expire field of +.Fa bp +is empty. +.El +.Pp +The +.Fn pw_error +function displays an error message, aborts the current passwd update, +and exits the current process. +If +.Fa err +is non-zero, a warning message beginning with +.Fa name +is printed for the current value of +.Va errno . +The process exits with status +.Fa eval . +.Sh RETURN VALUES +The +.Fn pw_copyx +function returns 1 if the new password entry was successfully written +to the destination file, and 0 otherwise. +.Pp +The +.Fn pw_scan +function prints a warning message and returns 0 if the string in the +.Fa bp +argument is not a valid passwd string. +Otherwise, +.Fn pw_scan +returns 1. +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/master.passwd +.It Pa /etc/ptmp +.El +.Sh SEE ALSO +.Xr pw_lock 3 , +.Xr passwd 5 diff --git a/lib/libutil/pw_lock.3 b/lib/libutil/pw_lock.3 new file mode 100644 index 000000000..e57d37dbb --- /dev/null +++ b/lib/libutil/pw_lock.3 @@ -0,0 +1,139 @@ +.\" $NetBSD: pw_lock.3,v 1.14 2010/05/05 22:05:31 wiz Exp $ +.\" +.\" Copyright (c) 1995 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software developed by the Computer Systems +.\" Engineering group at Lawrence Berkeley Laboratory under DARPA contract +.\" BG 91-66 and contributed to Berkeley. +.\" +.\" 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. +.\" +.Dd February 17, 2007 +.Dt PW_LOCK 3 +.Os +.Sh NAME +.Nm pw_lock , +.Nm pw_mkdb , +.Nm pw_abort , +.Nm pw_setprefix , +.Nm pw_getprefix +.Nd passwd file update functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn pw_lock "int retries" +.Ft int +.Fn pw_mkdb "const char *username" "int secureonly" +.Ft void +.Fn pw_abort "void" +.Ft int +.Fn pw_setprefix "const char *new_prefix" +.Ft "const char *" +.Fn pw_getprefix "void" +.Sh DESCRIPTION +The +.Fn pw_lock , +.Fn pw_mkdb , +and +.Fn pw_abort +functions allow a program to update the system passwd database. +.Pp +The +.Fn pw_lock +function attempts to lock the passwd database by creating the file +.Pa /etc/ptmp , +and returns the file descriptor of that file. +If +.Fa retries +is greater than zero, +.Fn pw_lock +will try multiple times to open +.Pa /etc/ptmp , +waiting one second between tries. +In addition to being a lock file, +.Pa /etc/ptmp +will also hold the contents of the new passwd file. +.Pp +The +.Fn pw_mkdb +function updates the passwd file from the contents of +.Pa /etc/ptmp . +You should finish writing to and close the file descriptor returned by +.Fn pw_lock +before calling +.Fn pw_mkdb . +If +.Fn pw_mkdb +fails and you do not wish to retry, you should make sure to call +.Fn pw_abort +to clean up the lock file. +If the +.Ar username +argument is not +.Dv NULL , +only database entries pertaining to the specified user +will be modified. +If the +.Ar secureonly +argument is non-zero, only the secure database will be updated. +.Pp +The +.Fn pw_abort +function aborts a passwd file update by deleting +.Pa /etc/ptmp . +The passwd database remains unchanged. +.Pp +The +.Fn pw_setprefix +function defines the root directory used for passwd file updates. +If the prefix is set to +.Pa /newroot +.Fn pw_lock +will operate on +.Pa /newroot/etc/ptmp +afterwards. +The default prefix is an empty string. +.Pp +The +.Fn pw_getprefix +function returns the root directory which is currently used for passwd file +updates. +.Sh RETURN VALUES +The +.Fn pw_lock +and +.Fn pw_mkdb +functions return -1 if they are unable to complete properly. +.Sh FILES +.Bl -tag -width /etc/master.passwd -compact +.It Pa /etc/master.passwd +.It Pa /etc/ptmp +.El +.Sh SEE ALSO +.Xr pw_init 3 , +.Xr pwd_mkdb 8 diff --git a/lib/libutil/raise_default_signal.3 b/lib/libutil/raise_default_signal.3 new file mode 100644 index 000000000..059a01e80 --- /dev/null +++ b/lib/libutil/raise_default_signal.3 @@ -0,0 +1,116 @@ +.\" $NetBSD: raise_default_signal.3,v 1.2 2008/04/30 13:10:52 martin Exp $ +.\" +.\" Copyright (c) 2007 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd September 25, 2007 +.Dt RAISE_DEFAULT_SIGNAL 3 +.Os +.Sh NAME +.Nm raise_default_signal +.Nd raise the default signal handler +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fo raise_default_signal +.Fa "int sig" +.Fc +.Sh DESCRIPTION +The +.Fn raise_default_signal +function raises the default signal handler for the signal +.Fa sig . +This function may be used by a user-defined signal handler router +to ensure that a parent process receives the correct notification +of a process termination by a signal. +This can be used to avoid a common programming mistake +when terminating a process from a custom +.Dv SIGINT +or +.Dv SIGQUIT +signal handler. +.Pp +The operations performed are: +.Bl -enum -offset indent +.It +Block all signals, using +.Xr sigprocmask 3 . +.It +Set the signal handler for signal +.Fa sig +to the default signal handler +.Dv ( SIG_DFL ) . +.It +.Xr raise 3 +signal +.Fa sig . +.It +Unblock signal +.Fa sig +to deliver it. +.It +Restore the original signal mask and handler, +even if there was a failure. +.El +.Pp +See +.Xr signal 7 +for a table of signals and default actions. +.Pp +The +.Fn raise_default_signal +function should be async-signal-safe. +.Sh RETURN VALUES +Upon successful completion, a value of 0 is returned. +Otherwise, a value of \-1 is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +The +.Fn raise_default_signal +function may fail and set +.Va errno +for any of the errors specified for the functions +.Xr sigemptyset 3 , +.Xr sigfillset 3 , +.Xr sigaddset 3 , +.Xr sigprocmask 2 , +.Xr sigaction 2 , +or +.Xr raise 3 . +.Sh SEE ALSO +.Xr sigaction 2 , +.Xr sigprocmask 2 , +.Xr raise 3 , +.Xr signal 7 +.Sh HISTORY +The +.Fn raise_default_signal +function first appeared in +.Nx 5.0 . diff --git a/lib/libutil/raise_default_signal.c b/lib/libutil/raise_default_signal.c new file mode 100644 index 000000000..50cffd440 --- /dev/null +++ b/lib/libutil/raise_default_signal.c @@ -0,0 +1,117 @@ +/* $NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $ */ + +/*- + * Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * 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. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: raise_default_signal.c,v 1.3 2008/04/28 20:23:03 martin Exp $"); +#endif + +#include +#include +#include +#include +#include + +#if ! HAVE_RAISE_DEFAULT_SIGNAL +/* + * raise_default_signal sig + * Raise the default signal handler for sig, by + * - block all signals + * - set the signal handler to SIG_DFL + * - raise the signal + * - unblock the signal to deliver it + * + * The original signal mask and signal handler is restored on exit + * (whether successful or not). + * + * Returns 0 on success, or -1 on failure with errno set to + * on of the values for sigemptyset(), sigaddset(), sigprocmask(), + * sigaction(), or raise(). + */ +int +raise_default_signal(int sig) +{ + struct sigaction origact, act; + sigset_t origmask, fullmask, mask; + int retval, oerrno; + + retval = -1; + + /* Setup data structures */ + /* XXX memset(3) isn't async-safe according to signal(7) */ + (void)memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; + act.sa_flags = 0; + if ((sigemptyset(&act.sa_mask) == -1) || + (sigfillset(&fullmask) == -1) || + (sigemptyset(&mask) == -1) || + (sigaddset(&mask, sig) == -1)) + goto restore_none; + + /* Block all signals */ + if (sigprocmask(SIG_BLOCK, &fullmask, &origmask) == -1) + goto restore_none; + /* (use 'goto restore_mask' to restore state) */ + + /* Enable the SIG_DFL handler */ + if (sigaction(sig, &act, &origact) == -1) + goto restore_mask; + /* (use 'goto restore_act' to restore state) */ + + /* Raise the signal, and unblock the signal to deliver it */ + if ((raise(sig) == -1) || + (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)) + goto restore_act; + + /* Flag successful raise() */ + retval = 0; + + /* Restore the original handler */ + restore_act: + oerrno = errno; + (void)sigaction(sig, &origact, NULL); + errno = oerrno; + + /* Restore the original mask */ + restore_mask: + oerrno = errno; + (void)sigprocmask(SIG_SETMASK, &origmask, NULL); + errno = oerrno; + + restore_none: + return retval; +} + +#endif /* ! HAVE_RAISE_DEFAULT_SIGNAL */ diff --git a/lib/libutil/secure_path.3 b/lib/libutil/secure_path.3 new file mode 100644 index 000000000..1506c3b1f --- /dev/null +++ b/lib/libutil/secure_path.3 @@ -0,0 +1,71 @@ +.\" $NetBSD: secure_path.3,v 1.10 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 1996,1997 Berkeley Software Design, Inc. 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Berkeley Software Design, +.\" Inc. +.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. +.\" +.\" from BSDI: login_cap.3,v 1.4 1997/11/07 16:22:27 jch Exp +.\" +.Dd May 4, 2010 +.Dt SECURE_PATH 3 +.Os +.Sh NAME +.Nm secure_path +.Nd determine if a file appears to be ``secure'' +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn secure_path "const char *path" +.Sh DESCRIPTION +The +.Fn secure_path +function takes a path name and returns zero if the referenced file is +.Dq secure , +non-zero if not. +Any +.Dq insecurity , +other than failure to access +the referenced file, will be logged to the system log. +.Pp +To be +.Dq secure , +the referenced file must exist, be a regular file (and not a +directory), owned by the super-user, and writable only by the super-user. +.Sh SEE ALSO +.Xr openlog 3 +.Sh HISTORY +The +.Fn secure_path +function is based on the +.Bsx +implementation of same, and appeared in +.Nx 1.5 +by kind permission. diff --git a/lib/libutil/secure_path.c b/lib/libutil/secure_path.c new file mode 100644 index 000000000..52ae92b16 --- /dev/null +++ b/lib/libutil/secure_path.c @@ -0,0 +1,72 @@ +/* $NetBSD: secure_path.c,v 1.2 2003/01/06 20:30:30 wiz Exp $ */ + +/*- + * Copyright (c) 1995,1997 Berkeley Software Design, Inc. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Berkeley Software Design, + * Inc. + * 4. The name of Berkeley Software Design, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. + * + * BSDI login_cap.c,v 2.13 1998/02/07 03:17:05 prb Exp + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: secure_path.c,v 1.2 2003/01/06 20:30:30 wiz Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include + +int +secure_path(const char *path) +{ + struct stat sb; + + _DIAGASSERT(path != NULL); + + /* + * If not a regular file, or is owned/writable by someone + * other than root, quit. + */ + if (lstat(path, &sb) < 0) + /* syslog(LOG_ERR, "cannot stat %s: %m", path) */; + else if (!S_ISREG(sb.st_mode)) + syslog(LOG_ERR, "%s: not a regular file", path); + else if (sb.st_uid != 0) + syslog(LOG_ERR, "%s: not owned by root", path); + else if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) + syslog(LOG_ERR, "%s: writable by non-root", path); + else + return (0); + + return (-1); +} diff --git a/lib/libutil/shlib_version b/lib/libutil/shlib_version new file mode 100644 index 000000000..0edfc77fe --- /dev/null +++ b/lib/libutil/shlib_version @@ -0,0 +1,5 @@ +# $NetBSD: shlib_version,v 1.47 2009/05/13 02:50:32 pgoyette Exp $ +# Remember to update distrib/sets/lists/base/shl.* when changing +# +major=7 +minor=17 diff --git a/lib/libutil/snprintb.3 b/lib/libutil/snprintb.3 new file mode 100644 index 000000000..ca12a5f18 --- /dev/null +++ b/lib/libutil/snprintb.3 @@ -0,0 +1,279 @@ +.\" $NetBSD: snprintb.3,v 1.14 2009/05/13 02:50:32 pgoyette Exp $ +.\" +.\" Copyright (c) 1998 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Jeremy Cooper. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 7, 2009 +.Dt SNPRINTB 3 +.Os +.Sh NAME +.Nm snprintb +.Nd bitmask output conversion +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn "snprintb" "char *buf" "size_t buflen" "const char *fmt" "uint64_t val" +.Ft int +.Fn "snprintb_m" "char *buf" "size_t buflen" "const char *fmt" "uint64_t val" \ +"size_t max" +.Sh DESCRIPTION +The +.Fn snprintb +function formats a bitmask into a mnemonic form suitable for printing. +.Pp +This conversion is useful for decoding bit fields in device registers. +It formats the integer +.Fa val +into the buffer +.Fa buf , +of size +.Fa buflen , +using a specified radix and an interpretation of +the bits within that integer as though they were flags. +The buffer is always NUL-terminated. +If the buffer +.Fa buf +is too small to hold the formatted output, +.Fn snprintb +will fill as much as it can, and return the number of bytes +that would have written if the buffer was long enough excluding the +terminating NUL. +.Pp +The decoding directive string +.Fa fmt +describes how the bitfield is to be interpreted and displayed. +It follows two possible syntaxes, referred to as +.Dq old +and +.Dq new . +The main advantage of the +.Dq new +formatting is that it is capable of handling multi-bit fields. +.Pp +The first character of +.Fa fmt +may be +.Li \e177 , +indicating that the remainder of the format string follows the +.Dq new +syntax. +The second character +.Pq the first for the old format +is a binary character representation of the +output numeral base in which the bitfield will be printed before it is decoded. +Recognized radix values +.Pq in C escape-character format +are +.Li \e10 +.Pq octal , +.Li \e12 +.Pq decimal , +and +.Li \e20 +.Pq hexadecimal . +.Pp +The remaining characters in +.Fa fmt +are interpreted as a list of bit-position\(endescription pairs. +From here the syntaxes diverge. +.Pp +The +.Dq old +format syntax is series of bit-position\(endescription pairs. +Each begins with a binary character value that represents the position +of the bit being described. +A bit position value of one describes the least significant bit. +Whereas a position value of 32 +.Pq octal 40, hexadecimal 20, the ASCII space character +describes the most significant bit. +.Pp +The remaining characters in a bit-position\(endescription pair are the +characters to print should the bit being described be set. +Description strings are delimited by the next bit position value character +encountered +.Pq distinguishable by its value being \*[Le] 32 , +or the end of the decoding directive string itself. +.Pp +For the +.Dq new +format syntax, a bit-position\(endescription begins with a field type +followed by a binary bit-position and possibly a field length. +The least significant bit is bit-position zero, unlike the +.Dq old +syntax where it is one. +.Bl -tag -width "xxxxx" +.It Cm b\eB +Describes a bit position. +The bit-position +.Fa B +indicates the corresponding bit, as in the +.Dq old +format. +.It Cm f\eB\eL +Describes a multi-bit field beginning at bit-position +.Fa B +and having a bit-length of +.Fa L . +The remaining characters are printed as a description of the field +followed by +.Sq \&= +and the value of the field. +The value of the field is printed in the base specified as the second +character of the decoding directive string +.Ar fmt . +.It Cm F\eB\eL +Describes a multi-bit field like +.Sq f , +but just extracts the value for use with the +.Sq \&= +and +.Sq \&: +formatting directives described below. +.It Cm \&=\eV +The field previously extracted by the last +.Sq f +or +.Sq F +operator is compared to the byte +.Sq Cm V +.Pq for values 0 through 255 . +If they are equal, +.Sq \&= +followed by the string following +.Sq Cm V +is printed. +This and the +.Sq \&: +operator may be repeated to annotate multiple possible values. +.It Cm :\eV +Operates like the +.Sq \&= +operator, but omits the leading +.Sq \&= . +.El +.Pp +Finally, each field is delimited by a NUL +.Pq Sq \e0 +character. +By convention, the format string has an additional NUL character at +the end, following that delimiting the last bit-position\(endescription +pair. +.Pp +The +.Fn snprintb_m +function accepts an additional +.Fa max +argument. +If this argument is zero, the +.Fn snprintb_m +function returns exactly the same results in the +.Fa buf +as the +.Fn snprintb +function. +If the +.Fa max +argument is present and has a non-zero value, it represents the maximum +length of a formatted string. +If the formatted string would require more than +.Fa max +characters, the +.Fn snprintb_m +function returns multiple formatted strings in the output buffer +.Fa buf . +Each string is NUL-terminated, and the last string is followed by an +additional NUL character (or, if you prefer, a zero-length string). +.Sh RETURN VALUES +The +.Fn snprintb +and +.Fn snprintb_m +functions return the number of bytes that would have written to the buffer +if there was adequate space, excluding the final terminating NUL, or \-1 in +case an error occurred. +For +.Fn snprintb_m , +the NUL characters terminating each individual string are included in the +total number of bytes. +.Sh EXAMPLES +Two examples of the old formatting style: +.Bd -literal -offset indent +snprintb(buf, buflen, "\e10\e2BITTWO\e1BITONE", 3) +\(rA "3\*[Lt]BITTWO,BITONE\*[Gt]" + +snprintb(buf, buflen, + "\e20\ex10NOTBOOT\ex0fFPP\ex0eSDVMA\ex0cVIDEO" + "\ex0bLORES\ex0aFPA\ex09DIAG\ex07CACHE" + "\ex06IOCACHE\ex05LOOPBACK\ex04DBGCACHE", + 0xe860) +\(rA "e860\*[Lt]NOTBOOT,FPP,SDVMA,VIDEO,CACHE,IOCACHE\*[Gt]" +.Ed +.Pp +An example of the new formatting style: +.Bd -literal -offset indent +snprintb(buf, buflen, + "\e177\e020b\e0LSB\e0b\e1_BITONE\e0f\e4\e4NIBBLE2\e0" + "f\ex10\e4BURST\e0=\e4FOUR\e0=\exfSIXTEEN\e0" + "b\ex1fMSB\e0\e0", + 0x800f0701) +\(rA "800f0701\*[Lt]LSB,NIBBLE2=0,BURST=f=SIXTEEN,MSB\*[Gt]" +.Ed +.Sh ERRORS +.Fn snprintb +will fail if: +.Bl -tag -width Er +.It Bq Er EINVAL +The leading character does not describe a supported format, +or +.Fn snprintf +failed. +.El +.Sh SEE ALSO +.Xr printf 3 , +.Xr snprintf 3 +.Sh HISTORY +The +.Fn snprintb +function was originally implemented as a non-standard +.Li %b +format string for the kernel +.Fn printf +function in +.Nx 1.5 +and earlier releases. +It was called +.Fn bitmask_snprintf +in +.Nx 5.0 +and earlier releases. +.Sh AUTHORS +The +.Dq new +format was the invention of +.An Chris Torek . diff --git a/lib/libutil/sockaddr_snprintf.3 b/lib/libutil/sockaddr_snprintf.3 new file mode 100644 index 000000000..b28fd0ed3 --- /dev/null +++ b/lib/libutil/sockaddr_snprintf.3 @@ -0,0 +1,236 @@ +.\" $NetBSD: sockaddr_snprintf.3,v 1.7 2009/04/11 16:13:49 joerg Exp $ +.\" +.\" Copyright (c) 2004 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. +.\" +.\" 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. +.\" +.Dd April 9, 2005 +.Dt SOCKADDR_SNPRINTF 3 +.Os +.Sh NAME +.Nm sockaddr_snprintf +.Nd formatting function for socket address structures +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn sockaddr_snprintf "char *buf" "size_t buflen" "const char *fmt" "const struct sockaddr *sa" +.Sh DESCRIPTION +The +.Fn sockaddr_snprintf +function formats a socket address into a form suitable for printing. +.Pp +This function is convenient because it is protocol independent, i.e. one does +not need to know the address family of the sockaddr in order to print it. +The +.Xr printf 3 +like format string specifies how the address is going to be printed. +Some formatting characters are only supported by some address families. +If a certain formatting character is not supported, then the string +.Dq N/A +is printed. +.Pp +The resulting formatted string is placed into +.Fa buf . +Up to +.Fa buflen +characters are placed in +.Fa buf . +.Pp +The following formatting characters are supported (immediately +after a percent +.Pq Sq % +sign): +.Bl -tag -width %a +.It a +The node address portion of the socket address is printed numerically. +For +.Dv AF_INET +the address is printed as: +.Dq A.B.C.D +and +for AF_INET6 +the address is printed as: +.Dq A:B...[%if] +using +.Xr getnameinfo 3 +internally with +.Dv NI_NUMERICHOST . +For +.Dv AF_APPLETALK +the address is printed as: +.Dq A.B +For +.Dv AF_LOCAL +.Pq Dv AF_UNIX +the address is printed as: +.Dq socket-path +For +.Dv AF_LINK +the address is printed as: +.Dq a.b.c.d.e.f +using +.Xr link_ntoa 3 , +but the interface portion is skipped (see below). +For +.Dv AF_UNSPEC +nothing is printed. +.It A +The symbolic name of the address is printed. +For +.Dv AF_INET +and +.Dv AF_INET6 +this is the hostname associated with the address. +For all other address families, it is the same as the +.Dq a +format. +.It f +The numeric value of the family of the address is printed. +.It l +The length of the socket address is printed. +.It p +For +.Dv AF_INET , +.Dv AF_INET6 , +and +.Dv AF_APPLETALK +the numeric value of the port portion of the address is printed. +.It P +For +.Dv AF_INET +and +.Dv AF_INET6 +this is the name of the service associated with the port number, if +available. +For all other address families, it is the same as the +.Dq p +format. +.It I +For +.Dv AF_LINK +addresses, the interface name portion is printed. +.It F +For +.Dv AF_INET6 +addresses, the flowinfo portion of the address is printed numerically. +.It R +For +.Dv AF_APPLETALK +addresses, the netrange portion of the address is printed as: +.Dq phase:[firstnet,lastnet] +.It S +For +.Dv AF_INET6 +addresses, the scope portion of the address is printed numerically. +.It ? +If present between +.Dq % +and the format character, and the selected format does not apply to +the given address family, the +.Dq N/A +string is elided and no output results. +.El +.Sh RETURN VALUES +The +.Fn sockaddr_snprintf +function returns the number of characters that are required to format the +value +.Fa val +given the format string +.Fa fmt +excluding the terminating NUL. +The returned string in +.Fa buf +is always NUL-terminated. +If the address family is not supported, +.Fn sockaddr_snprintf +returns \-1 and sets +.Va errno +to +.Dv EAFNOSUPPORT . +For +.Dv AF_INET +and +.Dv AF_INET6 +addresses +.Fn sockaddr_snprintf +returns \-1 if the +.Xr getnameinfo 3 +conversion failed, and +.Fa errno +is set to the error value from +.Xr getnameinfo 3 . +.Sh ERRORS +If the buffer +.Fa buf +is too small to hold the formatted output, +.Fn sockaddr_snprintf +will still return the buffer, containing a truncated string. +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr getnameinfo 3 , +.Xr link_ntoa 3 , +.Xr snprintf 3 +.Sh HISTORY +The +.Fn sockaddr_snprintf +first appeared in +.Nx 3.0 . +.Sh BUGS +The +.Fn sockaddr_snprintf +interface is experimental and might change in the future. +.Pp +There is no way to specify different formatting styles for particular +addresses. +For example it would be useful to print +.Dv AF_LINK +addresses as +.Dq %.2x:%.2x... +instead of +.Dq %x.%x... +.Pp +This function is supposed to be quick, but +.Xr getnameinfo 3 +might use system calls to convert the scope number to an interface +name and the +.Dq A +and +.Dq P +format characters call +.Xr getaddrinfo 3 +which may block for a noticeable period of time. +.Pp +Not all formatting characters are supported by all address families and +printing +.Dq N/A +is not very convenient. +The +.Dq \&? +character can suppress this, but other formatting (e.g., spacing or +punctuation) will remain. diff --git a/lib/libutil/sockaddr_snprintf.c b/lib/libutil/sockaddr_snprintf.c new file mode 100644 index 000000000..e0af1bdec --- /dev/null +++ b/lib/libutil/sockaddr_snprintf.c @@ -0,0 +1,229 @@ +/* $NetBSD: sockaddr_snprintf.c,v 1.9 2008/04/28 20:23:03 martin Exp $ */ + +/*- + * Copyright (c) 2004 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: sockaddr_snprintf.c,v 1.9 2008/04/28 20:23:03 martin Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +int +sockaddr_snprintf(char * const sbuf, const size_t len, const char * const fmt, + const struct sockaddr * const sa) +{ + const void *a = NULL; + char abuf[1024], nbuf[1024], *addr = NULL, *w = NULL; + char Abuf[1024], pbuf[32], *name = NULL, *port = NULL; + char *ebuf = &sbuf[len - 1], *buf = sbuf; + const char *ptr, *s; + int p = -1; + const struct sockaddr_at *sat = NULL; + const struct sockaddr_in *sin4 = NULL; + const struct sockaddr_in6 *sin6 = NULL; + const struct sockaddr_un *sun = NULL; + const struct sockaddr_dl *sdl = NULL; + int na = 1; + +#define ADDC(c) do { if (buf < ebuf) *buf++ = c; else buf++; } \ + while (/*CONSTCOND*/0) +#define ADDS(p) do { for (s = p; *s; s++) ADDC(*s); } \ + while (/*CONSTCOND*/0) +#define ADDNA() do { if (na) ADDS("N/A"); } \ + while (/*CONSTCOND*/0) + + switch (sa->sa_family) { + case AF_UNSPEC: + goto done; + case AF_APPLETALK: + sat = ((const struct sockaddr_at *)(const void *)sa); + p = ntohs(sat->sat_port); + (void)snprintf(addr = abuf, sizeof(abuf), "%u.%u", + ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node); + (void)snprintf(port = pbuf, sizeof(pbuf), "%d", p); + break; + case AF_LOCAL: + sun = ((const struct sockaddr_un *)(const void *)sa); + (void)strlcpy(addr = abuf, sun->sun_path, SUN_LEN(sun)); + break; + case AF_INET: + sin4 = ((const struct sockaddr_in *)(const void *)sa); + p = ntohs(sin4->sin_port); + a = &sin4->sin_addr; + break; + case AF_INET6: + sin6 = ((const struct sockaddr_in6 *)(const void *)sa); + p = ntohs(sin6->sin6_port); + a = &sin6->sin6_addr; + break; + case AF_LINK: + sdl = ((const struct sockaddr_dl *)(const void *)sa); + (void)strlcpy(addr = abuf, link_ntoa(sdl), sizeof(abuf)); + if ((w = strchr(addr, ':')) != 0) { + *w++ = '\0'; + addr = w; + } + break; + default: + errno = EAFNOSUPPORT; + return -1; + } + + if (addr == abuf) + name = addr; + + if (a && getnameinfo(sa, (socklen_t)sa->sa_len, addr = abuf, + (unsigned int)sizeof(abuf), NULL, 0, + NI_NUMERICHOST|NI_NUMERICSERV) != 0) + return -1; + + for (ptr = fmt; *ptr; ptr++) { + if (*ptr != '%') { + ADDC(*ptr); + continue; + } + next_char: + switch (*++ptr) { + case '?': + na = 0; + goto next_char; + case 'a': + ADDS(addr); + break; + case 'p': + if (p != -1) { + (void)snprintf(nbuf, sizeof(nbuf), "%d", p); + ADDS(nbuf); + } else + ADDNA(); + break; + case 'f': + (void)snprintf(nbuf, sizeof(nbuf), "%d", sa->sa_family); + ADDS(nbuf); + break; + case 'l': + (void)snprintf(nbuf, sizeof(nbuf), "%d", sa->sa_len); + ADDS(nbuf); + break; + case 'A': + if (name) + ADDS(name); + else if (!a) + ADDNA(); + else { + getnameinfo(sa, (socklen_t)sa->sa_len, + name = Abuf, + (unsigned int)sizeof(nbuf), NULL, 0, 0); + ADDS(name); + } + break; + case 'P': + if (port) + ADDS(port); + else if (p == -1) + ADDNA(); + else { + getnameinfo(sa, (socklen_t)sa->sa_len, NULL, 0, + port = pbuf, + (unsigned int)sizeof(pbuf), 0); + ADDS(port); + } + break; + case 'I': + if (sdl && addr != abuf) { + ADDS(abuf); + } else { + ADDNA(); + } + break; + case 'F': + if (sin6) { + (void)snprintf(nbuf, sizeof(nbuf), "%d", + sin6->sin6_flowinfo); + ADDS(nbuf); + break; + } else { + ADDNA(); + } + break; + case 'S': + if (sin6) { + (void)snprintf(nbuf, sizeof(nbuf), "%d", + sin6->sin6_scope_id); + ADDS(nbuf); + break; + } else { + ADDNA(); + } + break; + case 'R': + if (sat) { + const struct netrange *n = + &sat->sat_range.r_netrange; + (void)snprintf(nbuf, sizeof(nbuf), + "%d:[%d,%d]", n->nr_phase , n->nr_firstnet, + n->nr_lastnet); + ADDS(nbuf); + } else { + ADDNA(); + } + break; + default: + ADDC('%'); + if (na == 0) + ADDC('?'); + if (*ptr == '\0') + goto done; + /*FALLTHROUGH*/ + case '%': + ADDC(*ptr); + break; + } + na = 1; + } +done: + if (buf < ebuf) + *buf = '\0'; + else if (len != 0) + sbuf[len - 1] = '\0'; + return (int)(buf - sbuf); +} diff --git a/lib/libutil/stat_flags.3 b/lib/libutil/stat_flags.3 new file mode 100644 index 000000000..87b6407f6 --- /dev/null +++ b/lib/libutil/stat_flags.3 @@ -0,0 +1,125 @@ +.\" $NetBSD: stat_flags.3,v 1.6 2010/05/04 06:53:35 jruoho Exp $ +.\" +.\" Copyright (c) 1996 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt STAT_FLAGS 3 +.Os +.Sh NAME +.Nm string_to_flags , +.Nm flags_to_string +.Nd Stat flags parsing and printing functions +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft char * +.Fn flags_to_string "u_long flags" "const char *def" +.Ft int +.Fn string_to_flags "char **stringp" "u_long *setp" "u_long clrp" +.Sh DESCRIPTION +The +.Fn flags_to_string +and +.Fn string_to_flags +functions are used by +programs such as +.Xr ls 1 , +.Xr mtree 8 , +.Xr makefs 8 , +etc., to parse and/or print the +.Dv st_flags field in the +.Xr stat 2 +structure. +.Pp +They recognize the following flags: +.Bl -column -offset indent "uappnd " "SF_IMMUTABLE" "xxx" +.It Sy String Ta Sy Flag Ta Sy Description +.It Va arch Ta Dv SF_ARCHIVED Ta file is archived +.It Va nodump Ta Dv UF_NODUMP Ta do not dump file +.It Va opaque Ta Dv UF_OPAQUE Ta directory is opaque in union filesystems +.It Va sappnd Ta Dv SF_APPEND Ta writes to the file may only append +.It Va schg Ta Dv SF_IMMUTABLE Ta file cannot be changed; it is immutable +.It Va snap Ta Dv SF_SNAPSHOT Ta file is a snapshot inode +.It Va uappnd Ta Dv UF_APPEND Ta writes to the file may only append +.It Va uchg Ta Dv UF_IMMUTABLE Ta file cannot be changed; it is immutable +.El +.Pp +The +.Dv SF_APPEND +and +.Dv SF_IMMUTABLE +flags are for the superuser only, whereas +.Dv UF_APPEND +and +.Dv UF_IMMUTABLE +are for the user only. +.Pp +The +.Fn flags_to_string +function converts the bits set in the +.Fa flags +argument to a comma-separated string and returns it. +If no flags are set, then the +.Fa def +string is returned. +The returned string is allocated via +.Xr malloc 3 +and it is the responsibility of the caller to +.Xr free 3 +it. +.Pp +The +.Fn string_to_flags +function takes a +.Fa stringp +of space, comma, or tab separated flag names +and places their bit value on the +.Fa setp +argument. +If the flag name is prefixed by: +.Dq no , +then the bit value is placed on the +.Fa clrp +argument. +.Sh RETURN VALUES +.Fn flags_to_string +returns the symbolic representation of flags, the default string, or +.Dv NULL +if allocation failed. +.Pp +.Fn string_to_flags +returns +.Dv 0 +on success and +.Dv 1 +if it fails to parse the string, setting +.Fa stringp +to point to the first string that it failed to parse. +.Sh SEE ALSO +.Xr stat 2 diff --git a/lib/libutil/stat_flags.c b/lib/libutil/stat_flags.c new file mode 100644 index 000000000..d2c298e5f --- /dev/null +++ b/lib/libutil/stat_flags.c @@ -0,0 +1,186 @@ +/* $NetBSD: stat_flags.c,v 1.2 2007/01/16 17:34:02 cbiere Exp $ */ + +/*- + * Copyright (c) 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif + +#include +#if !defined(lint) +#if 0 +static char sccsid[] = "@(#)stat_flags.c 8.2 (Berkeley) 7/28/94"; +#else +__RCSID("$NetBSD: stat_flags.c,v 1.2 2007/01/16 17:34:02 cbiere Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include + +#include "util.h" + +#define SAPPEND(s) do { \ + if (prefix != NULL) \ + (void)strlcat(string, prefix, sizeof(string)); \ + (void)strlcat(string, s, sizeof(string)); \ + prefix = ","; \ +} while (/* CONSTCOND */ 0) + +/* + * flags_to_string -- + * Convert stat flags to a comma-separated string. If no flags + * are set, return the default string. + */ +char * +flags_to_string(u_long flags, const char *def) +{ + char string[128]; + const char *prefix; + + string[0] = '\0'; + prefix = NULL; +#if HAVE_STRUCT_STAT_ST_FLAGS + if (flags & UF_APPEND) + SAPPEND("uappnd"); + if (flags & UF_IMMUTABLE) + SAPPEND("uchg"); + if (flags & UF_NODUMP) + SAPPEND("nodump"); + if (flags & UF_OPAQUE) + SAPPEND("opaque"); + if (flags & SF_APPEND) + SAPPEND("sappnd"); + if (flags & SF_ARCHIVED) + SAPPEND("arch"); + if (flags & SF_IMMUTABLE) + SAPPEND("schg"); +#ifdef SF_SNAPSHOT + if (flags & SF_SNAPSHOT) + SAPPEND("snap"); +#endif +#endif + if (prefix != NULL) + return strdup(string); + return strdup(def); +} + +#define TEST(a, b, f) { \ + if (!strcmp(a, b)) { \ + if (clear) { \ + if (clrp) \ + *clrp |= (f); \ + if (setp) \ + *setp &= ~(f); \ + } else { \ + if (setp) \ + *setp |= (f); \ + if (clrp) \ + *clrp &= ~(f); \ + } \ + break; \ + } \ +} + +/* + * string_to_flags -- + * Take string of arguments and return stat flags. Return 0 on + * success, 1 on failure. On failure, stringp is set to point + * to the offending token. + */ +int +string_to_flags(char **stringp, u_long *setp, u_long *clrp) +{ + int clear; + char *string, *p; + + if (setp) + *setp = 0; + if (clrp) + *clrp = 0; + +#if HAVE_STRUCT_STAT_ST_FLAGS + string = *stringp; + while ((p = strsep(&string, "\t ,")) != NULL) { + clear = 0; + *stringp = p; + if (*p == '\0') + continue; + if (p[0] == 'n' && p[1] == 'o') { + clear = 1; + p += 2; + } + switch (p[0]) { + case 'a': + TEST(p, "arch", SF_ARCHIVED); + TEST(p, "archived", SF_ARCHIVED); + return (1); + case 'd': + clear = !clear; + TEST(p, "dump", UF_NODUMP); + return (1); + case 'n': + /* + * Support `nonodump'. Note that + * the state of clear is not changed. + */ + TEST(p, "nodump", UF_NODUMP); + return (1); + case 'o': + TEST(p, "opaque", UF_OPAQUE); + return (1); + case 's': + TEST(p, "sappnd", SF_APPEND); + TEST(p, "sappend", SF_APPEND); + TEST(p, "schg", SF_IMMUTABLE); + TEST(p, "schange", SF_IMMUTABLE); + TEST(p, "simmutable", SF_IMMUTABLE); + return (1); + case 'u': + TEST(p, "uappnd", UF_APPEND); + TEST(p, "uappend", UF_APPEND); + TEST(p, "uchg", UF_IMMUTABLE); + TEST(p, "uchange", UF_IMMUTABLE); + TEST(p, "uimmutable", UF_IMMUTABLE); + return (1); + default: + return (1); + } + } +#endif + + return (0); +} diff --git a/lib/libutil/ttyaction.3 b/lib/libutil/ttyaction.3 new file mode 100644 index 000000000..318ca3e9d --- /dev/null +++ b/lib/libutil/ttyaction.3 @@ -0,0 +1,108 @@ +.\" $NetBSD: ttyaction.3,v 1.15 2010/05/04 06:41:27 jruoho Exp $ +.\" +.\" Copyright (c) 1996 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Gordon W. Ross. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 4, 2010 +.Dt TTYACTION 3 +.Os +.Sh NAME +.Nm ttyaction +.Nd ttyaction utility function +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft int +.Fn ttyaction "char *ttyname" "char *action" "char *username" +.Sh DESCRIPTION +The +.Fn ttyaction +function is used by +.Xr login 1 , +.Xr getty 8 , +.Xr telnetd 8 +and +.Xr rlogind 8 +to execute site-specific commands +when a login session begins and ends. +.Pp +The +.Fn ttyaction +function scans the +.Pa /etc/ttyaction +file for any records that match the current +.Fa ttyname +and +.Fa action +parameters, and for each matching record, +runs the shell command shown in that record. +The record format is described in +.Xr ttyaction 5 . +The parameter +.Fa username +is the name of the new owner of the +.Fa ttyname +device. +Note that the +.Fa ttyname +parameter may be passed as a fully qualified pathname, and the +.Fn ttyaction +function will skip the leading "/dev/" part of the string. +(This is a convenience for login and getty.) +.Sh RETURN VALUES +.Fn ttyaction +returns the status of the last command it executed, +or zero if no matching commands were found. +.Sh FILES +.Bl -tag -width /etc/ttyaction -compact +.It Pa /dev/\(** +.It Pa /etc/ttyaction +.El +.Sh SEE ALSO +.Xr ttyaction 5 +.Sh AUTHORS +.An Gordon W. Ross +.Aq gwr@NetBSD.org , +.An Chris G. Demetriou +.Aq cgd@NetBSD.org , +.An Ty Sarna +.Aq tsarna@NetBSD.org . +.Sh BUGS +There should be some +.Em other +mechanism to allow selection of different access control policies +on a per-line basis. +It has been suggested that the same +.Fn ttyaction +mechanism should also be used for determining access control, but +it was decided (after much discussion) that +.Fn ttyaction +should only describe actions to be performed +.Em after +the system has decided to change the ownership of some tty. +Access control policies will be handled by a separate mechanism. diff --git a/lib/libutil/ttyaction.c b/lib/libutil/ttyaction.c new file mode 100644 index 000000000..dd6680127 --- /dev/null +++ b/lib/libutil/ttyaction.c @@ -0,0 +1,160 @@ +/* $NetBSD: ttyaction.c,v 1.19 2008/04/28 20:23:03 martin Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Gordon W. Ross. + * + * 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. + * + * 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. + */ + +/* + * For each matching "tty" and "action" run the "command." + * See fnmatch() for matching the tty name. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: ttyaction.c,v 1.19 2008/04/28 20:23:03 martin Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +#ifndef _PATH_TTYACTION +#define _PATH_TTYACTION "/etc/ttyaction" +#endif + +static const char *actfile = _PATH_TTYACTION; +static const char *pathenv = "PATH=" _PATH_STDPATH; + +int +ttyaction(const char *tty, const char *act, const char *user) +{ + FILE *fp; + char *p1, *p2; + const char *argv[4]; + const char *envp[8]; + char *lastp; + char line[1024]; + char env_tty[64]; + char env_act[64]; + char env_user[256]; + int error, linenum, status; + pid_t pid; + + _DIAGASSERT(tty != NULL); + _DIAGASSERT(act != NULL); + _DIAGASSERT(user != NULL); + + fp = fopen(actfile, "r"); + if (fp == NULL) + return 0; + + /* Skip the "/dev/" part of the first arg. */ + if (!strncmp(tty, "/dev/", (size_t)5)) + tty += 5; + + /* Args will be: "sh -c ..." */ + argv[0] = _PATH_BSHELL; + argv[1] = "-c"; + argv[2] = NULL; /* see below */ + argv[3] = NULL; + + /* + * Environment needs: TTY, ACT, USER + */ + snprintf(env_tty, sizeof(env_tty), "TTY=%s", tty); + snprintf(env_act, sizeof(env_act), "ACT=%s", act); + snprintf(env_user, sizeof(env_user), "USER=%s", user); + envp[0] = pathenv; + envp[1] = env_tty; + envp[2] = env_act; + envp[3] = env_user; + envp[4] = NULL; + + linenum = 0; + status = 0; + while (fgets(line, (int)sizeof(line), fp)) { + linenum++; + + /* Allow comment lines. */ + if (line[0] == '#') + continue; + + p1 = strtok_r(line, " \t", &lastp); + p2 = strtok_r(NULL, " \t", &lastp); + /* This arg goes to end of line. */ + argv[2] = strtok_r(NULL, "\n", &lastp); + if (!p1 || !p2 || !argv[2]) { + warnx("%s: line %d format error", actfile, linenum); + continue; + } + if (fnmatch(p1, tty, 0) || fnmatch(p2, act, 0)) + continue; + /* OK, this is a match. Run the command. */ + pid = fork(); + if (pid == -1) { + warnx("fork failed: %s", strerror(errno)); + continue; + } + if (pid == 0) { + /* This is the child. */ + error = execve(argv[0], + (char *const *)__UNCONST(argv), + (char *const *)__UNCONST(envp)); + /* If we get here, it is an error. */ + warnx("%s: line %d: exec failed: %s", + actfile, linenum, strerror(errno)); + _exit(1); + } + /* This is the parent. */ + error = waitpid(pid, &status, 0); + if (error == -1) { + warnx("%s: line %d: wait failed: %s", + actfile, linenum, strerror(errno)); + continue; + } + if (WTERMSIG(status)) { + warnx("%s: line %d: child died with signal %d", + actfile, linenum, WTERMSIG(status)); + continue; + } + } + fclose(fp); + return status; +} diff --git a/lib/libutil/ttymsg.3 b/lib/libutil/ttymsg.3 new file mode 100644 index 000000000..0a34f103f --- /dev/null +++ b/lib/libutil/ttymsg.3 @@ -0,0 +1,60 @@ +.\" $NetBSD: ttymsg.3,v 1.11 2008/04/30 13:10:52 martin Exp $ +.\" +.\" Copyright (c) 1996 The NetBSD Foundation, Inc. +.\" 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. +.\" +.\" 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. +.\" +.Dd June 29, 1997 +.Dt TTYMSG 3 +.Os +.Sh NAME +.Nm ttymsg +.Nd ttymsg utility function +.Sh LIBRARY +.Lb libutil +.Sh SYNOPSIS +.In util.h +.Ft char * +.Fn ttymsg "struct iovec *iov" "int iovlen" "const char *tty" "int tmout" +.Sh DESCRIPTION +The +.Fn ttymsg +function is used by +programs such as +.Xr talkd 8 , +.Xr syslogd 8 , +.Xr wall 1 , +etc., to display the contents of a uio structure on a terminal. +.Fn ttymsg +forks and finishes in the child if the write would block after +waiting up to +.Fa tmout +seconds. +.Sh RETURN VALUES +.Fn ttymsg +returns a pointer to an error string on unexpected +error; the string is not newline-terminated. +Various "normal" errors are +ignored (exclusive-use, lack of permission, etc.). +.Sh SEE ALSO +.Xr writev 2 diff --git a/lib/libutil/ttymsg.c b/lib/libutil/ttymsg.c new file mode 100644 index 000000000..12b2071c8 --- /dev/null +++ b/lib/libutil/ttymsg.c @@ -0,0 +1,208 @@ +/* $NetBSD: ttymsg.c,v 1.23 2009/01/18 12:13:04 lukem Exp $ */ + +/* + * Copyright (c) 1989, 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 +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)ttymsg.c 8.2 (Berkeley) 11/16/93"; +#else +__RCSID("$NetBSD: ttymsg.c,v 1.23 2009/01/18 12:13:04 lukem Exp $"); +#endif +#endif /* LIBC_SCCS and not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Display the contents of a uio structure on a terminal. Used by wall(1), + * syslogd(8), and talkd(8). Forks and finishes in child if write would block, + * waiting up to tmout seconds. Returns pointer to error string on unexpected + * error; string is not newline-terminated. Various "normal" errors are + * ignored (exclusive-use, lack of permission, etc.). + */ +char * +ttymsg(struct iovec *iov, int iovcnt, const char *line, int tmout) +{ + static char errbuf[1024]; + char device[MAXNAMLEN]; + const char *ptr; + int fd, ret; + struct iovec localiov[32]; + sigset_t nset; + int forked = 0; + size_t cnt, left, wret; + + _DIAGASSERT(iov != NULL); + _DIAGASSERT(iovcnt >= 0); + _DIAGASSERT(line != NULL); + + if (iovcnt < 0) { + (void)snprintf(errbuf, sizeof(errbuf), + "%s: negative iovcnt", __func__); + return errbuf; + } + + if ((size_t)iovcnt >= sizeof(localiov) / sizeof(localiov[0])) { + (void)snprintf(errbuf, sizeof(errbuf), + "%s: too many iov's (%d) max is %zu", __func__, + iovcnt, sizeof(localiov) / sizeof(localiov[0])); + return errbuf; + } + + ptr = strncmp(line, "pts/", (size_t)4) == 0 ? line + 4 : line; + if (strcspn(ptr, "./") != strlen(ptr)) { + /* A slash or dot is an attempt to break security... */ + (void)snprintf(errbuf, sizeof(errbuf), + "%s: '/' or '.' in \"%s\"", __func__, line); + return errbuf; + } + ret = snprintf(device, sizeof(device), "%s%s", _PATH_DEV, line); + if (ret == -1 || ret >= (int)sizeof(device)) { + (void) snprintf(errbuf, sizeof(errbuf), + "%s: line `%s' too long", __func__, line); + return errbuf; + } + cnt = (size_t)ret; + + /* + * open will fail on slip lines or exclusive-use lines + * if not running as root; not an error. + */ + if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { + if (errno == EBUSY || errno == EACCES) + return NULL; + (void)snprintf(errbuf, sizeof(errbuf), + "%s: Cannot open `%s' (%s)", + __func__, device, strerror(errno)); + return errbuf; + } + if (!isatty(fd)) { + (void)snprintf(errbuf, sizeof(errbuf), + "%s: line `%s' is not a tty device", __func__, device); + (void)close(fd); + return errbuf; + } + + for (cnt = left = 0; cnt < (size_t)iovcnt; ++cnt) + left += iov[cnt].iov_len; + + for (;;) { + wret = writev(fd, iov, iovcnt); + if (wret >= left) + break; + if (wret > 0) { + left -= wret; + if (iov != localiov) { + (void)memcpy(localiov, iov, + iovcnt * sizeof(struct iovec)); + iov = localiov; + } + for (cnt = 0; wret >= iov->iov_len; ++cnt) { + wret -= iov->iov_len; + ++iov; + --iovcnt; + } + if (wret) { + iov->iov_base = + (char *)iov->iov_base + wret; + iov->iov_len -= wret; + } + continue; + } else if (wret == 0) { + (void)snprintf(errbuf, sizeof(errbuf), + "%s: failed writing %zu bytes to `%s'", __func__, + left, device); + (void) close(fd); + if (forked) + _exit(1); + return errbuf; + } + if (errno == EWOULDBLOCK) { + pid_t cpid; + + if (forked) { + (void)close(fd); + _exit(1); + } + cpid = fork(); + if (cpid < 0) { + (void)snprintf(errbuf, sizeof(errbuf), + "%s: Cannot fork (%s)", __func__, + strerror(errno)); + (void)close(fd); + return errbuf; + } + if (cpid) { /* parent */ + (void)close(fd); + return NULL; + } + forked++; + /* wait at most tmout seconds */ + (void)signal(SIGALRM, SIG_DFL); + (void)signal(SIGTERM, SIG_DFL); /* XXX */ + sigfillset(&nset); + (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); + (void)alarm((u_int)tmout); + (void)fcntl(fd, F_SETFL, 0); /* clear O_NONBLOCK */ + continue; + } + /* + * We get ENODEV on a slip line if we're running as root, + * and EIO if the line just went away. + */ + if (errno == ENODEV || errno == EIO) + break; + (void) close(fd); + if (forked) + _exit(1); + (void)snprintf(errbuf, sizeof(errbuf), + "%s: Write to line `%s' failed (%s)", __func__, + device, strerror(errno)); + return errbuf; + } + + (void) close(fd); + if (forked) + _exit(0); + return NULL; +} diff --git a/lib/libutil/util.3 b/lib/libutil/util.3 new file mode 100644 index 000000000..d55b0cfa1 --- /dev/null +++ b/lib/libutil/util.3 @@ -0,0 +1,120 @@ +.\" $NetBSD: util.3,v 1.21 2010/05/05 06:11:14 jruoho Exp $ +.\" +.\" Copyright (c) 2001 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Gregory McGarry. +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd May 5, 2010 +.Dt UTIL 3 +.Os +.Sh NAME +.Nm util +.Nd system utilities library +.Sh LIBRARY +.Lb libutil +.Sh DESCRIPTION +The +.Nm +library is the system utilities library and contains various +system-dependent utility routines used in a wide variety of system daemons. +The abstracted functions are mostly related to pseudo-terminals +and login accounting. +These routines are +.Nx Ns -specific +and are not portable. +Their use should be restricted. +.Pp +Declarations for these functions may be obtained from the include file +.In util.h . +The +.Nm +library and the associated functions are implemented within the +.Pa /usr/src/lib/libutil +directory. +.Sh LIST OF FUNCTIONS +.Bl -column ".Xr sockaddr_snprintf 3" -compact +.It Sy Name Description +.It Xr disklabel_dkcksum 3 Ta compute the checksum for a disklabel +.It Xr disklabel_scan 3 Ta scan a buffer for a valid disklabel +.It Xr forkpty 3 Ta tty utility function +.It Xr getbootfile 3 Ta get the name of the booted kernel file +.It Xr getlabeloffset 3 Ta get the sector number and offset of the disklabel +.It Xr getlabelsector 3 Ta get the sector number and offset of the disklabel +.It Xr getmaxpartitions 3 Ta get the maximum number of partitions allowed per disk +.It Xr getrawpartition 3 Ta get the system ``raw'' partition +.It Xr login 3 Ta login utility function +.It Xr login_cap 3 Ta query login.conf database about a user class +.It Xr login_close 3 Ta query login.conf database about a user class +.It Xr login_getcapbool 3 Ta query login.conf database about a user class +.It Xr login_getcapnum 3 Ta query login.conf database about a user class +.It Xr login_getcapsize 3 Ta query login.conf database about a user class +.It Xr login_getcapstr 3 Ta query login.conf database about a user class +.It Xr login_getcaptime 3 Ta query login.conf database about a user class +.It Xr login_getclass 3 Ta query login.conf database about a user class +.It Xr login_tty 3 Ta tty utility function +.It Xr loginx 3 Ta login utility function +.It Xr logout 3 Ta login utility function +.It Xr logoutx 3 Ta login utility function +.It Xr logwtmp 3 Ta login utility function +.It Xr logwtmpx 3 Ta login utility function +.It Xr opendisk 3 Ta open a disk partition +.It Xr openpty 3 Ta tty utility function +.It Xr pidfile 3 Ta write a daemon pid file +.It Xr pidlock 3 Ta locks based on files containing PIDs +.It Xr pw_abort 3 Ta passwd file update function +.It Xr pw_copy 3 Ta utility function for interactive passwd file updates +.It Xr pw_edit 3 Ta utility function for interactive passwd file updates +.It Xr pw_error 3 Ta utility function for interactive passwd file updates +.It Xr pw_getconf 3 Ta password encryption configuration access function +.It Xr pw_getprefix 3 Ta passwd file update function +.It Xr pw_init 3 Ta utility function for interactive passwd file updates +.It Xr pw_lock 3 Ta passwd file update function +.It Xr pw_mkdb 3 Ta passwd file update function +.It Xr pw_prompt 3 Ta utility function for interactive passwd file updates +.It Xr pw_scan 3 Ta utility function for interactive passwd file updates +.It Xr pw_setprefix 3 Ta passwd file update function +.It Xr secure_path 3 Ta determine if a file appears to be ``secure'' +.It Xr setclasscontext 3 Ta query login.conf database about a user class +.It Xr setusercontext 3 Ta query login.conf database about a user class +.It Xr snprintb 3 Ta bitmask output conversion +.It Xr sockaddr_snprintf 3 Ta socket address formatting function +.It Xr ttyaction 3 Ta ttyaction utility function +.It Xr ttylock 3 Ta locks based on files containing PIDs +.It Xr ttymsg 3 Ta ttymsg utility function +.It Xr ttyunlock 3 Ta locks based on files containing PIDs +.El +.Sh FILES +.Bl -tag -width /usr/lib/libutil_p.a -compact +.It Pa /usr/lib/libutil.a +static util library +.It Pa /usr/lib/libutil.so +dynamic util library +.It Pa /usr/lib/libutil_p.a +static util library compiled for profiling +.El +.Sh SEE ALSO +.Xr efun 3 , +.Xr intro 3 diff --git a/libexec/Makefile b/libexec/Makefile new file mode 100644 index 000000000..58fc6a4fd --- /dev/null +++ b/libexec/Makefile @@ -0,0 +1,8 @@ +# Makefile for usr.bin + +.include + +# NetBSD imports +SUBDIR= makewhatis + +.include diff --git a/libexec/Makefile.inc b/libexec/Makefile.inc new file mode 100644 index 000000000..f4f52cdcf --- /dev/null +++ b/libexec/Makefile.inc @@ -0,0 +1,5 @@ +.include + +CPPFLAGS+= -D_NETBSD_SOURCE -D__NBSD_LIBC=1 + +BINDIR?= /usr/libexec diff --git a/libexec/makewhatis/Makefile b/libexec/makewhatis/Makefile new file mode 100644 index 000000000..a8252b0b0 --- /dev/null +++ b/libexec/makewhatis/Makefile @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.20 2009/03/16 02:24:56 lukem Exp $ + +WARNS?= 2 # XXX: const issues + +.include + +PROG= makewhatis +SRCS= makewhatis.c manconf.c +.PATH: ${NETBSDSRCDIR}/usr.bin/man +CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin + +MAN= ${PROG}.8 + +.ifndef HOSTPROG +DPADD+= ${LIBUTIL} ${LIBZ} +LDADD+= -lutil -lz +.endif + +.include diff --git a/libexec/makewhatis/makewhatis.8 b/libexec/makewhatis/makewhatis.8 new file mode 100644 index 000000000..dd1252a86 --- /dev/null +++ b/libexec/makewhatis/makewhatis.8 @@ -0,0 +1,125 @@ +.\" $NetBSD: makewhatis.8,v 1.12 2008/04/30 13:10:52 martin Exp $ +.\" +.\" Copyright (c) 1997, 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Robert Dobbs . +.\" +.\" 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. +.\" +.\" 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. +.\" +.Dd April 3, 2005 +.Dt MAKEWHATIS 8 +.Os +.Sh NAME +.Nm makewhatis +.Nd create a whatis.db database +.Sh SYNOPSIS +.Nm /usr/libexec/makewhatis +.Op Fl fw +.Op Fl C Ar file +.Op Ar manpath ... +.Sh DESCRIPTION +.Nm +strips the NAME lines from compiled or raw +.Xr man 1 +pages and creates a whatis.db database for use in +.Xr apropos 1 , +.Xr whatis 1 , +or with +.Xr man 1 Ns 's +.Fl k +option. +Man pages compressed with +.Xr compress 1 +and +.Xr gzip 1 +are uncompressed before processing. +.Pp +When +.Ar manpath +is provided multiple times, the resulting database file +is generated in the first directory specified, and contains +entries for all the directories. +.Pp +If +.Ar manpath +is not provided, +.Nm +parses +.Pa /etc/man.conf +and regenerates the whatis database files specified there. +Each database file is assumed to reside in the root of the appropriate +man page hierarchy. +.Pp +The options are as follows: +.Bl -tag -width XCXfileXX +.It Fl C Ar file +Use +.Ar file +(in +.Xr man.conf 5 +format) as configuration file instead of the default, +.Pa /etc/man.conf . +.It Fl f +Don't spawn child processes to generate the individual database files, +but do all the work synchronously in the foreground. +.It Fl w +Print warnings about input files we don't like. +.El +.Sh FILES +.Bl -tag -compact -width /etc/man.conf1 +.It Pa whatis.db +name of the whatis database +.It Pa /etc/man.conf +.Xr man 1 +configuration file, used to get the location of the whatis databases when +.Nm +is called without arguments +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr man 1 , +.Xr whatis 1 , +.Xr man.conf 5 +.Sh HISTORY +.An -nosplit +.Nm +first appeared in +.Nx 1.0 , +as a shell script written by +.An J.T. Conklin Aq jtc@NetBSD.org +and +.An Thorsten Frueauf Aq frueauf@ira.uka.de . +Further work was done by +.An Matthew Green , +.An Luke Mewburn , +and +.An Chris Demetriou . +.Pp +.An Matthias Scheler +has reimplemented +.Nm +in C in +.Nx 1.5 . +.Sh AUTHORS +.An Matthias Scheler Aq tron@NetBSD.org diff --git a/libexec/makewhatis/makewhatis.c b/libexec/makewhatis/makewhatis.c new file mode 100644 index 000000000..acaadcb60 --- /dev/null +++ b/libexec/makewhatis/makewhatis.c @@ -0,0 +1,1154 @@ +/* $NetBSD: makewhatis.c,v 1.47 2009/04/02 21:39:33 apb Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#if !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1999\ + The NetBSD Foundation, Inc. All rights reserved."); +__RCSID("$NetBSD: makewhatis.c,v 1.47 2009/04/02 21:39:33 apb Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef NROFF +#define NROFF "nroff" +#endif + +typedef struct manpagestruct manpage; +struct manpagestruct { + manpage *mp_left, *mp_right; + ino_t mp_inode; + size_t mp_sdoff; + size_t mp_sdlen; + char mp_name[1]; +}; + +typedef struct whatisstruct whatis; +struct whatisstruct { + whatis *wi_left, *wi_right; + char *wi_data; + char wi_prefix[1]; +}; + +int main(int, char * const *); +static char *findwhitespace(char *); +static char *strmove(char *, char *); +static char *GetS(gzFile, char *, size_t); +static int pathnamesection(const char *, const char *); +static int manpagesection(char *); +static char *createsectionstring(char *); +static void addmanpage(manpage **, ino_t, char *, size_t, size_t); +static void addwhatis(whatis **, char *, char *); +static char *makesection(int); +static char *makewhatisline(const char *, const char *, const char *); +static void catpreprocess(char *); +static char *parsecatpage(const char *, gzFile *); +static int manpreprocess(char *); +static char *nroff(const char *, gzFile *); +static char *parsemanpage(const char *, gzFile *, int); +static char *getwhatisdata(char *); +static void processmanpages(manpage **, whatis **); +static void dumpwhatis(FILE *, whatis *); +static int makewhatis(char * const *manpath); + +static char * const default_manpath[] = { + "/usr/man", + NULL +}; + +static const char *sectionext = "0123456789ln"; +static const char *whatisdb = _PATH_WHATIS; +static const char *whatisdb_new = _PATH_WHATIS ".new"; +static int dowarn = 0; + +#define ISALPHA(c) isalpha((unsigned char)(c)) +#define ISDIGIT(c) isdigit((unsigned char)(c)) +#define ISSPACE(c) isspace((unsigned char)(c)) + +int +main(int argc, char *const *argv) +{ + char * const *manpath; + int c, dofork; + const char *conffile; + ENTRY *ep; + TAG *tp; + int rv, jobs, status; + glob_t pg; + char *paths[2], **p, *sl; + int retval; + + dofork = 1; + conffile = NULL; + jobs = 0; + retval = EXIT_SUCCESS; + + (void)setlocale(LC_ALL, ""); + + while ((c = getopt(argc, argv, "C:fw")) != -1) { + switch (c) { + case 'C': + conffile = optarg; + break; + case 'f': + /* run all processing on foreground */ + dofork = 0; + break; + case 'w': + dowarn++; + break; + default: + fprintf(stderr, "Usage: %s [-fw] [-C file] [manpath ...]\n", + getprogname()); + exit(EXIT_FAILURE); + } + } + argc -= optind; + argv += optind; + + if (argc >= 1) { + manpath = &argv[0]; + + mkwhatis: + return makewhatis(manpath); + } + + /* + * Try read config file, fallback to default_manpath[] + * if man.conf not available. + */ + config(conffile); + if ((tp = gettag("_whatdb", 0)) == NULL) { + manpath = default_manpath; + goto mkwhatis; + } + + /* Build individual databases */ + paths[1] = NULL; + TAILQ_FOREACH(ep, &tp->entrylist, q) { + if ((rv = glob(ep->s, + GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK, + NULL, &pg)) != 0) + err(EXIT_FAILURE, "glob('%s')", ep->s); + + /* We always have something to work with here */ + for (p = pg.gl_pathv; *p; p++) { + sl = strrchr(*p, '/'); + if (sl == NULL) { + err(EXIT_FAILURE, "glob: _whatdb entry '%s' " + "doesn't contain slash", ep->s); + } + + /* + * Cut the last component of path, leaving just + * the directory. We will use the result as root + * for manpage search. + * glob malloc()s space for the paths, so it's + * okay to change it in-place. + */ + *sl = '\0'; + paths[0] = *p; + + if (!dofork) { + /* Do not fork child */ + makewhatis(paths); + continue; + } + + switch (fork()) { + case 0: + exit(makewhatis(paths)); + break; + case -1: + warn("fork"); + makewhatis(paths); + break; + default: + jobs++; + break; + } + + } + + globfree(&pg); + } + + /* Wait for the childern to finish */ + while (jobs > 0) { + (void)wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) + retval = EXIT_FAILURE; + jobs--; + } + + return retval; +} + +static int +makewhatis(char * const * manpath) +{ + FTS *fts; + FTSENT *fe; + manpage *source; + whatis *dest; + FILE *out; + size_t sdoff, sdlen; + int outfd; + struct stat st_before, st_after; + + if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL) + err(EXIT_FAILURE, "Cannot open `%s'", *manpath); + + source = NULL; + while ((fe = fts_read(fts)) != NULL) { + switch (fe->fts_info) { + case FTS_F: + if (manpagesection(fe->fts_path) >= 0) { + /* + * Get manpage subdirectory prefix. Most + * commonly, this is arch-specific subdirectory. + */ + if (fe->fts_level >= 3) { + int sl; + const char *s, *lsl; + + lsl = NULL; + s = &fe->fts_path[fe->fts_pathlen - 1]; + for(sl = fe->fts_level - 1; sl > 0; + sl--) { + s--; + while (s[0] != '/') + s--; + if (lsl == NULL) + lsl = s; + } + + /* + * Include trailing '/', so we get + * 'arch/'. + */ + sdoff = s + 1 - fe->fts_path; + sdlen = lsl - s + 1; + } else { + sdoff = 0; + sdlen = 0; + } + + addmanpage(&source, fe->fts_statp->st_ino, + fe->fts_path, sdoff, sdlen); + } + /*FALLTHROUGH*/ + case FTS_D: + case FTS_DC: + case FTS_DEFAULT: + case FTS_DP: + case FTS_SL: + case FTS_DOT: + case FTS_W: + case FTS_NSOK: + case FTS_INIT: + break; + case FTS_SLNONE: + warnx("Symbolic link with no target: `%s'", + fe->fts_path); + break; + case FTS_DNR: + warnx("Unreadable directory: `%s'", fe->fts_path); + break; + case FTS_NS: + errno = fe->fts_errno; + warn("Cannot stat `%s'", fe->fts_path); + break; + case FTS_ERR: + errno = fe->fts_errno; + warn("Error reading `%s'", fe->fts_path); + break; + default: + errx(EXIT_FAILURE, "Unknown info %d returned from fts " + " for path: `%s'", fe->fts_info, fe->fts_path); + } + } + + (void)fts_close(fts); + + dest = NULL; + processmanpages(&source, &dest); + + if (chdir(manpath[0]) == -1) + err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]); + + /* + * makewhatis runs unattended, so it needs to be able to + * recover if the last run crashed out. Therefore, if + * whatisdb_new exists and is more than (arbitrarily) sixteen + * hours old, nuke it. If it exists but is not so old, refuse + * to run until it's cleaned up, in case another makewhatis is + * already running. Also, open the output with O_EXCL to make + * sure we get our own, in case two copies start exactly at + * once. (Unlikely? Maybe, maybe not, if two copies of cron + * end up running.) + * + * Similarly, before renaming the file after we finish writing + * to it, make sure it's still the same file we opened. This + * can't be completely race-free, but getting caught by it + * would require an unexplained sixteen-hour-or-more lag + * between the last mtime update when we wrote to it and when + * we get to the stat call *and* another makewhatis starting + * out to write at exactly the wrong moment. Not impossible, + * but not likely enough to worry about. + * + * This is maybe unnecessarily elaborate, but generating + * corrupted output isn't so good either. + */ + + if (stat(whatisdb_new, &st_before) == 0) { + if (st_before.st_mtime - time(NULL) > 16*60*60) { + /* Don't complain if someone else just removed it. */ + if (unlink(whatisdb_new) == -1 && errno != ENOENT) { + err(EXIT_FAILURE, "Could not remove `%s'", + whatisdb_new); + } else { + warnx("Removed stale `%s'", whatisdb_new); + } + } else { + errx(EXIT_FAILURE, "The file `%s' already exists " + "-- am I already running?", whatisdb_new); + } + } else if (errno != ENOENT) { + /* Something unexpected happened. */ + err(EXIT_FAILURE, "Cannot stat `%s'", whatisdb_new); + } + + outfd = open(whatisdb_new, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IRGRP|S_IROTH); + if (outfd < 0) + err(EXIT_FAILURE, "Cannot open `%s'", whatisdb_new); + + if (fstat(outfd, &st_before) == -1) + err(EXIT_FAILURE, "Cannot fstat `%s'", whatisdb_new); + + if ((out = fdopen(outfd, "w")) == NULL) + err(EXIT_FAILURE, "Cannot fdopen `%s'", whatisdb_new); + + dumpwhatis(out, dest); + if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1) + err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb_new); + if (fclose(out) != 0) + err(EXIT_FAILURE, "Cannot close `%s'", whatisdb_new); + + if (stat(whatisdb_new, &st_after) == -1) + err(EXIT_FAILURE, "Cannot stat `%s' (after writing)", + whatisdb_new); + + if (st_before.st_dev != st_after.st_dev || + st_before.st_ino != st_after.st_ino) { + errx(EXIT_FAILURE, "The file `%s' changed under me; giving up", + whatisdb_new); + } + + if (rename(whatisdb_new, whatisdb) == -1) + err(EXIT_FAILURE, "Could not rename `%s' to `%s'", + whatisdb_new, whatisdb); + + return EXIT_SUCCESS; +} + +static char * +findwhitespace(char *str) +{ + while (!ISSPACE(*str)) + if (*str++ == '\0') { + str = NULL; + break; + } + + return str; +} + +static char * +strmove(char *dest, char *src) +{ + return memmove(dest, src, strlen(src) + 1); +} + +static char * +GetS(gzFile in, char *buffer, size_t length) +{ + char *ptr; + + if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0')) + ptr = NULL; + + return ptr; +} + +static char * +makesection(int s) +{ + char sectionbuffer[24]; + if (s == -1) + return NULL; + (void)snprintf(sectionbuffer, sizeof(sectionbuffer), + " (%c) - ", sectionext[s]); + return estrdup(sectionbuffer); +} + +static int +pathnamesection(const char *pat, const char *name) +{ + char *ptr, *ext; + size_t len = strlen(pat); + + + while ((ptr = strstr(name, pat)) != NULL) { + if ((ext = strchr(sectionext, ptr[len])) != NULL) { + return ext - sectionext; + } + name = ptr + 1; + } + return -1; +} + + +static int +manpagesection(char *name) +{ + char *ptr; + + if ((ptr = strrchr(name, '/')) != NULL) + ptr++; + else + ptr = name; + + while ((ptr = strchr(ptr, '.')) != NULL) { + int section; + + ptr++; + section = 0; + while (sectionext[section] != '\0') + if (sectionext[section] == *ptr) + return section; + else + section++; + } + return -1; +} + +static char * +createsectionstring(char *section_id) +{ + char *section; + + if (asprintf(§ion, " (%s) - ", section_id) < 0) + err(EXIT_FAILURE, "malloc failed"); + return section; +} + +static void +addmanpage(manpage **tree, ino_t inode, char *name, size_t sdoff, size_t sdlen) +{ + manpage *mp; + + while ((mp = *tree) != NULL) { + if (mp->mp_inode == inode) + return; + tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right; + } + + mp = emalloc(sizeof(manpage) + strlen(name)); + mp->mp_left = NULL; + mp->mp_right = NULL; + mp->mp_inode = inode; + mp->mp_sdoff = sdoff; + mp->mp_sdlen = sdlen; + (void)strcpy(mp->mp_name, name); + *tree = mp; +} + +static void +addwhatis(whatis **tree, char *data, char *prefix) +{ + whatis *wi; + int result; + + while (ISSPACE(*data)) + data++; + + if (*data == '/') { + char *ptr; + + ptr = ++data; + while ((*ptr != '\0') && !ISSPACE(*ptr)) + if (*ptr++ == '/') + data = ptr; + } + + while ((wi = *tree) != NULL) { + result = strcmp(data, wi->wi_data); + if (result == 0) result = strcmp(prefix, wi->wi_prefix); + if (result == 0) return; + tree = result < 0 ? &wi->wi_left : &wi->wi_right; + } + + wi = emalloc(sizeof(whatis) + strlen(prefix)); + + wi->wi_left = NULL; + wi->wi_right = NULL; + wi->wi_data = data; + if (prefix[0] != '\0') + (void) strcpy(wi->wi_prefix, prefix); + else + wi->wi_prefix[0] = '\0'; + *tree = wi; +} + +static void +catpreprocess(char *from) +{ + char *to; + + to = from; + while (ISSPACE(*from)) from++; + + while (*from != '\0') + if (ISSPACE(*from)) { + while (ISSPACE(*++from)); + if (*from != '\0') + *to++ = ' '; + } + else if (*(from + 1) == '\b') + from += 2; + else + *to++ = *from++; + + *to = '\0'; +} + +static char * +makewhatisline(const char *file, const char *line, const char *section) +{ + static const char *del[] = { + " - ", + " -- ", + "- ", + " -", + NULL + }; + size_t i, pos; + size_t llen, slen, dlen; + char *result, *ptr; + + ptr = NULL; + if (section == NULL) { + if (dowarn) + warnx("%s: No section provided for `%s'", file, line); + return estrdup(line); + } + + for (i = 0; del[i]; i++) + if ((ptr = strstr(line, del[i])) != NULL) + break; + + if (del[i] == NULL) { + if (dowarn) + warnx("%s: Bad format line `%s'", file, line); + return estrdup(line); + } + + slen = strlen(section); + llen = strlen(line); + dlen = strlen(del[i]); + + result = emalloc(llen - dlen + slen + 1); + pos = ptr - line; + + (void)memcpy(result, line, pos); + (void)memcpy(&result[pos], section, slen); + (void)strcpy(&result[pos + slen], &line[pos + dlen]); + return result; +} + +static char * +parsecatpage(const char *name, gzFile *in) +{ + char buffer[8192]; + char *section, *ptr, *last; + size_t size; + + do { + if (GetS(in, buffer, sizeof(buffer)) == NULL) + return NULL; + } + while (buffer[0] == '\n'); + + section = NULL; + if ((ptr = strchr(buffer, '(')) != NULL) { + if ((last = strchr(ptr + 1, ')')) !=NULL) { + size_t length; + + length = last - ptr + 1; + section = emalloc(length + 5); + *section = ' '; + (void) memcpy(section + 1, ptr, length); + (void) strcpy(section + 1 + length, " - "); + } + } + + for (;;) { + if (GetS(in, buffer, sizeof(buffer)) == NULL) { + free(section); + return NULL; + } + catpreprocess(buffer); + if (strncmp(buffer, "NAME", 4) == 0) + break; + } + if (section == NULL) + section = makesection(pathnamesection("/cat", name)); + + ptr = last = buffer; + size = sizeof(buffer) - 1; + while ((size > 0) && (GetS(in, ptr, size) != NULL)) { + int length; + + catpreprocess(ptr); + + length = strlen(ptr); + if (length == 0) { + *last = '\0'; + + ptr = makewhatisline(name, buffer, section); + free(section); + return ptr; + } + if ((length > 1) && (ptr[length - 1] == '-') && + ISALPHA(ptr[length - 2])) + last = &ptr[--length]; + else { + last = &ptr[length++]; + *last = ' '; + } + + ptr += length; + size -= length; + } + + free(section); + + return NULL; +} + +static int +manpreprocess(char *line) +{ + char *from, *to; + + to = from = line; + while (ISSPACE(*from)) + from++; + if (strncmp(from, ".\\\"", 3) == 0) + return 1; + + while (*from != '\0') + if (ISSPACE(*from)) { + while (ISSPACE(*++from)); + if ((*from != '\0') && (*from != ',')) + *to++ = ' '; + } else if (*from == '\\') { + switch (*++from) { + case '\0': + case '-': + break; + case 'f': + case 's': + from++; + if ((*from=='+') || (*from=='-')) + from++; + while (ISDIGIT(*from)) + from++; + break; + default: + from++; + } + } else { + if (*from == '"') + from++; + else + *to++ = *from++; + } + + *to = '\0'; + + if (strncasecmp(line, ".Xr", 3) == 0) { + char *sect; + + from = line + 3; + if (ISSPACE(*from)) + from++; + + if ((sect = findwhitespace(from)) != NULL) { + size_t length; + char *trail; + + *sect++ = '\0'; + if ((trail = findwhitespace(sect)) != NULL) + *trail++ = '\0'; + length = strlen(from); + (void) memmove(line, from, length); + line[length++] = '('; + to = &line[length]; + length = strlen(sect); + (void) memmove(to, sect, length); + if (trail == NULL) { + (void) strcpy(&to[length], ")"); + } else { + to += length; + *to++ = ')'; + length = strlen(trail); + (void) memmove(to, trail, length + 1); + } + } + } + + return 0; +} + +static char * +nroff(const char *inname, gzFile *in) +{ + char tempname[MAXPATHLEN], buffer[65536], *data; + int tempfd, bytes, pipefd[2], status; + static int devnull = -1; + pid_t child; + + if (gzrewind(in) < 0) + err(EXIT_FAILURE, "Cannot rewind pipe"); + + if ((devnull < 0) && + ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0)) + err(EXIT_FAILURE, "Cannot open `/dev/null'"); + + (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX", + sizeof(tempname)); + if ((tempfd = mkstemp(tempname)) == -1) + err(EXIT_FAILURE, "Cannot create temp file"); + + while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0) + if (write(tempfd, buffer, (size_t)bytes) != bytes) { + bytes = -1; + break; + } + + if (bytes < 0) { + (void)close(tempfd); + (void)unlink(tempname); + err(EXIT_FAILURE, "Read from pipe failed"); + } + if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) { + (void)close(tempfd); + (void)unlink(tempname); + err(EXIT_FAILURE, "Cannot rewind temp file"); + } + if (pipe(pipefd) == -1) { + (void)close(tempfd); + (void)unlink(tempname); + err(EXIT_FAILURE, "Cannot create pipe"); + } + + switch (child = vfork()) { + case -1: + (void)close(pipefd[1]); + (void)close(pipefd[0]); + (void)close(tempfd); + (void)unlink(tempname); + err(EXIT_FAILURE, "Fork failed"); + /* NOTREACHED */ + case 0: + (void)close(pipefd[0]); + if (tempfd != STDIN_FILENO) { + (void)dup2(tempfd, STDIN_FILENO); + (void)close(tempfd); + } + if (pipefd[1] != STDOUT_FILENO) { + (void)dup2(pipefd[1], STDOUT_FILENO); + (void)close(pipefd[1]); + } + if (devnull != STDERR_FILENO) { + (void)dup2(devnull, STDERR_FILENO); + (void)close(devnull); + } + (void)execlp(NROFF, NROFF, "-S", "-man", NULL); + _exit(EXIT_FAILURE); + /*NOTREACHED*/ + default: + (void)close(pipefd[1]); + (void)close(tempfd); + break; + } + + if ((in = gzdopen(pipefd[0], "r")) == NULL) { + if (errno == 0) + errno = ENOMEM; + (void)close(pipefd[0]); + (void)kill(child, SIGTERM); + while (waitpid(child, NULL, 0) != child); + (void)unlink(tempname); + err(EXIT_FAILURE, "Cannot read from pipe"); + } + + data = parsecatpage(inname, in); + while (gzread(in, buffer, sizeof(buffer)) > 0); + (void)gzclose(in); + + while (waitpid(child, &status, 0) != child); + if ((data != NULL) && + !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { + free(data); + errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status", + inname, WEXITSTATUS(status)); + } + + (void)unlink(tempname); + return data; +} + +static char * +parsemanpage(const char *name, gzFile *in, int defaultsection) +{ + char *section, buffer[8192], *ptr; + + section = NULL; + do { + if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { + free(section); + return NULL; + } + if (manpreprocess(buffer)) + continue; + if (strncasecmp(buffer, ".Dt", 3) == 0) { + char *end; + + ptr = &buffer[3]; + if (ISSPACE(*ptr)) + ptr++; + if ((ptr = findwhitespace(ptr)) == NULL) + continue; + + if ((end = findwhitespace(++ptr)) != NULL) + *end = '\0'; + + free(section); + section = createsectionstring(ptr); + } + else if (strncasecmp(buffer, ".TH", 3) == 0) { + ptr = &buffer[3]; + while (ISSPACE(*ptr)) + ptr++; + if ((ptr = findwhitespace(ptr)) != NULL) { + char *next; + + while (ISSPACE(*ptr)) + ptr++; + if ((next = findwhitespace(ptr)) != NULL) + *next = '\0'; + free(section); + section = createsectionstring(ptr); + } + } + else if (strncasecmp(buffer, ".Ds", 3) == 0) { + free(section); + return NULL; + } + } while (strncasecmp(buffer, ".Sh NAME", 8) != 0); + + do { + if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { + free(section); + return NULL; + } + } while (manpreprocess(buffer)); + + if (strncasecmp(buffer, ".Nm", 3) == 0) { + size_t length, offset; + + ptr = &buffer[3]; + while (ISSPACE(*ptr)) + ptr++; + + length = strlen(ptr); + if ((length > 1) && (ptr[length - 1] == ',') && + ISSPACE(ptr[length - 2])) { + ptr[--length] = '\0'; + ptr[length - 1] = ','; + } + (void) memmove(buffer, ptr, length + 1); + + offset = length + 3; + ptr = &buffer[offset]; + for (;;) { + size_t more; + + if ((sizeof(buffer) == offset) || + (GetS(in, ptr, sizeof(buffer) - offset) + == NULL)) { + free(section); + return NULL; + } + if (manpreprocess(ptr)) + continue; + + if (strncasecmp(ptr, ".Nm", 3) != 0) break; + + ptr += 3; + if (ISSPACE(*ptr)) + ptr++; + + buffer[length++] = ' '; + more = strlen(ptr); + if ((more > 1) && (ptr[more - 1] == ',') && + ISSPACE(ptr[more - 2])) { + ptr[--more] = '\0'; + ptr[more - 1] = ','; + } + + (void) memmove(&buffer[length], ptr, more + 1); + length += more; + offset = length + 3; + + ptr = &buffer[offset]; + } + + if (strncasecmp(ptr, ".Nd", 3) == 0) { + (void) strlcpy(&buffer[length], " -", + sizeof(buffer) - length); + + while (strncasecmp(ptr, ".Sh", 3) != 0) { + int more; + + if (*ptr == '.') { + char *space; + + if (strncasecmp(ptr, ".Nd", 3) != 0 || + strchr(ptr, '[') != NULL) { + free(section); + return NULL; + } + space = findwhitespace(ptr); + if (space == NULL) { + ptr = ""; + } else { + space++; + (void) strmove(ptr, space); + } + } + + if (*ptr != '\0') { + buffer[offset - 1] = ' '; + more = strlen(ptr) + 1; + offset += more; + } + ptr = &buffer[offset]; + if ((sizeof(buffer) == offset) || + (GetS(in, ptr, sizeof(buffer) - offset) + == NULL)) { + free(section); + return NULL; + } + if (manpreprocess(ptr)) + *ptr = '\0'; + } + } + } + else { + int offset; + + if (*buffer == '.') { + char *space; + + if ((space = findwhitespace(&buffer[1])) == NULL) { + free(section); + return NULL; + } + space++; + (void) strmove(buffer, space); + } + + offset = strlen(buffer) + 1; + for (;;) { + int more; + + ptr = &buffer[offset]; + if ((sizeof(buffer) == offset) || + (GetS(in, ptr, sizeof(buffer) - offset) + == NULL)) { + free(section); + return NULL; + } + if (manpreprocess(ptr) || (*ptr == '\0')) + continue; + + if ((strncasecmp(ptr, ".Sh", 3) == 0) || + (strncasecmp(ptr, ".Ss", 3) == 0)) + break; + + if (*ptr == '.') { + char *space; + + if ((space = findwhitespace(ptr)) == NULL) { + continue; + } + + space++; + (void) memmove(ptr, space, strlen(space) + 1); + } + + buffer[offset - 1] = ' '; + more = strlen(ptr); + if ((more > 1) && (ptr[more - 1] == ',') && + ISSPACE(ptr[more - 2])) { + ptr[more - 1] = '\0'; + ptr[more - 2] = ','; + } + else more++; + offset += more; + } + } + + if (section == NULL) + section = makesection(defaultsection); + + ptr = makewhatisline(name, buffer, section); + free(section); + return ptr; +} + +static char * +getwhatisdata(char *name) +{ + gzFile *in; + char *data; + int section; + + if ((in = gzopen(name, "r")) == NULL) { + if (errno == 0) + errno = ENOMEM; + err(EXIT_FAILURE, "Cannot open `%s'", name); + /* NOTREACHED */ + } + + section = manpagesection(name); + if (section == 0) { + data = parsecatpage(name, in); + } else { + data = parsemanpage(name, in, section); + if (data == NULL) + data = nroff(name, in); + } + + (void) gzclose(in); + return data; +} + +static void +processmanpages(manpage **source, whatis **dest) +{ + manpage *mp; + char sd[128]; + + mp = *source; + *source = NULL; + + while (mp != NULL) { + manpage *obsolete; + char *data; + + if (mp->mp_left != NULL) + processmanpages(&mp->mp_left, dest); + + if ((data = getwhatisdata(mp->mp_name)) != NULL) { + /* Pass eventual directory prefix to addwhatis() */ + if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1) + strlcpy(sd, &mp->mp_name[mp->mp_sdoff], + mp->mp_sdlen); + else + sd[0] = '\0'; + + addwhatis(dest, data, sd); + } + + obsolete = mp; + mp = mp->mp_right; + free(obsolete); + } +} + +static void +dumpwhatis(FILE *out, whatis *tree) +{ + while (tree != NULL) { + if (tree->wi_left) + dumpwhatis(out, tree->wi_left); + + if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) || + (fputs(tree->wi_data, out) == EOF) || + (fputc('\n', out) == EOF)) + err(EXIT_FAILURE, "Write failed"); + + tree = tree->wi_right; + } +} diff --git a/man/Makefile b/man/Makefile index b61e69fee..8a647f00c 100644 --- a/man/Makefile +++ b/man/Makefile @@ -2,19 +2,14 @@ SUBDIR= man1 man1x man2 man3 man4 man5 man6 man7 man8 man9 -#WHATISDBDIR?= /usr/man +WHATISDBDIR?= /usr/man makedb: - -# makewhatis ${WHATISDBDIR} -# ${TOOL_MAKEWHATIS} ${DESTDIR}${WHATISDBDIR} -# .if ${MKUNPRIVED} != "no" -# echo ".${WHATISDBDIR}/whatis.db type=file mode=0444 uname=${BINOWN} gname=${BINGRP}" \ -# | ${METALOG.add} -# .endif -# .else -# makedb: -# .endif + ${TOOL_MAKEWHATIS} ${DESTDIR}${WHATISDBDIR} +.if ${MKUNPRIVED} != "no" + echo ".${WHATISDBDIR}/whatis.db type=file mode=0444 uname=${BINOWN} gname=${BINGRP}" \ + | ${METALOG.add} +.endif .include .include diff --git a/man/man1/Makefile b/man/man1/Makefile index 5d1c8b1af..10e8d89ae 100644 --- a/man/man1/Makefile +++ b/man/man1/Makefile @@ -13,11 +13,11 @@ MAN= acd.1 anm.1 ar.1 ash.1 asize.1 at.1 banner.1 basename.1 \ look.1 lp.1 ls.1 lspci.1 M.1 mail.1 \ mesg.1 mixer.1 ackmkdep.1 mkfs.1 \ mkproto.1 modem.1 mount.1 mt.1 nice.1 nm.1 nohup.1 od.1 \ - ossinfo.1 ossmix.1 ossplay.1 ossrecord.1 osstest.1 passwd.1 \ + passwd.1 \ paste.1 ping.1 playwave.1 postmort.1 pr.1 prep.1 \ profile.1 ps.1 pwd.1 rcp.1 readall.1 recwave.1 \ ref.1 remsync.1 rget.1 rlogin.1 rmdir.1 rsh.1 rz.1 \ - shar.1 acksize.1 sleep.1 sort.1 soundoff.1 soundon.1 spell.1 \ + shar.1 acksize.1 sleep.1 sort.1 spell.1 \ split.1 strip.1 stty.1 su.1 sum.1 svc.1 \ synctree.1 sysenv.1 sz.1 tail.1 tee.1 telnet.1 template.1 \ term.1 termcap.1 tget.1 time.1 tr.1 true.1 \ @@ -25,6 +25,45 @@ MAN= acd.1 anm.1 ar.1 ash.1 asize.1 at.1 banner.1 basename.1 \ uud.1 uue.1 vol.1 wc.1 whereis.1 which.1 \ who.1 write.1 xargs.1 yap.1 yes.1 linkfarm.1 pkg_view.1 +MLINKS += ash.1 sh.1 +MLINKS += ash.1 bigsh.1 +MLINKS += ash.1 ..1 +MLINKS += ash.1 break.1 +MLINKS += ash.1 case.1 +MLINKS += ash.1 cd.1 +MLINKS += ash.1 command.1 +MLINKS += ash.1 continue.1 +MLINKS += ash.1 eval.1 +MLINKS += ash.1 exec.1 +MLINKS += ash.1 exit.1 +MLINKS += ash.1 export.1 +MLINKS += ash.1 for.1 +MLINKS += ash.1 getopts.1 +MLINKS += ash.1 hash.1 +MLINKS += ash.1 if.1 +MLINKS += ash.1 jobs.1 +MLINKS += ash.1 local.1 +MLINKS += ash.1 read.1 +MLINKS += ash.1 readonly.1 +MLINKS += ash.1 return.1 +MLINKS += ash.1 set.1 +MLINKS += ash.1 setvar.1 +MLINKS += ash.1 shift.1 +MLINKS += ash.1 trap.1 +MLINKS += ash.1 umask.1 +MLINKS += ash.1 unset.1 +MLINKS += ash.1 wait.1 +MLINKS += compress.1 uncompress.1 +MLINKS += cp.1 mv.1 +MLINKS += cp.1 rm.1 +MLINKS += cp.1 ln.1 +MLINKS += cp.1 cpdir.1 +MLINKS += elvis.1 ex.1 +MLINKS += expr.1 test.1 +MLINKS += expr.1 [.1 +MLINKS += passwd.1 chfn.1 +MLINKS += svc.1 ci.1 +MLINKS += svc.1 co.1 .include .include diff --git a/man/man1/ossinfo.1 b/man/man1/ossinfo.1 deleted file mode 100644 index 699820b15..000000000 --- a/man/man1/ossinfo.1 +++ /dev/null @@ -1,33 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "User Commands" -.SH NAME -ossinfo - Open Sound System information/status program - -.SH SYNOPSIS -ossinfo [-Aaeghmpx] [-v #] - -.SH DESCRIPTION -The ossinfo program displays OSS device information. - -.SH OPTIONS --v# Verbose output. Number indicates level of verobisity (0-9). --p Display only physical audio/midi devices --g Display ALL audio/midi/mixer devices (physical and virtual) --a Display audio device files --A Display audio device files (for applications using O_EXCL) --e Display all audio engines --m Display only the MIDI devices --x Display only the mixer devices --h Display help. - -.SH FILES -/usr/bin/ossinfo - -.SH SEE ALSO -ossdevlinks(1), ossmix(1), ossxmix(1) -The Getting information about devices section of the OSS Programmer's Guide -(device_discovery(2)) gives instructions for getting device information -in applications. - -.SH AUTHOR -4Front Technologies diff --git a/man/man1/ossmix.1 b/man/man1/ossmix.1 deleted file mode 100644 index 37c36a4ec..000000000 --- a/man/man1/ossmix.1 +++ /dev/null @@ -1,133 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "User Commands" -.SH NAME -ossmix - Open Sound System command-line mixer program. - -.SH SYNOPSIS -ossmix [-d ] [-chqD] [control name] [value] - -.SH DESCRIPTION -ossmix is a simple command-line mixer utility that is used to display the mixer -settings of physical and virtual audio devices. OSS version 4 has an extended -mixer API which supports some device specific features that may not available -using other mixer applications. - -.SH OPTIOMS --D Display device information. --c Dump mixer settings for all mixers. --h Display usage information. --q Quiet mode. --v[1|2] Verbose mode. -v2 prints more detailed infoamation than -v1. -ctrl# value Change value of a mixer control. - Display current/possible settings. - -.SH USAGE -ossmix without any arguments displays the current settings of the -default mixer device (usually the motherboard sound chip). This -printout can also be used to find out the supported control names and -their possible values. Currently all controls accept an ON/OFF value, a -mono value (0 to 100) or a stereo value (left:right where both channel -volumes can be between 0 and 100). The value can also be expressed in a -relative form (e.g. +1 to add 1 to the previous volume). -The following is a sample printout produced by ossmix: - - Selected mixer 0/Creative AudioPCI - Known controls are: - vol [:] (currently 50:50) - pcm [:] (currently 50:50) - speaker (currently 21) - line [:] (currently 32:32) - line.rec ON|OFF (currently OFF) - mic (currently 16) - mic.rec ON|OFF (currently ON) - cd [:] (currently 100:100) - cd.rec ON|OFF (currently OFF) - pcm2 [:] (currently 75:75) - line1 [:] (currently 32:32) - line1.rec ON|OFF (currently OFF) - line2 (currently 32) - line2.rec ON|OFF (currently OFF) - line3 (currently 0) - line3.rec ON|OFF (currently OFF) - mic.micboost ON|OFF (currently ON) - mic.micbias ON|OFF (currently ON) - mute.pcmmute ON|OFF (currently OFF) - mute.pcm2mute ON|OFF (currently OFF) - mute.micmute ON|OFF (currently OFF) - mute.cdmute ON|OFF (currently OFF) - mute.linemute ON|OFF (currently OFF) - mute.line1mute ON|OFF (currently OFF) - mute.line2mute ON|OFF (currently OFF) - mute.line3mute ON|OFF (currently OFF) - -.SH SELECTING MIXER DEVICE -It's possible to select the mixer device by using the -d -command line argument. This argument (when used) should be the first one -on the command line. By default the mixer number 0 will be accessed. -To find the available mixer devices, type ossinfo -x and look -under the Mixers heading for available mixer devices. - -.SH CHANGING MIXER SETTINGS -Changing the values is done just like with the original "mixer" applet. -For example: - - ossmix pcm 50:60 - -The above sets the pcm control (audio playback volume) so that the left -channel volume is 50 and the right channel volume is 60. With just -"ossmix pcm 50" the both channel volumes will be set to 50. - -In addition to the old mixer there are now some (usually ON/OFF) settings. -These settings are device specific and don't work with all soundcards. -The easiest way to find them out is to start ossmix without command line -arguments (other than -d#). - -Some control names contain a dot ("."). This dot is required when changing -the value. For example: "ossmix -d0 mic.micboost ON". - -."USING OSSMIX WITH A MIDI CONTROLLED MIXER -."The ossmix program has capability to listen MIDI main volume controller -."messages from a MIDI port. You can assign a ossmix control to each MIDI -."channel. After receiving a channel main volume change message ossmix will then -."change the mixer level of the volume control assigned to the channel. In this -."mode ossmix will not exit (you need to kill it manually). -." -."This mode is very useful if you need to make several rapid mixer changes -."simultaneously. -." -."To use this mode you need to give the MIDI device file and a list of the -."volume sliders on command line. For example: -." -." ossmix -d1 -m/dev/midi00 vol mic pcm line gain.out1/2 gain.in3/4 -." -."After that the MIDI channels will be assigned in the following way: -." -." Ch 0 = "vol" -." Ch 1 = "mic" -." Ch 2 = "pcm" -." Ch 3 = "line" -." Ch 4 = "gain.out1/2" -." Ch 5 = "gain.in3/4" -." -."Other MIDI channels (6 to 15) will be ignored. -." -."Only mono and stereo slider type controls can be assigned to MIDI channels. -."Both stereo channels will be set to the same volume (there is no balance -."support). -." -."After starting ossmix you should move the sliders on the external fader box -."so that ossmix can figure out their current settings. -." -."At this moment only MIDI fader boxes that send only main volume change messages -."are supported (any other MIDI data will make ossmix to behave incorrectly). -."For example the FM3 MIDI Mixer (AKA "FaderBaby") by JLCooper is compatible -."with ossmix. -." -.SH FILES -/usr/bin/ossmix - -.SH SEE ALSO -ossdevlinks(1), ossxmix(1), savemixer(1) - -.SH AUTHOR -4Front Technologies diff --git a/man/man1/ossplay.1 b/man/man1/ossplay.1 deleted file mode 100644 index 02d76772b..000000000 --- a/man/man1/ossplay.1 +++ /dev/null @@ -1,55 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "User Commands" -.SH NAME -ossplay - Open Sound System playback program. - -.SH SYNOPSIS -ossplay [-FRhlvq] [-S secs ] [ -c channels ] [ -d devname ] - [ -f fmtname | ? ] [ -g gain ] [ -o playtarget | ? ] - [ -s rate ] filename | - ... - -.SH DESCRIPTION -ossplay plays raw PCM, Microsoft RIFF (.wav), Sun ULaw (.au), Mac AIFF (.aif) -and other types of audio files. By default the application will try to -determine the audio file's format and play audio based on the stored -inforation about sample format, number of channels and sampling rate. - -.SH OPTIONS --v Verbose output. Multiple invocations increase the level - of verbosity. --q Quiet (no information printed). --l Loop playback indefinately. --d Select as the device (eg -d/dev/dsp2). --s Select the playback rate for raw PCM audio (eg -s48000). --c Select the input format (eg -fU8 or -fS16_BE). --f? Prints the list of supported format names. --o Selects the play target name if the device supports multiple - play targets (such as front, rear, side). --o? Prints the list of available play targets. --g Amplify all played samples by percentage given as argument. - 100 (default) means normal signal level, 200 means double level. --F Treat all input as raw PCM data. --R Disable redirection to virtual mixer engines and sample - rate/format conversions. Should not be used unless absolutely - necessary. --S Start playing at seconds from start of file. - The argument can contain a fractional part (e.g. -S1.2) --h Display usage information. - -.SH INTERRUPT -Sending a SIGQUIT (Ctrl-\\ in most terminals) will make ossplay stop playing -the currently played file and skip to the next file. - -.SH NOTES -The ossplay executable is the same as the ossrecord executable. -Behaviour is decided by the name used to invoke the program. - -.SH SEE ALSO -ossrecord(1), ossmix(1), ossxmix(1) - -.SH FILES -/usr/bin/ossplay - -.SH AUTHOR -4Front Technologies diff --git a/man/man1/ossrecord.1 b/man/man1/ossrecord.1 deleted file mode 100644 index 7f1ed2367..000000000 --- a/man/man1/ossrecord.1 +++ /dev/null @@ -1,103 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "User Commands" -.SH NAME -ossrecord - Open Sound System recording program. - -.SH USAGE -ossrecord [options] filename - -.SH DESCRIPTION -The ossrecord program records audio in Microsoft RIFF (wav) format. It -will record from any input that's currently set as the recording source -by the ossxmix/ossmix mixer programs. With the -l option, you also get -a level meter that will display VU levels in a character mode. - -The filename parameter is name of the (.wav) file to be produced. Output can be -sent to stdout by giving - as the file name. - -.SH OPTIONS --s Select the recording rate for raw PCM audio (eg -s48000). --c Select the number of channels 1=mono 2=stereo, 4, 6, 8, etc. --d Select as the device (eg -d/dev/dsp2). --f Select the output sample format (eg -fS32_LE or -fMU_LAW) --f? Prints the list of supported format names. --F Select the container format (eg WAV or AU). Default is WAV. --F? Prints the list of supported container formats. --R Open audio device in raw mode to disable virtual mixing and - sample rate/format conversions. Can be used when recording - from a digital source (S/PDIF input). --v Verbose output. --l Display level meters (character based). --i Select the recording source or display available recording - sources if '?' is supplied. - e.g. ossrecord -i? may display: - vol - line (currently selected) - mic - cd - aux1 - phone - mono - video --m Repeat the recording operation times. The filename - argument must have %d (or %02d) somewhere in the file to - guarantee unique filenames. If no %d is given then subsequent - recordings will overwrite the previous one(s). This option is - useful only with loopback audio devices or if the -t option - is used. --r This option launches the in background after - recording the file has completed. The name of the recorded file - will be given as the (only) command line argument. When the -m - option is used the script will run in parallel while recording - the next file. See the COMMAND SCRIPT section (below) for more - info. --g Amplify recorded samples by percentage given as argument. - 100 (default) means normal signal level, 200 means double level. - Only supported in 16 and 32 bit modes. --t Do not record more than seconds in a single recording - operation. --L Set the recording level to . --O Allow overwriting of file when recording. --h Display usage instructions. - -.SH COMMAND SCRIPT - The -r command line argument makes it possible to execute a - script or program after recording of the wave file is finished. - Below is a simple scell script that does MP3 encoding using - lame. - -#!/bin/sh - -WAVENAME=$1 - -MP3NAME=$1.mp3 - -lame -m s -h --preset studio $WAVENAME $MP3NAME - -exit 0 - -Another example script for ossrecord is a simple CGI script for live MP3 -streaming (from /dev/dsp). - -#!/bin/sh - -echo Content-Type: audio/mp3 - -echo - -ossrecord -S -b16 -s48 - | lame -m j - - - -exit 0 - -.SH NOTES -The ossrecord executable is the same as the ossplay executable. -Behaviour is decided by the name used to invoke the program. - -.SH SEE ALSO -ossplay(1), ossmix(1), ossxmix(1) - -.SH FILES -/usr/bin/ossrecord - -.SH AUTHOR -4Front Technologies diff --git a/man/man1/osstest.1 b/man/man1/osstest.1 deleted file mode 100644 index bcfdbf9be..000000000 --- a/man/man1/osstest.1 +++ /dev/null @@ -1,92 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "User Commands" -.SH NAME -osstest - Open Sound System audio self test applet. - -.SH DESCRIPTION -The osstest applet is a simple test application that can be used to test -functionality of the sound hardware installed in the system. - -osstest performs a playback test for each installed audio device. If there -are any "machine detectable" problems they will be reported. You will first -hear an audio sample played on the left speaker, then the right speaker and -finally in stereo on both speakers. - -It's user's responsibility to listen if the test sound is audible. If no -sound output can be heard the possible reason is one of the following: - -1. An error was reported by osstest. In this case there will usually not be -any sound output. The error needs to be fixed before running osstest -again. -2. There is no headphones or speakers connected. Or the connection is not -made correctly. -3. The mixer volume level is set to a too low value. By default it should -be OK. The mixer level can be adjusted using the mixer, ossmix and ossxmix -utilities distributed with OSS. -4. Some notebooks have nonstandard volume control and/or speaker selection -hardware that is not supported by OSS. It's very likely that OSS doesn't -support such vendor specific additions. - -If no errors were reported and the test sound was audible it means that -OSS and your sound hardware is functioning correctly. If you still encounter -problems with some sound applications the reason is almost certainly in -the application. Check it's configuration or try to use another equivivalent -application. - -If you are having problems with JDS, KDE and/or Gnome system sounds, you need -to make sure that OSS gets started before the GUI environment. Refer to your -operating system's startup procedures. - -.SH SAMPLE RATE DRIFT - -The osstest utility measures a sample rate drift value after playing -back the test sound. Ideally it should be 0% but in practice there -will be an error of few percents. 0% means that the 48000 Hz test file -was played exactly at 48000 Hz sampling rate. - -The sample rate measurement is based on the system timer which has limited -precision. It's likely that less than 1% differenc between the nominal and -the measured sampling rates are actually caused by an error in the measurement. -For this reason the drift reported by osstest should not be used as any kind of -quality measurement. However if the drift is very large it means that there is -something wrong in the system. The oscillator chip used with the sound chip is -broken or the system clock is running at a wrong speed. - -.SH USING OSSTEST MANUALLY - -The osstest utility is located in the /usr/bin directory. It can be run -manually to test functionality of OSS and your sound hardware. When invoked -without any command line parameters osstest performs the default test on all -devices. However it will skip some of the devices base on the following rules. - -.IP \(bu 3 -It is possible to test just one of the available audio devices by giving -its number on command line (for example osstest 1). Use the device index -numbers reported by "ossinfo -a". -.IP \(bu 3 -Use the -l command line option to loop the test infinitely. -.IP \(bu 3 -Virtual mixer devices will not be tested. Use the -V command line option to -force test of virtual devices. -.IP \(bu 3 -The actual (physical) audio devices will be tested directly (bypassing -virtual mixer). If you want to test playback through vmix then use the --V option. -.IP \(bu 3 -Multiple device files related with the same physical device will not -be tested. Only the first one gets tested while the remaining ones will be -skipped. At this moment there is no way to force osstest to test this kind of -devices. -.IP \(bu 3 -Only stereo devices will be tested. Future versions of osstest will be -able to test mono and multi channel devices too. Also osstest requires that -the device supports the 16 bit signed format and 48kHz sampling rate. - -.SH FILES -/usr/bin/osstest - -.SH SEE ALSO -savemixer(1) - -.SH AUTHOR -4Front Technologies diff --git a/man/man1/soundoff.1 b/man/man1/soundoff.1 deleted file mode 100644 index 56a5ad626..000000000 --- a/man/man1/soundoff.1 +++ /dev/null @@ -1,38 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "OSS System Administration Commands" -.SH NAME -soundoff - Stop Open Sound System - -.SH DESCRIPTION - -The soundoff command can be used to stop Open Sound System and to unload the -kernel modules related with it. - -There are no command line arguments. Only the super user (root) can use this -command. - -Open Sound System can be loaded by executing the soundon command. - -.SH SAVING THE MIXER AND CONTROL PANEL SETTINGS AUTOMATICALLY - -By default soundoff will save the current mixer and control panel settings -automatically each time soundoff is executed. The saved settings will be -restored automatically when soundon is executed next time. - -This automatic save feature can be disabled by editing /usr/lib/oss/etc/userdefs -and by changing the line containing "autosave_mixer yes" to -"autosave_mixer no". After this the mixer settings will only be saved when -the savemixer command is executed (by super user). - -.SH FILES -/usr/lib/oss/etc/userdefs -/usr/sbin/soundoff -/usr/lib/oss/etc/installed_drivers. - -.SH SEE ALSO -soundon(1) -ossdetect(1) -ossdevlinks(1) - -.SH AUTHOR -4Front Technologies diff --git a/man/man1/soundon.1 b/man/man1/soundon.1 deleted file mode 100644 index 258b15fb1..000000000 --- a/man/man1/soundon.1 +++ /dev/null @@ -1,28 +0,0 @@ -." Automatically generated text -.TH 1 "August 31, 2006" "OSS" "OSS System Administration Commands" -.SH NAME -soundon - Start Open Sound System - -.SH DESCRIPTION - -The soundon command is used to load the OSS core module (osscore) and the -low level drivers for the sound devices detected in ths system (by ossdetect). -The list of low level sound device drivers to load is located in -/usr/lib/oss/etc/installed_drivers which is maintained by the ossdetect command. - -There are no command line arguments. Only the super user (root) can use this -command. - -Open Sound System can be unloaded by executing the soundoff command. - -.SH FILES -/usr/sbin/soundon -/usr/lib/oss/etc/installed_drivers. - -.SH SEE ALSO -soundoff(1) -ossdetect(1) -ossdevlinks(1) - -.SH AUTHOR -4Front Technologies diff --git a/man/man2/Makefile b/man/man2/Makefile index 29d54b4cd..44f54aba2 100644 --- a/man/man2/Makefile +++ b/man/man2/Makefile @@ -11,5 +11,12 @@ MAN= accept.2 access.2 alarm.2 bind.2 brk.2 chdir.2 chmod.2 chown.2 \ statvfs.2 svrctl.2 symlink.2 sync.2 time.2 times.2 truncate.2 \ umask.2 uname.2 unlink.2 utime.2 wait.2 write.2 +MLINKS += select.2 FD_CLR.2 +MLINKS += select.2 FD_ISSET.2 +MLINKS += select.2 FD_SET.2 +MLINKS += setuid.2 seteuid.2 +MLINKS += setuid.2 setgid.2 +MLINKS += statvfs.2 fstatvfs.2 + .include .include diff --git a/man/man3/Makefile b/man/man3/Makefile index f72e864f2..57d96877b 100644 --- a/man/man3/Makefile +++ b/man/man3/Makefile @@ -15,5 +15,192 @@ MAN= abort.3 abs.3 assert.3 atof.3 bstring.3 configfile.3 \ termios.3 time2posix.3 ttyname.3 ttyslot.3 ungetc.3 fetch.3 md5.3 \ sha1.3 +MLINKS += md5.3 MD5Init.3 +MLINKS += md5.3 MD5Update.3 +MLINKS += md5.3 MD5Final.3 +MLINKS += md5.3 MD5End.3 +MLINKS += md5.3 MD5File.3 +MLINKS += sha1.3 SHA1Update.3 +MLINKS += sha1.3 SHA1Init.3 +MLINKS += sha1.3 SHA1Final.3 +MLINKS += sha1.3 SHA1Transform.3 +MLINKS += sha1.3 SHA1End.3 +MLINKS += sha1.3 SHA1File.3 +MLINKS += atof.3 atoi.3 +MLINKS += bstring.3 bcopy.3 +MLINKS += bstring.3 bcmp.3 +MLINKS += bstring.3 bzero.3 +MLINKS += configfile.3 config_read.3 +MLINKS += configfile.3 config_delete.3 +MLINKS += configfile.3 config_renewed.3 +MLINKS += configfile.3 config_length.3 +MLINKS += configfile.3 config_issub.3 +MLINKS += configfile.3 config_isatom.3 +MLINKS += ctime.3 localtime.3 +MLINKS += ctime.3 difftime.3 +MLINKS += ctime.3 gmtime.3 +MLINKS += ctime.3 localtime.3 +MLINKS += ctype.3 isalpha.3 +MLINKS += ctype.3 isupper.3 +MLINKS += ctype.3 islower.3 +MLINKS += ctype.3 isdigit.3 +MLINKS += ctype.3 isxdigit.3 +MLINKS += ctype.3 isalnum.3 +MLINKS += ctype.3 isspace.3 +MLINKS += ctype.3 ispunct.3 +MLINKS += ctype.3 isprint.3 +MLINKS += ctype.3 isgraph.3 +MLINKS += ctype.3 iscntrl.3 +MLINKS += ctype.3 isascii.3 +MLINKS += ctype.3 toupper.3 +MLINKS += ctype.3 tolower.3 +MLINKS += directory.3 opendir.3 +MLINKS += directory.3 readdir.3 +MLINKS += directory.3 rewinddir.3 +MLINKS += directory.3 closedir.3 +MLINKS += directory.3 telldir.3 +MLINKS += end.3 etext.3 +MLINKS += execl.3 execv.3 +MLINKS += execl.3 execle.3 +MLINKS += execl.3 execlp.3 +MLINKS += execl.3 execvp.3 +MLINKS += execl.3 exec.3 +MLINKS += ferror.3 feof.3 +MLINKS += ferror.3 clearerr.3 +MLINKS += fopen.3 freopen.3 +MLINKS += fpclassify.3 isfinite.3 +MLINKS += fpclassify.3 isinf.3 +MLINKS += fpclassify.3 isnan.3 +MLINKS += fpclassify.3 isnormal.3 +MLINKS += fseek.3 fseeko.3 +MLINKS += fseek.3 ftell.3 +MLINKS += fseek.3 ftello.3 +MLINKS += g_h_b_n.3 gethostbyname.3 +MLINKS += g_h_b_n.3 gethostbyaddr.3 +MLINKS += g_h_b_n.3 gethostent.3 +MLINKS += g_h_b_n.3 sethostent.3 +MLINKS += g_h_b_n.3 endhostent.3 +MLINKS += getaddrinfo.3 freeaddrinfo.3 +MLINKS += getaddrinfo.3 gai_strerror.3 +MLINKS += getc.3 getchar.3 +MLINKS += getc.3 fgetc.3 +MLINKS += getgrent.3 getgrnam.3 +MLINKS += getgrent.3 getgrgid.3 +MLINKS += getgrent.3 setgrent.3 +MLINKS += getgrent.3 endgrent.3 +MLINKS += getpwent.3 getpwnam.3 +MLINKS += getpwent.3 getpwuid.3 +MLINKS += getpwent.3 setpwent.3 +MLINKS += getpwent.3 endpwent.3 +MLINKS += getservent.3 getservbyport.3 +MLINKS += getservent.3 getservbyname.3 +MLINKS += getservent.3 setservent.3 +MLINKS += getttyent.3 getttynam.3 +MLINKS += getttyent.3 setttyent.3 +MLINKS += hton.3 htons.3 +MLINKS += hton.3 htonl.3 +MLINKS += hton.3 ntohs.3 +MLINKS += int64.3 add64.3 +MLINKS += int64.3 add64u.3 +MLINKS += int64.3 add64ul.3 +MLINKS += int64.3 sub64.3 +MLINKS += int64.3 sub64u.3 +MLINKS += int64.3 sub64ul.3 +MLINKS += int64.3 diff64.3 +MLINKS += int64.3 bsr64.3 +MLINKS += int64.3 cvu64.3 +MLINKS += int64.3 cvul64.3 +MLINKS += int64.3 cv64u.3 +MLINKS += int64.3 cv64ul.3 +MLINKS += int64.3 div64.3 +MLINKS += int64.3 div64u.3 +MLINKS += int64.3 div64u64.3 +MLINKS += int64.3 rem64.3 +MLINKS += int64.3 rem64u.3 +MLINKS += int64.3 mul64.3 +MLINKS += int64.3 mul64u.3 +MLINKS += int64.3 cmp64.3 +MLINKS += int64.3 cmp64u.3 +MLINKS += int64.3 cmp64ul.3 +MLINKS += int64.3 ex64lo.3 +MLINKS += int64.3 ex64hi.3 +MLINKS += islessgreater.3 isgreater.3 +MLINKS += islessgreater.3 isgreaterequal.3 +MLINKS += islessgreater.3 isless.3 +MLINKS += islessgreater.3 islessequal.3 +MLINKS += ldexp.3 scalbn.3 +MLINKS += ldexp.3 scalbnf.3 +MLINKS += ldexp.3 scalbln.3 +MLINKS += malloc.3 free.3 +MLINKS += malloc.3 realloc.3 +MLINKS += malloc.3 calloc.3 +MLINKS += nearbyint.3 ceil.3 +MLINKS += nearbyint.3 floor.3 +MLINKS += printf.3 fprintf.3 +MLINKS += printf.3 sprintf.3 +MLINKS += printf.3 snprintf.3 +MLINKS += printf.3 vprintf.3 +MLINKS += printf.3 vfprintf.3 +MLINKS += printf.3 vsprintf.3 +MLINKS += putc.3 putchar.3 +MLINKS += putc.3 fputc.3 +MLINKS += random.3 srandom.3 +MLINKS += random.3 initstate.3 +MLINKS += rcmd.3 rresvport.3 +MLINKS += regex.3 regcomp.3 +MLINKS += regex.3 regexec.3 +MLINKS += regex.3 regerror.3 +MLINKS += resolver.3 res_query.3 +MLINKS += resolver.3 res_search.3 +MLINKS += resolver.3 res_mkquery.3 +MLINKS += resolver.3 res_send.3 +MLINKS += resolver.3 res_init.3 +MLINKS += resolver.3 dn_comp.3 +MLINKS += scanf.3 fscanf.3 +MLINKS += scanf.3 sscanf.3 +MLINKS += scanf.3 vscanf.3 +MLINKS += scanf.3 vfscanf.3 +MLINKS += setjmp.3 longjmp.3 +MLINKS += setjmp.3 _setjmp.3 +MLINKS += setjmp.3 _longjmp.3 +MLINKS += setjmp.3 sigsetjmp.3 +MLINKS += sigset.3 sigaddset.3 +MLINKS += sigset.3 sigdelset.3 +MLINKS += sigset.3 sigemptyset.3 +MLINKS += sigset.3 sigfillset.3 +MLINKS += string.3 strcat.3 +MLINKS += string.3 strncat.3 +MLINKS += string.3 strcmp.3 +MLINKS += string.3 strncmp.3 +MLINKS += string.3 strcpy.3 +MLINKS += string.3 strncpy.3 +MLINKS += string.3 strlen.3 +MLINKS += string.3 strchr.3 +MLINKS += string.3 strrchr.3 +MLINKS += string.3 strerror.3 +MLINKS += string.3 memcmp.3 +MLINKS += string.3 memcpy.3 +MLINKS += string.3 memmove.3 +MLINKS += string.3 memchr.3 +MLINKS += string.3 memset.3 +MLINKS += string.3 index.3 +MLINKS += strtol.3 strtoll.3 +MLINKS += strtol.3 strtoul.3 +MLINKS += syslog.3 openlog.3 +MLINKS += termcap.3 tgetent.3 +MLINKS += termcap.3 tgetnum.3 +MLINKS += termcap.3 tgetflag.3 +MLINKS += termcap.3 tgetstr.3 +MLINKS += termcap.3 tgoto.3 +MLINKS += termios.3 tcgetattr.3 +MLINKS += termios.3 tcsetattr.3 +MLINKS += termios.3 cfgetispeed.3 +MLINKS += termios.3 cfgetospeed.3 +MLINKS += termios.3 cfsetispeed.3 +MLINKS += termios.3 cfsetospeed.3 +MLINKS += termios.3 tcsendbreak.3 +MLINKS += termios.3 tcdrain.3 +MLINKS += termios.3 tcflush.3 + .include .include diff --git a/man/man4/Makefile b/man/man4/Makefile index 0075231c9..534ff766d 100644 --- a/man/man4/Makefile +++ b/man/man4/Makefile @@ -1,4 +1,17 @@ MAN= console.4 controller.4 dev.4 fd.4 ip.4 lp.4 mtio.4 tty.4 uds.4 +MLINKS += console.4 keyboard.4 +MLINKS += controller.4 disk.4 +MLINKS += controller.4 tape.4 +MLINKS += controller.4 at.4 +MLINKS += controller.4 bios.4 +MLINKS += controller.4 esdi.4 +MLINKS += controller.4 aha1540.4 +MLINKS += controller.4 ncr810.4 +MLINKS += controller.4 dosfile.4 +MLINKS += ip.4 eth.4 +MLINKS += ip.4 psip.4 +MLINKS += ip.4 udp.4 + .include .include diff --git a/man/man5/Makefile b/man/man5/Makefile index 4bd5f51b6..27ace22c2 100644 --- a/man/man5/Makefile +++ b/man/man5/Makefile @@ -4,5 +4,7 @@ MAN= configfile.5 crontab.5 dhcp.conf.5 dir.5 ethers.5 \ system.conf.5 syslog.conf.5 termcap.5 ttytab.5 TZ.5 tzfile.5 utmp.5 \ whatis.5 pkg_install.conf.5 pkg_summary.5 +MLINKS += passwd.5 group.5 + .include .include diff --git a/man/man7/Makefile b/man/man7/Makefile index 3658228a1..1a59859b7 100644 --- a/man/man7/Makefile +++ b/man/man7/Makefile @@ -1,10 +1,10 @@ -MAN= ACK.7 ascii.7 environ.7 hier.7 man.7 oss_atiaudio.7 \ - oss_audigyls.7 oss_audiopci.7 oss_cmi878x.7 oss_cmpci.7 \ - osscore.7 oss_cs4281.7 oss_cs461x.7 oss_digi96.7 \ - oss_emu10k1x.7 oss_envy24.7 oss_envy24ht.7 oss_fmedia.7 \ - oss_geode.7 oss_hdaudio.7 oss_ich.7 oss_sblive.7 \ - oss_sbpci.7 oss_sbxfi.7 oss_solo.7 oss_trident.7 \ - oss_via823x.7 oss_via97.7 oss_ymf7xx.7 re_format.7 \ +MAN= ACK.7 ascii.7 environ.7 hier.7 man.7 \ + \ + \ + \ + \ + \ + re_format.7 \ pkgsrc.7 .include diff --git a/man/man7/oss_atiaudio.7 b/man/man7/oss_atiaudio.7 deleted file mode 100644 index dd5f1ab9e..000000000 --- a/man/man7/oss_atiaudio.7 +++ /dev/null @@ -1,21 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_atiaudio - ATI IXP southbridge audio driver. - -.SH DESCRIPTION -Open Sound System driver for ATI IXP 150/200/250 audio controller -ATI IXP device characteristics: - o 8/16 bit playback/record - o mono/stereo/4ch/5.1ch playback - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_atiaudio.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_audigyls.7 b/man/man7/oss_audigyls.7 deleted file mode 100644 index 1bf87687a..000000000 --- a/man/man7/oss_audigyls.7 +++ /dev/null @@ -1,51 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_audigyls - Creative Labs CA106 (AudigyLS/SBLive 24bit) driver. - -.SH DESCRIPTION -Open Sound System driver for Creative Labs Audigy2-LS and SBLive 24bit 7.1 -soundcards. - -Audigy-LS device characteristics: - o 8/16/24 bit playback/record - o mono/stereo/4/5.1 playback - o 8KHz to 192Khz sample rate. - -.SH AUDIGYLS MODELS - -There are 2 models of the AudigyLS device: one with an AC97 codec called the -AudigyLS and the one without called the SBLive 7.1. Essentially they are -the same chip but behave a bit differently. - -When playing AC3 on the AudigyLS (the one with the AC97 mixer) - you -need to ensure that the igain slider is set to 0. - -.SH AUDIGYLS MIXER - -.IP \(bu 3 -The AudigyLS has 4 mixer controls for each channel. -.IP \(bu 3 -The "spread" button will simply duplicate the front audio on the other 3 - channels so that every speaker is playing what the front L/R is playing. -.IP \(bu 3 -LoopBack recording allows you to capture any channel that's playing audio. -.IP \(bu 3 -Record Volume slider just adjusts the input gain. -.IP \(bu 3 -Record Source selector selects the input. - -.SH OPTIONS -.IP \(bu 3 -audigyls_spdif_enable=0|1 -The Audigy LS has a versa-jack (orange) that can be set as SPDIF output -or the Side-Surround left/right speakers in a 7.1 setup. -When set as SPDIF, you can get play PCM/AC3 audio to a Dolby(R) capable -receiver. - -.SH FILES -/usr/lib/oss/conf/oss_audigyls.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_audiopci.7 b/man/man7/oss_audiopci.7 deleted file mode 100644 index f1ac4aa80..000000000 --- a/man/man7/oss_audiopci.7 +++ /dev/null @@ -1,22 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_audiopci - Creative/Ensoniq Audiopci - ES1370 audio driver. - -.SH DESCRIPTION -Open Sound System driver for Creative AudioPCI ES1370 (also sold as SBPCI128) -audio controllers - -ES1370 device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_audiopci.conf Device configuration file - -.SH AUTHOR -4Front Technologies diff --git a/man/man7/oss_cmi878x.7 b/man/man7/oss_cmi878x.7 deleted file mode 100644 index 689f8d806..000000000 --- a/man/man7/oss_cmi878x.7 +++ /dev/null @@ -1,60 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_cmi878x - CMedia CMI8788 audio driver. - -.SH DESCRIPTION -Open Sound System driver for CMedia Electronics CMI8788 audio - -CMI87xx device characteristics: - o 8/16/24/32 bit playback/record - o mono/stereo/4ch/5.1/7.1 playback - o 8KHz to 192Khz sample rate. - -The CMedia 8788 device provides 3 types of devices. The first devices is -a Multichannel full duplex device. The second device provides Front Panel -audio access and the SPDIF device provide SPDIF (Digital) audio I/O. - -.SH MIXER PANEL -The CMedia chip provides some unique features that are set up -by the Mixer chip. There are 3 mixer devices presented to the user. - -Main Mixer Panel (/dev/mixer0) - -.IP \(bu 3 -Master Mixer panel is for controlling output volumes for each of the 8 -channels. - -.IP \(bu 3 -Monitor buttons will allow you to monitor the input from the Rear Panel -inputs, Front Panel Inputs and SPDIF IN. - -.IP \(bu 3 -Speaker-Spread function duplicates the front channel output on all 8 -speakers. - -.IP \(bu 3 -SPDIF Loopback simply takes SPDIF Input and Plays it out the SPDIF Output. -panel. - - -AC97 Input Mixer Panel (/dev/mixer1) -This mixer panel is used to switch between the various inputs like line-in, -mic, cd. When the Rear Panel Monitor button is check marked in the Main -mixer panel, the IGAIN slider in this panel controls the level of the input -that can be hear on the speakers. - - -AC97 Front Panel Mixer (/dev/mixer2) -This mixer controls the front panel ac97 device. It can be used to control -all the volumes and inputs as well as SPDIF output on the front panel device. - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_cmi878x.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_cmpci.7 b/man/man7/oss_cmpci.7 deleted file mode 100644 index 860673ad8..000000000 --- a/man/man7/oss_cmpci.7 +++ /dev/null @@ -1,63 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_cmpci - CMedia CMI8738/8768 audio driver. - -.SH DESCRIPTION -Open Sound System driver for CMedia Electronics CMI8738/8768 audio - -CMI87xx device characteristics: - o 8/16 bit playback/record - o mono/stereo/4ch/5.1ch playback - o 8KHz to 48Khz sample rate. - -.SH MIXER PANEL -The CMedia chip provides some unique features that are set up -by the Mixer chip. Running ossxmix will display the CMI8738 mixer -panel. - -Most of the sliders and buttons are self evident. However there -are some options that need explaining: - -Dual Dac: Enabling this button sets the CMPCI device as two -separate output devices with /dev/dsp1 audio going to the front and -/dev/dsp0 going to the rear outputs. Separate audio streams can -be send to the device simultaneously. - -Speaker Mode: The audio can be sent just to the front speakers or -it can be sent simultaneously to all speakers in the "Spread" mode. - -AC3 passthrough only works on Models 037 and higher. This is because of -a hardware bug in the earlier models so check the model number -(ossinfo -a). - -SPDIF: - -.IP \(bu 3 -Enable will enable SPDIF output. - -.IP \(bu 3 -Rec will allow you to record from the SPDIF device. Note that when -you have SPDIF recording enabled, you cannot play 4/6 channel audio. - -.IP \(bu 3 -Polarity - certain models require you to flip the bit otherwise you - get distorted audio. - -.IP \(bu 3 -IMon - monitor input via SPDIF in. - -.IP \(bu 3 -Optical - sets the SPDIF to Optical (TOSLINK) or RCA Jacks interface. - - - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_cmpci.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_cs4281.7 b/man/man7/oss_cs4281.7 deleted file mode 100644 index a6fde424b..000000000 --- a/man/man7/oss_cs4281.7 +++ /dev/null @@ -1,23 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_cs4281 - Cirrus Logic CS4281 driver - -.SH DESCRIPTION -Open Sound System driver for Cirrus Logic (Crystal Semicoductor) CS4281 audio -controller. - -CS4281 device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_cs4281.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_cs461x.7 b/man/man7/oss_cs461x.7 deleted file mode 100644 index 2d1ee20e2..000000000 --- a/man/man7/oss_cs461x.7 +++ /dev/null @@ -1,27 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_cs461x - Cirrus Logic CS461x/CS4280 audio driver. - -.SH DESCRIPTION -Open Sound System driver for Crystal Semiconductor (Cirrus Logic) CS4280 and -461x, audio controllers. - -CS4280 device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -.IP \(bu 3 -cs461x_clk_run_fix=0|1 (feature not used anylonger) -Certain IBM Thinkpads required the CLK_RUN bit flipped in order to wake up -the audio device. - - -.SH FILES -/usr/lib/oss/conf/oss_cs461x.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_digi96.7 b/man/man7/oss_digi96.7 deleted file mode 100644 index eb4db2d83..000000000 --- a/man/man7/oss_digi96.7 +++ /dev/null @@ -1,72 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_digi96 - RME Digi96 professional audio driver. - -.SH DESCRIPTION -Audio driver for the RME Digi96 family of profressional audio controllers. -- Only 16 and 24/32 bit audio formats are supported. -- All Digi96 family members support 32kHz, 44.1kHz, 48kHz, 64kHz, 88.2kHz and -96kHz. sampling rates. - - -.SH MIXER PANEL - -Note! For recording you need to set digi96.sync to INTERNAL and the values - of digi96.mode and digi96.input to match your studio setup. Otherwise - recordings will fail with I/O error. - -There are several settings that can be changed using the ossmix program -shipped with OSS. Note that some features don't work with all Digi96 -family members. For example ADAT mode is not supported by the base -model. - -.IP \(bu 3 -digi96.mode : -This setting controls the output mode which can be S/PDIF (consumer), -AES/EBU (professional) or ADAT. The input mode is detected automatically. -If ADAT input is detected the output mode will be switched to ADAT -automatically (this doesn't work in the other direction). - -.IP \(bu 3 -digi96.sync : -This setting tells if the playback sampling rate is based on the internal -oscillator or the sample rate detected in the input port. See also the -definition of the digi96.worldclk setting. - -.IP \(bu 3 -digi96.input : Selects the active input. - -.IP \(bu 3 -digi96.sel : -When set to BYPASS the input signal will be routed directly to the -output (also sets digi96.sync automatically to EXTERNAL). In this mode -audio data written to /dev/dsp will be muted. - -.IP \(bu 3 -digi96.worldclk ON|OFF: -Setting this control to ON will enable the optional worldclock input as -the sample rate source (overrides the digi96.sync setting). - -.IP \(bu 3 -digi96.emph ON|OFF: -Enables/disables the de-emphasis option on the analog (monitor) output -connector. - -.IP \(bu 3 -digi96.data : -Specifies if the output signal is audio or AC3 data (sets the non-audio -bit in the channel status data). - - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_digi96.conf Device configuration file - -.SH AUTHOR -4Front Technologies - - - diff --git a/man/man7/oss_emu10k1x.7 b/man/man7/oss_emu10k1x.7 deleted file mode 100644 index be4fdb859..000000000 --- a/man/man7/oss_emu10k1x.7 +++ /dev/null @@ -1,30 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_emu10k1x - Creative Labs P16x (EMU10K1X) driver. - -.SH DESCRIPTION -Open Sound System driver for Creative Labs SBLive 5.1 Dell OEM version -soundcards. The device has a chipset called the EMU10K1X and is not the same -as the SBLive EMU10K1/EMU10K2 audio processors found in the SBLive! and Audigy -soundcards. - -EMU10K1X device characteristics: - o 8/16/24 bit playback/record - o mono/stereo/4/5.1 playback - o 8KHz to 192Khz sample rate. - -.SH OPTIONS -.IP \(bu 3 -emu10k1x_spdif_enable=<0|1> -The EMU10K1X has a versa-jack (orange) that can be set as SPDIF output -or the Side-Surround left/right speakers in a 5.1 setup. -When set as SPDIF, you can get play PCM/AC3 audio to a Dolby(R) capable -receiver. - -.SH FILES -/usr/lib/oss/conf/oss_emu10k1x.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_envy24.7 b/man/man7/oss_envy24.7 deleted file mode 100644 index b99fd01f5..000000000 --- a/man/man7/oss_envy24.7 +++ /dev/null @@ -1,358 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_envy24 - ICE Envy24 audio device driver. - -.SH DESCRIPTION -Open Sound System driver for Envy24 based audio cards such as the -M-Audio Delta Series, Terratec EWS88 Series, Hoontech DSP24. - -ENVY24 device characteristics: - -.IP \(bu 3 -8/16 bit playback/record -.IP \(bu 3 -mono/stereo/4ch/5.1ch/7.1ch playback -.IP \(bu 3 -mono/sterero recording -.IP \(bu 3 -8KHz to 192Khz sample rate. - -ENVY24 AUDIO DEVICES - - Audio devices: - 0: M Audio Delta 1010 out1/2 - 1: M Audio Delta 1010 out3/4 - 2: M Audio Delta 1010 out5/6 - 3: M Audio Delta 1010 out7/8 - 4: M Audio Delta 1010 S/PDIF out - 5: M Audio Delta 1010 in1/2 - 6: M Audio Delta 1010 in3/4 - 7: M Audio Delta 1010 in5/6 - 8: M Audio Delta 1010 in7/8 - 9: M Audio Delta 1010 S/PDIF in - 10: M Audio Delta 1010 input from mon. mixer - 11: M Audio Delta 1010 (all outputs) - 12: M Audio Delta 1010 (all inputs) - - Synth devices: - - Midi devices: - 0: M Audio Delta 1010 - - Timers: - 0: System clock - - Mixers: - 0: M Audio Delta 1010 - - -The actual /dev/dsp# numbers may be different on your system. Check the right -ones by looking at the output procuced by "ossinfo -a" command. With the -above configuration you can use /dev/dsp0 to /dev/dsp4 for playback of stereo -streams. If you play mono files the signal will be output only from the left -channel. /dev/dsp0 to /dev/dsp3 are connected to the analog outputs while -/dev/dsp4 is the S/PDIF output. - -The /dev/dsp5 to /dev/dsp10 device files can be used for recording. /dev/dsp5 -to /dev/dsp8 are the analog inputs. /dev/dsp11 and /dev/dsp12 are raw -input/output device files. They will be described in detail in the "Raw I/O -devices" section below. - -It's also possible to make OSS to create individual device files for every -channel this creates twice as many device files than the default setting. To -do this just append envy24_skipdevs=1 to the oss_envy24.conf file. This is useful -only if you are working on mono rather than stereo signals. However please -note that setting envy24_skipdevs=1 does _NOT_ lock the device files to one -channel mode, the application can still set them to stereo or multi channel -mode if it likes. - -It is possible to set all device files to mono only mode by setting -envy24_skipdevs=1 and envy24_force_mono=1. However this mode disables stereo -and multi channel usage for all devices so in general it should not be used. -.SH -By default the driver will create output devices before the input ones. By -setting envy24_swapdevs=1 in oss_envy24.conf you can ask OSS to create the device -files in opposite order i.e. input device files before the output ones. This -may be useful when using RealProducer. - -As a workaround to a bug in RealProducer you also need to create some dummy -mixer devices by defining envy24_realencoder_hack=1 in oss_envy24.conf. Without -these extra mixer devices RealProducer will not be able to access other than -the first input device. - - -.SH DEVICE MANAGEMENT - -By default OSS creates a large number of device files for each envy24 card. -This may be a problem when multiple cards need to be used in the same system. -Adding the envy24_devmask option to oss_envy24.conf should help -in most cases because it removes the devices that are actually not needed in -the system. - -The envy24_devmask number is the SUM of the following values: - - 1: Create primary (analog/ADAT/TDIF) outputs. - 2: Create primary (analog/ADAT/TDIF) inputs. - 4: Create S/PDIF outputs. - 8: Create S/PDIF inputs. - 16: Create monitor input device. - 32: Create the raw input and output devices. - -For example envy24_devmask=12 (4+8) creates only the S/PDIF devices. -To enable all possible (current or future) device files set envy24_devmask -to 65535 (default). - -If possible make your application to open the right device file -(/dev/dsp0 to /dev/dsp10) explicitly. It's also possible to use the -default devicefile (/dev/dsp) since OSS now supports automatic device -allocation (it opens the first available input or output devicefile -depending on the open mode). - -The channel allocation mechanism between device files is very flexible. -Even there is a device file for every stereo pair (or a mono channel) -it's possible to use any of the device file to access multiple channels. -For example an application can open /dev/dsp0 and set the number of -channels to 10. In this way the application can play all 10 channels -(or any number between 1 and 10) simultaneously (the samples will be -interleaved). - -There is simple automatic syncstart feature when using multiple -applications at the same time. Playback will not start before all -currently open devices files have started the playback operation. -The same mechanism works for recording (recording and playback -operations are fully independent). - -The Envy24 driver supports 8, 16 and 24/32 bit sample formats. - - -.SH SAMPLING RATE - -Envy24 based cards are multi channel devices and all the channels share the -same sampling rate. For this reason the sampling rate is normally locked to the -value selected using ossmix. However OSS supports some other methods for -changing the sampling rate. There are four ways to change the sampling rate. - - BASIC METHOD: - -Since all input and output channels of Envy24 work at the same sampling rate -it's not possible for the applications to select the rate themselves. Instead -the sampling rate is always locked to the currently selected rate. This rate -selection can be changed using the ossmix program shipped with OSS. - -For example: - - ossmix envy24.rate 48000 - -sets the sampling rate to 48000 Hz (default). The possible alternatives are -- 8000 -- 9600 -- 11025 -- 12000 -- 16000 -- 22050 -- 24000 -- 32000 -- 44100 -- 48000 -- 88200 -- 96000 - -When using S/PDIF inputs/outputs only the sampling rates 32000, 44100, 48000, 88200 or 96000 should be used. - -.SH EXTERNAL SYNC -It's possible to lock the sampling rate to the S/PDIF or world clock inputs -by setting the envy24.sync setting in ossmix to SPDIF or WCLOCK. However -the envy24.rate setting should be set manually to match the rate being used -(there is no autodetection for that). - -.SH NONLOCKED METHOD -It's also possible to turn the envy24.ratelock setting to OFF using ossmix. -After that the first application that opens the device can change the sampling -rate. However great care should be taken that this application gets the -recording/playback process fully started before any of the other -applications open their devices. Otherwise all devices will be locked to 8Khz. -Also keep in mind that subsequent applications will be forced to use the -sampling rate set by the first one. - -.SH SOFTWARE SRC -OSS contains a very high quality software based sample rate converter. -It can be enabled by setting envy24.src to ON using ossmix. - -After that OSS can do on-fly sample rate conversions between the actual -"hardware" sampling rate and the sampling rates used by the applications. In -this way every application may use different sampling rate. However there are -some drawbacks in this method: - -.IP \(bu 3 -The hardware rate needs to be 44100, 48000 or 96000 Hz. -.IP \(bu 3 -The software SRC algorithm consumes some CPU time (1% to 20% per audio -channel depending on the CPU speed and sampling rates). For this reason this -method may be useless in multi channel use with anything else but the fastest -high end CPUs. -.IP \(bu 3 -Only mono and stereo (1 or 2 channel) streams are supported. -.IP \(bu 3 -The SRC algorithm does cause minor artifacts to the sound (SNR is around 60 dB). - - -.SH RAW IO DEVICES - -These device files provide an alternative way to access Envy24 based devices. -With these devices it's possible to bypass the dual buffering required by the -"normal" input-output device files described above. This means that also the -mmap() feature is available and that the latencies caused by dual buffering -are gone. So these device files work much like "ordinary" soundcards. However -due to multi channel professional nature of the Envy24 chip there are some very -fundamental differences. This means that these device files can only be used -with applications that are aware of them. - -The differences from normal audio device files are: - -1. The sample format will always be 32 bit msb aligned (AFMT_S32_LE). Trying to -use any other sample format will cause unexpected results. -2. Number of channels is fixed and cannot be changed. The output device has -always 10 channels (0 to 7 are analog outputs and 8 to 9 are the digital -outputs). This assignment will be used even with cards that don't support -digital (or analog) outputs at all. If the actual hardware being used has -less channels the unused ones will be discarded (however they will be fed to -the on board monitor mixer). - -The input device is fixed to 12 channels. Channels 0 to 7 are analog inputs. -Channels 8 to 9 are digital inputs. Channels 10 and 11 are for the result -signal from the on board monitor mixer. - - -.SH DIGITAL MONITOR MIXER - -All Envy24 based cards have a built in monitor mixer. It can be used to mix -allinput and output signals together. The result can be recorded from the -"input from mon mixer" device (device 10 in the /dev/sndstat example above). -The monitor mix signal can also be routed to any of the outputs (including -S/PDIF and the "consumer" AC97 output of Terratec EWS88MT/D and any other card -that support s it). - -The settings in the gain.* group of ossmix are used to change the levels of all -inputs and outputs in the digital monitor mixer. The possible values are -between 0 (minimum) and 144 (maximum). - -OSS permits using all 10 possible output channels of the monitor mixer even -with cards that have less physical outputs. These "virtual" outputs are only -sent to the monitor mixer and their signal is only present in the monitor mixer -output. To enable these "virtual" channels set the envy24_virtualout parameter -to 1 in oss_envy24.conf. This option has no effect with Delta1010, EWS88MT and -other cards that have 10 "real" outputs. - - -.SH SYNC SOURCE - -On cards with S/PDIF and/or World Clock inputs it's possible to select the -sync source using - - ossmix envy24.sync - -The possible choices are: - -.IP \(bu 3 -INTERNAL: Use the internal sampling rate as defined by envy24.rate -.IP \(bu 3 -SPDIF: Use the S/PDIF input as the clock source. The envy24.rate setting -must be set manually to match the actual input sampling rate. -.IP \(bu 3 -WCLOCK: Like SPDIF but uses the world clock input signal (Delta 1010 only). - - -.SH OUTPUT ROUTINGS - -Output routing of output ports can be changed by changing the route.* settings -using ossmix. The possible choices are: - -.IP \(bu 3 -DMA: Playback from the associated /dev/dsp# device. -.IP \(bu 3 -MONITOR: Output of the digital mixer (only out1/2 and S/PDIF). -.IP \(bu 3 -IN1/2 to IN9/10 or IN1 to IN10: Loopback from the analog inputs -.IP \(bu 3 -SPDIFL or SPDIFR or SPDIF: Loopback from the S/PDIF input. - - -.SH PEAK METERS - -Envy24 based cards have peak meters for the input and output ports of the -digital monitor mixer. ossmix can show these values under the peak.* group -(these settings are read only). The values are between 0 (minimum) and 255 -(maximum). At this moment the only applications that supports these peak meters -are ossmix and ossxmix. - - -.SH AUDIO LATENCY - -IDE disk and CD-ROM drives may cause some interrupt latency problems which -may cause dropouts in recording/playback with Envy24 based cards. For this -reason ensure that DMA is turned on for the disk drive. - -Another method to solve the dropout problems is making the fragment size used -by the driver longer. This can be done by adding envy24_nfrags=N to the -oss_envy24.conf file. By default N is 16. Values 2, 4 or 8 make the fragments -longer which should cure the dropout problems. However this may cause -latency problems with some applications. Values 32 and 64 decrease the -latencies but may cause dropouts with IDE. - - -.SH OPTIONS - -.IP \(bu 3 -envy24_skipdevs: It's also possible to make OSS to create individual device -files for every channel. This creates twice as many device files than the -default setting. -Values: 1, 0 Default: 0 - -.IP \(bu 3 -envy24_swapdevs: By default the driver will create output devices before the -input ones. You can force the input devices to be configured before output -devices. -Values: 1, 0 Default: 0 - -.IP \(bu 3 -envy24_realencoder_hack: RealProducer wants to see a mixer device in -/dev/mixer. This option allows you to define a dummy /dev/mixer mixer device. -Envy24 Mixer device doesn't provide any consumer level soundcard compatibility -so this dummy mixer fools RealProducer into thinking it's running on a consumer -soundcard like SB Pro or SBLive. -Values: 1, 0 Default: 0 - -.IP \(bu 3 -envy24_gain_sliders: With some devices it's possible to change the gain -controllers to be continuous sliders instead of just enumerated ones. -Values: 1, 0 Default: 0 - -.IP \(bu 3 -envy24_nfrags: To solve the dropout problems make the fragment size used by -the driver longer. By default is 16. Values 2, 4 or 8 make the fragments longer -which should cure the dropout problems. However this may cause latency problems -with some applications. Values 32 and 64 decrease the latencies but may cause -dropouts with IDE drives. -Values: 2-64 Default: 16 - -.IP \(bu 3 -envy24_virtualout: OSS permits using all 10 possible output channels of the -monitor mixer even with cards that have less physical outputs. These "virtual" -outputs are only sent to the monitor mixer and their signal is only present in -the monitor mixer output. This has no effect for Delta1010 or Terratec EWS88MT. -Values: 1, 0 Default: 0 - -.IP \(bu 3 -envy24_force_mono: It is possible to set all device files to mono only mode -by setting envy24_skipdevs=1 and envy24_force_mono=1. However this mode -disables stereo and multi channel usage for all devices so in general it should -not be used. -Values: 1, 0 Default: 0 - -.SH FILES -/usr/lib/oss/conf/oss_envy24.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_envy24ht.7 b/man/man7/oss_envy24ht.7 deleted file mode 100644 index a620a6576..000000000 --- a/man/man7/oss_envy24ht.7 +++ /dev/null @@ -1,33 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_envy24ht - VIA Envy24HT/PT audio driver. - -.SH DESCRIPTION -Open Sound System driver for Envy24HT, Envy24HT-S, Envy24PT based sound -cards. - -Envy24HT device characteristics: - o 8/16 bit playback/record - o mono/stereo/4ch/5.1ch/7.1ch playback - o mono/sterero recording - o 8KHz to 192Khz sample rate. - -.SH OPTIONS -o envy24ht_model = -1|0|1 -Select the Model number if the card isn't autodetected -Values: 0 = Envy24ht 1=Envy24PT/HT-s compatible -1=Autodetect Default: -1 - -o envy24ht_fake_mixer = 0|1 -Some old applications may refuse to run if they don't find some legacy mixer -controls the envy24ht chip doesn't support. A "fake" legacy mixer can be -enabled to make such applications to run. However these fake legacy controls -will be permanently bound to full level. -Values: 0 = Disabled 1 = Enabled. Default: 0. - -.SH FILES -/usr/lib/oss/conf/oss_envy24ht.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_fmedia.7 b/man/man7/oss_fmedia.7 deleted file mode 100644 index 1a889c7da..000000000 --- a/man/man7/oss_fmedia.7 +++ /dev/null @@ -1,26 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_fmedia - Forte Media FM801 driver. - -.SH DESCRIPTION -Open Sound System driver for Forte Media FM801/FM801-AU audio controllers. - -FM801 device characteristics: - o 8/16 bit playback/record - o mono/stereo/4ch/5.1ch playback - o mono/sterero recording - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -.IP \(bu 3 -fmedia_mpu_irq= -Set the IRQ for the UART401 MPU. Refer to device conf file (see below) for -valid IRQs. - -.SH FILES -/usr/lib/oss/conf/oss_fmedia.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_geode.7 b/man/man7/oss_geode.7 deleted file mode 100644 index 53b31f1ff..000000000 --- a/man/man7/oss_geode.7 +++ /dev/null @@ -1,27 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_geode - National Semiconductor Geode audio driver. - -.SH DESCRIPTION -Open Sound System driver for National Semiconductor Geode/CS5530/CS5536 audio -controllers. - -Geode device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - -.SH NOTES -Some old Geode CPUs are not able to handle heavy computational loads. -If your audio streams are use a lot of CPU, you can start getting garbled audio -since the OSS Sample Rate Convertor is CPU intensive. Setting vmix0-src to -OFF will allow you to play audio but only at a fixed rate set via vmixctl -(Default: 48Khz). - -.SH FILES -/usr/lib/oss/conf/oss_geode.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_hdaudio.7 b/man/man7/oss_hdaudio.7 deleted file mode 100644 index 22e5ff740..000000000 --- a/man/man7/oss_hdaudio.7 +++ /dev/null @@ -1,81 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_hdaudio - Intel High Definition Audio (AZALIA) - -.SH DESCRIPTION - Open Sound System driver for Intels high definition audio known as - "Azalia". This driver supports Intel 915/925 chipsets with the - Realtek ALC880 and CMedia 9880 8 channel codecs. - - The HDA driver supports: - - o 8-96Khz Playback/Recording - o 8 or 16 or 32 bits - o 2, 4, 6 or 8 channel audio. - o SPDIF digital output and Input - o AC3 passthrough - -.SH HDAUDIO MIXER - The Intel HDA mixer is a new type of mixer that doesn't have - the normal volume controls found on AC97 or legacy SB devices. - The HDA mixer presents a concept of Jacks and you can configure - any jack to be either an output or an input jack. - - Some motherboards may not correctly initialize the jacks according - to their color and functionality but in general here's the - configuration that should generally be followed: - - o Orange = Center/LFE o Blue = Line-in - o Black = Rear o Green = Front - o Grey = Side o Pink = Mic - -Some Azalia codecs support front panel connectors and so if you see -fp-green and fp-pink connectors, then these are for front panel -speaker and mic/line-in Jacks. - -There is a function selector for most of the analog audio jacks (for example -connector.pink.mode). This selector is used to control if the jack is used -as an input (microphone or line in) or output (front, rear, side, speaker, -etc). - -.SH KNOWN PROBLEMS -In general Azalia based systems (laptops/motherboards) would require a custom -driver to work properly. Due to enormous number of different systems it is not -possible to develop such custom drivers for all systems. A generic driver is -used for systems that don't have dedicated drivers. - -Unfortunately the mixer and control panel interface (see ossmix(1)) -for "generic" systems is very cryptic and difficult to -understand. To solve problems with volumes or signal routing you need to -start ossxmix(1) and change the controls one at time until you get the desired -effect. - -.SH OPTIONS -.IP \(bu 3 -hdaudio_jacksense enables jack sensing mode when the hdaudio driver is - loaded. In this mode all I/O pin's that are not - in use will be disabled as well as the mixer controls - that are related with them. In this way the - mixer/control panel will become more intuitive. - However OSS will need to be restarted with soundoff; - soundon every time new inputs or outputs are attached - to the audio jacks. Default : 0. - - NOTE! hdaudio_jacksense=1 works only in some systems. - Many laptops and motherboards don't support jack - sensing. - -.IP \(bu 3 -hdaudio_noskip Disable skipping unconnected jack. All mixer controls - will be shown, even for disabled I/O pins. - Can get values 0-7. 1-7 is a bitmask, where every bit - masks a different check. Bit 3 (= value 4) overrides - jacksense check too. - Default: 0 - unconnected jacks are skipped. -.SH FILES - /usr/lib/oss/conf/oss_hdaudio.conf Device configuration file - -.SH AUTHOR - 4Front Technologies - diff --git a/man/man7/oss_ich.7 b/man/man7/oss_ich.7 deleted file mode 100644 index adcd0fc1f..000000000 --- a/man/man7/oss_ich.7 +++ /dev/null @@ -1,39 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_ich - Intel ICH/SiS7012/Nvidia/AMD audio device driver. - -.SH DESCRIPTION -Open Sound System driver for Intel ICH, nVidia Nforce, AMD and SiS 7012 -devices. - -.SH OPTIONS - -.IP \(bu 3 -intelpci_rate_tuning= (default is 240) -Some Compaq Deskpro models (EN and EX at least) and certain Dell models -play and record audio at a higher speed than what is expected. If you have -an Intel815 motherboard with an AD1885 you can try setting the parameter -to 240, 280 or 330 and see which works for your system. The way to figure -out the the right intelpci_rate_tuning value is using the osstest application. -It reports a sample rate drift value ("Sample rate drift" or "srate drift"). -Use the following formula (round the result to the nearest integer): - - - -.IP \(bu 3 -intelpci_force_mmio=<0|1> (default is 0=Disable) -This option can be used to force the ICH4/ICH5 and ICH6 controllers to -run in memory mapped mode to free up I/O address space. - -.IP \(bu 3 -ich_jacksense=<0|1> (default is 0) -Force use of jacksensing on some AD198x mixers. - -.SH FILES -/usr/lib/oss/conf/oss_ich.conf Device configuration file. - - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_sblive.7 b/man/man7/oss_sblive.7 deleted file mode 100644 index f7d15880d..000000000 --- a/man/man7/oss_sblive.7 +++ /dev/null @@ -1,245 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_sblive - Creative Labs Sound Blaster Live/Audigy family driver. - -.SH DESCRIPTION - Open Sound System driver for Creative Labs Sound Blaster Live!, Audigy, - Audigy2, Audigy2-Value and sound cards. - - The sblive driver supports: - - o 8-48Khz Playback/Recording - o 8 or 16 bits - o SPDIF digital output and Input - o Multi channel 5.1 (Live!) and 7.1 (Audigy) output. - - AC3 passthrough is only supported on Audigy series of the soundcards. - -.SH OTHER SIMILAR CARDS -There are several Sound Blaster cards that are also called as Live or -Audigy. However these cards are based on entirely different hardware design -and they are not compatible with this driver. - -.IP \(bu 3 -Sound Blaster Live 5.1 card is used in some Dell machines but it's - driven by the emu10k1x driver. -.IP \(bu 3 -Sound Blaster AudigyLS and Live 7.1 models are driven by the audigyls - driver of OSS. - -.SH SBLIVE COMBO SPDIF AND AUDIO JACKS -Most models of Live! and Audigy cards have an orange combo jack that is -used both for the analog center/LFE output and for digital DIN (S/PDIF) -output. The output mode is selected by a driver configuration option -(seel below) which should be set to proper value depending on the actual -speaker configuration. - -.IP \(bu 3 -Noisy analog center/LFE output. The orange combo jack at the rear plate - of the Live/Audigy card is shared between the digital DIN and the analog - center/LFE outputs. In digital DIN mode (default) you will hear very noisy - output from the speakers connected to this output jack. If you have analog - center/LFE (subwoofer) speakers connected then you need to turn off the - sblive_digital_din (or audigy_digital_din) option. - -.IP \(bu 3 -There is a new configuration option to enable/disable the "digital DIN" - output. By default the digital DIN interface is enabled which disables the - center/LFE analog output (uses the same combo jack). By setting the - sblive_digital_din (or audigy_digital_din) option to 0 you can enable the - analog C/LFE output feature. When digital DIN is disabled you can still get - S/PDIF (or AC3) output from the digital (optical/coax) outputs of the - optional livedrive unit. - -.SH SBLIVE MIXER -SB Live cards have actually two mixer chips. In OSS both of them are -controlled together. However only limited set of features can be controlled -using ordinary mixer programs (such as the mixer applet included in OSS). -Majority of features can only be accessed using the ossmix and ossxmix -programs included in OSS. - -The AC97 mixer is used to control volumes of the back bracket inputs (mic and -line in) and the _analog_ CD input connector on the soundcard. The 'mic' -volume controls the level of the rear bracket microphone input sent directly -to the front (only front) speakers. The 'line' and 'cd' controls do the same -for the back bracket line in connector and the on board analog CD input -connector. It's usually recommended to set these volumes to 0. - -Another function of the AC97 mixer is selecting the signal that is passed to -the master mixer (for example for recording). One of the 'mic', 'line' or -'cd' signals can be routed to the master mixer by selecting that device as -the recording source in the AC97 mixer. The 'rec' volume control slider can -be used to adjust the signal strength. The 'igain' control doesn't usually -have any effect but some hardware revisions may use it for controlling the -microphone recording level. - - -.SH SBLIVE MASTER MIXER -Other mixer functions are handled by the DSP engine of the EMU 10k1 chip. -Most input signals (including all digital signals and LiveDrive inputs). - -There are only two master mixer settings that can be controlled using all -mixer programs. The 'vol' setting is the master output volume that affects -both the front and rear speakers and the headphone output (digital output -volumes are not affected). The 'pcm' setting controls volumes of all PCM -playback channels (/dev/dsp#). - -In addition to volume sliders most inputs have a stereo VU meter pair -(only in ossxmix) that can be used to monitor the input and to adjust the -input levels properly. - -The master mixer consists of several sections that are: - -.IP \(bu 3 -Primary section: This section has two settings. The "spkmode" setting - selects how front/rear speakers are used for PCM playback (outputs from - programs using /dev/dsp#). The possible settings are FRONT, REAR and - FRONT+REAR. The default is FRONT+REAR. Change this setting if you like to - get PCM playback only from front or rear speakers. The "autoreset" flag is - used to control the "/dev" section. - -.IP \(bu 3 -"/dev" section: This section controls the volumes of each /dev/dsp# device - file supported by the device (there are 8 of them at this moment). These - volumes will return back to maximum every time the device is opened. However - this can be disabled by setting the 'autoreset' option to OFF. The ossxmix - program has special ability to show the application using the particular - /dev/dsp device (for layout reasons only the first 4 characters of the - program name are shown). - -.IP \(bu 3 -The equalizer section: This section controls the graphic equalizer for - front speakers only. - -.IP \(bu 3 -The front rear, and record sections: These three identical sections control - the levels of external inputs and PCM playback (/dev/dsp# devices) to be - sent to the front/rear speakers and to the recording device. - The CD Analog audio will only be heard from the FRONT speakers. - -.SH SBLIVE RECORDING -Before recording anything you need to set the volumes in the recording -section properly. To enable recording from the AC97 connected inputs -(mic, line in and analog CD) use the AC97 mixer to select the desired input -and then tune the input level using the rec (and igain) setting. - -Finally set the 'ac97' slider in the record section of the master mixer so -that the recording level is suitable. - -The OSS drivers permit recording any application that's currently playing. - -To record audio that's playing on any of the SB Live channels: - -.IP \(bu 3 -Turn down the AC97 control in the "record" section. This prevents any audio - being fed to the soundcard from MIC/Line-in/CD-in from getting mixed with - the audio produced by the application that's currently playing. - -.IP \(bu 3 -Type ossrecord -s -b -c test.wav - -.IP \(bu 3 -To stop recording press and then you can play back the test.wav - file using ossplay command. - - - RECORDING ISSUES: -In most cases noise is caused by the microphone input or some other -(unused) input. Use the ossxmix program to turn off all unused inputs and -finally save the current mixer settings (see below). - -Hint: Look at the VU meter panels of ossxmix. It's usually very easy to -locate the noise source by looking which input has some signal coming from -it. - -WARNING! If you turn off some of the signals in recording section or the -AC97 mixer section this affects all subsequent recordings. Remember -to raise the volume prior doing any recording. After that decrease -the volumes again if necessary. - - -.SH SBLIVE HARDWARE MIXING -You can use /dev/oss/oss_sblive0/ pcm0-pcm7 to play multiple audio programs -using the hardware mixing. -Simply specify the device name with the application. A simple test is -to do the following: - ossplay -d/dev/oss/oss_sblive0/pcm0 & - ossplay -d/dev/oss/oss_sblive0/pcm1 & - ossplay -d/dev/oss/oss_sblive0/pcm2 & - -You should hear all three wav files playing simultaneously. - -NOTE: Some apps may desire the old /dev/dspN names. e.g. /dev/dsp0 - /dev/dsp7. - -NOTE: You can increase the number of output devices from the standard 8 devices -to 32 device. For this, run soundconf, select Set configuration options and -look for the entry "sblive_device", now type any number between 1 and 32 -for the number of channels you wish. You can also do this manually by editing -oss_sblive.conf and inserting sblive_devices=XX entry, - e.g.: sblive_devices=27 - -.SH CDROM CONNNECTIONS -There are two alternative ways to connect audio signal from a CD-ROM drive tor -the SB Live soundcard. You can use a (three wire) analog cable or a (2 wire) -digital cable. OSS now supports both of these choices. Note that there are -separate mixer settings for both of these connections. - -The analog CD-ROM wire is connected to the AC97 code chip and this method -works in most cases. To route the analog CD -input to the (front) speakers -you need to raise the volume of the 'cd' control in mixer. However if you -like to hear the analog CD input both from the front and rears speakers you -need to do this in slightly different way (please read the description of -the mixer above). - -The digital connection works only with CD-ROM drives that has support for it. -Note that some CD-ROM drives having this digital output connector use a -different signal level than the one required by SB Live. This means that the -digital connection doesn't work with all CD-ROM drives (no sound). If you -have problems with the digital connection you should use the analog one. -When using the the digital CD input you may need to adjust the 'digcd' -volumes using ossxmix (or ossmix). - -It should be noted that SB Live works internally at 48 kHz. This means that -all S/PDIF input signals are automatically sample rate converted to 48 kHz. -If you record from a 44.1 kHz (CD-ROM) and save the result to a 44.1 kHz -file the signal will be sample rate converted twice. First from the 44.1 kHz -input to internal 48 kHz and then back to 44.1 kHz. While the sample rate -converter of SB Live is very precise this will cause some change. This should -not be any problem when doing audio recordings but it may cause unwanted -results when transferring digital data (such as AC3/DTS) using the S/PDIF -the interface. - -.SH CONFIGURATION OPTIONS -.IP \(bu 3 -sblive_digital_din=<0|1> - This option is to enable/disable the "digital DIN" - output of SB Live. By default the digital DIN interface is disabled which - enables the center/LFE analog output (uses the same combo jack). By - setting the sblive_digital_din option to 0 you can enable the analog - Center/LFE output feature. When digital DIN is disabled you can still - get S/PDIF (or AC3) output from the digital (optical/coax) outputs of the - optional livedrive unit. Default: 0=analog output. - -.IP \(bu 3 -audigy_digital_din=<0|1> - same as "sblive_digital_din" option except for - the Audigy soundcards. Default: 1=digital output. - -.IP \(bu 3 -sblive_devices=<1..32> - Number of audio devices to be configured. - - -.SH LIMITATION -.IP \(bu 3 -SB Live! devices will not work in Sparc systems due to PCI addressing - limitations. Only Audigy/Audigy2 models work under Sparc. -.IP \(bu 3 -EMU Wavetable MIDI synthesizer is not supported -.IP \(bu 3 -AC3 passthrough only supported on Audigy/Audigy but not on SB Live! devices. - -.SH FILES -/usr/lib/oss/conf/oss_sblive.conf Device configuration file - -.SH AUTHOR - 4Front Technologies - diff --git a/man/man7/oss_sbpci.7 b/man/man7/oss_sbpci.7 deleted file mode 100644 index 6da5cd55f..000000000 --- a/man/man7/oss_sbpci.7 +++ /dev/null @@ -1,43 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_sbpci - Creative Labs ES1371 audio driver. - -.SH DESCRIPTION -Open Sound System driver for Creative Labs ES1371/ES1373/5880, Ectiva 1938 -audio controllers. - -APCI97 device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate - -APCI97 MIXER EXTENSIONS - -Dual Dac mode: This feature turns the APCI97 into two output devices with -the output going to front and rear speakers independantly (however volume -control is global). - -Speaker Mode: This feature allows you to either have the audio coming out -the front speakers or you can have audio duplicated on rear speakers. This -mode is disabled when Dual Dac mode is enabled. - -SPDIF: This button enables or disables SPDIF output. - -.SH OPTIONS -.IP \(bu 3 -apci97_latency= -Certain models of the ES1371 sound devices will sound distorted playing stereo -audio and setting the PCI latency fixes the problem - -.IP \(bu 3 -apci_spdif=0|1 -Certain models like the SB 4.1D/SB PCI128D have SPDIF output jacks and -this setting enables the output device. - -.SH FILES -/usr/lib/oss/conf/oss_sbpci.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_sbxfi.7 b/man/man7/oss_sbxfi.7 deleted file mode 100644 index 35b7a2f3b..000000000 --- a/man/man7/oss_sbxfi.7 +++ /dev/null @@ -1,23 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_sbxfi - SoundBlaster X-Fi audio driver - -.SH DESCRIPTION -Open Sound System driver for the SoundBlaster X-Fi cards. - -.SH OPTIONS -.IP \(bu 3 -sbxfi_type Override X-Fi type autodetection. Values: - 0 - Autodetect type - 1 - Sound Blaster X-Fi (SB046x/067x/076x) - 2 - Sound Blaster X-Fi (SB073x) - 3 - Sound Blaster X-Fi (SB055x) - 4 - Sound Blaster X-Fi (UAA) - Default : 0. - -.SH FILES -/usr/lib/oss/conf/oss_sbxfi.conf Device configuration file. - -.SH AUTHOR -4Front Technologies diff --git a/man/man7/oss_solo.7 b/man/man7/oss_solo.7 deleted file mode 100644 index 0e91b0aab..000000000 --- a/man/man7/oss_solo.7 +++ /dev/null @@ -1,21 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_solo - ESS Solo-1 audio driver - -.SH DESCRIPTION -Open Sound System driver for ESS Solo1/1938/1968 audio controllers. -ESS Solo1 device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_solo.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/oss_trident.7 b/man/man7/oss_trident.7 deleted file mode 100644 index 45fbbec13..000000000 --- a/man/man7/oss_trident.7 +++ /dev/null @@ -1,30 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_trident - SiS7018, 4Dwave, ALIM5451 audio driver. - -.SH DESCRIPTION -Open Sound System driver for Trident 4DWave DX/NX, SiS7018 and ALI5451 audio -controllers. - -Trident device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - o Upto 8 hardware channels to mixing audio streams - -.SH OPTIONS -trident_mpu_ioaddr= -Set the MPU I/O address. Refer to the driver.conf file for valid addresses. - -.SH LIMITATIONS -.IP \(bu 3 -Due to PCI addressing limitations any add-on cards based on these chips -will not work under Sparc. The only exception is the ALI5451 chip that is -used on the main boards on many Sparc based systems. - -.SH FILES -/usr/lib/oss/conf/oss_trident.conf Device configuration file - -.SH AUTHOR -4Front Technologies diff --git a/man/man7/oss_via823x.7 b/man/man7/oss_via823x.7 deleted file mode 100644 index fa1360df7..000000000 --- a/man/man7/oss_via823x.7 +++ /dev/null @@ -1,22 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_via823x - Open Sound System driver for VIA 8233/8235/8237 audio controllers. - -.SH DESCRIPTION - -VIA823x device characteristics: - o 8/16 bit playback/record - o mono/stereo/4/5.1 (Dolby) playback - o mono/stereo recording - o 8KHz to 48Khz sample rate. - o SPDIF input/output capability based on AC97 codec attech to the - VIA823x controller. -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_via823x.conf Device configuration - -.SH AUTHOR -4Front Technologies diff --git a/man/man7/oss_via97.7 b/man/man7/oss_via97.7 deleted file mode 100644 index 7b62ac3c7..000000000 --- a/man/man7/oss_via97.7 +++ /dev/null @@ -1,21 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_via97 - VIA 82C686 audio driver - -.SH DESCRIPTION -Open Sound System driver for VIA 82C686A and 82C686B audio controllers. - -VIA97 device characteristics: - o 8/16 bit playback/record - o mono/stereo playback/recording - o 8KHz to 48Khz sample rate. - -.SH OPTIONS -None - -.SH FILES -/usr/lib/oss/conf/oss_via97.conf Device configuration file. - -.SH AUTHOR -4Front Technologies diff --git a/man/man7/oss_ymf7xx.7 b/man/man7/oss_ymf7xx.7 deleted file mode 100644 index 329bddede..000000000 --- a/man/man7/oss_ymf7xx.7 +++ /dev/null @@ -1,38 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -oss_ymf7xx - Yamaha DS-XG audio driver. - -.SH DESCRIPTION -Open Sound System driver for Yamaha DS-XG YMF724/740/744/754 audio controllers. - -ymf7xx device characteristics: - o 8/16 bit playback/record - o mono/stereo/4 channel playback - o mono/stereo recording - o 8KHz to 48Khz sample rate. - -.SH MIXER -The Yamaha DSXG models 744 and 754 supports SPDIF and AC3 multichannel output -and the Mixer extentions will allow you to enable/disable SPDIF output. - -.SH CONFIG OPTIONS -.IP \(bu 3 -yamaha_mpu_ioaddr= -MPU I/O address. Refer to the device conf file (see below) for legal values. - -.IP \(bu 3 -yamaha_mpu_irq= -MPU IRQ. Refer to device conf file (see below) for legal values - -.IP \(bu 3 -yamaha_fm_ioaddr= -Yamaha FM SYnthesizer IO address. Refer to driver conf file (see below) for -possible values. - -.SH FILES -/usr/lib/oss/conf/oss_ymf7xx.conf Device configuration file - -.SH AUTHOR -4Front Technologies - diff --git a/man/man7/osscore.7 b/man/man7/osscore.7 deleted file mode 100644 index bdf3f63fe..000000000 --- a/man/man7/osscore.7 +++ /dev/null @@ -1,84 +0,0 @@ -." Automatically generated text -.TH 7 "August 31, 2006" "OSS" "OSS Devices" -.SH NAME -osscore - Open Sound Sytem core audio framework. - -.SH DESCRIPTION -Open Sound System core audio support psudo driver. This driver implements the core Open Sound System API for audio, midi, mixer and synth functions. This driver also implements the OS driver interface as well as device configuration and setup. -More information on programming on the OSS API is avaialable at: -http://manuals.opensound.com/ - -.SH OPTIONS -.IP \(bu 3 -ac97_recselect When set to 1 this option enables independent - recording source selection for the left and the right channel - on AC97 devices. In this way it's possible to record audio - streams so that (for example) the left channel signal comes - from the microphone and - the right channel signal from line-in. However when this - option is enabled it's only possible to select the recording - source by using a fully OSS 4.0 compatible mixer program such - as ossxmix. - Default: 0 - recording source is common to both channels. -.IP \(bu 3 -ac97_amplifier When set to 1 this option enables the speaker power - amplifier feature of AC97 codec (if available). - Some boards have this inverted, so this feature can be - disabled by setting this option to 0. - Affects all AC97 based audio devices in the system. - Default: -1=autodetect correct setting. -.IP \(bu 3 -cooked_enable By default OSS will let applications to use sampling - rates and formats that are not supported by the hardware. - Instead OSS performs the necessary format conversions in - software. Applications that don't tolerate this kind of - conversions usually disable them by using features of the OSS - API (SNDCTL_DSP_COOKEDMODE). If this option is set to 0 then - the format conversions will be disabled for all applications - and devices unless the application explicitly enables them. - Default: 1 - conversions are enabled. - This option should not be changed without very strong reasons. -.IP \(bu 3 -detect_trace Internal debugging (do not change). Default: 0 -.IP \(bu 3 -dma_buffsize By default OSS will allocate audio DMA buffers with some - system dependent default size (usually 64k but sometimes - smaller). It is possible to change this default allocation by - setting this option. Value of 0 means that the default size - will be used. Value between 16 and 128 (kilobytes) can be used - if the default size is not suitable for some reason. - Default: 0 - system dependent buffsize. - This option mustn't be changed unless it's absolutely necessary. -.IP \(bu 3 -max_intrate Set the maximum number of interrupts per second. - Default: 100 interrupts per second which equals to about - 10 msec minimum latencies. -.IP \(bu 3 -vmix_disabled The virtual mixer subsystem can be disabled by setting - this configuration option to 1. - Default: 0 - virtual mixer is enabled. -.IP \(bu 3 -vmix_loopdevs Optionally the virtual mixer subsystem can create - special loopback audio devices that can be used to record the - output mix sent to the device. This option tells how many - loopback devices will be created (0, 1 or 2). If there are - multiple audio devices in the system the all of them will have - the same number of loopback devices. - Default: 0 - no loopback devices are created. - This setting should be left to 0 unless there are specific - reasons to enable the loopback devices. -.IP \(bu 3 -vmix_no_autoattach By default (0) the low level - drivers for most sound cards will automatically - attach virtual mixer (vmix) to the primary audio devices of the cards. - In some situations it may be necessary to attach virtual mixer using - nonstandard parameters. If vmix_no_autoattach is set to 1 then user - can use vmixctl attach command to attach virtual mixer manually to - the device(s). - Default: 0 - Automatically attach virtual mixer. - -.SH FILES -/usr/lib/oss/conf/osscore.conf Device configuration file - -.SH AUTHOR -4Front Technologies diff --git a/man/man8/Makefile b/man/man8/Makefile index 27a722da6..272803074 100644 --- a/man/man8/Makefile +++ b/man/man8/Makefile @@ -7,9 +7,12 @@ MAN= add_route.8 adduser.8 backup.8 badblocks.8 boot.8 \ ossdevlinks.8 part.8 partition.8 \ poweroff.8 printroot.8 pr_routes.8 pwdauth.8 rarpd.8 \ rdate.8 readclock.8 reboot.8 repartition.8 rlogind.8 \ - rshd.8 savemixer.8 screendump.8 serial-ip.8 service.8 \ + rshd.8 screendump.8 serial-ip.8 service.8 \ setup.8 shutdown.8 slip.8 srccrc.8 sync.8 syslogd.8 tcpd.8 \ - unix.8 update.8 usage.8 vmixctl.8 + unix.8 update.8 usage.8 + +MLINKS += ftpd.8 in.ftpd.8 +MLINKS += httpd.8 in.httpd.8 .include .include diff --git a/man/man8/savemixer.8 b/man/man8/savemixer.8 deleted file mode 100644 index ce16a07ab..000000000 --- a/man/man8/savemixer.8 +++ /dev/null @@ -1,39 +0,0 @@ -." Automatically generated text -.TH 8 "August 31, 2006" "OSS" "System Administration Commands" -.SH NAME -savemixer - Open Sound System program for saving and restoring mixer settings. - -.SH SYNOPSIS -savemixer [-LVv] [-f ] - -.SH DESCRIPTION -The savemixer program saves mixer settings. It can also load saved mixer -settings back into the mixer. - -Running this program without any parameters will save the current mixer -settings into /etc/oss/mixer.save or $OSSLIBDIR/etc/mixer.save file. -OSSLIBDIR is decided by reading /etc/oss.conf, and defaults to /usr/lib/oss. - -.SH AUTOMATIC SAVE -By default the soundoff command will automatically run savemixer to save -the active mixer settings. See the manual page for soundoff(1) if you -like to turn this feature off. - -.SH OPTIONS --f Use as setting file. --L Loads saved mixer and device map information from mixer.save. --V Version information. --v Verbose output. - -.SH SEE ALSO -soundoff(1), soundon(1), ossdetect(1), ossdevlinks(1), ossmix(1), ossxmix(1) - -.SH FILES -/etc/oss.conf -/usr/sbin/savemixer -/usr/lib/oss/etc/mixer.save -/usr/lib/oss/etc/dspdevs.map -/usr/lib/oss/etc/applist.conf - -.SH AUTHOR -4Front Technologies diff --git a/man/man8/vmixctl.8 b/man/man8/vmixctl.8 deleted file mode 100644 index 39202c607..000000000 --- a/man/man8/vmixctl.8 +++ /dev/null @@ -1,99 +0,0 @@ -." Automatically generated text -.TH 8 "August 31, 2006" "OSS" "System Administration Commands" -.SH NAME -vmixctl - Open Sound System utility to control the vmix subsystem. - -.SH SYNOPSIS -o vmixctl attach [attach_options...] audiodev [inputdev] -o vmixctl detach [attach_options...] audiodev -o vmixctl rate audiodev samplerate - -.SH DESCRIPTION -The vmixctl program can be used to attach or detach the virtual mixer subsystem -(vmix) to/from audio devices. In addition it can be used to control vmix -related parameters such as the sampling rate to be used with the device. - -By default most OSS drivers will attach virtual mixer to the primary audio device -of the sound card (or motherboard audio chip) when the device is attached. -However possible secondary audio devices (engines) will not have vmix -attached by default. In additional professional audio devices will be -attached without vmix because mixing may cause some unwanted distortion -to the signal. - -.SH ATTACHING VMIX TO AN AUDIO DEVICE -There are two forms of vmixctl attach command: - -o vmixctl attach audiodev -This alternative is to be used with devices that support only output or -have a single audio device file that supports full duplex. -o vmixctl attach audiodev inputdev -The second form is to be used with devices that have separate output and -input device files. The "audiodev" parameter defines the output device and -the "inputdev" parameter is the device file to be used for input direction. -Note that both device files must belong to the same "physical" sound card. -In some cases it might be possible to use one sound card for playback and -another for recording. However this configuration is not supported and the -result may not be functional. - -To find out the right device file names (audiodev and inputdev) you can use -the "ossinfo -a" command. - -.SH ATTACH OPTIONS -o -r Disable recording functionality. By default vmix will suppor - recording if the master device(s) support it. -o -p Do not preallocate client engines. By default vmix will - preallocate first 4 (out of 8) client engines when attaching - to the device. The remaining engines will be allocated - on-demand if there are more concurrent applications that - use the device. -o -V Make client devices visible (have private device nodes under /dev). -o -c Preallocate client engines instead of 4. However -p - option makes this option ineffective. - -.SH EXAMPLES -o vmixctl attach /dev/oss/oss_envy240/pcm0 -o vmixctl attach /dev/oss/oss_envy240/pcm0 /dev/oss/oss_envy240/pcmin0 - -.SH SETTING THE SAMPLING RATE USED BY VMIX -The virtual mixer subsystem will set the physical audio devce(s) to use -fixed sampling rate that is 48000 Hz by default. It is possible to use -"vmixctl rate audiodev" to switch vmix to use some different rate with this -device (pair). You should use "ossinfo -a -v2" to verify that the sampling rate -is actually supported by the device. Otherwise the actual device may enforce -vmix to use the nearest supported rate (or some default rate). - -The "audiodev" parameter is the device file name (see ossinfo -a) that is -used for playback. The input device name doesn't need to be specified. - -Note that some professional audio devices may be locked to external sampling -rate or some fixed rate (defined in ossmix/ossxmis). In such case the rate is -not changeable by vmixctl. - -.SH EXAMPLE -o vmixctl rate /dev/oss/oss_envy240/pcm0 - -.SH DETACHING VMIX FROM AN AUDIO DEVICE -It is possible to detach vmix from an audio device if it causes problems -with applications by using "vmix detach audiodev". - -It is not possible to detach and (re)attach vmix to the same device more -than few times. Use the vmix-enable setting in the control panel -(ossxmix or ossmix) to disable/re-enable vmix if you need to do it -repeatedly. Use vmix detach only if you need to attach virtual mixer using -different parameters. - -.SH EXAMPLE -o vmix detach /dev/oss/oss_envy240/pcm0 - -.SH POSSIBLE BUGS -o The control panel elements related with vmix are not removed from the - mixer API when vmix is detached. This may be somehow confusing. - -.SH SEE ALSO -soundoff(1), soundon(1), ossmix(1), ossxmix(1) - -.SH FILES -/usr/sbin/vmixct - -.SH AUTHOR -4Front Technologies diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk index 9d5a21220..fdcc62e38 100644 --- a/share/mk/bsd.own.mk +++ b/share/mk/bsd.own.mk @@ -375,8 +375,8 @@ TOOL_M4= m4 TOOL_MACPPCFIXCOFF= macppc-fixcoff TOOL_MAKEFS= makefs TOOL_MAKEINFO= makeinfo -#TOOL_MAKEWHATIS= /usr/libexec/makewhatis -TOOL_MAKEWHATIS= /usr/bin/makewhatis +TOOL_MAKEWHATIS= /usr/libexec/makewhatis +#TOOL_MAKEWHATIS= /usr/bin/makewhatis TOOL_MDSETIMAGE= mdsetimage TOOL_MENUC= menuc TOOL_MIPSELF2ECOFF= mips-elf2ecoff diff --git a/share/mk/minix.newlibc.mk b/share/mk/minix.newlibc.mk index a102b6fb7..56b76e09b 100644 --- a/share/mk/minix.newlibc.mk +++ b/share/mk/minix.newlibc.mk @@ -3,3 +3,4 @@ NBSD_LIBC:= yes CC:=${CC:C/^cc/clang/} COMPILER_TYPE:= gnu +AR=ar diff --git a/tools/nbsd_ports b/tools/nbsd_ports index 05a3bae5b..0f4681af1 100644 --- a/tools/nbsd_ports +++ b/tools/nbsd_ports @@ -4,6 +4,8 @@ lib/nbsd_libm src/lib/libm lib/libcrypt src/lib/libcrypt lib/libterminfo src/lib/libterminfo lib/libcurses src/lib/libcurses +lib/libutil src/lib/libutil +common/lib/libutil src/common/lib/libutil nbsd_include src/include bin/mkdir src/bin/mkdir usr.bin/m4 src/usr.bin/m4 @@ -14,3 +16,7 @@ usr.bin/tic src/usr.bin/tic usr.bin/mkdep src/usr.bin/mkdep usr.bin/uniq src/usr.bin/uniq usr.bin/seq src/usr.bin/seq +usr.bin/man src/usr.bin/man +usr.bin/apropos src/usr.bin/apropos +usr.bin/mdocml src/external/bsd/mdocml +libexec/makewhatis src/libexec/makewhatis diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 09dda125b..11bf1ce00 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -3,7 +3,8 @@ .include # NetBSD imports -SUBDIR= indent m4 stat tic sed mkdep uniq seq +SUBDIR= indent m4 stat tic sed mkdep uniq seq man mdocml \ + apropos # Non-NetBSD imports SUBDIR+= ministat diff --git a/usr.bin/apropos/Makefile b/usr.bin/apropos/Makefile new file mode 100644 index 000000000..9cba264f5 --- /dev/null +++ b/usr.bin/apropos/Makefile @@ -0,0 +1,14 @@ +# $NetBSD: Makefile,v 1.13 2009/04/14 22:15:17 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +.include + +MDIST= ${NETBSDSRCDIR}/usr.bin/man + +PROG= apropos +SRCS= apropos.c manconf.c + +.PATH: ${MDIST} +CPPFLAGS+=-I${MDIST} + +.include diff --git a/usr.bin/apropos/apropos.1 b/usr.bin/apropos/apropos.1 new file mode 100644 index 000000000..1003f0e09 --- /dev/null +++ b/usr.bin/apropos/apropos.1 @@ -0,0 +1,140 @@ +.\" $NetBSD: apropos.1,v 1.11 2009/05/08 12:48:43 wiz Exp $ +.\" +.\" Copyright (c) 1989, 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. +.\" +.\" @(#)apropos.1 8.1 (Berkeley) 6/29/93 +.\" +.Dd May 7, 2009 +.Dt APROPOS 1 +.Os +.Sh NAME +.Nm apropos +.Nd locate commands by keyword lookup +.Sh SYNOPSIS +.Nm apropos +.Op Fl C Ar path +.Op Fl M Ar path +.Op Fl m Ar path +.Op Fl S Ar subsection +.Op Fl s Ar section +.Ar keyword ... +.Sh DESCRIPTION +.Nm +shows which manual pages contain instances of any of the given +.Ar keyword(s) +in their title line. +Each word is considered separately and case of letters is ignored. +Words which are part of other words are considered; when looking for +.Dq compile , +.Nm +will also list all instances of +.Dq compiler . +.Pp +If the line output by +.Nm +starts +.Dq Li name(section) ... +you can enter +.Dq Li man section name +to get +its documentation. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl C +Use different +.Xr man 1 +configuration file than the default, +.Pa /etc/man.conf . +.It Fl M +Override the list of standard directories +.Nm +searches for a database named +.Pa whatis.db . +The supplied +.Ar path +must be a colon +.Dq \&: +separated list of directories. +This search path may also be set using the environment variable +.Ev MANPATH . +.It Fl m +Augment the list of standard directories +.Nm +searches for its database. +The supplied +.Ar path +must be a colon +.Dq \&: +separated list of directories. +These directories will be searched before the standard directories, +or the directories supplied with the +.Fl M +option or the +.Ev MANPATH +environment variable. +.It Fl S Ar subsection +Restrict the search to pages for the specified machine architecture. +By default, pages for all architectures are shown. +.It Fl s Ar section +Restrict the search to the specified section of the manual. +By default, pages from all sections are shown. +.El +.Sh ENVIRONMENT +.Bl -tag -width MANPATH +.It Ev MANPATH +The standard search path used by +.Xr man 1 +may be overridden by specifying a path in the +.Ev MANPATH +environment variable. +The format of the path is a colon +.Dq \&: +separated list of directories. +.El +.Sh FILES +.Bl -tag -width /etc/man.conf1 -compact +.It Pa whatis.db +name of the apropos database +.It Pa /etc/man.conf +.Xr man 1 +configuration file, used to get location of whatis database if +.Ev MANPATH +is not set. +.El +.Sh SEE ALSO +.Xr man 1 , +.Xr whatis 1 , +.Xr whereis 1 , +.Xr man.conf 5 , +.Xr makewhatis 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/usr.bin/apropos/apropos.c b/usr.bin/apropos/apropos.c new file mode 100644 index 000000000..06168ab85 --- /dev/null +++ b/usr.bin/apropos/apropos.c @@ -0,0 +1,277 @@ +/* $NetBSD: apropos.c,v 1.30 2009/05/08 12:48:43 wiz Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * 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 + +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)apropos.c 8.8 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: apropos.c,v 1.30 2009/05/08 12:48:43 wiz Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "manconf.h" /* from ../man/ */ +#include "pathnames.h" /* from ../man/ */ + +static bool *found; +static bool foundman = false; + +#define MAXLINELEN 8192 /* max line handled */ + +static void apropos(char **, const char *, bool, const char *, const char *); +static void lowstr(const char *, char *); +static bool match(const char *, const char *); +static void usage(void) __dead; + +int +main(int argc, char *argv[]) +{ + ENTRY *ep; + TAG *tp; + int ch, rv; + char *conffile, *machine, **p, *p_augment, *p_path, *sflag; + glob_t pg; + + conffile = NULL; + p_augment = p_path = NULL; + machine = sflag = NULL; + while ((ch = getopt(argc, argv, "C:M:m:P:S:s:")) != -1) { + switch (ch) { + case 'C': + conffile = optarg; + break; + case 'M': + case 'P': /* backward compatible */ + p_path = optarg; + break; + case 'm': + p_augment = optarg; + break; + case 'S': + machine = optarg; + lowstr(machine, machine); + break; + case 's': + sflag = optarg; + lowstr(sflag, sflag); + break; + case '?': + default: + usage(); + } + } + argv += optind; + argc -= optind; + + if (argc < 1) + usage(); + + if ((found = malloc(argc * sizeof(*found))) == NULL) + err(EXIT_FAILURE, "malloc"); + (void)memset(found, 0, argc * sizeof(*found)); + + for (p = argv; *p; ++p) /* convert to lower-case */ + lowstr(*p, *p); + + if (p_augment) + apropos(argv, p_augment, true, sflag, machine); + if (p_path || (p_path = getenv("MANPATH"))) + apropos(argv, p_path, true, sflag, machine); + else { + config(conffile); + tp = gettag("_whatdb", 1); + if (!tp) + errx(EXIT_FAILURE, "malloc"); + TAILQ_FOREACH(ep, &tp->entrylist, q) { + if ((rv = glob(ep->s, GLOB_BRACE | GLOB_NOSORT, NULL, + &pg)) != 0) { + if (rv == GLOB_NOMATCH) + continue; + else + err(EXIT_FAILURE, "glob"); + } + if (pg.gl_pathc) + for (p = pg.gl_pathv; *p; p++) + apropos(argv, *p, false, sflag, + machine); + globfree(&pg); + } + } + + if (!foundman) + errx(EXIT_FAILURE, "no %s file found", _PATH_WHATIS); + + rv = 1; + for (p = argv; *p; ++p) + if (found[p - argv]) + rv = 0; + else + (void)printf("%s: nothing appropriate\n", *p); + return rv; +} + +static void +apropos(char **argv, const char *path, bool buildpath, + const char *sflag, const char *machine) +{ + char *end, **p; + const char *name; + char buf[MAXLINELEN + 1]; + char hold[MAXPATHLEN + 1]; + char wbuf[MAXLINELEN + 1]; + size_t slen = 0, mlen = 0; + + if (sflag) + slen = strlen(sflag); + if (machine) + mlen = strlen(machine); + + for (name = path; name; name = end) { /* through name list */ + if ((end = strchr(name, ':')) != NULL) + *end++ = '\0'; + + if (buildpath) { + (void)snprintf(hold, sizeof(hold), "%s/%s", name, + _PATH_WHATIS); + name = hold; + } + + if (!freopen(name, "r", stdin)) + continue; + + foundman = true; + + /* for each file found */ + while (fgets(buf, (int)sizeof(buf), stdin)) { + if (!strchr(buf, '\n')) { + warnx("%s: line too long", name); + continue; + } + lowstr(buf, wbuf); + if (machine) { + if ((strncmp(wbuf, machine, mlen) != 0) || + strlen(wbuf) <= mlen || wbuf[mlen] != '/') + continue; + } + if (sflag) { + char *s = strchr(wbuf, '('); + + if (!s) + continue; + if (strncmp(s+1, sflag, slen) != 0) + continue; + } + for (p = argv; *p; ++p) { + if (match(wbuf, *p)) { + (void)printf("%s", buf); + found[p - argv] = true; + + /* only print line once */ + while (*++p) + if (match(wbuf, *p)) + found[p - argv] = true; + break; + } + } + } + } +} + +/* + * match -- + * match anywhere the string appears + */ +static bool +match(const char *bp, const char *str) +{ + size_t len; + char test; + + if (!*bp) + return false; + /* backward compatible: everything matches empty string */ + if (!*str) + return true; + for (test = *str++, len = strlen(str); *bp;) + if (test == *bp++ && !strncmp(bp, str, len)) + return true; + return false; +} + +/* + * lowstr -- + * convert a string to lower case + */ +static void +lowstr(const char *from, char *to) +{ + char ch; + + while ((ch = *from++) && ch != '\n') + *to++ = tolower((unsigned char)ch); + *to = '\0'; +} + +/* + * usage -- + * print usage message and die + */ +__dead +static void +usage(void) +{ + + (void)fprintf(stderr, + "usage: %s [-C file] [-M path] [-m path] " + "[-S subsection] [-s section]\n" + " keyword ...\n", + getprogname()); + exit(1); +} diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile new file mode 100644 index 000000000..ec1b1f0fd --- /dev/null +++ b/usr.bin/man/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.11 2009/04/14 22:15:23 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +WARNS?= 2 # XXX -Wcast-qual issues + +PROG= man +SRCS= man.c manconf.c +MAN= man.1 man.conf.5 + +DPADD+= ${LIBUTIL} +LDADD+= -lutil + +.include diff --git a/commands/man/man.1 b/usr.bin/man/man.1 similarity index 96% rename from commands/man/man.1 rename to usr.bin/man/man.1 index 96b0ce763..9bc818d2f 100644 --- a/commands/man/man.1 +++ b/usr.bin/man/man.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: man.1,v 1.20 2006/04/13 21:10:44 wiz Exp $ +.\" $NetBSD: man.1,v 1.21 2009/10/07 08:30:31 cegger Exp $ .\" .\" Copyright (c) 1989, 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -29,7 +29,7 @@ .\" .\" @(#)man.1 8.2 (Berkeley) 1/2/94 .\" -.Dd April 10, 2006 +.Dd October 6, 2009 .Dt MAN 1 .Os .Sh NAME @@ -168,6 +168,17 @@ option is not used, and the first argument is a valid section, then that argument will be used as if specified by the .Ql Fl s option. +.Pp +If +.Ar name +is given with a full or relative path then +.Nm +interprets it as a file specification, so that you can do +.Nm +.Cm ./foo.5 +or even +.Nm +.Cm /cd/foo/bar.1.gz . .Sh ENVIRONMENT .Bl -tag -width MANPATHX .It Ev MACHINE diff --git a/commands/man/man.c b/usr.bin/man/man.c similarity index 80% rename from commands/man/man.c rename to usr.bin/man/man.c index d539c8d6d..ccc4149dc 100644 --- a/commands/man/man.c +++ b/usr.bin/man/man.c @@ -1,4 +1,4 @@ -/* $NetBSD: man.c,v 1.37 2008/07/21 14:19:24 lukem Exp $ */ +/* $NetBSD: man.c,v 1.41 2010/07/07 21:24:34 christos Exp $ */ /* * Copyright (c) 1987, 1993, 1994, 1995 @@ -31,6 +31,19 @@ #include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95"; +#else +__RCSID("$NetBSD: man.c,v 1.41 2010/07/07 21:24:34 christos Exp $"); +#endif +#endif /* not lint */ + #include #include #include @@ -47,6 +60,7 @@ #include #include #include +#include #include "manconf.h" #include "pathnames.h" @@ -85,13 +99,14 @@ struct manstate { /* other misc stuff */ const char *pager; /* pager to use */ + const char *machine; /* machine */ + const char *machclass; /* machine class */ size_t pagerlen; /* length of the above */ }; /* * prototypes */ -int main(int, char **); static void build_page(char *, char **, struct manstate *); static void cat(char *); static const char *check_pager(const char *); @@ -100,7 +115,9 @@ static void how(char *); static void jump(char **, char *, char *); static int manual(char *, struct manstate *, glob_t *); static void onsig(int); -static void usage(void); +static void usage(void) __attribute__((__noreturn__)); +static void addpath(struct manstate *, const char *, size_t, const char *); +static const char *getclass(const char *); /* * main function @@ -110,12 +127,13 @@ main(int argc, char **argv) { static struct manstate m = { 0 }; /* init to zero */ int ch, abs_section, found; - const char *machine; ENTRY *esubd, *epath; - char *p, **ap, *cmd, buf[MAXPATHLEN * 2]; + char *p, **ap, *cmd; size_t len; glob_t pg; + setprogname(argv[0]); + setlocale(LC_ALL, ""); /* * parse command line... */ @@ -179,16 +197,16 @@ main(int argc, char **argv) */ config(m.conffile); /* exits on error ... */ - if ((machine = getenv("MACHINE")) == NULL) { + if ((m.machine = getenv("MACHINE")) == NULL) { struct utsname utsname; - if (uname(&utsname) == -1) { - perror("uname"); - exit(1); - } - machine = utsname.machine; + if (uname(&utsname) == -1) + err(EXIT_FAILURE, "uname"); + m.machine = utsname.machine; } + m.machclass = getclass(m.machine); + if (!m.cat && !m.how && !m.where) { /* if we need a pager ... */ if (!isatty(STDOUT_FILENO)) { m.cat = 1; @@ -207,7 +225,7 @@ main(int argc, char **argv) m.section = gettag(m.sectionname, 0); /* -s must be a section */ if (m.section == NULL) - errx(1, "unknown section: %s", m.sectionname); + errx(EXIT_FAILURE, "unknown section: %s", m.sectionname); } else if (argc > 1) { @@ -239,7 +257,7 @@ main(int argc, char **argv) m.intmp = gettag("_intmp", 1); if (!m.defaultpath || !m.subdirs || !m.suffixlist || !m.buildlist || !m.mymanpath || !m.missinglist || !m.intmp) - errx(1, "malloc failed"); + errx(EXIT_FAILURE, "malloc failed"); /* * are we using a section whose elements are all absolute paths? @@ -303,38 +321,21 @@ main(int argc, char **argv) len = strlen(p); if (len < 1) continue; - TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) { - snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", - p, (p[len-1] == '/') ? "" : "/", - esubd->s, machine); - if (addentry(m.mymanpath, buf, 0) < 0) - errx(1, "malloc failed"); - } + TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) + addpath(&m, p, len, esubd->s); } } else { TAILQ_FOREACH(epath, &m.defaultpath->entrylist, q) { /* handle trailing "/" magic here ... */ - if (abs_section && - epath->s[epath->len - 1] != '/') { - - (void)snprintf(buf, sizeof(buf), - "%s{/%s,}", epath->s, machine); - if (addentry(m.mymanpath, buf, 0) < 0) - errx(1, "malloc failed"); + if (abs_section && epath->s[epath->len - 1] != '/') { + addpath(&m, "", 1, epath->s); continue; } - TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) { - snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", - epath->s, - (epath->s[epath->len-1] == '/') ? "" - : "/", - esubd->s, machine); - if (addentry(m.mymanpath, buf, 0) < 0) - errx(1, "malloc failed"); - } + TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) + addpath(&m, epath->s, epath->len, esubd->s); } } @@ -351,14 +352,8 @@ main(int argc, char **argv) len = strlen(p); if (len < 1) continue; - TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) { - snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", - p, (p[len-1] == '/') ? "" : "/", - esubd->s, machine); - /* add at front */ - if (addentry(m.mymanpath, buf, 1) < 0) - errx(1, "malloc failed"); - } + TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) + addpath(&m, p, len, esubd->s); } } @@ -391,7 +386,7 @@ main(int argc, char **argv) /* if nothing found, we're done. */ if (!found) { (void)cleanup(); - exit (1); + exit(EXIT_FAILURE); } /* @@ -403,7 +398,7 @@ main(int argc, char **argv) continue; cat(*ap); } - exit (cleanup()); + exit(cleanup()); } if (m.how) { for (ap = pg.gl_pathv; *ap != NULL; ++ap) { @@ -435,7 +430,7 @@ main(int argc, char **argv) if ((cmd = malloc(len)) == NULL) { warn("malloc"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } /* now build the command string... */ @@ -460,6 +455,39 @@ main(int argc, char **argv) exit(cleanup()); } +static int +manual_find_buildkeyword(char *escpage, const char *fmt, + struct manstate *mp, glob_t *pg, size_t cnt) +{ + ENTRY *suffix; + int found; + char *p, buf[MAXPATHLEN]; + + found = 0; + /* Try the _build key words next. */ + TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) { + for (p = suffix->s; + *p != '\0' && !isspace((unsigned char)*p); + ++p) + continue; + if (*p == '\0') + continue; + + *p = '\0'; + (void)snprintf(buf, sizeof(buf), fmt, escpage, suffix->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + if (!mp->where) + build_page(p + 1, &pg->gl_pathv[cnt], mp); + *p = ' '; + found = 1; + break; + } + *p = ' '; + } + + return found; +} + /* * manual -- * Search the manuals for the pages. @@ -482,7 +510,7 @@ manual(char *page, struct manstate *mp, glob_t *pg) if ((escpage = malloc((2 * strlen(page)) + 1)) == NULL) { warn("malloc"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } p = page; @@ -498,6 +526,63 @@ manual(char *page, struct manstate *mp, glob_t *pg) *eptr = '\0'; + /* + * If 'page' is given with a full or relative path + * then interpret it as a file specification. + */ + if ((page[0] == '/') || (page[0] == '.')) { + /* check if file actually exists */ + (void)strlcpy(buf, escpage, sizeof(buf)); + error = glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg); + if (error != 0) { + if (error == GLOB_NOMATCH) { + goto notfound; + } else { + errx(EXIT_FAILURE, "glob failed"); + } + } + + if (pg->gl_matchc == 0) + goto notfound; + + /* clip suffix for the suffix check below */ + p = strrchr(escpage, '.'); + if (p && p[0] == '.' && isdigit((unsigned char)p[1])) + p[0] = '\0'; + + found = 0; + for (cnt = pg->gl_pathc - pg->gl_matchc; + cnt < pg->gl_pathc; ++cnt) + { + found = manual_find_buildkeyword(escpage, "%s%s", + mp, pg, cnt); + if (found) { + anyfound = 1; + if (!mp->all) { + /* Delete any other matches. */ + while (++cnt< pg->gl_pathc) + pg->gl_pathv[cnt] = ""; + break; + } + continue; + } + + /* It's not a man page, forget about it. */ + pg->gl_pathv[cnt] = ""; + } + + notfound: + if (!anyfound) { + if (addentry(mp->missinglist, page, 0) < 0) { + warn("malloc"); + (void)cleanup(); + exit(EXIT_FAILURE); + } + } + free(escpage); + return anyfound; + } + /* For each man directory in mymanpath ... */ TAILQ_FOREACH(mdir, &mp->mymanpath->entrylist, q) { @@ -513,7 +598,7 @@ manual(char *page, struct manstate *mp, glob_t *pg) else { warn("globbing"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } } if (pg->gl_matchc == 0) @@ -564,28 +649,8 @@ manual(char *page, struct manstate *mp, glob_t *pg) goto next; /* Try the _build key words next. */ - found = 0; - TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) { - for (p = suffix->s; - *p != '\0' && !isspace((unsigned char)*p); - ++p) - continue; - if (*p == '\0') - continue; - *p = '\0'; - (void)snprintf(buf, - sizeof(buf), "*/%s%s", escpage, - suffix->s); - if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { - if (!mp->where) - build_page(p + 1, - &pg->gl_pathv[cnt], mp); - *p = ' '; - found = 1; - break; - } - *p = ' '; - } + found = manual_find_buildkeyword(escpage, "*/%s%s", + mp, pg, cnt); if (found) { next: anyfound = 1; if (!mp->all) { @@ -610,12 +675,12 @@ next: anyfound = 1; if (addentry(mp->missinglist, page, 0) < 0) { warn("malloc"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } } free(escpage); - return (anyfound); + return anyfound; } /* @@ -688,7 +753,7 @@ build_page(char *fmt, char **pathp, struct manstate *mp) if ((fd = mkstemp(tpath)) == -1) { warn("%s", tpath); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); (void)snprintf(cmd, sizeof(cmd), buf, p); @@ -697,14 +762,14 @@ build_page(char *fmt, char **pathp, struct manstate *mp) if ((*pathp = strdup(tpath)) == NULL) { warn("malloc"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } /* Link the built file into the remove-when-done list. */ if (addentry(mp->intmp, *pathp, 0) < 0) { warn("malloc"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } /* restore old directory so relative manpaths still work */ @@ -729,7 +794,7 @@ how(char *fname) if (!(fp = fopen(fname, "r"))) { warn("%s", fname); (void)cleanup(); - exit (1); + exit(EXIT_FAILURE); } #define S1 "SYNOPSIS" #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" @@ -774,18 +839,18 @@ cat(char *fname) if ((fd = open(fname, O_RDONLY, 0)) < 0) { warn("%s", fname); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } while ((n = read(fd, buf, sizeof(buf))) > 0) if (write(STDOUT_FILENO, buf, n) != n) { warn("write"); (void)cleanup(); - exit (1); + exit(EXIT_FAILURE); } if (n == -1) { warn("read"); (void)cleanup(); - exit(1); + exit(EXIT_FAILURE); } (void)close(fd); } @@ -812,14 +877,11 @@ check_pager(const char *name) /* make sure it's "more", not "morex" */ if (!strncmp(p, "more", 4) && (!p[4] || isspace((unsigned char)p[4]))){ char *newname; - if(!(newname = malloc(strlen(p) + 20))) { - errx(1, "malloc failed"); - } - (void)sprintf(newname, "%s %s", p, "-s"); + (void)asprintf(&newname, "%s %s", p, "-s"); name = newname; } - return (name); + return name; } /* @@ -838,8 +900,7 @@ jump(char **argv, char *flag, char *name) for (; *arg; ++arg) arg[0] = arg[1]; execvp(name, argv); - (void)fprintf(stderr, "%s: Command not found.\n", name); - exit(1); + err(EXIT_FAILURE, "Cannot execute `%s'", name); } /* @@ -852,12 +913,10 @@ onsig(int signo) (void)cleanup(); -#if 0 (void)raise_default_signal(signo); -#endif /* NOTREACHED */ - exit (1); + exit(EXIT_FAILURE); } /* @@ -865,13 +924,13 @@ onsig(int signo) * Clean up temporary files, show any error messages. */ static int -cleanup() +cleanup(void) { TAG *intmpp, *missp; ENTRY *ep; int rval; - rval = 0; + rval = EXIT_SUCCESS; /* * note that _missing and _intmp were created by main(), so * gettag() cannot return NULL here. @@ -881,13 +940,36 @@ cleanup() TAILQ_FOREACH(ep, &missp->entrylist, q) { warnx("no entry for %s in the manual.", ep->s); - rval = 1; + rval = EXIT_FAILURE; } TAILQ_FOREACH(ep, &intmpp->entrylist, q) (void)unlink(ep->s); - return (rval); + return rval; +} + +static const char * +getclass(const char *machine) +{ + char buf[BUFSIZ]; + TAG *t; + snprintf(buf, sizeof(buf), "_%s", machine); + t = gettag(buf, 0); + return t != NULL && !TAILQ_EMPTY(&t->entrylist) ? + TAILQ_FIRST(&t->entrylist)->s : NULL; +} + +static void +addpath(struct manstate *m, const char *dir, size_t len, const char *sub) +{ + char buf[2 * MAXPATHLEN + 1]; + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,%s%s%s}", + dir, (dir[len - 1] == '/') ? "" : "/", sub, m->machine, + m->machclass ? "/" : "", m->machclass ? m->machclass : "", + m->machclass ? "," : ""); + if (addentry(m->mymanpath, buf, 0) < 0) + errx(EXIT_FAILURE, "malloc failed"); } /* @@ -895,12 +977,12 @@ cleanup() * print usage message and die */ static void -usage() +usage(void) { - (void)fprintf(stderr, "usage: %s [-acw|-h] [-C cfg] [-M path] " + (void)fprintf(stderr, "Usage: %s [-acw|-h] [-C cfg] [-M path] " "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname()); (void)fprintf(stderr, - "usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n", + "Usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n", getprogname()); - exit(1); + exit(EXIT_FAILURE); } diff --git a/commands/man/man.conf.5 b/usr.bin/man/man.conf.5 similarity index 100% rename from commands/man/man.conf.5 rename to usr.bin/man/man.conf.5 diff --git a/commands/man/manconf.c b/usr.bin/man/manconf.c similarity index 97% rename from commands/man/manconf.c rename to usr.bin/man/manconf.c index 63c5a94eb..348590453 100644 --- a/commands/man/manconf.c +++ b/usr.bin/man/manconf.c @@ -41,6 +41,13 @@ #endif #include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)config.c 8.8 (Berkeley) 1/31/95"; +#else +__RCSID("$NetBSD: manconf.c,v 1.6 2008/03/08 15:48:27 christos Exp $"); +#endif +#endif /* not lint */ #include #include diff --git a/commands/man/manconf.h b/usr.bin/man/manconf.h similarity index 100% rename from commands/man/manconf.h rename to usr.bin/man/manconf.h diff --git a/commands/man/pathnames.h b/usr.bin/man/pathnames.h similarity index 100% rename from commands/man/pathnames.h rename to usr.bin/man/pathnames.h diff --git a/usr.bin/mdocml/Makefile b/usr.bin/mdocml/Makefile new file mode 100644 index 000000000..e432c64ca --- /dev/null +++ b/usr.bin/mdocml/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2009/10/21 18:04:52 joerg Exp $ + +SUBDIR= lib .WAIT bin man + +.include diff --git a/usr.bin/mdocml/Makefile.inc b/usr.bin/mdocml/Makefile.inc new file mode 100644 index 000000000..7368b890c --- /dev/null +++ b/usr.bin/mdocml/Makefile.inc @@ -0,0 +1,25 @@ +# $NetBSD: Makefile.inc,v 1.12 2010/07/25 19:16:18 joerg Exp $ + +.include +.include "../Makefile.inc" + +VERSION!= cd ${.PARSEDIR}/dist && ${MAKE} -V VERSION + +CPPFLAGS+= -DVERSION=\"${VERSION}\" -DUGLY +CPPFLAGS+= -DOSNAME=\"Minix\" + +.if (${HOSTPROG:U} == "") +CPPFLAGE+= -DHAVE_STRLCAT -DHAVE_STRLCPY +.endif + + +DISTDIR:= ${.PARSEDIR}/dist + +.PATH: ${DISTDIR} + +.for _LIB in man mdoc roff +MDOCMLOBJDIR.${_LIB} != cd ${.PARSEDIR}/lib/lib${_LIB} && ${PRINTOBJDIR} +MDOCMLLIB.${_LIB}= ${MDOCMLOBJDIR.${_LIB}}/lib${_LIB}.a +.endfor + +WARNS?= 4 diff --git a/usr.bin/mdocml/bin/Makefile b/usr.bin/mdocml/bin/Makefile new file mode 100644 index 000000000..a373a2f45 --- /dev/null +++ b/usr.bin/mdocml/bin/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.1 2009/10/21 18:04:52 joerg Exp $ + +SUBDIR= mandoc + +.include diff --git a/usr.bin/mdocml/bin/Makefile.inc b/usr.bin/mdocml/bin/Makefile.inc new file mode 100644 index 000000000..42bbc4691 --- /dev/null +++ b/usr.bin/mdocml/bin/Makefile.inc @@ -0,0 +1,5 @@ +# $NetBSD: Makefile.inc,v 1.2 2009/10/21 22:33:12 joerg Exp $ + +BINDIR?= /usr/bin + +.include "${.PARSEDIR}/../Makefile.inc" diff --git a/usr.bin/mdocml/bin/mandoc/Makefile b/usr.bin/mdocml/bin/mandoc/Makefile new file mode 100644 index 000000000..741409b63 --- /dev/null +++ b/usr.bin/mdocml/bin/mandoc/Makefile @@ -0,0 +1,24 @@ +# $NetBSD: Makefile,v 1.4 2011/01/12 23:02:21 joerg Exp $ + +.include + +PROG= mandoc + +SRCS= main.c mdoc_term.c chars.c term.c term_ascii.c term_ps.c \ + tbl_term.c tbl_html.c tree.c compat.c \ + man_term.c html.c mdoc_html.c man_html.c out.c + +.ifndef HOSTPROG +DPADD+= ${MDOCMLLIB.man} ${MDOCMLLIB.mdoc} ${MDOCMLLIB.roff} +LDADD+= -L${MDOCMLOBJDIR.man} -lman \ + -L${MDOCMLOBJDIR.mdoc} -lmdoc \ + -L${MDOCMLOBJDIR.roff} -lroff +.else +SRCS.libman!= cd ${.PARSEDIR}/../../lib/libman && ${MAKE} -V '$${SRCS}' +SRCS.libmdoc!= cd ${.PARSEDIR}/../../lib/libmdoc && ${MAKE} -V '$${SRCS}' +SRCS.libroff!= cd ${.PARSEDIR}/../../lib/libroff && ${MAKE} -V '$${SRCS}' + +SRCS+= ${SRCS.libman} ${SRCS.libmdoc:Nmandoc.c} ${SRCS.libroff} +.endif + +.include diff --git a/usr.bin/mdocml/dist/Makefile b/usr.bin/mdocml/dist/Makefile new file mode 100644 index 000000000..934d6072f --- /dev/null +++ b/usr.bin/mdocml/dist/Makefile @@ -0,0 +1,346 @@ +.SUFFIXES: .html .xml .sgml .1 .3 .7 .md5 .tar.gz +.SUFFIXES: .1.txt .3.txt .7.txt +.SUFFIXES: .1.xhtml .3.xhtml .7.xhtml +.SUFFIXES: .1.sgml .3.sgml .7.sgml +.SUFFIXES: .h .h.html +.SUFFIXES: .1.ps .3.ps .7.ps +.SUFFIXES: .1.pdf .3.pdf .7.pdf + +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin +INCLUDEDIR = $(PREFIX)/include +LIBDIR = $(PREFIX)/lib +MANDIR = $(PREFIX)/man +EXAMPLEDIR = $(PREFIX)/share/examples/mandoc +INSTALL = install +INSTALL_PROGRAM = $(INSTALL) -m 0755 +INSTALL_DATA = $(INSTALL) -m 0444 +INSTALL_LIB = $(INSTALL) -m 0644 +INSTALL_MAN = $(INSTALL_DATA) + +VERSION = 1.10.9 +VDATE = 07 January 2010 + +VFLAGS = -DVERSION="\"$(VERSION)\"" +WFLAGS = -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings +CFLAGS += -g $(WFLAGS) $(VFLAGS) -DHAVE_CONFIG_H + +# 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\"" + CFLAGS += -DOSNAME="\"Minix\"" + +LINTFLAGS += $(VFLAGS) + +ROFFLNS = roff.ln tbl.ln tbl_opts.ln tbl_layout.ln tbl_data.ln + +ROFFSRCS = roff.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c + +ROFFOBJS = roff.o tbl.o tbl_opts.o tbl_layout.o tbl_data.o + +MANDOCLNS = mandoc.ln + +MANDOCSRCS = mandoc.c + +MANDOCOBJS = mandoc.o + +MDOCLNS = mdoc_macro.ln mdoc.ln mdoc_hash.ln mdoc_strings.ln \ + mdoc_argv.ln mdoc_validate.ln \ + lib.ln att.ln arch.ln vol.ln msec.ln st.ln + +MDOCOBJS = mdoc_macro.o mdoc.o mdoc_hash.o mdoc_strings.o \ + mdoc_argv.o mdoc_validate.o lib.o att.o \ + arch.o vol.o msec.o st.o + +MDOCSRCS = mdoc_macro.c mdoc.c mdoc_hash.c mdoc_strings.c \ + mdoc_argv.c mdoc_validate.c lib.c att.c \ + arch.c vol.c msec.c st.c + +MANLNS = man_macro.ln man.ln man_hash.ln man_validate.ln \ + man_argv.ln + +MANOBJS = man_macro.o man.o man_hash.o man_validate.o \ + man_argv.o +MANSRCS = man_macro.c man.c man_hash.c man_validate.c \ + man_argv.c + +MAINLNS = main.ln mdoc_term.ln chars.ln term.ln tree.ln \ + compat.ln man_term.ln html.ln mdoc_html.ln \ + man_html.ln out.ln term_ps.ln term_ascii.ln \ + tbl_term.ln tbl_html.ln + +MAINOBJS = main.o mdoc_term.o chars.o term.o tree.o compat.o \ + man_term.o html.o mdoc_html.o man_html.o out.o \ + term_ps.o term_ascii.o tbl_term.o tbl_html.o + +MAINSRCS = main.c mdoc_term.c chars.c term.c tree.c compat.c \ + man_term.c html.c mdoc_html.c man_html.c out.c \ + term_ps.c term_ascii.c tbl_term.c tbl_html.c + +LLNS = llib-llibmdoc.ln llib-llibman.ln llib-lmandoc.ln \ + llib-llibmandoc.ln llib-llibroff.ln + +LNS = $(MAINLNS) $(MDOCLNS) $(MANLNS) \ + $(MANDOCLNS) $(ROFFLNS) + +LIBS = libmdoc.a libman.a libmandoc.a libroff.a + +OBJS = $(MDOCOBJS) $(MAINOBJS) $(MANOBJS) \ + $(MANDOCOBJS) $(ROFFOBJS) + +SRCS = $(MDOCSRCS) $(MAINSRCS) $(MANSRCS) \ + $(MANDOCSRCS) $(ROFFSRCS) + +DATAS = arch.in att.in lib.in msec.in st.in \ + vol.in chars.in + +HEADS = mdoc.h libmdoc.h man.h libman.h term.h \ + libmandoc.h html.h chars.h out.h main.h roff.h \ + mandoc.h libroff.h + +GSGMLS = mandoc.1.sgml mdoc.3.sgml mdoc.7.sgml \ + mandoc_char.7.sgml man.7.sgml man.3.sgml roff.7.sgml \ + roff.3.sgml tbl.7.sgml + +SGMLS = index.sgml + +XHTMLS = mandoc.1.xhtml mdoc.3.xhtml \ + man.3.xhtml mdoc.7.xhtml man.7.xhtml mandoc_char.7.xhtml \ + roff.7.xhtml roff.3.xhtml tbl.7.xhtml + +HTMLS = ChangeLog.html index.html man.h.html mdoc.h.html \ + mandoc.h.html roff.h.html mandoc.1.html mdoc.3.html \ + man.3.html mdoc.7.html man.7.html mandoc_char.7.html \ + roff.7.html roff.3.html tbl.7.html + +PSS = mandoc.1.ps mdoc.3.ps man.3.ps mdoc.7.ps man.7.ps \ + mandoc_char.7.ps roff.7.ps roff.3.ps tbl.7.ps + +PDFS = mandoc.1.pdf mdoc.3.pdf man.3.pdf mdoc.7.pdf man.7.pdf \ + mandoc_char.7.pdf roff.7.pdf roff.3.pdf tbl.7.pdf + +XSLS = ChangeLog.xsl + +TEXTS = mandoc.1.txt mdoc.3.txt man.3.txt mdoc.7.txt man.7.txt \ + mandoc_char.7.txt ChangeLog.txt \ + roff.7.txt roff.3.txt tbl.7.txt + +EXAMPLES = example.style.css + +XMLS = ChangeLog.xml + +STATICS = index.css style.css external.png + +MD5S = mdocml-$(VERSION).md5 + +TARGZS = mdocml-$(VERSION).tar.gz + +MANS = mandoc.1 mdoc.3 mdoc.7 mandoc_char.7 man.7 \ + man.3 roff.7 roff.3 tbl.7 + +BINS = mandoc + +TESTS = test-strlcat.c test-strlcpy.c + +CONFIGS = config.h.pre config.h.post + +DOCLEAN = $(BINS) $(LNS) $(LLNS) $(LIBS) $(OBJS) $(HTMLS) \ + $(TARGZS) tags $(MD5S) $(XMLS) $(TEXTS) $(GSGMLS) \ + config.h config.log $(PSS) $(PDFS) $(XHTMLS) + +DOINSTALL = $(SRCS) $(HEADS) Makefile $(MANS) $(SGMLS) $(STATICS) \ + $(DATAS) $(XSLS) $(EXAMPLES) $(TESTS) $(CONFIGS) + +all: $(BINS) + +lint: $(LLNS) + +clean: + rm -f $(DOCLEAN) + +dist: mdocml-$(VERSION).tar.gz + +www: all $(GSGMLS) $(HTMLS) $(XHTMLS) $(TEXTS) $(MD5S) $(TARGZS) $(PSS) $(PDFS) + +ps: $(PSS) + +pdf: $(PDFS) + +installwww: www + $(INSTALL_DATA) $(HTMLS) $(XHTMLS) $(PSS) $(PDFS) $(TEXTS) $(STATICS) $(DESTDIR)$(PREFIX)/ + $(INSTALL_DATA) mdocml-$(VERSION).tar.gz $(DESTDIR)$(PREFIX)/snapshots/ + $(INSTALL_DATA) mdocml-$(VERSION).md5 $(DESTDIR)$(PREFIX)/snapshots/ + $(INSTALL_DATA) mdocml-$(VERSION).tar.gz $(DESTDIR)$(PREFIX)/snapshots/mdocml.tar.gz + $(INSTALL_DATA) mdocml-$(VERSION).md5 $(DESTDIR)$(PREFIX)/snapshots/mdocml.md5 + +install: + mkdir -p $(DESTDIR)$(BINDIR) + mkdir -p $(DESTDIR)$(EXAMPLEDIR) + mkdir -p $(DESTDIR)$(MANDIR)/man1 + mkdir -p $(DESTDIR)$(MANDIR)/man7 + $(INSTALL_PROGRAM) mandoc $(DESTDIR)$(BINDIR) + $(INSTALL_MAN) mandoc.1 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL_MAN) man.7 mdoc.7 roff.7 tbl.7 mandoc_char.7 $(DESTDIR)$(MANDIR)/man7 + $(INSTALL_DATA) example.style.css $(DESTDIR)$(EXAMPLEDIR) + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/mandoc + rm -f $(DESTDIR)$(MANDIR)/man1/mandoc.1 + rm -f $(DESTDIR)$(MANDIR)/man7/mdoc.7 + rm -f $(DESTDIR)$(MANDIR)/man7/roff.7 + rm -f $(DESTDIR)$(MANDIR)/man7/tbl.7 + rm -f $(DESTDIR)$(MANDIR)/man7/man.7 + rm -f $(DESTDIR)$(MANDIR)/man7/mandoc_char.7 + rm -f $(DESTDIR)$(EXAMPLEDIR)/example.style.css + +$(OBJS): config.h + +$(LNS): config.h + +man_macro.ln man_macro.o: man_macro.c libman.h + +lib.ln lib.o: lib.c lib.in libmdoc.h + +att.ln att.o: att.c att.in libmdoc.h + +arch.ln arch.o: arch.c arch.in libmdoc.h + +vol.ln vol.o: vol.c vol.in libmdoc.h + +chars.ln chars.o: chars.c chars.in chars.h + +msec.ln msec.o: msec.c msec.in libmdoc.h + +st.ln st.o: st.c st.in libmdoc.h + +mdoc_macro.ln mdoc_macro.o: mdoc_macro.c libmdoc.h + +mdoc_term.ln mdoc_term.o: mdoc_term.c term.h mdoc.h + +mdoc_strings.ln mdoc_strings.o: mdoc_strings.c libmdoc.h + +man_hash.ln man_hash.o: man_hash.c libman.h + +mdoc_hash.ln mdoc_hash.o: mdoc_hash.c libmdoc.h + +mdoc.ln mdoc.o: mdoc.c libmdoc.h + +man.ln man.o: man.c libman.h + +main.ln main.o: main.c mdoc.h man.h roff.h + +compat.ln compat.o: compat.c + +term.ln term.o: term.c term.h man.h mdoc.h chars.h + +term_ps.ln term_ps.o: term_ps.c term.h main.h + +term_ascii.ln term_ascii.o: term_ascii.c term.h main.h + +html.ln html.o: html.c html.h chars.h + +mdoc_html.ln mdoc_html.o: mdoc_html.c html.h mdoc.h + +man_html.ln man_html.o: man_html.c html.h man.h out.h + +out.ln out.o: out.c out.h + +mandoc.ln mandoc.o: mandoc.c libmandoc.h + +tree.ln tree.o: tree.c man.h mdoc.h + +mdoc_argv.ln mdoc_argv.o: mdoc_argv.c libmdoc.h + +man_argv.ln man_argv.o: man_argv.c libman.h + +man_validate.ln man_validate.o: man_validate.c libman.h + +mdoc_validate.ln mdoc_validate.o: mdoc_validate.c libmdoc.h + +libmdoc.h: mdoc.h + +ChangeLog.xml: + cvs2cl --xml --xml-encoding iso-8859-15 -t --noxmlns -f $@ + +ChangeLog.txt: + cvs2cl -t -f $@ + +ChangeLog.html: ChangeLog.xml ChangeLog.xsl + xsltproc -o $@ ChangeLog.xsl ChangeLog.xml + +mdocml-$(VERSION).tar.gz: $(DOINSTALL) + mkdir -p .dist/mdocml/mdocml-$(VERSION)/ + cp -f $(DOINSTALL) .dist/mdocml/mdocml-$(VERSION)/ + ( cd .dist/mdocml/ && tar zcf ../../$@ mdocml-$(VERSION)/ ) + rm -rf .dist/ + +llib-llibmdoc.ln: $(MDOCLNS) + $(LINT) -Clibmdoc $(MDOCLNS) + +llib-llibman.ln: $(MANLNS) + $(LINT) -Clibman $(MANLNS) + +llib-llibmandoc.ln: $(MANDOCLNS) + $(LINT) -Clibmandoc $(MANDOCLNS) + +llib-llibroff.ln: $(ROFFLNS) + $(LINT) -Clibroff $(ROFFLNS) + +llib-lmandoc.ln: $(MAINLNS) llib-llibmdoc.ln llib-llibman.ln llib-llibmandoc.ln llib-llibroff.ln + $(LINT) -Cmandoc $(MAINLNS) llib-llibmdoc.ln llib-llibman.ln llib-llibmandoc.ln llib-llibroff.ln + +libmdoc.a: $(MDOCOBJS) + $(AR) rs $@ $(MDOCOBJS) + +libman.a: $(MANOBJS) + $(AR) rs $@ $(MANOBJS) + +libmandoc.a: $(MANDOCOBJS) + $(AR) rs $@ $(MANDOCOBJS) + +libroff.a: $(ROFFOBJS) + $(AR) rs $@ $(ROFFOBJS) + +mandoc: $(MAINOBJS) libroff.a libmdoc.a libman.a libmandoc.a + $(CC) $(CFLAGS) -o $@ $(MAINOBJS) libroff.a libmdoc.a libman.a libmandoc.a + +.sgml.html: + validate --warn $< + sed -e "s!@VERSION@!$(VERSION)!" -e "s!@VDATE@!$(VDATE)!" $< > $@ + +.1.1.txt .3.3.txt .7.7.txt: + ./mandoc -Tascii -Wall,stop $< | col -b > $@ + +.1.1.sgml .3.3.sgml .7.7.sgml: + ./mandoc -Thtml -Wall,stop -Ostyle=style.css,man=%N.%S.html,includes=%I.html $< > $@ + +.1.1.ps .3.3.ps .7.7.ps: + ./mandoc -Tps -Wall,stop $< > $@ + +.1.1.xhtml .3.3.xhtml .7.7.xhtml: + ./mandoc -Txhtml -Wall,stop -Ostyle=style.css,man=%N.%S.xhtml,includes=%I.html $< > $@ + +.1.1.pdf .3.3.pdf .7.7.pdf: + ./mandoc -Tpdf -Wall,stop $< > $@ + +.tar.gz.md5: + md5 $< > $@ + +.h.h.html: + highlight -I $< >$@ + +config.h: config.h.pre config.h.post + rm -f config.log + ( cat config.h.pre; \ + echo; \ + if $(CC) $(CFLAGS) -Werror -c test-strlcat.c >> config.log 2>&1; then \ + echo '#define HAVE_STRLCAT'; \ + rm test-strlcat.o; \ + fi; \ + if $(CC) $(CFLAGS) -Werror -c test-strlcpy.c >> config.log 2>&1; then \ + echo '#define HAVE_STRLCPY'; \ + rm test-strlcpy.o; \ + fi; \ + echo; \ + cat config.h.post \ + ) > $@ diff --git a/commands/mdocml/arch.c b/usr.bin/mdocml/dist/arch.c similarity index 94% rename from commands/mdocml/arch.c rename to usr.bin/mdocml/dist/arch.c index c3d76340a..26f1962f4 100644 --- a/commands/mdocml/arch.c +++ b/usr.bin/mdocml/dist/arch.c @@ -1,4 +1,4 @@ -/* $Id: arch.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: arch.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/arch.in b/usr.bin/mdocml/dist/arch.in similarity index 92% rename from commands/mdocml/arch.in rename to usr.bin/mdocml/dist/arch.in index d73e24610..fd1cb6eb0 100644 --- a/commands/mdocml/arch.in +++ b/usr.bin/mdocml/dist/arch.in @@ -1,4 +1,4 @@ -/* $Id: arch.in,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: arch.in,v 1.10 2010/09/27 06:56:44 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -22,6 +22,8 @@ * formatted output. * * Be sure to escape strings. + * + * REMEMBER TO ADD NEW ARCHITECTURES TO MDOC.7! */ LINE("alpha", "Alpha") @@ -40,6 +42,7 @@ LINE("loongson", "Loongson") LINE("luna88k", "Luna88k") LINE("mac68k", "Mac68k") LINE("macppc", "MacPPC") +LINE("mips64", "MIPS64") LINE("mvme68k", "MVME68k") LINE("mvme88k", "MVME88k") LINE("mvmeppc", "MVMEPPC") diff --git a/commands/mdocml/att.c b/usr.bin/mdocml/dist/att.c similarity index 94% rename from commands/mdocml/att.c rename to usr.bin/mdocml/dist/att.c index cfd6b4436..13c68bcb2 100644 --- a/commands/mdocml/att.c +++ b/usr.bin/mdocml/dist/att.c @@ -1,4 +1,4 @@ -/* $Id: att.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: att.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/att.in b/usr.bin/mdocml/dist/att.in similarity index 95% rename from commands/mdocml/att.in rename to usr.bin/mdocml/dist/att.in index 48fcd30b9..5dfcd8271 100644 --- a/commands/mdocml/att.in +++ b/usr.bin/mdocml/dist/att.in @@ -1,4 +1,4 @@ -/* $Id: att.in,v 1.6 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: att.in,v 1.6 2010/06/19 20:46:27 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/chars.c b/usr.bin/mdocml/dist/chars.c similarity index 62% rename from commands/mdocml/chars.c rename to usr.bin/mdocml/dist/chars.c index b938c58c2..c22142b8e 100644 --- a/commands/mdocml/chars.c +++ b/usr.bin/mdocml/dist/chars.c @@ -1,6 +1,6 @@ -/* $Id: chars.c,v 1.20 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: chars.c,v 1.31 2011/01/02 10:10:57 kristaps Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010 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,50 +33,43 @@ struct ln { struct ln *next; const char *code; const char *ascii; - const char *html; - size_t codesz; - size_t asciisz; - size_t htmlsz; + int unicode; int type; #define CHARS_CHAR (1 << 0) #define CHARS_STRING (1 << 1) #define CHARS_BOTH (CHARS_CHAR | CHARS_STRING) }; -#define LINES_MAX 370 +#define LINES_MAX 351 -#define CHAR(w, x, y, z, a, b) \ - { NULL, (w), (y), (a), (x), (z), (b), CHARS_CHAR }, -#define STRING(w, x, y, z, a, b) \ - { NULL, (w), (y), (a), (x), (z), (b), CHARS_STRING }, -#define BOTH(w, x, y, z, a, b) \ - { NULL, (w), (y), (a), (x), (z), (b), CHARS_BOTH }, +#define CHAR(in, ch, code) \ + { NULL, (in), (ch), (code), CHARS_CHAR }, +#define STRING(in, ch, code) \ + { NULL, (in), (ch), (code), CHARS_STRING }, +#define BOTH(in, ch, code) \ + { NULL, (in), (ch), (code), CHARS_BOTH }, #define CHAR_TBL_START static struct ln lines[LINES_MAX] = { #define CHAR_TBL_END }; #include "chars.in" -struct tbl { +struct ctab { enum chars type; struct ln **htab; }; -#ifndef __GNUC__ -#define inline -#endif static inline int match(const struct ln *, const char *, size_t, int); -static const char *find(struct tbl *, const char *, - size_t, size_t *, int); +static const struct ln *find(struct ctab *, const char *, size_t, int); void chars_free(void *arg) { - struct tbl *tab; + struct ctab *tab; - tab = (struct tbl *)arg; + tab = (struct ctab *)arg; free(tab->htab); free(tab); @@ -86,7 +79,7 @@ chars_free(void *arg) void * chars_init(enum chars type) { - struct tbl *tab; + struct ctab *tab; struct ln **htab; struct ln *pp; int i, hash; @@ -98,16 +91,16 @@ chars_init(enum chars type) * (they're in-line re-ordered during lookup). */ - tab = malloc(sizeof(struct tbl)); + tab = malloc(sizeof(struct ctab)); if (NULL == tab) { perror(NULL); - exit(EXIT_FAILURE); + exit((int)MANDOCLEVEL_SYSERR); } htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **)); if (NULL == htab) { perror(NULL); - exit(EXIT_FAILURE); + exit((int)MANDOCLEVEL_SYSERR); } for (i = 0; i < LINES_MAX; i++) { @@ -129,31 +122,80 @@ chars_init(enum chars type) } -const char * -chars_a2ascii(void *arg, const char *p, size_t sz, size_t *rsz) +/* + * Special character to Unicode codepoint. + */ +int +chars_spec2cp(void *arg, const char *p, size_t sz) { + const struct ln *ln; - return(find((struct tbl *)arg, p, sz, rsz, CHARS_CHAR)); + ln = find((struct ctab *)arg, p, sz, CHARS_CHAR); + if (NULL == ln) + return(-1); + return(ln->unicode); } -const char * -chars_a2res(void *arg, const char *p, size_t sz, size_t *rsz) +/* + * Reserved word to Unicode codepoint. + */ +int +chars_res2cp(void *arg, const char *p, size_t sz) { + const struct ln *ln; - return(find((struct tbl *)arg, p, sz, rsz, CHARS_STRING)); + ln = find((struct ctab *)arg, p, sz, CHARS_STRING); + if (NULL == ln) + return(-1); + return(ln->unicode); } -static const char * -find(struct tbl *tab, const char *p, size_t sz, size_t *rsz, int type) +/* + * Special character to string array. + */ +const char * +chars_spec2str(void *arg, const char *p, size_t sz, size_t *rsz) +{ + const struct ln *ln; + + ln = find((struct ctab *)arg, p, sz, CHARS_CHAR); + if (NULL == ln) + return(NULL); + + *rsz = strlen(ln->ascii); + return(ln->ascii); +} + + +/* + * Reserved word to string array. + */ +const char * +chars_res2str(void *arg, const char *p, size_t sz, size_t *rsz) +{ + const struct ln *ln; + + ln = find((struct ctab *)arg, p, sz, CHARS_STRING); + if (NULL == ln) + return(NULL); + + *rsz = strlen(ln->ascii); + return(ln->ascii); +} + + +static const struct ln * +find(struct ctab *tab, const char *p, size_t sz, int type) { struct ln *pp, *prev; struct ln **htab; int hash; assert(p); - assert(sz > 0); + if (0 == sz) + return(NULL); if (p[0] < PRINT_LO || p[0] > PRINT_HI) return(NULL); @@ -182,12 +224,7 @@ find(struct tbl *tab, const char *p, size_t sz, size_t *rsz, int type) htab[hash] = pp; } - if (CHARS_HTML == tab->type) { - *rsz = pp->htmlsz; - return(pp->html); - } - *rsz = pp->asciisz; - return(pp->ascii); + return(pp); } return(NULL); @@ -200,7 +237,7 @@ match(const struct ln *ln, const char *p, size_t sz, int type) if ( ! (ln->type & type)) return(0); - if (ln->codesz != sz) + if (strncmp(ln->code, p, sz)) return(0); - return(0 == strncmp(ln->code, p, sz)); + return('\0' == ln->code[(int)sz]); } diff --git a/commands/mdocml/chars.h b/usr.bin/mdocml/dist/chars.h similarity index 71% rename from commands/mdocml/chars.h rename to usr.bin/mdocml/dist/chars.h index e1000e659..ef67d21a2 100644 --- a/commands/mdocml/chars.h +++ b/usr.bin/mdocml/dist/chars.h @@ -1,6 +1,6 @@ -/* $Id: chars.h,v 1.4 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: chars.h,v 1.6 2010/07/31 23:52:58 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -25,8 +25,10 @@ enum chars { }; void *chars_init(enum chars); -const char *chars_a2ascii(void *, const char *, size_t, size_t *); -const char *chars_a2res(void *, const char *, size_t, size_t *); +const char *chars_spec2str(void *, const char *, size_t, size_t *); +int chars_spec2cp(void *, const char *, size_t); +const char *chars_res2str(void *, const char *, size_t, size_t *); +int chars_res2cp(void *, const char *, size_t); void chars_free(void *); __END_DECLS diff --git a/usr.bin/mdocml/dist/chars.in b/usr.bin/mdocml/dist/chars.in new file mode 100644 index 000000000..668f47f8e --- /dev/null +++ b/usr.bin/mdocml/dist/chars.in @@ -0,0 +1,425 @@ +/* $Vendor-Id: chars.in,v 1.35 2010/09/15 13:10:30 kristaps Exp $ */ +/* + * Copyright (c) 2009, 2010 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. + */ + +/* + * The ASCII translation tables. STRING corresponds to predefined + * strings (cf. mdoc_samples.7 and tmac/mdoc/doc-nroff). CHAR + * corresponds to special characters (cf. groff_char.7). BOTH contains + * sequences that are equivalent in both STRING and CHAR. + * + * Either way, the left-hand side corresponds to the input sequence (\x, + * \(xx, \*(xx and so on) whose length is listed second element. The + * right-hand side is what's produced by the front-end, with the fourth + * element being its length. + * + * XXX - C-escape strings! + * XXX - update LINES_MAX if adding more! + */ + +/* Non-breaking, non-collapsing space uses unit separator. */ +static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' }; + +CHAR_TBL_START + +/* Spacing. */ +CHAR("c", "", 0) +CHAR("0", " ", 8194) +CHAR(" ", ascii_nbrsp, 160) +CHAR("~", ascii_nbrsp, 160) +CHAR("%", "", 0) +CHAR("&", "", 0) +CHAR("^", "", 0) +CHAR("|", "", 0) +CHAR("}", "", 0) + +/* Accents. */ +CHAR("a\"", "\"", 779) +CHAR("a-", "-", 175) +CHAR("a.", ".", 729) +CHAR("a^", "^", 770) +BOTH("\'", "\'", 769) +BOTH("aa", "\'", 769) +BOTH("ga", "`", 768) +BOTH("`", "`", 768) +CHAR("ab", "`", 774) +CHAR("ac", ",", 807) +CHAR("ad", "\"", 776) +CHAR("ah", "v", 711) +CHAR("ao", "o", 730) +CHAR("a~", "~", 771) +CHAR("ho", ",", 808) +CHAR("ha", "^", 94) +CHAR("ti", "~", 126) + +/* Quotes. */ +CHAR("Bq", ",,", 8222) +CHAR("bq", ",", 8218) +BOTH("lq", "``", 8220) +BOTH("rq", "\'\'", 8221) +CHAR("oq", "`", 8216) +CHAR("cq", "\'", 8217) +CHAR("aq", "\'", 39) +CHAR("dq", "\"", 34) +CHAR("Fo", "<<", 171) +CHAR("Fc", ">>", 187) +CHAR("fo", "<", 8249) +CHAR("fc", ">", 8250) + +/* Brackets. */ +CHAR("lB", "[", 91) +CHAR("rB", "]", 93) +CHAR("lC", "{", 123) +CHAR("rC", "}", 125) +CHAR("la", "<", 60) +CHAR("ra", ">", 62) +CHAR("bv", "|", 9130) +CHAR("braceex", "|", 9130) +CHAR("bracketlefttp", "|", 9121) +CHAR("bracketleftbp", "|", 9123) +CHAR("bracketleftex", "|", 9122) +CHAR("bracketrighttp", "|", 9124) +CHAR("bracketrightbp", "|", 9126) +CHAR("bracketrightex", "|", 9125) +CHAR("lt", ",-", 9127) +CHAR("bracelefttp", ",-", 9127) +CHAR("lk", "{", 9128) +CHAR("braceleftmid", "{", 9128) +CHAR("lb", ",-", 9129) +CHAR("braceleftbp", "`-", 9129) +CHAR("braceleftex", "|", 9130) +CHAR("rt", "-.", 9131) +CHAR("bracerighttp", "-.", 9131) +CHAR("rk", "}", 9132) +CHAR("bracerightmid", "}", 9132) +CHAR("rb", "-\'", 9133) +CHAR("bracerightbp", "-\'", 9133) +CHAR("bracerightex", "|", 9130) +CHAR("parenlefttp", "/", 9115) +CHAR("parenleftbp", "\\", 9117) +CHAR("parenleftex", "|", 9116) +CHAR("parenrighttp", "\\", 9118) +CHAR("parenrightbp", "/", 9120) +CHAR("parenrightex", "|", 9119) + +/* Greek characters. */ +CHAR("*A", "A", 913) +CHAR("*B", "B", 914) +CHAR("*G", "|", 915) +CHAR("*D", "/\\", 916) +CHAR("*E", "E", 917) +CHAR("*Z", "Z", 918) +CHAR("*Y", "H", 919) +CHAR("*H", "O", 920) +CHAR("*I", "I", 921) +CHAR("*K", "K", 922) +CHAR("*L", "/\\", 923) +CHAR("*M", "M", 924) +CHAR("*N", "N", 925) +CHAR("*C", "H", 926) +CHAR("*O", "O", 927) +CHAR("*P", "TT", 928) +CHAR("*R", "P", 929) +CHAR("*S", ">", 931) +CHAR("*T", "T", 932) +CHAR("*U", "Y", 933) +CHAR("*F", "O_", 934) +CHAR("*X", "X", 935) +CHAR("*Q", "Y", 936) +CHAR("*W", "O", 937) +CHAR("*a", "a", 945) +CHAR("*b", "B", 946) +CHAR("*g", "y", 947) +CHAR("*d", "d", 948) +CHAR("*e", "e", 949) +CHAR("*z", "C", 950) +CHAR("*y", "n", 951) +CHAR("*h", "0", 952) +CHAR("*i", "i", 953) +CHAR("*k", "k", 954) +CHAR("*l", "\\", 955) +CHAR("*m", "u", 956) +CHAR("*n", "v", 957) +CHAR("*c", "E", 958) +CHAR("*o", "o", 959) +CHAR("*p", "n", 960) +CHAR("*r", "p", 961) +CHAR("*s", "o", 963) +CHAR("*t", "t", 964) +CHAR("*u", "u", 965) +CHAR("*f", "o", 981) +CHAR("*x", "x", 967) +CHAR("*q", "u", 968) +CHAR("*w", "w", 969) +CHAR("+h", "0", 977) +CHAR("+f", "o", 966) +CHAR("+p", "w", 982) +CHAR("+e", "e", 1013) +CHAR("ts", "s", 962) + +/* Accented letters. */ +CHAR(",C", "C", 199) +CHAR(",c", "c", 231) +CHAR("/L", "L", 321) +CHAR("/O", "O", 216) +CHAR("/l", "l", 322) +CHAR("/o", "o", 248) +CHAR("oA", "A", 197) +CHAR("oa", "a", 229) +CHAR(":A", "A", 196) +CHAR(":E", "E", 203) +CHAR(":I", "I", 207) +CHAR(":O", "O", 214) +CHAR(":U", "U", 220) +CHAR(":a", "a", 228) +CHAR(":e", "e", 235) +CHAR(":i", "i", 239) +CHAR(":o", "o", 245) +CHAR(":u", "u", 252) +CHAR(":y", "y", 255) +CHAR("\'A", "A", 193) +CHAR("\'E", "E", 201) +CHAR("\'I", "I", 205) +CHAR("\'O", "O", 211) +CHAR("\'U", "U", 218) +CHAR("\'a", "a", 225) +CHAR("\'e", "e", 233) +CHAR("\'i", "i", 237) +CHAR("\'o", "o", 243) +CHAR("\'u", "u", 250) +CHAR("^A", "A", 194) +CHAR("^E", "E", 202) +CHAR("^I", "I", 206) +CHAR("^O", "O", 212) +CHAR("^U", "U", 219) +CHAR("^a", "a", 226) +CHAR("^e", "e", 234) +CHAR("^i", "i", 238) +CHAR("^o", "o", 244) +CHAR("^u", "u", 251) +CHAR("`A", "A", 192) +CHAR("`E", "E", 200) +CHAR("`I", "I", 204) +CHAR("`O", "O", 210) +CHAR("`U", "U", 217) +CHAR("`a", "a", 224) +CHAR("`e", "e", 232) +CHAR("`i", "i", 236) +CHAR("`o", "o", 242) +CHAR("`u", "u", 249) +CHAR("~A", "A", 195) +CHAR("~N", "N", 209) +CHAR("~O", "O", 213) +CHAR("~a", "a", 227) +CHAR("~n", "n", 241) +CHAR("~o", "o", 245) + +/* Arrows and lines. */ +CHAR("<-", "<-", 8592) +CHAR("->", "->", 8594) +CHAR("<>", "<>", 8596) +CHAR("da", "v", 8595) +BOTH("ua", "^", 8593) +BOTH("va", "^v", 8597) +CHAR("lA", "<=", 8656) +CHAR("rA", "=>", 8658) +CHAR("hA", "<=>", 8660) +CHAR("dA", "v", 8659) +CHAR("uA", "^", 8657) +CHAR("vA", "^=v", 8661) + +/* Logic. */ +CHAR("AN", "^", 8743) +CHAR("OR", "v", 8744) +CHAR("no", "~", 172) +CHAR("tno", "~", 172) +CHAR("te", "3", 8707) +CHAR("fa", "V", 8704) +CHAR("st", "-)", 8715) +CHAR("tf", ".:.", 8756) +CHAR("3d", ".:.", 8756) +CHAR("or", "|", 124) + +/* Mathematicals. */ +CHAR("pl", "+", 43) +CHAR("mi", "-", 8722) +CHAR("-", "-", 45) +CHAR("-+", "-+", 8723) +CHAR("+-", "+-", 177) +CHAR("t+-", "+-", 177) +CHAR("pc", ".", 183) +CHAR("md", ".", 8901) +CHAR("mu", "x", 215) +CHAR("tmu", "x", 215) +CHAR("c*", "x", 8855) +CHAR("c+", "+", 8853) +CHAR("di", "-:-", 247) +CHAR("tdi", "-:-", 247) +CHAR("f/", "/", 8260) +CHAR("**", "*", 8727) +BOTH("<=", "<=", 8804) +BOTH(">=", ">=", 8805) +CHAR("<<", "<<", 8810) +CHAR(">>", ">>", 8811) +CHAR("eq", "=", 61) +CHAR("!=", "!=", 8800) +CHAR("==", "==", 8801) +CHAR("ne", "!==", 8802) +CHAR("=~", "=~", 8773) +CHAR("-~", "-~", 8771) +CHAR("ap", "~", 8764) +CHAR("~~", "~~", 8776) +CHAR("~=", "~=", 8780) +CHAR("pt", "oc", 8733) +CHAR("es", "{}", 8709) +CHAR("mo", "E", 8712) +CHAR("nm", "!E", 8713) +CHAR("sb", "(=", 8834) +CHAR("nb", "(!=", 8836) +CHAR("sp", "=)", 8835) +CHAR("nc", "!=)", 8837) +CHAR("ib", "(=", 8838) +CHAR("ip", "=)", 8839) +CHAR("ca", "(^)", 8745) +CHAR("cu", "U", 8746) +CHAR("/_", "/_", 8736) +CHAR("pp", "_|_", 8869) +CHAR("is", "I", 8747) +CHAR("integral", "I", 8747) +CHAR("sum", "E", 8721) +CHAR("product", "TT", 8719) +CHAR("coproduct", "U", 8720) +CHAR("gr", "V", 8711) +CHAR("sr", "\\/", 8730) +CHAR("sqrt", "\\/", 8730) +CHAR("lc", "|~", 8968) +CHAR("rc", "~|", 8969) +CHAR("lf", "|_", 8970) +CHAR("rf", "_|", 8971) +CHAR("if", "oo", 8734) +CHAR("Ah", "N", 8501) +CHAR("Im", "I", 8465) +CHAR("Re", "R", 8476) +CHAR("pd", "a", 8706) +CHAR("-h", "/h", 8463) + +/* Ligatures. */ +CHAR("ff", "ff", 64256) +CHAR("fi", "fi", 64257) +CHAR("fl", "fl", 64258) +CHAR("Fi", "ffi", 64259) +CHAR("Fl", "ffl", 64260) +CHAR("AE", "AE", 198) +CHAR("ae", "ae", 230) +CHAR("OE", "OE", 338) +CHAR("oe", "oe", 339) +CHAR("ss", "ss", 223) +CHAR("IJ", "IJ", 306) +CHAR("ij", "ij", 307) + +/* Special letters. */ +CHAR("-D", "D", 208) +CHAR("Sd", "o", 240) +CHAR("TP", "b", 222) +CHAR("Tp", "b", 254) +CHAR(".i", "i", 305) +CHAR(".j", "j", 567) + +/* Currency. */ +CHAR("Do", "$", 36) +CHAR("ct", "c", 162) +CHAR("Eu", "EUR", 8364) +CHAR("eu", "EUR", 8364) +CHAR("Ye", "Y", 165) +CHAR("Po", "L", 163) +CHAR("Cs", "x", 164) +CHAR("Fn", "f", 402) + +/* Old style. */ +STRING("Am", "&", 38) +STRING("Ba", "|", 124) +STRING("Ge", ">=", 8805) +STRING("Gt", ">", 62) +STRING("If", "infinity", 0) +STRING("Le", "<=", 8804) +STRING("Lq", "``", 8220) +STRING("Lt", "<", 60) +STRING("Na", "NaN", 0) +STRING("Ne", "!=", 8800) +STRING("Pi", "pi", 960) +STRING("Pm", "+-", 177) +STRING("Rq", "\'\'", 8221) +STRING("left-bracket", "[", 91) +STRING("left-parenthesis", "(", 40) +STRING("left-singlequote", "`", 8216) +STRING("lp", "(", 40) +STRING("q", "\"", 34) +STRING("quote-left", "`", 8216) +STRING("quote-right", "\'", 8217) +STRING("R", "(R)", 174) +STRING("right-bracket", "]", 93) +STRING("right-parenthesis", ")", 41) +STRING("right-singlequote", "\'", 8217) +STRING("rp", ")", 41) +STRING("Tm", "(Tm)", 8482) + +/* Lines. */ +CHAR("ba", "|", 124) +CHAR("br", "|", 9474) +CHAR("ul", "_", 95) +CHAR("rl", "-", 8254) +CHAR("bb", "|", 166) +CHAR("sl", "/", 47) +CHAR("rs", "\\", 92) + +/* Text markers. */ +CHAR("ci", "o", 9675) +CHAR("bu", "o", 8226) +CHAR("dd", "=", 8225) +CHAR("dg", "-", 8224) +CHAR("lz", "<>", 9674) +CHAR("sq", "[]", 9633) +CHAR("ps", "9|", 182) +CHAR("sc", "S", 167) +CHAR("lh", "<=", 9756) +CHAR("rh", "=>", 9758) +CHAR("at", "@", 64) +CHAR("sh", "#", 35) +CHAR("CR", "_|", 8629) +CHAR("OK", "\\/", 10003) + +/* Legal symbols. */ +CHAR("co", "(C)", 169) +CHAR("rg", "(R)", 174) +CHAR("tm", "tm", 8482) + +/* Punctuation. */ +CHAR(".", ".", 46) +CHAR("r!", "i", 161) +CHAR("r?", "c", 191) +CHAR("em", "--", 8212) +CHAR("en", "-", 8211) +CHAR("hy", "-", 8208) +CHAR("e", "\\", 92) + +/* Units. */ +CHAR("de", "o", 176) +CHAR("%0", "%o", 8240) +CHAR("fm", "\'", 8242) +CHAR("sd", "\"", 8243) +CHAR("mc", "mu", 181) + +CHAR_TBL_END diff --git a/commands/mdocml/compat.c b/usr.bin/mdocml/dist/compat.c similarity index 100% rename from commands/mdocml/compat.c rename to usr.bin/mdocml/dist/compat.c diff --git a/usr.bin/mdocml/dist/config.h.post b/usr.bin/mdocml/dist/config.h.post new file mode 100644 index 000000000..81c01b931 --- /dev/null +++ b/usr.bin/mdocml/dist/config.h.post @@ -0,0 +1,25 @@ +#include + +#if !defined(__BEGIN_DECLS) +# ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# else +# define __BEGIN_DECLS +# endif +#endif +#if !defined(__END_DECLS) +# ifdef __cplusplus +# define __END_DECLS } +# else +# define __END_DECLS +# endif +#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 + +#endif /* MANDOC_CONFIG_H */ diff --git a/usr.bin/mdocml/dist/config.h.pre b/usr.bin/mdocml/dist/config.h.pre new file mode 100644 index 000000000..a309ed956 --- /dev/null +++ b/usr.bin/mdocml/dist/config.h.pre @@ -0,0 +1,6 @@ +#ifndef MANDOC_CONFIG_H +#define MANDOC_CONFIG_H + +#if defined(__linux__) || defined(__MINT__) +# define _GNU_SOURCE /* strptime(), getsubopt() */ +#endif diff --git a/usr.bin/mdocml/dist/example.style.css b/usr.bin/mdocml/dist/example.style.css new file mode 100644 index 000000000..bdd1edf63 --- /dev/null +++ b/usr.bin/mdocml/dist/example.style.css @@ -0,0 +1,146 @@ +/* $Vendor-Id: example.style.css,v 1.41 2011/01/05 13:00:11 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. + * + * See mdoc(7) and man(7) for macro explanations. + */ + +html { min-width: 580px; width: 580px; } +body { font-family: monospace; } + +/* Preamble structure. */ + +table.foot { width: 100%; } /* Document footer. */ +td.foot-date { width: 50%; } /* Document footer: date. */ +td.foot-os { width: 50%; text-align: right; } /* Document footer: OS/source. */ +table.head { width: 100%; } /* 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. */ + +/* Sections. */ + +h1 { margin-bottom: 0px; font-size: medium; margin-left: -4ex; } /* Section header (Sh, SH). */ +h2 { margin-bottom: 0px; font-size: medium; margin-left: -2ex; } /* Sub-section header (Ss, SS). */ +div.section { margin-bottom: 2ex; margin-left: 4ex; } /* Sections (Sh, SH). */ +div.subsection { } /* Sub-sections (Ss, SS). */ +table.synopsis { } /* SYNOPSIS section table. */ + +/* Vertical spacing. */ + +p { } /* Paragraph: Pp, Lp. */ +blockquote { margin-top: 0px; margin-bottom: 0px; } +table { margin-top: 0px; margin-bottom: 0px; } +td { vertical-align: top; } + +/* 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: 0em; } + +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 { } + +/* Table modes. See tbl(7). */ + +table.tbl { } diff --git a/usr.bin/mdocml/dist/external.png.uu b/usr.bin/mdocml/dist/external.png.uu new file mode 100644 index 000000000..6e651bcc9 --- /dev/null +++ b/usr.bin/mdocml/dist/external.png.uu @@ -0,0 +1,7 @@ +begin 644 external.png +MB5!.1PT*&@H````-24A$4@````H````*"`,```"Z[#^/````%5!,5$5FFRR#IR])3+$:.A0J[. +>G@?Y%>,A'2_X`5GL`9N;Q#5F`````$E%3D2N0F"" +` +end diff --git a/commands/mdocml/html.c b/usr.bin/mdocml/dist/html.c similarity index 78% rename from commands/mdocml/html.c rename to usr.bin/mdocml/dist/html.c index 817272ad2..f02a80846 100644 --- a/commands/mdocml/html.c +++ b/usr.bin/mdocml/dist/html.c @@ -1,6 +1,6 @@ -/* $Id: html.c,v 1.102 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: html.c,v 1.124 2010/12/27 21:41:05 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -57,38 +57,43 @@ static const struct htmldata htmltags[TAG_MAX] = { {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */ {"a", 0}, /* TAG_A */ {"table", HTML_CLRLINE}, /* TAG_TABLE */ + {"tbody", HTML_CLRLINE}, /* TAG_TBODY */ {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */ {"tr", HTML_CLRLINE}, /* TAG_TR */ {"td", HTML_CLRLINE}, /* TAG_TD */ {"li", HTML_CLRLINE}, /* TAG_LI */ {"ul", HTML_CLRLINE}, /* TAG_UL */ {"ol", HTML_CLRLINE}, /* TAG_OL */ -}; - -static const char *const htmlfonts[HTMLFONT_MAX] = { - "roman", - "bold", - "italic" + {"dl", HTML_CLRLINE}, /* TAG_DL */ + {"dt", HTML_CLRLINE}, /* TAG_DT */ + {"dd", HTML_CLRLINE}, /* TAG_DD */ + {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */ + {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */ + {"pre", HTML_CLRLINE }, /* TAG_PRE */ + {"b", 0 }, /* TAG_B */ + {"i", 0 }, /* TAG_I */ + {"code", 0 }, /* TAG_CODE */ + {"small", 0 }, /* TAG_SMALL */ }; static const char *const htmlattrs[ATTR_MAX] = { - "http-equiv", - "content", - "name", - "rel", - "href", - "type", - "media", - "class", - "style", - "width", - "valign", - "target", - "id", - "summary", + "http-equiv", /* ATTR_HTTPEQUIV */ + "content", /* ATTR_CONTENT */ + "name", /* ATTR_NAME */ + "rel", /* ATTR_REL */ + "href", /* ATTR_HREF */ + "type", /* ATTR_TYPE */ + "media", /* ATTR_MEDIA */ + "class", /* ATTR_CLASS */ + "style", /* ATTR_STYLE */ + "width", /* ATTR_WIDTH */ + "id", /* ATTR_ID */ + "summary", /* ATTR_SUMMARY */ + "align", /* ATTR_ALIGN */ }; -static void print_spec(struct html *, const char *, size_t); +static void print_spec(struct html *, enum roffdeco, + const char *, size_t); static void print_res(struct html *, const char *, size_t); static void print_ctag(struct html *, enum htmltag); static void print_doctype(struct html *); @@ -115,12 +120,11 @@ ml_alloc(char *outopts, enum htmltype type) h = calloc(1, sizeof(struct html)); if (NULL == h) { perror(NULL); - exit(EXIT_FAILURE); + exit((int)MANDOCLEVEL_SYSERR); } h->type = type; h->tags.head = NULL; - h->ords.head = NULL; h->symtab = chars_init(CHARS_HTML); while (outopts && *outopts) @@ -161,16 +165,10 @@ void html_free(void *p) { struct tag *tag; - struct ord *ord; struct html *h; h = (struct html *)p; - while ((ord = h->ords.head) != NULL) { - h->ords.head = ord->next; - free(ord); - } - while ((tag = h->tags.head) != NULL) { h->tags.head = tag->next; free(tag); @@ -215,49 +213,41 @@ print_gen_head(struct html *h) static void -print_spec(struct html *h, const char *p, size_t len) +print_spec(struct html *h, enum roffdeco d, const char *p, size_t len) { + int cp; const char *rhs; size_t sz; - rhs = chars_a2ascii(h->symtab, p, len, &sz); - - if (NULL == rhs) + if ((cp = chars_spec2cp(h->symtab, p, len)) > 0) { + printf("&#%d;", cp); return; - fwrite(rhs, 1, sz, stdout); + } else if (-1 == cp && DECO_SSPECIAL == d) { + fwrite(p, 1, len, stdout); + return; + } else if (-1 == cp) + return; + + if (NULL != (rhs = chars_spec2str(h->symtab, p, len, &sz))) + fwrite(rhs, 1, sz, stdout); } static void print_res(struct html *h, const char *p, size_t len) { + int cp; const char *rhs; size_t sz; - rhs = chars_a2res(h->symtab, p, len, &sz); - - if (NULL == rhs) + if ((cp = chars_res2cp(h->symtab, p, len)) > 0) { + printf("&#%d;", cp); + return; + } else if (-1 == cp) return; - fwrite(rhs, 1, sz, stdout); -} - -struct tag * -print_ofont(struct html *h, enum htmlfont font) -{ - struct htmlpair tag; - - h->metal = h->metac; - h->metac = font; - - /* FIXME: DECO_ROMAN should just close out preexisting. */ - - if (h->metaf && h->tags.head == h->metaf) - print_tagq(h, h->metaf); - - PAIR_CLASS_INIT(&tag, htmlfonts[font]); - h->metaf = print_otag(h, TAG_SPAN, 1, &tag); - return(h->metaf); + if (NULL != (rhs = chars_res2str(h->symtab, p, len, &sz))) + fwrite(rhs, 1, sz, stdout); } @@ -284,7 +274,18 @@ print_metaf(struct html *h, enum roffdeco deco) /* NOTREACHED */ } - (void)print_ofont(h, font); + if (h->metaf) { + print_tagq(h, h->metaf); + h->metaf = NULL; + } + + h->metal = h->metac; + h->metac = font; + + if (HTMLFONT_NONE != font) + h->metaf = HTMLFONT_BOLD == font ? + print_otag(h, TAG_B, 0, NULL) : + print_otag(h, TAG_I, 0, NULL); } @@ -334,8 +335,10 @@ print_encode(struct html *h, const char *p, int norecurse) case (DECO_RESERVED): print_res(h, seq, sz); break; + case (DECO_SSPECIAL): + /* FALLTHROUGH */ case (DECO_SPECIAL): - print_spec(h, seq, sz); + print_spec(h, deco, seq, sz); break; case (DECO_PREVIOUS): /* FALLTHROUGH */ @@ -384,7 +387,7 @@ print_otag(struct html *h, enum htmltag tag, t = malloc(sizeof(struct tag)); if (NULL == t) { perror(NULL); - exit(EXIT_FAILURE); + exit((int)MANDOCLEVEL_SYSERR); } t->tag = tag; t->next = h->tags.head; @@ -393,8 +396,20 @@ print_otag(struct html *h, enum htmltag tag, t = NULL; if ( ! (HTML_NOSPACE & h->flags)) - if ( ! (HTML_CLRLINE & htmltags[tag].flags)) - putchar(' '); + if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { + /* Manage keeps! */ + if ( ! (HTML_KEEP & h->flags)) { + if (HTML_PREKEEP & h->flags) + h->flags |= HTML_KEEP; + putchar(' '); + } else + printf(" "); + } + + if ( ! (h->flags & HTML_NONOSPACE)) + h->flags &= ~HTML_NOSPACE; + else + h->flags |= HTML_NOSPACE; /* Print out the tag name and attributes. */ @@ -424,6 +439,10 @@ print_otag(struct html *h, enum htmltag tag, putchar('>'); h->flags |= HTML_NOSPACE; + + if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) + putchar('\n'); + return(t); } @@ -454,7 +473,7 @@ print_xmltype(struct html *h) { if (HTML_XHTML_1_0_STRICT == h->type) - printf(""); + puts(""); } @@ -484,11 +503,11 @@ print_doctype(struct html *h) void -print_text(struct html *h, const char *p) +print_text(struct html *h, const char *word) { - if (*p && 0 == *(p + 1)) - switch (*p) { + if (word[0] && '\0' == word[1]) + switch (word[0]) { case('.'): /* FALLTHROUGH */ case(','): @@ -511,19 +530,40 @@ print_text(struct html *h, const char *p) break; } - if ( ! (h->flags & HTML_NOSPACE)) - putchar(' '); + if ( ! (HTML_NOSPACE & h->flags)) { + /* Manage keeps! */ + if ( ! (HTML_KEEP & h->flags)) { + if (HTML_PREKEEP & h->flags) + h->flags |= HTML_KEEP; + putchar(' '); + } else + printf(" "); + } - assert(p); - if ( ! print_encode(h, p, 0)) - h->flags &= ~HTML_NOSPACE; + assert(NULL == h->metaf); + if (HTMLFONT_NONE != h->metac) + h->metaf = HTMLFONT_BOLD == h->metac ? + print_otag(h, TAG_B, 0, NULL) : + print_otag(h, TAG_I, 0, NULL); + + assert(word); + if ( ! print_encode(h, word, 0)) + if ( ! (h->flags & HTML_NONOSPACE)) + h->flags &= ~HTML_NOSPACE; + + if (h->metaf) { + print_tagq(h, h->metaf); + h->metaf = NULL; + } + + h->flags &= ~HTML_IGNDELIM; /* * Note that we don't process the pipe: the parser sees it as * punctuation, but we don't in terms of typography. */ - if (*p && 0 == *(p + 1)) - switch (*p) { + if (word[0] && '\0' == word[1]) + switch (word[0]) { case('('): /* FALLTHROUGH */ case('['): @@ -718,11 +758,11 @@ bufcat_su(struct html *h, const char *p, const struct roffsu *su) break; } - if (su->pt) - buffmt(h, "%s: %f%s;", p, v, u); - else - /* LINTED */ - buffmt(h, "%s: %d%s;", p, (int)v, u); + /* + * XXX: the CSS spec isn't clear as to which types accept + * integer or real numbers, so we just make them all decimals. + */ + buffmt(h, "%s: %.2f%s;", p, v, u); } @@ -731,20 +771,24 @@ html_idcat(char *dst, const char *src, int sz) { int ssz; - assert(sz); + assert(sz > 2); /* Cf. . */ - for ( ; *dst != '\0' && sz; dst++, sz--) - /* Jump to end. */ ; - - assert(sz > 2); - /* We can't start with a number (bah). */ - *dst++ = 'x'; - *dst = '\0'; - sz--; + if ('#' == *dst) { + dst++; + sz--; + } + if ('\0' == *dst) { + *dst++ = 'x'; + *dst = '\0'; + sz--; + } + + for ( ; *dst != '\0' && sz; dst++, sz--) + /* Jump to end. */ ; for ( ; *src != '\0' && sz > 1; src++) { ssz = snprintf(dst, (size_t)sz, "%.2x", *src); diff --git a/commands/mdocml/html.h b/usr.bin/mdocml/dist/html.h similarity index 75% rename from commands/mdocml/html.h rename to usr.bin/mdocml/dist/html.h index 874adc075..65e35946b 100644 --- a/commands/mdocml/html.h +++ b/usr.bin/mdocml/dist/html.h @@ -1,6 +1,6 @@ -/* $Id: html.h,v 1.24 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: html.h,v 1.38 2011/01/06 11:55:39 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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,12 +33,23 @@ enum htmltag { TAG_BR, TAG_A, TAG_TABLE, + TAG_TBODY, TAG_COL, TAG_TR, TAG_TD, TAG_LI, TAG_UL, TAG_OL, + TAG_DL, + TAG_DT, + TAG_DD, + TAG_BLOCKQUOTE, + TAG_P, + TAG_PRE, + TAG_B, + TAG_I, + TAG_CODE, + TAG_SMALL, TAG_MAX }; @@ -53,10 +64,9 @@ enum htmlattr { ATTR_CLASS, ATTR_STYLE, ATTR_WIDTH, - ATTR_VALIGN, - ATTR_TARGET, ATTR_ID, ATTR_SUMMARY, + ATTR_ALIGN, ATTR_MAX }; @@ -72,18 +82,9 @@ struct tag { enum htmltag tag; }; -struct ord { - struct ord *next; - const void *cookie; - int pos; -}; - struct tagq { struct tag *head; }; -struct ordq { - struct ord *head; -}; struct htmlpair { enum htmlattr key; @@ -110,32 +111,32 @@ enum htmltype { struct html { int flags; #define HTML_NOSPACE (1 << 0) -#define HTML_IGNDELIM (1 << 2) - struct tagq tags; - struct ordq ords; - void *symtab; - char *base; - char *base_man; - char *base_includes; - char *style; - char buf[BUFSIZ]; +#define HTML_IGNDELIM (1 << 1) +#define HTML_KEEP (1 << 2) +#define HTML_PREKEEP (1 << 3) +#define HTML_NONOSPACE (1 << 4) + struct tagq tags; /* stack of open tags */ + struct rofftbl tbl; /* current table */ + void *symtab; /* character-escapes */ + char *base_man; /* base for manpage href */ + char *base_includes; /* base for include href */ + char *style; /* style-sheet URI */ + char buf[BUFSIZ]; /* see bufcat and friends */ size_t buflen; - struct tag *metaf; - enum htmlfont metal; - enum htmlfont metac; + struct tag *metaf; /* current open font scope */ + enum htmlfont metal; /* last used font */ + enum htmlfont metac; /* current font mode */ enum htmltype type; }; -struct roffsu; - void print_gen_decls(struct html *); void print_gen_head(struct html *); -struct tag *print_ofont(struct html *, enum htmlfont); struct tag *print_otag(struct html *, enum htmltag, int, const struct htmlpair *); void print_tagq(struct html *, const struct tag *); void print_stagq(struct html *, const struct tag *); void print_text(struct html *, const char *); +void print_tbl(struct html *, const struct tbl_span *); void bufcat_su(struct html *, const char *, const struct roffsu *); diff --git a/commands/mdocml/lib.c b/usr.bin/mdocml/dist/lib.c similarity index 94% rename from commands/mdocml/lib.c rename to usr.bin/mdocml/dist/lib.c index bbf2aec8b..85094818f 100644 --- a/commands/mdocml/lib.c +++ b/usr.bin/mdocml/dist/lib.c @@ -1,4 +1,4 @@ -/* $Id: lib.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: lib.c,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/lib.in b/usr.bin/mdocml/dist/lib.in similarity index 97% rename from commands/mdocml/lib.in rename to usr.bin/mdocml/dist/lib.in index 18ee711c1..6d2788959 100644 --- a/commands/mdocml/lib.in +++ b/usr.bin/mdocml/dist/lib.in @@ -1,4 +1,4 @@ -/* $Id: lib.in,v 1.9 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: lib.in,v 1.9 2010/06/19 20:46:27 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -52,6 +52,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)") diff --git a/commands/mdocml/libman.h b/usr.bin/mdocml/dist/libman.h similarity index 81% rename from commands/mdocml/libman.h rename to usr.bin/mdocml/dist/libman.h index 4a0df6331..fd37aa675 100644 --- a/commands/mdocml/libman.h +++ b/usr.bin/mdocml/dist/libman.h @@ -1,6 +1,6 @@ -/* $Id: libman.h,v 1.36 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: libman.h,v 1.44 2010/11/30 15:36:28 kristaps Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010 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 @@ -25,9 +25,8 @@ enum man_next { }; struct man { - void *data; - mandocmsg msg; - int pflags; /* parse flags (see man.h) */ + void *data; /* private application data */ + mandocmsg msg; /* output message handler */ int flags; /* parse flags */ #define MAN_HALT (1 << 0) /* badness happened: die */ #define MAN_ELINE (1 << 1) /* Next-line element scope. */ @@ -35,14 +34,19 @@ struct man { #define MAN_ILINE (1 << 3) /* Ignored in next-line scope. */ #define MAN_LITERAL (1 << 4) /* Literal input. */ #define MAN_BPLINE (1 << 5) - enum man_next next; - struct man_node *last; - struct man_node *first; - struct man_meta meta; + enum man_next next; /* where to put the next node */ + 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 */ }; -#define MACRO_PROT_ARGS struct man *m, enum mant tok, int line, \ - int ppos, int *pos, char *buf +#define MACRO_PROT_ARGS struct man *m, \ + enum mant tok, \ + int line, \ + int ppos, \ + int *pos, \ + char *buf struct man_macro { int (*fp)(MACRO_PROT_ARGS); @@ -80,8 +84,6 @@ int man_vmsg(struct man *, enum mandocerr, int, int, const char *, ...); int man_valid_post(struct man *); int man_valid_pre(struct man *, struct man_node *); -int man_action_post(struct man *); -int man_action_pre(struct man *, struct man_node *); int man_unscope(struct man *, const struct man_node *, enum mandocerr); diff --git a/commands/mdocml/libmandoc.h b/usr.bin/mdocml/dist/libmandoc.h similarity index 83% rename from commands/mdocml/libmandoc.h rename to usr.bin/mdocml/dist/libmandoc.h index b25e4a7e6..ca05d9d17 100644 --- a/commands/mdocml/libmandoc.h +++ b/usr.bin/mdocml/dist/libmandoc.h @@ -1,6 +1,6 @@ -/* $Id: libmandoc.h,v 1.8 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: libmandoc.h,v 1.10 2011/01/03 22:42:37 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010 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 @@ -24,12 +24,13 @@ void *mandoc_calloc(size_t, size_t); char *mandoc_strdup(const char *); void *mandoc_malloc(size_t); void *mandoc_realloc(void *, size_t); +char *mandoc_getarg(char **, mandocmsg, void *, int, int *); time_t mandoc_a2time(int, const char *); #define MTIME_CANONICAL (1 << 0) #define MTIME_REDUCED (1 << 1) #define MTIME_MDOCDATE (1 << 2) #define MTIME_ISO_8601 (1 << 3) -int mandoc_eos(const char *, size_t); +int mandoc_eos(const char *, size_t, int); int mandoc_hyph(const char *, const char *); __END_DECLS diff --git a/commands/mdocml/libmdoc.h b/usr.bin/mdocml/dist/libmdoc.h similarity index 83% rename from commands/mdocml/libmdoc.h rename to usr.bin/mdocml/dist/libmdoc.h index e0935be05..827960e5d 100644 --- a/commands/mdocml/libmdoc.h +++ b/usr.bin/mdocml/dist/libmdoc.h @@ -1,6 +1,6 @@ -/* $Id: libmdoc.h,v 1.53 2010/06/19 20:46:27 kristaps Exp $ */ +/* $Vendor-Id: libmdoc.h,v 1.63 2010/11/30 13:04:14 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -25,8 +25,8 @@ enum mdoc_next { }; struct mdoc { - void *data; - mandocmsg msg; + void *data; /* private application data */ + mandocmsg msg; /* message callback */ int flags; #define MDOC_HALT (1 << 0) /* error in parse: halt */ #define MDOC_LITERAL (1 << 1) /* in a literal scope */ @@ -35,17 +35,22 @@ struct mdoc { #define MDOC_PHRASELIT (1 << 4) /* literal within a partila phrase */ #define MDOC_PPHRASE (1 << 5) /* within a partial phrase */ #define MDOC_FREECOL (1 << 6) /* `It' invocation should close */ - int pflags; - enum mdoc_next next; - struct mdoc_node *last; - struct mdoc_node *first; - struct mdoc_meta meta; +#define MDOC_SYNOPSIS (1 << 7) /* SYNOPSIS-style formatting */ + enum mdoc_next next; /* where to put the next node */ + struct mdoc_node *last; /* the last node parsed */ + struct mdoc_node *first; /* the first node parsed */ + struct mdoc_meta meta; /* document meta-data */ enum mdoc_sec lastnamed; enum mdoc_sec lastsec; + struct regset *regs; /* registers */ }; -#define MACRO_PROT_ARGS struct mdoc *m, enum mdoct tok, \ - int line, int ppos, int *pos, char *buf +#define MACRO_PROT_ARGS struct mdoc *m, \ + enum mdoct tok, \ + int line, \ + int ppos, \ + int *pos, \ + char *buf struct mdoc_macro { int (*fp)(MACRO_PROT_ARGS); @@ -103,6 +108,9 @@ int mdoc_block_alloc(struct mdoc *, int, int, int mdoc_head_alloc(struct mdoc *, int, int, enum mdoct); int mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct); int mdoc_body_alloc(struct mdoc *, int, int, enum mdoct); +int mdoc_endbody_alloc(struct mdoc *m, int line, int pos, + enum mdoct tok, struct mdoc_node *body, + enum mdoc_endbody end); void mdoc_node_delete(struct mdoc *, struct mdoc_node *); void mdoc_hash_init(void); enum mdoct mdoc_hash_find(const char *); @@ -120,9 +128,6 @@ 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 *); -int mdoc_action_pre(struct mdoc *, - struct mdoc_node *); -int mdoc_action_post(struct mdoc *); enum margverr mdoc_argv(struct mdoc *, int, enum mdoct, struct mdoc_arg **, int *, char *); void mdoc_argv_free(struct mdoc_arg *); diff --git a/usr.bin/mdocml/dist/libroff.h b/usr.bin/mdocml/dist/libroff.h new file mode 100644 index 000000000..d9ba014f8 --- /dev/null +++ b/usr.bin/mdocml/dist/libroff.h @@ -0,0 +1,62 @@ +/* $Vendor-Id: libroff.h,v 1.16 2011/01/04 15:02:00 kristaps Exp $ */ +/* + * Copyright (c) 2009, 2010 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 LIBROFF_H +#define LIBROFF_H + +__BEGIN_DECLS + +enum tbl_part { + TBL_PART_OPTS, /* in options (first line) */ + TBL_PART_LAYOUT, /* describing layout */ + TBL_PART_DATA, /* creating data rows */ + TBL_PART_CDATA /* continue previous row */ +}; + +struct tbl_node { + mandocmsg msg; /* status messages */ + void *data; /* privdata for messages */ + int pos; /* invocation column */ + int line; /* invocation line */ + enum tbl_part part; + struct tbl opts; + struct tbl_row *first_row; + struct tbl_row *last_row; + struct tbl_span *first_span; + struct tbl_span *last_span; + struct tbl_head *first_head; + struct tbl_head *last_head; + struct tbl_node *next; +}; + +#define TBL_MSG(tblp, type, line, col) \ + (*(tblp)->msg)((type), (tblp)->data, (line), (col), NULL) + +struct tbl_node *tbl_alloc(int, int, void *, mandocmsg); +void tbl_restart(int, int, struct tbl_node *); +void tbl_free(struct tbl_node *); +void tbl_reset(struct tbl_node *); +enum rofferr tbl_read(struct tbl_node *, int, const char *, int); +int tbl_option(struct tbl_node *, int, const char *); +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(const struct tbl_node *); +void tbl_end(struct tbl_node *); + +__END_DECLS + +#endif /*LIBROFF_H*/ diff --git a/usr.bin/mdocml/dist/main.c b/usr.bin/mdocml/dist/main.c new file mode 100644 index 000000000..8418744f2 --- /dev/null +++ b/usr.bin/mdocml/dist/main.c @@ -0,0 +1,1046 @@ +/* $Vendor-Id: main.c,v 1.135 2011/01/04 15:02:00 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2010 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 + +#ifndef __minix +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "main.h" +#include "mdoc.h" +#include "man.h" +#include "roff.h" + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +#define REPARSE_LIMIT 1000 +#define UNCONST(a) ((void *)(uintptr_t)(const void *)(a)) + +/* FIXME: Intel's compiler? LLVM? pcc? */ + +#if !defined(__GNUC__) || (__GNUC__ < 2) +# if !defined(lint) +# define __attribute__(x) +# endif +#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ + +typedef void (*out_mdoc)(void *, const struct mdoc *); +typedef void (*out_man)(void *, const struct man *); +typedef void (*out_free)(void *); + +struct buf { + char *buf; + size_t sz; +}; + +enum intt { + INTT_AUTO, + INTT_MDOC, + INTT_MAN +}; + +enum outt { + OUTT_ASCII = 0, + OUTT_TREE, + OUTT_HTML, + OUTT_XHTML, + OUTT_LINT, + OUTT_PS, + OUTT_PDF +}; + +struct curparse { + const char *file; /* Current parse. */ + int fd; /* Current parse. */ + int line; /* Line number in the file. */ + enum mandoclevel wlevel; /* Ignore messages below this. */ + int wstop; /* Stop after a file with a warning. */ + enum intt inttype; /* which parser to use */ + struct man *pman; /* persistent man parser */ + struct mdoc *pmdoc; /* persistent mdoc parser */ + 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 interpolation stack */ + enum outt outtype; /* which output to use */ + out_mdoc outmdoc; /* mdoc output ptr */ + out_man outman; /* man output ptr */ + out_free outfree; /* free output ptr */ + void *outdata; /* data for output */ + char outopts[BUFSIZ]; /* buf of output opts */ +}; + +static const char * const mandoclevels[MANDOCLEVEL_MAX] = { + "SUCCESS", + "RESERVED", + "WARNING", + "ERROR", + "FATAL", + "BADARG", + "SYSERR" +}; + +static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = { + MANDOCERR_OK, + MANDOCERR_WARNING, + MANDOCERR_WARNING, + MANDOCERR_ERROR, + MANDOCERR_FATAL, + MANDOCERR_MAX, + MANDOCERR_MAX +}; + +static const char * const mandocerrs[MANDOCERR_MAX] = { + "ok", + + "generic warning", + + /* related to the prologue */ + "no title in document", + "document title should be all caps", + "unknown manual section", + "cannot parse date argument", + "prologue macros out of order", + "duplicate prologue macro", + "macro not allowed in prologue", + "macro not allowed in body", + + /* related to document structure */ + ".so is fragile, better use ln(1)", + "NAME section must come first", + "bad NAME section contents", + "manual name not yet set", + "sections out of conventional order", + "duplicate section name", + "section not in conventional manual section", + + /* related to macros and nesting */ + "skipping obsolete macro", + "skipping paragraph macro", + "blocks badly nested", + "child violates parent syntax", + "nested displays are not portable", + "already in literal mode", + + /* related to missing macro arguments */ + "skipping empty macro", + "argument count wrong", + "missing display type", + "list type must come first", + "tag lists require a width argument", + "missing font type", + + /* related to bad macro arguments */ + "skipping argument", + "duplicate argument", + "duplicate display type", + "duplicate list type", + "unknown AT&T UNIX version", + "bad Boolean value", + "unknown font", + "unknown standard specifier", + "bad width argument", + + /* related to plain text */ + "blank line in non-literal context", + "tab in non-literal context", + "end of line whitespace", + "bad comment style", + "unknown escape sequence", + "unterminated quoted string", + + /* related to tables */ + "extra data cells", + + "generic error", + + /* related to tables */ + "bad table syntax", + "bad table option", + "bad table layout", + "no table layout cells specified", + "no table data cells specified", + "ignore data in cell", + "data block still open", + + "input stack limit exceeded, infinite loop?", + "skipping bad character", + "skipping text before the first section header", + "skipping unknown macro", + "NOT IMPLEMENTED: skipping request", + "line scope broken", + "argument count wrong", + "skipping end of block that is not open", + "missing end of block", + "scope open on exit", + "uname(3) system call failed", + "macro requires line argument(s)", + "macro requires body argument(s)", + "macro requires argument(s)", + "missing list type", + "line argument(s) will be lost", + "body argument(s) will be lost", + + "generic fatal error", + + "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", + "NOT IMPLEMENTED: .so with absolute path or \"..\"", + "no document body", + "no document prologue", + "static buffer exhausted", +}; + +static void parsebuf(struct curparse *, struct buf, int); +static void pdesc(struct curparse *); +static void fdesc(struct curparse *); +static void ffile(const char *, struct curparse *); +static int pfile(const char *, struct curparse *); +static int moptions(enum intt *, char *); +static int mmsg(enum mandocerr, void *, + int, int, const char *); +static void pset(const char *, int, struct curparse *); +static int toptions(struct curparse *, char *); +static void usage(void) __attribute__((noreturn)); +static void version(void) __attribute__((noreturn)); +static int woptions(struct curparse *, char *); + +static const char *progname; +static enum mandoclevel file_status = MANDOCLEVEL_OK; +static enum mandoclevel exit_status = MANDOCLEVEL_OK; + +int +main(int argc, char *argv[]) +{ + int c; + struct curparse curp; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + ++progname; + + memset(&curp, 0, sizeof(struct curparse)); + + curp.inttype = INTT_AUTO; + curp.outtype = OUTT_ASCII; + curp.wlevel = MANDOCLEVEL_FATAL; + + /* LINTED */ + while (-1 != (c = getopt(argc, argv, "m:O:T:VW:"))) + switch (c) { + case ('m'): + if ( ! moptions(&curp.inttype, optarg)) + return((int)MANDOCLEVEL_BADARG); + break; + case ('O'): + (void)strlcat(curp.outopts, optarg, BUFSIZ); + (void)strlcat(curp.outopts, ",", BUFSIZ); + break; + case ('T'): + if ( ! toptions(&curp, optarg)) + return((int)MANDOCLEVEL_BADARG); + break; + case ('W'): + if ( ! woptions(&curp, optarg)) + return((int)MANDOCLEVEL_BADARG); + break; + case ('V'): + version(); + /* NOTREACHED */ + default: + usage(); + /* NOTREACHED */ + } + + argc -= optind; + argv += optind; + + if (NULL == *argv) { + curp.file = ""; + curp.fd = STDIN_FILENO; + + fdesc(&curp); + } + + while (*argv) { + ffile(*argv, &curp); + if (MANDOCLEVEL_OK != exit_status && curp.wstop) + break; + ++argv; + } + + if (curp.outfree) + (*curp.outfree)(curp.outdata); + if (curp.pmdoc) + mdoc_free(curp.pmdoc); + if (curp.pman) + man_free(curp.pman); + if (curp.roff) + roff_free(curp.roff); + + return((int)exit_status); +} + + +static void +version(void) +{ + + (void)printf("%s %s\n", progname, VERSION); + exit((int)MANDOCLEVEL_OK); +} + + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: %s " + "[-V] " + "[-foption] " + "[-mformat] " + "[-Ooption] " + "[-Toutput] " + "[-Werr] " + "[file...]\n", + progname); + + exit((int)MANDOCLEVEL_BADARG); +} + +static void +ffile(const char *file, struct curparse *curp) +{ + + /* + * Called once per input file. Get the file ready for reading, + * pass it through to the parser-driver, then close it out. + * XXX: don't do anything special as this is only called for + * files; stdin goes directly to fdesc(). + */ + + curp->file = file; + + if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) { + perror(curp->file); + exit_status = MANDOCLEVEL_SYSERR; + return; + } + + fdesc(curp); + + if (-1 == close(curp->fd)) + perror(curp->file); +} + +static int +pfile(const char *file, struct curparse *curp) +{ + const char *savefile; + int fd, savefd; + + if (-1 == (fd = open(file, O_RDONLY, 0))) { + perror(file); + file_status = MANDOCLEVEL_SYSERR; + return(0); + } + + savefile = curp->file; + savefd = curp->fd; + + curp->file = file; + curp->fd = fd; + + pdesc(curp); + + curp->file = savefile; + curp->fd = savefd; + + if (-1 == close(fd)) + perror(file); + + return(MANDOCLEVEL_FATAL > file_status ? 1 : 0); +} + + +static void +resize_buf(struct buf *buf, size_t initial) +{ + + buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial; + buf->buf = realloc(buf->buf, buf->sz); + if (NULL == buf->buf) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } +} + + +static int +read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap) +{ + struct stat st; + size_t off; + ssize_t ssz; + + if (-1 == fstat(curp->fd, &st)) { + perror(curp->file); + return(0); + } + + /* + * If we're a regular file, try just reading in the whole entry + * via mmap(). This is faster than reading it into blocks, and + * since each file is only a few bytes to begin with, I'm not + * concerned that this is going to tank any machines. + */ + +#ifndef __minix + if (S_ISREG(st.st_mode)) { + if (st.st_size >= (1U << 31)) { + fprintf(stderr, "%s: input too large\n", + curp->file); + return(0); + } + *with_mmap = 1; + fb->sz = (size_t)st.st_size; + fb->buf = mmap(NULL, fb->sz, PROT_READ, + MAP_FILE|MAP_SHARED, curp->fd, 0); + if (fb->buf != MAP_FAILED) + return(1); + } +#endif + + /* + * If this isn't a regular file (like, say, stdin), then we must + * go the old way and just read things in bit by bit. + */ + + *with_mmap = 0; + off = 0; + fb->sz = 0; + fb->buf = NULL; + for (;;) { + if (off == fb->sz) { + if (fb->sz == (1U << 31)) { + fprintf(stderr, "%s: input too large\n", + curp->file); + break; + } + resize_buf(fb, 65536); + } + ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off); + if (ssz == 0) { + fb->sz = off; + return(1); + } + if (ssz == -1) { + perror(curp->file); + break; + } + off += (size_t)ssz; + } + + free(fb->buf); + fb->buf = NULL; + return(0); +} + + +static void +fdesc(struct curparse *curp) +{ + + /* + * Called once per file with an opened file descriptor. All + * pre-file-parse operations (whether stdin or a file) should go + * here. + * + * This calls down into the nested parser, which drills down and + * fully parses a file and all its dependences (i.e., `so'). It + * then runs the cleanup validators and pushes to output. + */ + + /* Zero the parse type. */ + + curp->mdoc = NULL; + curp->man = NULL; + file_status = MANDOCLEVEL_OK; + + /* Make sure the mandotory roff parser is initialised. */ + + if (NULL == curp->roff) { + curp->roff = roff_alloc(&curp->regs, curp, mmsg); + assert(curp->roff); + } + + /* Fully parse the file. */ + + pdesc(curp); + + if (MANDOCLEVEL_FATAL <= file_status) + goto cleanup; + + /* NOTE a parser may not have been assigned, yet. */ + + if ( ! (curp->man || curp->mdoc)) { + fprintf(stderr, "%s: Not a manual\n", curp->file); + file_status = MANDOCLEVEL_FATAL; + goto cleanup; + } + + /* Clean up the parse routine ASTs. */ + + if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) { + assert(MANDOCLEVEL_FATAL <= file_status); + goto cleanup; + } + + if (curp->man && ! man_endparse(curp->man)) { + assert(MANDOCLEVEL_FATAL <= file_status); + goto cleanup; + } + + assert(curp->roff); + roff_endparse(curp->roff); + + /* + * With -Wstop and warnings or errors of at least + * the requested level, do not produce output. + */ + + if (MANDOCLEVEL_OK != file_status && curp->wstop) + goto cleanup; + + /* If unset, allocate output dev now (if applicable). */ + + if ( ! (curp->outman && curp->outmdoc)) { + switch (curp->outtype) { + case (OUTT_XHTML): + curp->outdata = xhtml_alloc(curp->outopts); + break; + case (OUTT_HTML): + curp->outdata = html_alloc(curp->outopts); + break; + case (OUTT_ASCII): + curp->outdata = ascii_alloc(curp->outopts); + curp->outfree = ascii_free; + break; + case (OUTT_PDF): + curp->outdata = pdf_alloc(curp->outopts); + curp->outfree = pspdf_free; + break; + case (OUTT_PS): + curp->outdata = ps_alloc(curp->outopts); + curp->outfree = pspdf_free; + break; + default: + break; + } + + switch (curp->outtype) { + case (OUTT_HTML): + /* FALLTHROUGH */ + case (OUTT_XHTML): + curp->outman = html_man; + curp->outmdoc = html_mdoc; + curp->outfree = html_free; + break; + case (OUTT_TREE): + curp->outman = tree_man; + curp->outmdoc = tree_mdoc; + break; + case (OUTT_PDF): + /* FALLTHROUGH */ + case (OUTT_ASCII): + /* FALLTHROUGH */ + case (OUTT_PS): + curp->outman = terminal_man; + curp->outmdoc = terminal_mdoc; + break; + default: + break; + } + } + + /* Execute the out device, if it exists. */ + + if (curp->man && curp->outman) + (*curp->outman)(curp->outdata, curp->man); + if (curp->mdoc && curp->outmdoc) + (*curp->outmdoc)(curp->outdata, curp->mdoc); + + cleanup: + + memset(&curp->regs, 0, sizeof(struct regset)); + + /* Reset the current-parse compilers. */ + + if (curp->mdoc) + mdoc_reset(curp->mdoc); + if (curp->man) + man_reset(curp->man); + + assert(curp->roff); + roff_reset(curp->roff); + + if (exit_status < file_status) + exit_status = file_status; + + return; +} + +static void +pdesc(struct curparse *curp) +{ + 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(curp, &blk, &with_mmap)) { + file_status = MANDOCLEVEL_SYSERR; + return; + } + + /* Line number is per-file. */ + + curp->line = 1; + + parsebuf(curp, blk, 1); + +#ifndef __minix + if (with_mmap) + munmap(blk.buf, blk.sz); + else +#endif + free(blk.buf); +} + +static void +parsebuf(struct curparse *curp, struct buf blk, int start) +{ + struct buf ln; + enum rofferr rr; + int i, of, rc; + int pos; /* byte number in the ln buffer */ + int lnn; /* line number in the real file */ + unsigned char c; + + /* + * Main parse routine for an opened file. This is called for + * each opened file and simply loops around the full input file, + * possibly nesting (i.e., with `so'). + */ + + memset(&ln, 0, sizeof(struct buf)); + + lnn = curp->line; + pos = 0; + + for (i = 0; i < (int)blk.sz; ) { + if (0 == pos && '\0' == blk.buf[i]) + break; + + if (start) { + curp->line = lnn; + curp->reparse_count = 0; + } + + while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) { + if ('\n' == blk.buf[i]) { + ++i; + ++lnn; + break; + } + + /* + * 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. + */ + + c = (unsigned char) blk.buf[i]; + + if ( ! (isascii(c) && + (isgraph(c) || isblank(c)))) { + mmsg(MANDOCERR_BADCHAR, curp, + curp->line, pos, "ignoring byte"); + i++; + continue; + } + + /* Trailing backslash = a plain char. */ + + if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) { + if (pos >= (int)ln.sz) + resize_buf(&ln, 256); + ln.buf[pos++] = blk.buf[i++]; + continue; + } + + /* Found escape & at least one other char. */ + + if ('\n' == blk.buf[i + 1]) { + i += 2; + /* Escaped newlines are skipped over */ + ++lnn; + continue; + } + + if ('"' == blk.buf[i + 1]) { + i += 2; + /* Comment, skip to end of line */ + for (; i < (int)blk.sz; ++i) { + if ('\n' == blk.buf[i]) { + ++i; + ++lnn; + break; + } + } + + /* Backout trailing whitespaces */ + for (; pos > 0; --pos) { + if (ln.buf[pos - 1] != ' ') + break; + if (pos > 2 && ln.buf[pos - 2] == '\\') + break; + } + break; + } + + /* Some other escape sequence, copy & cont. */ + + if (pos + 1 >= (int)ln.sz) + resize_buf(&ln, 256); + + ln.buf[pos++] = blk.buf[i++]; + ln.buf[pos++] = blk.buf[i++]; + } + + if (pos >= (int)ln.sz) + resize_buf(&ln, 256); + + ln.buf[pos] = '\0'; + + /* + * A significant amount of complexity is contained by + * the roff preprocessor. It's line-oriented but can be + * expressed on one line, so we need at times to + * readjust our starting point and re-run it. The roff + * preprocessor can also readjust the buffers with new + * data, so we pass them in wholesale. + */ + + of = 0; + +rerun: + rr = roff_parseln + (curp->roff, curp->line, + &ln.buf, &ln.sz, of, &of); + + switch (rr) { + case (ROFF_REPARSE): + if (REPARSE_LIMIT >= ++curp->reparse_count) + parsebuf(curp, ln, 0); + else + mmsg(MANDOCERR_ROFFLOOP, curp, + curp->line, pos, NULL); + pos = 0; + continue; + case (ROFF_APPEND): + pos = strlen(ln.buf); + continue; + case (ROFF_RERUN): + goto rerun; + case (ROFF_IGN): + pos = 0; + continue; + case (ROFF_ERR): + assert(MANDOCLEVEL_FATAL <= file_status); + break; + case (ROFF_SO): + if (pfile(ln.buf + of, curp)) { + pos = 0; + continue; + } else + break; + default: + break; + } + + /* + * If we encounter errors in the recursive parsebuf() + * call, make sure we don't continue parsing. + */ + + if (MANDOCLEVEL_FATAL <= file_status) + break; + + /* + * If input parsers have not been allocated, do so now. + * We keep these instanced betwen parsers, but set them + * locally per parse routine since we can use different + * parsers with each one. + */ + + if ( ! (curp->man || curp->mdoc)) + pset(ln.buf + of, pos - of, curp); + + /* + * Lastly, push down into the parsers themselves. One + * of these will have already been set in the pset() + * routine. + * If libroff returns ROFF_TBL, then add it to the + * currently open parse. Since we only get here if + * there does exist data (see tbl_data.c), we're + * guaranteed that something's been allocated. + */ + + if (ROFF_TBL == rr) { + assert(curp->man || curp->mdoc); + if (curp->man) + man_addspan(curp->man, roff_span(curp->roff)); + else + mdoc_addspan(curp->mdoc, roff_span(curp->roff)); + + } else if (curp->man || curp->mdoc) { + rc = curp->man ? + man_parseln(curp->man, + curp->line, ln.buf, of) : + mdoc_parseln(curp->mdoc, + curp->line, ln.buf, of); + + if ( ! rc) { + assert(MANDOCLEVEL_FATAL <= file_status); + break; + } + } + + /* Temporary buffers typically are not full. */ + + if (0 == start && '\0' == blk.buf[i]) + break; + + /* Start the next input line. */ + + pos = 0; + } + + free(ln.buf); +} + +static void +pset(const char *buf, int pos, struct curparse *curp) +{ + int i; + + /* + * Try to intuit which kind of manual parser should be used. If + * passed in by command-line (-man, -mdoc), then use that + * explicitly. If passed as -mandoc, then try to guess from the + * line: either skip dot-lines, use -mdoc when finding `.Dt', or + * default to -man, which is more lenient. + * + * Separate out pmdoc/pman from mdoc/man: the first persists + * through all parsers, while the latter is used per-parse. + */ + + if ('.' == buf[0] || '\'' == buf[0]) { + for (i = 1; buf[i]; i++) + if (' ' != buf[i] && '\t' != buf[i]) + break; + if ('\0' == buf[i]) + return; + } + + switch (curp->inttype) { + case (INTT_MDOC): + if (NULL == curp->pmdoc) + curp->pmdoc = mdoc_alloc + (&curp->regs, curp, mmsg); + assert(curp->pmdoc); + curp->mdoc = curp->pmdoc; + return; + case (INTT_MAN): + if (NULL == curp->pman) + curp->pman = man_alloc + (&curp->regs, curp, mmsg); + assert(curp->pman); + curp->man = curp->pman; + return; + default: + break; + } + + if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { + if (NULL == curp->pmdoc) + curp->pmdoc = mdoc_alloc + (&curp->regs, curp, mmsg); + assert(curp->pmdoc); + curp->mdoc = curp->pmdoc; + return; + } + + if (NULL == curp->pman) + curp->pman = man_alloc(&curp->regs, curp, mmsg); + assert(curp->pman); + curp->man = curp->pman; +} + +static int +moptions(enum intt *tflags, char *arg) +{ + + if (0 == strcmp(arg, "doc")) + *tflags = INTT_MDOC; + else if (0 == strcmp(arg, "andoc")) + *tflags = INTT_AUTO; + else if (0 == strcmp(arg, "an")) + *tflags = INTT_MAN; + else { + fprintf(stderr, "%s: Bad argument\n", arg); + return(0); + } + + return(1); +} + +static int +toptions(struct curparse *curp, char *arg) +{ + + if (0 == strcmp(arg, "ascii")) + curp->outtype = OUTT_ASCII; + else if (0 == strcmp(arg, "lint")) { + curp->outtype = OUTT_LINT; + curp->wlevel = MANDOCLEVEL_WARNING; + } + else if (0 == strcmp(arg, "tree")) + curp->outtype = OUTT_TREE; + else if (0 == strcmp(arg, "html")) + curp->outtype = OUTT_HTML; + else if (0 == strcmp(arg, "xhtml")) + curp->outtype = OUTT_XHTML; + else if (0 == strcmp(arg, "ps")) + curp->outtype = OUTT_PS; + else if (0 == strcmp(arg, "pdf")) + curp->outtype = OUTT_PDF; + else { + fprintf(stderr, "%s: Bad argument\n", arg); + return(0); + } + + return(1); +} + +static int +woptions(struct curparse *curp, char *arg) +{ + char *v, *o; + const char *toks[6]; + + toks[0] = "stop"; + toks[1] = "all"; + toks[2] = "warning"; + toks[3] = "error"; + toks[4] = "fatal"; + toks[5] = NULL; + + while (*arg) { + o = arg; + switch (getsubopt(&arg, UNCONST(toks), &v)) { + case (0): + curp->wstop = 1; + break; + case (1): + /* FALLTHROUGH */ + case (2): + curp->wlevel = MANDOCLEVEL_WARNING; + break; + case (3): + curp->wlevel = MANDOCLEVEL_ERROR; + break; + case (4): + curp->wlevel = MANDOCLEVEL_FATAL; + break; + default: + fprintf(stderr, "-W%s: Bad argument\n", o); + return(0); + } + } + + return(1); +} + +static int +mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg) +{ + struct curparse *cp; + enum mandoclevel level; + + level = MANDOCLEVEL_FATAL; + while (t < mandoclimits[level]) + /* LINTED */ + level--; + + cp = (struct curparse *)arg; + if (level < cp->wlevel) + return(1); + + fprintf(stderr, "%s:%d:%d: %s: %s", + cp->file, ln, col + 1, mandoclevels[level], mandocerrs[t]); + if (msg) + fprintf(stderr, ": %s", msg); + fputc('\n', stderr); + + if (file_status < level) + file_status = level; + + return(level < MANDOCLEVEL_FATAL); +} diff --git a/commands/mdocml/main.h b/usr.bin/mdocml/dist/main.h similarity index 88% rename from commands/mdocml/main.h rename to usr.bin/mdocml/dist/main.h index c48d00535..8d839f2fd 100644 --- a/commands/mdocml/main.h +++ b/usr.bin/mdocml/dist/main.h @@ -1,6 +1,6 @@ -/* $Id: main.h,v 1.7 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: main.h,v 1.10 2010/07/31 23:52:58 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010 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 @@ -44,8 +44,9 @@ void tree_man(void *, const struct man *); void *ascii_alloc(char *); void ascii_free(void *); -void *ps_alloc(void); -void ps_free(void *); +void *pdf_alloc(char *); +void *ps_alloc(char *); +void pspdf_free(void *); void terminal_mdoc(void *, const struct mdoc *); void terminal_man(void *, const struct man *); diff --git a/commands/mdocml/man.3 b/usr.bin/mdocml/dist/man.3 similarity index 70% rename from commands/mdocml/man.3 rename to usr.bin/mdocml/dist/man.3 index 082914473..78edcefff 100644 --- a/commands/mdocml/man.3 +++ b/usr.bin/mdocml/dist/man.3 @@ -1,4 +1,4 @@ -.\" $Id: man.3,v 1.18 2010/05/25 22:16:59 kristaps Exp $ +.\" $Vendor-Id: man.3,v 1.29 2011/01/03 11:31:26 kristaps Exp $ .\" .\" Copyright (c) 2009-2010 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: May 25 2010 $ +.Dd January 3, 2011 .Dt MAN 3 .Os .Sh NAME @@ -31,8 +31,17 @@ .In mandoc.h .In man.h .Vt extern const char * const * man_macronames; +.Ft int +.Fo man_addspan +.Fa "struct man *man" +.Fa "const struct tbl_span *span" +.Fc .Ft "struct man *" -.Fn man_alloc "void *data" "int pflags" "mandocmsg msgs" +.Fo man_alloc +.Fa "struct regset *regs" +.Fa "void *data" +.Fa "mandocmsg msgs" +.Fc .Ft int .Fn man_endparse "struct man *man" .Ft void @@ -42,7 +51,11 @@ .Ft "const struct man_node *" .Fn man_node "const struct man *man" .Ft int -.Fn man_parseln "struct man *man" "int line" "char *buf" +.Fo man_parseln +.Fa "struct man *man" +.Fa "int line" +.Fa "char *buf" +.Fc .Ft void .Fn man_reset "struct man *man" .Sh DESCRIPTION @@ -68,108 +81,52 @@ The .Fn man_reset function may be used in order to reset the parser for another input sequence. -See the -.Sx EXAMPLES -section for a full example. .Pp Beyond the full set of macros defined in .Xr man 7 , the .Nm -library also accepts the following macros: +library also accepts the following macro: .Pp .Bl -tag -width Ds -compact .It PD -Has no effect. Handled as a current-scope line macro. -.It Sp -A synonym for -.Sq sp 0.5v -.Pq part of the standard preamble for Perl documentation . -Handled as a line macro. -.It Vb -A synonym for -.Sq nf -.Pq part of the standard preamble for Perl documentation . -Handled as a current-scope line macro. -.It Ve -A synonym for -.Sq fi , -closing -.Sq Vb -.Pq part of the standard preamble for Perl documentation . +Has no effect. Handled as a current-scope line macro. .El -.Pp -Furthermore, the following escapes are accepted to allow -.Xr pod2man 1 -documents to be correctly formatted: -\e*(-- (dash), -\e*(PI (pi), -\e*(L" (left double-quote), -\e*(R" (right double-quote), -\e*(C+ (C++), -\e*(C` (left single-quote), -\e*(C' (right single-quote), -\e*(Aq (apostrophe), -\e*^ (hat), -\e*, (comma), -\e*~ (tilde), -\e*/ (forward slash), -\e*: (umlaut), -\e*8 (beta), -\e*o (degree), -\e*(D- (Eth), -\e*(d- (eth), -\e*(Th (Thorn), -and -\e*(th (thorn). -.Sh REFERENCE -This section further defines the -.Sx Types , -.Sx Functions -and -.Sx Variables -available to programmers. -Following that, the -.Sx Abstract Syntax Tree -section documents the output tree. .Ss Types -Both functions (see -.Sx Functions ) -and variables (see -.Sx Variables ) -may use the following types: .Bl -ohang .It Vt struct man -An opaque type defined in -.Pa man.c . +An opaque type. Its values are only used privately within the library. -.It Vt mandocmsg -A function callback type defined in -.Pa mandoc.h . .It Vt struct man_node A parsed node. -Defined in -.Pa man.h . See .Sx Abstract Syntax Tree for details. .El .Ss Functions -Function descriptions follow: +If +.Fn man_addspan , +.Fn man_parseln , +or +.Fn man_endparse +return 0, calls to any function but +.Fn man_reset +or +.Fn man_free +will raise an assertion. .Bl -ohang +.It Fn man_addspan +Add a table span to the parsing stream. +Returns 0 on failure, 1 on success. .It Fn man_alloc Allocates a parsing structure. The .Fa data pointer is passed to .Fa msgs . -The -.Fa pflags -arguments are defined in -.Pa man.h . -Returns NULL on failure. -If non-NULL, the pointer must be freed with +Always returns a valid pointer. +The pointer must be freed with .Fn man_free . .It Fn man_reset Reset the parser for another parse routine. @@ -188,26 +145,11 @@ The input buffer is modified by this function. .It Fn man_endparse Signals that the parse is complete. -Note that if -.Fn man_endparse -is called subsequent to -.Fn man_node , -the resulting tree is incomplete. Returns 0 on failure, 1 on success. .It Fn man_node Returns the first node of the parse. -Note that if -.Fn man_parseln -or -.Fn man_endparse -return 0, the tree will be incomplete. .It Fn man_meta Returns the document's parsed meta-data. -If this information has not yet been supplied or -.Fn man_parseln -or -.Fn man_endparse -return 0, the data will be incomplete. .El .Ss Variables The following variables are also defined: @@ -281,14 +223,16 @@ on the finished parse tree with .Fn parsed . This example does not error-check nor free memory upon failure. .Bd -literal -offset indent +struct regset regs; struct man *man; struct man_node *node; char *buf; size_t len; int line; +bzero(®s, sizeof(struct regset)); line = 1; -man = man_alloc(NULL, 0, NULL); +man = man_alloc(®s, NULL, NULL); buf = NULL; alloc_len = 0; @@ -311,9 +255,13 @@ parsed(man, node); man_free(man); .Ed .Pp -Please see +To compile this, execute +.Pp +.Dl % cc main.c libman.a libmandoc.a +.Pp +where .Pa main.c -in the source archive for a rigorous reference. +is the example file. .Sh SEE ALSO .Xr mandoc 1 , .Xr man 7 diff --git a/commands/mdocml/man.7 b/usr.bin/mdocml/dist/man.7 similarity index 83% rename from commands/mdocml/man.7 rename to usr.bin/mdocml/dist/man.7 index 39006ef3a..7d9ee19df 100644 --- a/commands/mdocml/man.7 +++ b/usr.bin/mdocml/dist/man.7 @@ -1,6 +1,6 @@ -.\" $Id: man.7,v 1.74 2010/05/26 14:03:54 kristaps Exp $ +.\" $Vendor-Id: man.7,v 1.94 2011/01/04 23:32:21 kristaps Exp $ .\" -.\" Copyright (c) 2009 Kristaps Dzonsons +.\" Copyright (c) 2009, 2010 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 26 2010 $ +.Dd January 4, 2011 .Dt MAN 7 .Os .Sh NAME @@ -37,7 +37,7 @@ Use the .Xr mdoc 7 language, instead. .Pp -An +A .Nm document follows simple rules: lines beginning with the control character @@ -52,7 +52,7 @@ Other lines are interpreted within the current state. .Sh INPUT ENCODING .Nm documents may contain only graphable 7-bit ASCII characters, the -space character, and the tabs character. +space character, and the tab character. All manuals must have .Ux line termination. @@ -61,11 +61,11 @@ Blank lines are acceptable; where found, the output will assert a vertical space. .Ss Comments Text following a -.Sq \e\*" , +.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" , +.Sq \&.\e\*q , is also ignored. Macro lines with only a control character and optionally whitespace are stripped from input. @@ -92,7 +92,7 @@ and .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 +escape followed by an indicator: B (bold), I (italic), R (Roman), or P (revert to previous mode): .Pp .D1 \efBbold\efR \efIitalic\efP @@ -106,35 +106,12 @@ Note that macros like .Sx \&BR open and close a font scope with each argument. .Pp -Text may also be sized with the -.Sq \es -escape, whose syntax is one of -.Sq \es+-n -for one-digit numerals; -.Sq \es(+-nn -or -.Sq \es+-(nn -for two-digit numerals; and -.Sq \es[+-N] , -.Sq \es+-[N] , -.Sq \es'+-N' , -or -.Sq \es+-'N' -for arbitrary-digit numerals: -.Pp -.D1 \es+1bigger\es-1 -.D1 \es[+10]much bigger\es[-10] -.D1 \es+(10much bigger\es-(10 -.D1 \es+'100'much much bigger\es-'100' -.Pp -Both -.Sq \es -and +The .Sq \ef -attributes are forgotten when entering or exiting a macro block. +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; un-escaped +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. @@ -213,23 +190,25 @@ this differs from 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 your sentences end at the end of +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 ( -.Ns Sq \&) , +delimiters +.Po +.Sq \&) , .Sq \&] , .Sq \&' , -.Sq \&" ) . +.Sq \&" +.Pc . .Sh MANUAL STRUCTURE Each .Nm -document must contain contains at least the +document must contain the .Sx \&TH macro describing the document's section and title. -It may occur anywhere in the document, although conventionally, it +It may occur anywhere in the document, although conventionally it appears as the first macro. .Pp Beyond @@ -238,31 +217,29 @@ at least one macro or text node must appear in the document. Documents are generally structured as follows: .Bd -literal -offset indent \&.TH FOO 1 2009-10-10 -\&. \&.SH NAME \efBfoo\efR \e(en a description goes here -\&.\e\*q The next is for sections 2 & 3 only. \&.\e\*q .SH LIBRARY -\&. +\&.\e\*q For sections 2 & 3 only. +\&.\e\*q Not used in OpenBSD. \&.SH SYNOPSIS \efBfoo\efR [\efB\e-options\efR] arguments... -\&. \&.SH DESCRIPTION The \efBfoo\efR utility processes files... -\&. \&.\e\*q .SH IMPLEMENTATION NOTES -\&.\e\*q The next is for sections 2, 3, & 9 only. +\&.\e\*q Not used in OpenBSD. \&.\e\*q .SH RETURN VALUES -\&.\e\*q The next is for sections 1, 6, 7, & 8 only. +\&.\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 The next is for sections 1 & 8 only. \&.\e\*q .SH EXIT STATUS +\&.\e\*q For sections 1, 6, & 8 only. \&.\e\*q .SH EXAMPLES -\&.\e\*q The next is for sections 1, 4, 6, 7, & 8 only. \&.\e\*q .SH DIAGNOSTICS -\&.\e\*q The next is for sections 2, 3, & 9 only. +\&.\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 @@ -271,6 +248,7 @@ The \efBfoo\efR utility processes files... \&.\e\*q .SH CAVEATS \&.\e\*q .SH BUGS \&.\e\*q .SH SECURITY CONSIDERATIONS +\&.\e\*q Not used in OpenBSD. .Ed .Pp The sections in a @@ -318,22 +296,17 @@ Implementation-specific notes should be kept here. This is useful when implementing standard functions that may have side effects or notable algorithmic implications. .It Em RETURN VALUES -This section is the dual of -.Em EXIT STATUS , -which is used for commands. -It documents the return values of functions in sections 2, 3, and 9. +This section documents the return values of functions in sections 2, 3, and 9. .It Em ENVIRONMENT Documents any usages of environment variables, e.g., .Xr environ 7 . .It Em FILES Documents files used. -It's helpful to document both the file and a short description of how +It's helpful to document both the file name and a short description of how the file is used (created, modified, etc.). .It Em EXIT STATUS -Command exit status for section 1, 6, and 8 manuals. -This section is the dual of -.Em RETURN VALUES , -which is used for functions. +This section documents the command exit status for +section 1, 6, and 8 utilities. Historically, this information was described in .Em DIAGNOSTICS , a practise that is now discouraged. @@ -341,7 +314,7 @@ a practise that is now discouraged. Example usages. This often contains snippets of well-formed, well-tested invocations. -Make doubly sure that your examples work properly! +Make sure that examples work properly! .It Em DIAGNOSTICS Documents error conditions. This is most useful in section 4 manuals. @@ -368,23 +341,22 @@ If not adhering to any standards, the .Em HISTORY section should be used. .It Em HISTORY -The history of any manual without a -.Em STANDARDS -section should be described in this section. +A brief history of the subject, including where support first appeared. .It Em AUTHORS -Credits to authors, if applicable, should appear in this section. -Authors should generally be noted by both name and an e-mail address. +Credits to the person or persons who wrote the code and/or documentation. +Authors should generally be noted by both name and email address. .It Em CAVEATS -Explanations of common misuses and misunderstandings should be explained +Common misuses and misunderstandings should be explained in this section. .It Em BUGS -Extant bugs should be described in this section. +Known bugs, limitations, and work-arounds should be described +in this section. .It Em SECURITY CONSIDERATIONS Documents any security precautions that operators should consider. .El .Sh MACRO SYNTAX -Macros are one to three three characters in length and begin with a -control character , +Macros are one to three characters in length and begin with a +control character, .Sq \&. , at the beginning of the line. The @@ -420,11 +392,11 @@ 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 (unless in the case of +raised, except for .Sx \&br , .Sx \&sp , -or -.Sx \&na ) . +and +.Sx \&na . .Pp The syntax is as follows: .Bd -literal -offset indent @@ -442,7 +414,6 @@ The syntax is as follows: .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 \&PD Ta n 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 \& @@ -452,14 +423,11 @@ The syntax is as follows: .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 \&i Ta n 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 \&r Ta 0 Ta current Ta compat .It Sx \&sp Ta 1 Ta current Ta compat -.\" .It Sx \&Sp Ta <1 Ta current Ta compat -.\" .It Sx \&Vb Ta <1 Ta current Ta compat -.\" .It Sx \&Ve Ta 0 Ta current Ta compat .El .Pp Macros marked as @@ -470,8 +438,8 @@ These macros should not be used for portable .Nm manuals. .Ss Block Macros -Block macros are comprised of a head and body. -Like for in-line macros, the head is scoped to the current line and, in +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). @@ -539,12 +507,9 @@ The optional arguments specify which release it is from. Text is rendered in bold face. .Pp See also -.Sx \&I , -.Sx \&R , -.Sx \&b , -.Sx \&i , +.Sx \&I and -.Sx \&r . +.Sx \&R . .Ss \&BI Text is rendered alternately in bold face and italic. Thus, @@ -562,7 +527,7 @@ Whitespace between arguments is omitted in output. .Pp Examples: .Pp -.D1 \&.BI bold italic bold italic +.Dl \&.BI bold italic bold italic .Pp The output of this example will be emboldened .Dq bold @@ -621,15 +586,12 @@ and Text is rendered in italics. .Pp See also -.Sx \&B , -.Sx \&R , -.Sx \&b , -.Sx \&i , +.Sx \&B and -.Sx \&r . +.Sx \&R . .Ss \&IB -Text is rendered alternately in italics and bold face. Whitespace -between arguments is omitted in output. +Text is rendered alternately in italics and bold face. +Whitespace between arguments is omitted in output. .Pp See .Sx \&BI @@ -652,7 +614,7 @@ Begin an indented paragraph with the following syntax: The .Cm width argument defines the width of the left margin and is defined by -.Sx Scaling Widths , +.Sx Scaling Widths . It's saved for later paragraph left-margins; if unspecified, the saved or default width is used. .Pp @@ -687,7 +649,7 @@ and Begin an undecorated paragraph. The scope of a paragraph is closed by a subsequent paragraph, sub-section, section, or end of file. -The saved paragraph left-margin width is re-set to the default. +The saved paragraph left-margin width is reset to the default. .Pp See also .Sx \&HP , @@ -722,12 +684,9 @@ and Text is rendered in roman (the default font). .Pp See also -.Sx \&I , -.Sx \&B , -.Sx \&b , -.Sx \&i , +.Sx \&I and -.Sx \&r . +.Sx \&B . .Ss \&RB Text is rendered alternately in roman (the default font) and bold face. Whitespace between arguments is omitted in output. @@ -784,7 +743,7 @@ bold face. Begin a section. The scope of a section is only closed by another section or the end of file. -The paragraph left-margin width is re-set to the default. +The paragraph left-margin width is reset to the default. .Ss \&SM Text is rendered in small size (one point smaller than the default font). @@ -792,7 +751,7 @@ font). Begin a sub-section. The scope of a sub-section is closed by a subsequent sub-section, section, or end of file. -The paragraph left-margin width is re-set to the default. +The paragraph left-margin width is reset to the default. .Ss \&TH Sets the title of the manual page with the following syntax: .Bd -filled -offset indent @@ -801,9 +760,9 @@ Sets the title of the manual page with the following syntax: .Op Cm date Op Cm source Op Cm volume .Ed .Pp -At least the upper-case document title +At least the upper-case document .Cm title -and numeric manual section +and the manual .Cm section arguments must be provided. The @@ -822,7 +781,7 @@ manual section. .Pp Examples: .Pp -.D1 \&.TH CVS 5 "1992-02-12" GNU +.Dl \&.TH CVS 5 "1992-02-12" GNU .Ss \&TP Begin a paragraph where the head, if exceeding the indentation width, is followed by a newline; if not, the body follows on the same line after a @@ -848,12 +807,6 @@ See also .Sx \&P , and .Sx \&PP . -.\" . -.\" . -.\" .Ss \&PD -.\" Has no effect. Included for compatibility. -.\" . -.\" . .Ss \&UC Sets the volume for the footer for compatibility with man pages from BSD releases. @@ -867,18 +820,21 @@ See also .Ss \&fi End literal mode begun by .Sx \&nf . -.Ss \&i -Italicise arguments. -Synonym for -.Sx \&I . +.Ss \&ft +Change the current font mode. +See +.Sx Text Decoration +for a listing of available font modes. +.Ss \&in +Indent relative to the current indentation: .Pp -See also -.Sx \&B , -.Sx \&I , -.Sx \&R . -.Sx \&b , -and -.Sx \&r . +.D1 Pf \. Sx \&in Op Cm width +.Pp +If +.Cm width +is signed, the new offset is relative. +Otherwise, it is absolute. +This value is reset upon the next paragraph, section, or sub-section. .Ss \&na Don't align to the right margin. .Ss \&nf @@ -886,16 +842,6 @@ Begin literal mode: all subsequent free-form lines have their end of line boundaries preserved. May be ended by .Sx \&fi . -.Ss \&r -Fonts and styles (bold face, italics) reset to roman (default font). -.Pp -See also -.Sx \&B , -.Sx \&I , -.Sx \&R , -.Sx \&b , -and -.Sx \&i . .Ss \&sp Insert vertical spaces into output with the following syntax: .Bd -filled -offset indent @@ -914,21 +860,6 @@ Defaults to 1, if unspecified. .Pp See also .Sx \&br . -.\" .Ss \&Sp -.\" A synonym for -.\" .Sx \&sp -.\" .Cm 0.5v . -.\" . -.\" .Ss \&Vb -.\" A synonym for -.\" .Sx \&nf . -.\" Accepts an argument (the height of the formatted space) which is -.\" disregarded. -.\" . -.\" .Ss \&Ve -.\" A synonym for -.\" .Sx \&fi . -.\" . .Sh COMPATIBILITY This section documents areas of questionable portability between implementations of the @@ -941,24 +872,64 @@ 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. .It +troff suppresses a newline before +.Sq \(aq +macro output; in mandoc, it is an alias for the standard +.Sq \&. +control character. +.It +The +.Sq \eh +.Pq horizontal position , +.Sq \ev +.Pq vertical position , +.Sq \em +.Pq text colour , +.Sq \eM +.Pq text filling colour , +.Sq \ez +.Pq zero-length character , +.Sq \ew +.Pq string length , +.Sq \ek +.Pq horizontal position marker , +.Sq \eo +.Pq text overstrike , +and +.Sq \es +.Pq text size +escape sequences are all discarded in mandoc. +.It +The +.Sq \ef +scaling unit is accepted by mandoc, but rendered as the default unit. +.It The .Sx \&sp macro does not accept negative values in mandoc. In GNU troff, this would result in strange behaviour. -.It -The -.Sq \(aq -macro control character, in GNU troff (and prior troffs) suppresses a -newline before macro output; in mandoc, it is an alias for the standard -.Sq \&. -control character. .El .Sh SEE ALSO +.Xr man 1 , .Xr mandoc 1 , -.Xr mandoc_char 7 -.Sh AUTHORS +.Xr mandoc_char 7 , +.Xr mdoc 7 , +.Xr roff 7 , +.Xr tbl 7 +.Sh HISTORY The .Nm +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. +The stand-alone implementation that is part of the +.Xr mandoc 1 +utility written by Kristaps Dzonsons appeared in +.Ox 4.6 . +.Sh AUTHORS +This +.Nm reference was written by .An Kristaps Dzonsons Aq kristaps@bsd.lv . .Sh CAVEATS diff --git a/commands/mdocml/man.c b/usr.bin/mdocml/dist/man.c similarity index 83% rename from commands/mdocml/man.c rename to usr.bin/mdocml/dist/man.c index b77fbb2df..0700dee42 100644 --- a/commands/mdocml/man.c +++ b/usr.bin/mdocml/dist/man.c @@ -1,6 +1,6 @@ -/* $Id: man.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: man.c,v 1.96 2011/01/03 11:31:26 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -37,10 +36,10 @@ const char *const __man_macronames[MAN_MAX] = { "IP", "HP", "SM", "SB", "BI", "IB", "BR", "RB", "R", "B", "I", "IR", - "RI", "na", "i", "sp", - "nf", "fi", "r", "RE", - "RS", "DT", "UC", "PD", - "Sp", "Vb", "Ve", "AT", + "RI", "na", "sp", "nf", + "fi", "RE", "RS", "DT", + "UC", "PD", "AT", "in", + "ft" }; const char * const *man_macronames = __man_macronames; @@ -49,6 +48,8 @@ static struct man_node *man_node_alloc(int, int, enum man_type, enum mant); static int man_node_append(struct man *, struct man_node *); +static int man_span_alloc(struct man *, + const struct tbl_span *); static void man_node_free(struct man_node *); static void man_node_unlink(struct man *, struct man_node *); @@ -56,14 +57,15 @@ static int man_ptext(struct man *, int, char *, int); static int man_pmacro(struct man *, int, char *, int); static void man_free1(struct man *); static void man_alloc1(struct man *); -static int macrowarn(struct man *, int, const char *, int); +static int man_descope(struct man *, int, int); const struct man_node * man_node(const struct man *m) { - return(MAN_HALT & m->flags ? NULL : m->first); + assert( ! (MAN_HALT & m->flags)); + return(m->first); } @@ -71,7 +73,8 @@ const struct man_meta * man_meta(const struct man *m) { - return(MAN_HALT & m->flags ? NULL : &m->meta); + assert( ! (MAN_HALT & m->flags)); + return(&m->meta); } @@ -94,7 +97,7 @@ man_free(struct man *man) struct man * -man_alloc(void *data, int pflags, mandocmsg msg) +man_alloc(struct regset *regs, void *data, mandocmsg msg) { struct man *p; @@ -102,8 +105,8 @@ man_alloc(void *data, int pflags, mandocmsg msg) man_hash_init(); p->data = data; - p->pflags = pflags; p->msg = msg; + p->regs = regs; man_alloc1(p); return(p); @@ -114,9 +117,8 @@ int man_endparse(struct man *m) { - if (MAN_HALT & m->flags) - return(0); - else if (man_macroend(m)) + assert( ! (MAN_HALT & m->flags)); + if (man_macroend(m)) return(1); m->flags |= MAN_HALT; return(0); @@ -127,9 +129,7 @@ int man_parseln(struct man *m, int ln, char *buf, int offs) { - if (MAN_HALT & m->flags) - return(0); - + assert( ! (MAN_HALT & m->flags)); return(('.' == buf[offs] || '\'' == buf[offs]) ? man_pmacro(m, ln, buf, offs) : man_ptext(m, ln, buf, offs)); @@ -214,11 +214,11 @@ man_node_append(struct man *man, struct man_node *p) man->last = p; switch (p->type) { + case (MAN_TBL): + /* FALLTHROUGH */ case (MAN_TEXT): if ( ! man_valid_post(man)) return(0); - if ( ! man_action_post(man)) - return(0); break; default: break; @@ -293,6 +293,21 @@ man_block_alloc(struct man *m, int line, int pos, enum mant tok) return(1); } +static int +man_span_alloc(struct man *m, const struct tbl_span *span) +{ + struct man_node *n; + + /* FIXME: grab from span */ + n = man_node_alloc(0, 0, MAN_TBL, MAN_MAX); + n->span = span; + + if ( ! man_node_append(m, n)) + return(0); + + m->next = MAN_NEXT_SIBLING; + return(1); +} int man_word_alloc(struct man *m, int line, int pos, const char *word) @@ -343,74 +358,19 @@ man_node_delete(struct man *m, struct man_node *p) } -static int -man_ptext(struct man *m, int line, char *buf, int offs) +int +man_addspan(struct man *m, const struct tbl_span *sp) { - int i; - /* Ignore bogus comments. */ - - if ('\\' == buf[offs] && - '.' == buf[offs + 1] && - '"' == buf[offs + 2]) - return(man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT)); - - /* Literal free-form text whitespace is preserved. */ - - if (MAN_LITERAL & m->flags) { - if ( ! man_word_alloc(m, line, offs, buf + offs)) - return(0); - goto descope; - } - - /* Pump blank lines directly into the backend. */ - - for (i = offs; ' ' == buf[i]; i++) - /* Skip leading whitespace. */ ; - - if ('\0' == buf[i]) { - /* Allocate a blank entry. */ - if ( ! man_word_alloc(m, line, offs, "")) - return(0); - goto descope; - } - - /* - * Warn if the last un-escaped character is whitespace. Then - * strip away the remaining spaces (tabs stay!). - */ - - i = (int)strlen(buf); - assert(i); - - if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { - if (i > 1 && '\\' != buf[i - 2]) - if ( ! man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE)) - return(0); - - for (--i; i && ' ' == buf[i]; i--) - /* Spin back to non-space. */ ; - - /* Jump ahead of escaped whitespace. */ - i += '\\' == buf[i] ? 2 : 1; - - buf[i] = '\0'; - } - - if ( ! man_word_alloc(m, line, offs, buf + offs)) + assert( ! (MAN_HALT & m->flags)); + if ( ! man_span_alloc(m, sp)) return(0); + return(man_descope(m, 0, 0)); +} - /* - * End-of-sentence check. If the last character is an unescaped - * EOS character, then flag the node as being the end of a - * sentence. The front-end will know how to interpret this. - */ - - assert(i); - if (mandoc_eos(buf, (size_t)i)) - m->last->flags |= MAN_EOS; - -descope: +static int +man_descope(struct man *m, int line, int offs) +{ /* * Co-ordinate what happens with having a next-line scope open: * first close out the element scope (if applicable), then close @@ -434,15 +394,74 @@ descope: static int -macrowarn(struct man *m, int ln, const char *buf, int offs) +man_ptext(struct man *m, int line, char *buf, int offs) { - int rc; + int i; - rc = man_vmsg(m, MANDOCERR_MACRO, ln, offs, - "unknown macro: %s%s", - buf, strlen(buf) > 3 ? "..." : ""); + /* Ignore bogus comments. */ - return(MAN_IGN_MACRO & m->pflags ? rc : 0); + if ('\\' == buf[offs] && + '.' == buf[offs + 1] && + '"' == buf[offs + 2]) { + man_pmsg(m, line, offs, MANDOCERR_BADCOMMENT); + return(1); + } + + /* Literal free-form text whitespace is preserved. */ + + if (MAN_LITERAL & m->flags) { + if ( ! man_word_alloc(m, line, offs, buf + offs)) + return(0); + return(man_descope(m, line, offs)); + } + + /* Pump blank lines directly into the backend. */ + + for (i = offs; ' ' == buf[i]; i++) + /* Skip leading whitespace. */ ; + + if ('\0' == buf[i]) { + /* Allocate a blank entry. */ + if ( ! man_word_alloc(m, line, offs, "")) + return(0); + return(man_descope(m, line, offs)); + } + + /* + * Warn if the last un-escaped character is whitespace. Then + * strip away the remaining spaces (tabs stay!). + */ + + i = (int)strlen(buf); + assert(i); + + if (' ' == buf[i - 1] || '\t' == buf[i - 1]) { + if (i > 1 && '\\' != buf[i - 2]) + man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE); + + for (--i; i && ' ' == buf[i]; i--) + /* Spin back to non-space. */ ; + + /* Jump ahead of escaped whitespace. */ + i += '\\' == buf[i] ? 2 : 1; + + buf[i] = '\0'; + } + + if ( ! man_word_alloc(m, line, offs, buf + offs)) + return(0); + + /* + * End-of-sentence check. If the last character is an unescaped + * EOS character, then flag the node as being the end of a + * sentence. The front-end will know how to interpret this. + */ + + assert(i); + if (mandoc_eos(buf, (size_t)i, 0)) + m->last->flags |= MAN_EOS; + + return(man_descope(m, line, offs)); } @@ -478,34 +497,19 @@ man_pmacro(struct man *m, int ln, char *buf, int offs) ppos = i; - /* Copy the first word into a nil-terminated buffer. */ - - for (j = 0; j < 4; j++, i++) { - if ('\0' == (mac[j] = buf[i])) - break; - else if (' ' == buf[i]) - break; - - /* Check for invalid characters. */ - - if (isgraph((u_char)buf[i])) - continue; - if ( ! man_pmsg(m, ln, i, MANDOCERR_BADCHAR)) - return(0); - i--; - } + /* + * Copy the first word into a nil-terminated buffer. + * Stop copying when a tab, space, or eoln is encountered. + */ + j = 0; + while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i]) + mac[j++] = buf[i++]; mac[j] = '\0'; - if (j == 4 || j < 1) { - if ( ! macrowarn(m, ln, mac, ppos)) - goto err; - return(1); - } - - if (MAN_MAX == (tok = man_hash_find(mac))) { - if ( ! macrowarn(m, ln, mac, ppos)) - goto err; + tok = (j > 0 && j < 4) ? man_hash_find(mac) : MAN_MAX; + if (MAN_MAX == tok) { + man_vmsg(m, MANDOCERR_MACRO, ln, ppos, "%s", buf + ppos - 1); return(1); } @@ -520,41 +524,26 @@ man_pmacro(struct man *m, int ln, char *buf, int offs) */ if ('\0' == buf[i] && ' ' == buf[i - 1]) - if ( ! man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE)) - goto err; + man_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE); /* - * Remove prior ELINE macro, as it's being clobbering by a new + * Remove prior ELINE macro, as it's being clobbered by a new * macro. Note that NSCOPED macros do not close out ELINE * macros---they don't print text---so we let those slip by. */ if ( ! (MAN_NSCOPED & man_macros[tok].flags) && m->flags & MAN_ELINE) { - assert(MAN_TEXT != m->last->type); - - /* - * This occurs in the following construction: - * .B - * .br - * .B - * .br - * I hate man macros. - * Flat-out disallow this madness. - */ - if (MAN_NSCOPED & man_macros[m->last->tok].flags) { - man_pmsg(m, ln, ppos, MANDOCERR_SYNTLINESCOPE); - return(0); - } - n = m->last; + assert(MAN_TEXT != n->type); - assert(n); - assert(NULL == n->child); - assert(0 == n->nchild); + /* Remove repeated NSCOPED macros causing ELINE. */ - if ( ! man_nmsg(m, n, MANDOCERR_LINESCOPE)) - return(0); + if (MAN_NSCOPED & man_macros[n->tok].flags) + n = n->parent; + + man_vmsg(m, MANDOCERR_LINESCOPE, n->line, n->pos, + "%s", man_macronames[n->tok]); man_node_delete(m, n); m->flags &= ~MAN_ELINE; diff --git a/commands/mdocml/man.h b/usr.bin/mdocml/dist/man.h similarity index 52% rename from commands/mdocml/man.h rename to usr.bin/mdocml/dist/man.h index 46ec7f8e7..2a37d0168 100644 --- a/commands/mdocml/man.h +++ b/usr.bin/mdocml/dist/man.h @@ -1,6 +1,6 @@ -/* $Id: man.h,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: man.h,v 1.50 2011/01/01 12:59:17 kristaps Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010 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 @@ -17,8 +17,9 @@ #ifndef MAN_H #define MAN_H -#include - +/* + * What follows is a list of ALL possible macros. + */ enum mant { MAN_br = 0, MAN_TH, @@ -42,63 +43,71 @@ enum mant { MAN_IR, MAN_RI, MAN_na, - MAN_i, MAN_sp, MAN_nf, MAN_fi, - MAN_r, MAN_RE, MAN_RS, MAN_DT, MAN_UC, MAN_PD, - MAN_Sp, - MAN_Vb, - MAN_Ve, MAN_AT, + MAN_in, + MAN_ft, MAN_MAX }; +/* + * Type of a syntax node. + */ enum man_type { MAN_TEXT, MAN_ELEM, MAN_ROOT, MAN_BLOCK, MAN_HEAD, - MAN_BODY + MAN_BODY, + MAN_TBL }; +/* + * Information from prologue. + */ struct man_meta { - char *msec; - time_t date; - char *rawdate; - char *vol; - char *title; - char *source; + char *msec; /* `TH' section (1, 3p, etc.) */ + time_t date; /* `TH' normalised date */ + char *rawdate; /* raw `TH' date */ + char *vol; /* `TH' volume */ + char *title; /* `TH' title (e.g., FOO) */ + char *source; /* `TH' source (e.g., GNU) */ }; +/* + * Single node in tree-linked AST. + */ struct man_node { - struct man_node *parent; - struct man_node *child; - struct man_node *next; - struct man_node *prev; - int nchild; + struct man_node *parent; /* parent AST node */ + struct man_node *child; /* first child AST node */ + struct man_node *next; /* sibling AST node */ + struct man_node *prev; /* prior sibling AST node */ + int nchild; /* number children */ int line; int pos; - enum mant tok; + enum mant tok; /* tok or MAN__MAX if none */ int flags; -#define MAN_VALID (1 << 0) -#define MAN_ACTED (1 << 1) -#define MAN_EOS (1 << 2) - enum man_type type; - char *string; - struct man_node *head; - struct man_node *body; +#define MAN_VALID (1 << 0) /* has been validated */ +#define MAN_EOS (1 << 2) /* at sentence boundary */ + enum man_type type; /* AST node type */ + char *string; /* TEXT node argument */ + struct man_node *head; /* BLOCK node HEAD ptr */ + struct man_node *body; /* BLOCK node BODY ptr */ + const struct tbl_span *span; /* TBL */ }; -#define MAN_IGN_MACRO (1 << 0) -#define MAN_IGN_ESCAPE (1 << 2) - +/* + * Names of macros. Index is enum mant. Indexing into this returns + * the normalised name, e.g., man_macronames[MAN_SH] -> "SH". + */ extern const char *const *man_macronames; __BEGIN_DECLS @@ -106,10 +115,12 @@ __BEGIN_DECLS struct man; void man_free(struct man *); -struct man *man_alloc(void *, int, mandocmsg); +struct man *man_alloc(struct regset *, void *, mandocmsg); void man_reset(struct man *); int man_parseln(struct man *, int, char *, int); int man_endparse(struct man *); +int man_addspan(struct man *, + const struct tbl_span *); const struct man_node *man_node(const struct man *); const struct man_meta *man_meta(const struct man *); diff --git a/usr.bin/mdocml/dist/man_argv.c b/usr.bin/mdocml/dist/man_argv.c new file mode 100644 index 000000000..652e30671 --- /dev/null +++ b/usr.bin/mdocml/dist/man_argv.c @@ -0,0 +1,44 @@ +/* $Vendor-Id: man_argv.c,v 1.5 2011/01/03 22:42:37 schwarze Exp $ */ +/* + * 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 "mandoc.h" +#include "libman.h" +#include "libmandoc.h" + + +int +man_args(struct man *m, int line, int *pos, char *buf, char **v) +{ + char *start; + + assert(*pos); + *v = start = buf + *pos; + assert(' ' != *start); + + if ('\0' == *start) + return(ARGS_EOLN); + + *v = mandoc_getarg(v, m->msg, m->data, line, pos); + return('"' == *start ? ARGS_QWORD : ARGS_WORD); +} diff --git a/commands/mdocml/man_hash.c b/usr.bin/mdocml/dist/man_hash.c similarity index 94% rename from commands/mdocml/man_hash.c rename to usr.bin/mdocml/dist/man_hash.c index e80a39c46..7c31e1b48 100644 --- a/commands/mdocml/man_hash.c +++ b/usr.bin/mdocml/dist/man_hash.c @@ -1,6 +1,6 @@ -/* $Id: man_hash.c,v 1.22 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: man_hash.c,v 1.23 2010/07/31 23:52:58 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 diff --git a/commands/mdocml/man_html.c b/usr.bin/mdocml/dist/man_html.c similarity index 56% rename from commands/mdocml/man_html.c rename to usr.bin/mdocml/dist/man_html.c index 2406393a3..53fc62cc5 100644 --- a/commands/mdocml/man_html.c +++ b/usr.bin/mdocml/dist/man_html.c @@ -1,6 +1,6 @@ -/* $Id: man_html.c,v 1.37 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: man_html.c,v 1.62 2011/01/07 13:20:58 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -40,8 +40,14 @@ #define MAN_ARGS const struct man_meta *m, \ const struct man_node *n, \ + struct mhtml *mh, \ struct html *h +struct mhtml { + int fl; +#define MANH_LITERAL (1 << 0) /* literal context */ +}; + struct htmlman { int (*pre)(MAN_ARGS); int (*post)(MAN_ARGS); @@ -58,6 +64,8 @@ static int a2width(const struct man_node *, 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 int man_root_pre(MAN_ARGS); static int man_B_pre(MAN_ARGS); @@ -66,7 +74,6 @@ static int man_I_pre(MAN_ARGS); static int man_IP_pre(MAN_ARGS); static int man_PP_pre(MAN_ARGS); static int man_RS_pre(MAN_ARGS); -static int man_SB_pre(MAN_ARGS); static int man_SH_pre(MAN_ARGS); static int man_SM_pre(MAN_ARGS); static int man_SS_pre(MAN_ARGS); @@ -83,7 +90,7 @@ static const struct htmlman mans[MAN_MAX] = { { man_IP_pre, NULL }, /* IP */ { man_HP_pre, NULL }, /* HP */ { man_SM_pre, NULL }, /* SM */ - { man_SB_pre, NULL }, /* SB */ + { man_SM_pre, NULL }, /* SB */ { man_alt_pre, NULL }, /* BI */ { man_alt_pre, NULL }, /* IB */ { man_alt_pre, NULL }, /* BR */ @@ -94,20 +101,17 @@ static const struct htmlman mans[MAN_MAX] = { { man_alt_pre, NULL }, /* IR */ { man_alt_pre, NULL }, /* RI */ { NULL, NULL }, /* na */ - { NULL, NULL }, /* i */ { man_br_pre, NULL }, /* sp */ - { NULL, NULL }, /* nf */ - { NULL, NULL }, /* fi */ - { NULL, NULL }, /* r */ + { man_literal_pre, NULL }, /* nf */ + { man_literal_pre, NULL }, /* fi */ { NULL, NULL }, /* RE */ { man_RS_pre, NULL }, /* RS */ { man_ign_pre, NULL }, /* DT */ { man_ign_pre, NULL }, /* UC */ { man_ign_pre, NULL }, /* PD */ - { man_br_pre, NULL }, /* Sp */ - { man_ign_pre, NULL }, /* Vb */ - { NULL, NULL }, /* Ve */ { man_ign_pre, NULL }, /* AT */ + { man_in_pre, NULL }, /* in */ + { man_ign_pre, NULL }, /* ft */ }; @@ -116,13 +120,16 @@ 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), h); + print_man(man_meta(m), man_node(m), &mh, h); print_tagq(h, t); printf("\n"); @@ -133,20 +140,13 @@ static void print_man(MAN_ARGS) { struct tag *t; - struct htmlpair tag; t = print_otag(h, TAG_HEAD, 0, NULL); - - print_man_head(m, n, h); + print_man_head(m, n, mh, h); print_tagq(h, t); + t = print_otag(h, TAG_BODY, 0, NULL); - - tag.key = ATTR_CLASS; - tag.val = "body"; - print_otag(h, TAG_DIV, 1, &tag); - - print_man_nodelist(m, n, h); - + print_man_nodelist(m, n, mh, h); print_tagq(h, t); } @@ -169,9 +169,9 @@ static void print_man_nodelist(MAN_ARGS) { - print_man_node(m, n, h); + print_man_node(m, n, mh, h); if (n->next) - print_man_nodelist(m, n->next, h); + print_man_nodelist(m, n->next, mh, h); } @@ -194,30 +194,33 @@ print_man_node(MAN_ARGS) switch (n->type) { case (MAN_ROOT): - child = man_root_pre(m, n, h); + child = man_root_pre(m, n, mh, h); break; case (MAN_TEXT): print_text(h, n->string); + if (MANH_LITERAL & mh->fl) + print_otag(h, TAG_BR, 0, NULL); return; + case (MAN_TBL): + print_tbl(h, n->span); + break; default: /* * Close out scope of font prior to opening a macro * scope. Assert that the metafont is on the top of the * stack (it's never nested). */ - if (h->metaf) { - assert(h->metaf == t); - print_tagq(h, h->metaf); - assert(NULL == h->metaf); - t = h->tags.head; + if (HTMLFONT_NONE != h->metac) { + h->metal = h->metac; + h->metac = HTMLFONT_NONE; } if (mans[n->tok].pre) - child = (*mans[n->tok].pre)(m, n, h); + child = (*mans[n->tok].pre)(m, n, mh, h); break; } if (child && n->child) - print_man_nodelist(m, n->child, h); + print_man_nodelist(m, n->child, mh, h); /* This will automatically close out any font scope. */ print_stagq(h, t); @@ -226,13 +229,13 @@ print_man_node(MAN_ARGS) switch (n->type) { case (MAN_ROOT): - man_root_post(m, n, h); + man_root_post(m, n, mh, h); break; - case (MAN_TEXT): + case (MAN_TBL): break; default: if (mans[n->tok].post) - (*mans[n->tok].post)(m, n, h); + (*mans[n->tok].post)(m, n, mh, h); break; } } @@ -265,35 +268,45 @@ man_root_pre(MAN_ARGS) snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec); - PAIR_CLASS_INIT(&tag[0], "header"); - bufcat_style(h, "width", "100%"); - PAIR_STYLE_INIT(&tag[1], h); - PAIR_SUMMARY_INIT(&tag[2], "header"); + 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); + + print_otag(h, TAG_TBODY, 0, NULL); - t = print_otag(h, TAG_TABLE, 3, tag); tt = print_otag(h, TAG_TR, 0, NULL); - bufinit(h); - bufcat_style(h, "width", "10%"); - PAIR_STYLE_INIT(&tag[0], h); + PAIR_CLASS_INIT(&tag[0], "head-ltitle"); print_otag(h, TAG_TD, 1, tag); + print_text(h, title); print_stagq(h, tt); - bufinit(h); - bufcat_style(h, "width", "80%"); - bufcat_style(h, "white-space", "nowrap"); - bufcat_style(h, "text-align", "center"); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_TD, 1, tag); + 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); + print_text(h, b); print_stagq(h, tt); - bufinit(h); - bufcat_style(h, "width", "10%"); - bufcat_style(h, "text-align", "right"); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_TD, 1, tag); + 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); + print_text(h, title); print_tagq(h, t); return(1); @@ -313,26 +326,32 @@ man_root_post(MAN_ARGS) else time2a(m->date, b, DATESIZ); - PAIR_CLASS_INIT(&tag[0], "footer"); - bufcat_style(h, "width", "100%"); - PAIR_STYLE_INIT(&tag[1], h); - PAIR_SUMMARY_INIT(&tag[2], "footer"); + 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); - t = print_otag(h, TAG_TABLE, 3, tag); tt = print_otag(h, TAG_TR, 0, NULL); - bufinit(h); - bufcat_style(h, "width", "50%"); - PAIR_STYLE_INIT(&tag[0], h); + PAIR_CLASS_INIT(&tag[0], "foot-date"); print_otag(h, TAG_TD, 1, tag); + print_text(h, b); print_stagq(h, tt); - bufinit(h); - bufcat_style(h, "width", "50%"); - bufcat_style(h, "text-align", "right"); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_TD, 1, tag); + 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); + if (m->source) print_text(h, m->source); print_tagq(h, t); @@ -349,18 +368,11 @@ man_br_pre(MAN_ARGS) SCALE_VS_INIT(&su, 1); - switch (n->tok) { - case (MAN_Sp): - SCALE_VS_INIT(&su, 0.5); - break; - case (MAN_sp): + if (MAN_sp == n->tok) { if (n->child) a2roffsu(n->child->string, &su, SCALE_VS); - break; - default: + } else su.scale = 0; - break; - } bufcat_su(h, "height", &su); PAIR_STYLE_INIT(&tag, h); @@ -377,35 +389,16 @@ man_br_pre(MAN_ARGS) static int man_SH_pre(MAN_ARGS) { - struct htmlpair tag[2]; - struct roffsu su; + struct htmlpair tag; - if (MAN_BODY == n->type) { - SCALE_HS_INIT(&su, INDENT); - bufcat_su(h, "margin-left", &su); - PAIR_CLASS_INIT(&tag[0], "sec-body"); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); + if (MAN_BLOCK == n->type) { + PAIR_CLASS_INIT(&tag, "section"); + print_otag(h, TAG_DIV, 1, &tag); return(1); - } else if (MAN_BLOCK == n->type) { - PAIR_CLASS_INIT(&tag[0], "sec-block"); - if (n->prev && MAN_SH == n->prev->tok) - if (NULL == n->prev->body->child) { - print_otag(h, TAG_DIV, 1, tag); - return(1); - } - - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - if (NULL == n->next) - bufcat_su(h, "margin-bottom", &su); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); + } else if (MAN_BODY == n->type) return(1); - } - PAIR_CLASS_INIT(&tag[0], "sec-head"); - print_otag(h, TAG_DIV, 1, tag); + print_otag(h, TAG_H1, 0, NULL); return(1); } @@ -415,29 +408,30 @@ static int man_alt_pre(MAN_ARGS) { const struct man_node *nn; - struct tag *t; - int i; - enum htmlfont fp; + int i; + enum htmltag fp; + struct tag *t; for (i = 0, nn = n->child; nn; nn = nn->next, i++) { + t = NULL; switch (n->tok) { case (MAN_BI): - fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_BOLD; + fp = i % 2 ? TAG_I : TAG_B; break; case (MAN_IB): - fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_ITALIC; + fp = i % 2 ? TAG_B : TAG_I; break; case (MAN_RI): - fp = i % 2 ? HTMLFONT_ITALIC : HTMLFONT_NONE; + fp = i % 2 ? TAG_I : TAG_MAX; break; case (MAN_IR): - fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_ITALIC; + fp = i % 2 ? TAG_MAX : TAG_I; break; case (MAN_BR): - fp = i % 2 ? HTMLFONT_NONE : HTMLFONT_BOLD; + fp = i % 2 ? TAG_MAX : TAG_B; break; case (MAN_RB): - fp = i % 2 ? HTMLFONT_BOLD : HTMLFONT_NONE; + fp = i % 2 ? TAG_B : TAG_MAX; break; default: abort(); @@ -447,41 +441,27 @@ man_alt_pre(MAN_ARGS) if (i) h->flags |= HTML_NOSPACE; - /* - * Open and close the scope with each argument, so that - * internal \f escapes, which are common, are also - * closed out with the scope. - */ - t = print_ofont(h, fp); - print_man_node(m, nn, h); - print_tagq(h, t); + if (TAG_MAX != fp) + t = print_otag(h, fp, 0, NULL); + + print_man_node(m, nn, mh, h); + + if (t) + print_tagq(h, t); } return(0); } -/* ARGSUSED */ -static int -man_SB_pre(MAN_ARGS) -{ - struct htmlpair tag; - - /* FIXME: print_ofont(). */ - PAIR_CLASS_INIT(&tag, "small bold"); - print_otag(h, TAG_SPAN, 1, &tag); - return(1); -} - - /* ARGSUSED */ static int man_SM_pre(MAN_ARGS) { - struct htmlpair tag; - PAIR_CLASS_INIT(&tag, "small"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_SMALL, 0, NULL); + if (MAN_SB == n->tok) + print_otag(h, TAG_B, 0, NULL); return(1); } @@ -490,41 +470,16 @@ man_SM_pre(MAN_ARGS) static int man_SS_pre(MAN_ARGS) { - struct htmlpair tag[3]; - struct roffsu su; + struct htmlpair tag; - SCALE_VS_INIT(&su, 1); - - if (MAN_BODY == n->type) { - PAIR_CLASS_INIT(&tag[0], "ssec-body"); - if (n->parent->next && n->child) { - bufcat_su(h, "margin-bottom", &su); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); - return(1); - } - - print_otag(h, TAG_DIV, 1, tag); + if (MAN_BLOCK == n->type) { + PAIR_CLASS_INIT(&tag, "subsection"); + print_otag(h, TAG_DIV, 1, &tag); return(1); - } else if (MAN_BLOCK == n->type) { - PAIR_CLASS_INIT(&tag[0], "ssec-block"); - if (n->prev && MAN_SS == n->prev->tok) - if (n->prev->body->child) { - bufcat_su(h, "margin-top", &su); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); - return(1); - } - - print_otag(h, TAG_DIV, 1, tag); + } else if (MAN_BODY == n->type) return(1); - } - SCALE_HS_INIT(&su, INDENT - HALFINDENT); - bufcat_su(h, "margin-left", &su); - PAIR_CLASS_INIT(&tag[0], "ssec-head"); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); + print_otag(h, TAG_H2, 0, NULL); return(1); } @@ -533,28 +488,12 @@ man_SS_pre(MAN_ARGS) static int man_PP_pre(MAN_ARGS) { - struct htmlpair tag; - struct roffsu su; - int i; - if (MAN_BLOCK != n->type) - return(1); + if (MAN_HEAD == n->type) + return(0); + else if (MAN_BODY == n->type && n->prev) + print_otag(h, TAG_P, 0, NULL); - i = 0; - - if (MAN_ROOT == n->parent->type) { - SCALE_HS_INIT(&su, INDENT); - bufcat_su(h, "margin-left", &su); - i = 1; - } - if (n->prev) { - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - i = 1; - } - - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, i, &tag); return(1); } @@ -566,7 +505,6 @@ man_IP_pre(MAN_ARGS) struct roffsu su; struct htmlpair tag; const struct man_node *nn; - int width; /* * This scattering of 1-BU margins and pads is to make sure that @@ -576,10 +514,7 @@ man_IP_pre(MAN_ARGS) */ if (MAN_BODY == n->type) { - SCALE_HS_INIT(&su, INDENT); - bufcat_su(h, "margin-left", &su); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); + print_otag(h, TAG_TD, 0, NULL); return(1); } @@ -587,16 +522,12 @@ man_IP_pre(MAN_ARGS) n->head->child : n->parent->head->child; SCALE_HS_INIT(&su, INDENT); - width = 0; - /* Width is the last token. */ + /* Width is the second token. */ if (MAN_IP == n->tok && NULL != nn) - if (NULL != (nn = nn->next)) { - for ( ; nn->next; nn = nn->next) - /* Do nothing. */ ; - width = a2width(nn, &su); - } + if (NULL != (nn = nn->next)) + a2width(nn, &su); /* Width is the first token. */ @@ -605,52 +536,34 @@ man_IP_pre(MAN_ARGS) while (nn && MAN_TEXT != nn->type) nn = nn->next; if (nn) - width = a2width(nn, &su); + a2width(nn, &su); } if (MAN_BLOCK == n->type) { - bufcat_su(h, "margin-left", &su); - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - bufcat_style(h, "clear", "both"); + 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_DIV, 1, &tag); + 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); } - bufcat_su(h, "min-width", &su); - SCALE_INVERT(&su); - bufcat_su(h, "margin-left", &su); - SCALE_HS_INIT(&su, 1); - bufcat_su(h, "margin-right", &su); - bufcat_style(h, "clear", "left"); + print_otag(h, TAG_TD, 0, NULL); - if (n->next && n->next->child) - bufcat_style(h, "float", "left"); + /* For IP, only print the first header element. */ - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); + if (MAN_IP == n->tok && n->child) + print_man_node(m, n->child, mh, h); - /* - * Without a length string, we can print all of our children. - */ + /* For TP, only print next-line header elements. */ - if ( ! width) - return(1); - - /* - * When a length has been specified, we need to carefully print - * our child context: IP gets all children printed but the last - * (the width), while TP gets all children printed but the first - * (the width). - */ - - if (MAN_IP == n->tok) - for (nn = n->child; nn->next; nn = nn->next) - print_man_node(m, nn, h); if (MAN_TP == n->tok) - for (nn = n->child->next; nn; nn = nn->next) - print_man_node(m, nn, h); + for (nn = n->child; nn; nn = nn->next) + if (nn->line > n->line) + print_man_node(m, nn, mh, h); return(0); } @@ -660,37 +573,36 @@ man_IP_pre(MAN_ARGS) static int man_HP_pre(MAN_ARGS) { - const struct man_node *nn; - struct htmlpair tag; - struct roffsu su; + struct htmlpair tag; + struct roffsu su; + const struct man_node *np; - if (MAN_HEAD == n->type) + np = MAN_BLOCK == n->type ? + n->head->child : + n->parent->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); - - nn = MAN_BLOCK == n->type ? - n->head->child : n->parent->head->child; - - SCALE_HS_INIT(&su, INDENT); - - if (NULL != nn) - (void)a2width(nn, &su); - - if (MAN_BLOCK == n->type) { - bufcat_su(h, "margin-left", &su); - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - bufcat_style(h, "clear", "both"); + } 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_DIV, 1, &tag); + 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); } - bufcat_su(h, "margin-left", &su); - SCALE_INVERT(&su); + su.scale = -su.scale; bufcat_su(h, "text-indent", &su); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); + print_otag(h, TAG_TD, 1, &tag); return(1); } @@ -700,7 +612,7 @@ static int man_B_pre(MAN_ARGS) { - print_ofont(h, HTMLFONT_BOLD); + print_otag(h, TAG_B, 0, NULL); return(1); } @@ -710,11 +622,36 @@ static int man_I_pre(MAN_ARGS) { - print_ofont(h, HTMLFONT_ITALIC); + print_otag(h, TAG_I, 0, NULL); return(1); } +/* ARGSUSED */ +static int +man_literal_pre(MAN_ARGS) +{ + + if (MAN_nf == n->tok) { + print_otag(h, TAG_BR, 0, NULL); + mh->fl |= MANH_LITERAL; + } else + mh->fl &= ~MANH_LITERAL; + + return(1); +} + + +/* ARGSUSED */ +static int +man_in_pre(MAN_ARGS) +{ + + print_otag(h, TAG_BR, 0, NULL); + return(0); +} + + /* ARGSUSED */ static int man_ign_pre(MAN_ARGS) @@ -737,14 +674,10 @@ man_RS_pre(MAN_ARGS) return(1); SCALE_HS_INIT(&su, INDENT); - bufcat_su(h, "margin-left", &su); - - if (n->head->child) { - SCALE_VS_INIT(&su, 1); + if (n->head->child) a2width(n->head->child, &su); - bufcat_su(h, "margin-top", &su); - } + bufcat_su(h, "margin-left", &su); PAIR_STYLE_INIT(&tag, h); print_otag(h, TAG_DIV, 1, &tag); return(1); diff --git a/commands/mdocml/man_macro.c b/usr.bin/mdocml/dist/man_macro.c similarity index 95% rename from commands/mdocml/man_macro.c rename to usr.bin/mdocml/dist/man_macro.c index 3e7a51b6a..fc9a80ba4 100644 --- a/commands/mdocml/man_macro.c +++ b/usr.bin/mdocml/dist/man_macro.c @@ -1,6 +1,6 @@ -/* $Id: man_macro.c,v 1.47 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: man_macro.c,v 1.54 2010/12/08 10:58:22 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -69,20 +69,17 @@ const struct man_macro __man_macros[MAN_MAX] = { { in_line_eoln, 0 }, /* IR */ { in_line_eoln, 0 }, /* RI */ { in_line_eoln, MAN_NSCOPED }, /* na */ - { in_line_eoln, 0 }, /* i */ { in_line_eoln, MAN_NSCOPED }, /* sp */ { in_line_eoln, 0 }, /* nf */ { in_line_eoln, 0 }, /* fi */ - { in_line_eoln, 0 }, /* r */ { blk_close, 0 }, /* RE */ { blk_exp, MAN_EXPLICIT }, /* RS */ { in_line_eoln, 0 }, /* DT */ { in_line_eoln, 0 }, /* UC */ { in_line_eoln, 0 }, /* PD */ - { in_line_eoln, MAN_NSCOPED }, /* Sp */ - { in_line_eoln, 0 }, /* Vb */ - { in_line_eoln, 0 }, /* Ve */ { in_line_eoln, 0 }, /* AT */ + { in_line_eoln, 0 }, /* in */ + { in_line_eoln, 0 }, /* ft */ }; const struct man_macro * const man_macros = __man_macros; @@ -122,8 +119,6 @@ man_unscope(struct man *m, const struct man_node *n, return(0); if ( ! man_valid_post(m)) return(0); - if ( ! man_action_post(m)) - return(0); m->last = m->last->parent; assert(m->last); } @@ -132,8 +127,6 @@ man_unscope(struct man *m, const struct man_node *n, return(0); if ( ! man_valid_post(m)) return(0); - if ( ! man_action_post(m)) - return(0); m->next = MAN_ROOT == m->last->type ? MAN_NEXT_CHILD : MAN_NEXT_SIBLING; @@ -258,7 +251,7 @@ rew_scope(enum man_type type, struct man *m, enum mant tok) * Close out a generic explicit macro. */ /* ARGSUSED */ -static int +int blk_close(MACRO_PROT_ARGS) { enum mant ntok; @@ -290,7 +283,8 @@ blk_close(MACRO_PROT_ARGS) } -static int +/* ARGSUSED */ +int blk_exp(MACRO_PROT_ARGS) { int w, la; @@ -341,7 +335,8 @@ blk_exp(MACRO_PROT_ARGS) * scopes, such as `SH' closing out an `SS', are defined in the rew * routines. */ -static int +/* ARGSUSED */ +int blk_imp(MACRO_PROT_ARGS) { int w, la; @@ -398,7 +393,8 @@ blk_imp(MACRO_PROT_ARGS) } -static int +/* ARGSUSED */ +int in_line_eoln(MACRO_PROT_ARGS) { int w, la; @@ -454,8 +450,6 @@ in_line_eoln(MACRO_PROT_ARGS) break; if ( ! man_valid_post(m)) return(0); - if ( ! man_action_post(m)) - return(0); } assert(m->last); @@ -466,8 +460,6 @@ in_line_eoln(MACRO_PROT_ARGS) if (m->last->type != MAN_ROOT && ! man_valid_post(m)) return(0); - if (m->last->type != MAN_ROOT && ! man_action_post(m)) - return(0); m->next = MAN_ROOT == m->last->type ? MAN_NEXT_CHILD : MAN_NEXT_SIBLING; diff --git a/commands/mdocml/man_term.c b/usr.bin/mdocml/dist/man_term.c similarity index 73% rename from commands/mdocml/man_term.c rename to usr.bin/mdocml/dist/man_term.c index db2e9cc4a..dd479885c 100644 --- a/commands/mdocml/man_term.c +++ b/usr.bin/mdocml/dist/man_term.c @@ -1,6 +1,7 @@ -/* $Id: man_term.c,v 1.76 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: man_term.c,v 1.94 2011/01/04 01:23:18 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2010, 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 @@ -70,8 +71,8 @@ struct termact { #define MAN_NOTEXT (1 << 0) /* Never has text children. */ }; -static int a2width(const struct man_node *); -static int a2height(const struct man_node *); +static int a2width(const struct termp *, const char *); +static size_t a2height(const struct termp *, const char *); static void print_man_nodelist(DECL_ARGS); static void print_man_node(DECL_ARGS); @@ -80,23 +81,21 @@ 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_BI(DECL_ARGS); static int pre_HP(DECL_ARGS); static int pre_I(DECL_ARGS); static int pre_IP(DECL_ARGS); static int pre_PP(DECL_ARGS); -static int pre_RB(DECL_ARGS); -static int pre_RI(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_br(DECL_ARGS); -static int pre_fi(DECL_ARGS); static int pre_ign(DECL_ARGS); -static int pre_nf(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); @@ -106,7 +105,7 @@ static void post_SS(DECL_ARGS); static void post_TP(DECL_ARGS); static const struct termact termacts[MAN_MAX] = { - { pre_br, NULL, MAN_NOTEXT }, /* br */ + { pre_sp, NULL, MAN_NOTEXT }, /* br */ { NULL, NULL, 0 }, /* TH */ { pre_SH, post_SH, 0 }, /* SH */ { pre_SS, post_SS, 0 }, /* SS */ @@ -118,30 +117,27 @@ static const struct termact termacts[MAN_MAX] = { { pre_HP, post_HP, 0 }, /* HP */ { NULL, NULL, 0 }, /* SM */ { pre_B, NULL, 0 }, /* SB */ - { pre_BI, NULL, 0 }, /* BI */ - { pre_BI, NULL, 0 }, /* IB */ - { pre_RB, NULL, 0 }, /* BR */ - { pre_RB, NULL, 0 }, /* RB */ + { pre_alternate, NULL, 0 }, /* BI */ + { pre_alternate, NULL, 0 }, /* IB */ + { pre_alternate, NULL, 0 }, /* BR */ + { pre_alternate, NULL, 0 }, /* RB */ { NULL, NULL, 0 }, /* R */ { pre_B, NULL, 0 }, /* B */ { pre_I, NULL, 0 }, /* I */ - { pre_RI, NULL, 0 }, /* IR */ - { pre_RI, NULL, 0 }, /* RI */ + { pre_alternate, NULL, 0 }, /* IR */ + { pre_alternate, NULL, 0 }, /* RI */ { NULL, NULL, MAN_NOTEXT }, /* na */ - { pre_I, NULL, 0 }, /* i */ { pre_sp, NULL, MAN_NOTEXT }, /* sp */ - { pre_nf, NULL, 0 }, /* nf */ - { pre_fi, NULL, 0 }, /* fi */ - { NULL, NULL, 0 }, /* r */ + { pre_literal, NULL, 0 }, /* nf */ + { pre_literal, NULL, 0 }, /* fi */ { NULL, NULL, 0 }, /* RE */ { pre_RS, post_RS, 0 }, /* RS */ { pre_ign, NULL, 0 }, /* DT */ { pre_ign, NULL, 0 }, /* UC */ { pre_ign, NULL, 0 }, /* PD */ - { pre_sp, NULL, MAN_NOTEXT }, /* Sp */ - { pre_nf, NULL, 0 }, /* Vb */ - { pre_fi, NULL, 0 }, /* Ve */ { pre_ign, NULL, 0 }, /* AT */ + { pre_in, NULL, MAN_NOTEXT }, /* in */ + { pre_ft, NULL, MAN_NOTEXT }, /* ft */ }; @@ -158,7 +154,7 @@ terminal_man(void *arg, const struct man *man) p->overstep = 0; p->maxrmargin = p->defrmargin; - p->tabwidth = 5; + p->tabwidth = term_len(p, 5); if (NULL == p->symtab) switch (p->enc) { @@ -177,8 +173,8 @@ terminal_man(void *arg, const struct man *man) p->flags |= TERMP_NOSPACE; mt.fl = 0; - mt.lmargin = INDENT; - mt.offset = INDENT; + mt.lmargin = term_len(p, INDENT); + mt.offset = term_len(p, INDENT); if (n->child) print_man_nodelist(p, &mt, n->child, m); @@ -187,31 +183,27 @@ terminal_man(void *arg, const struct man *man) } -static int -a2height(const struct man_node *n) +static size_t +a2height(const struct termp *p, const char *cp) { struct roffsu su; - assert(MAN_TEXT == n->type); - assert(n->string); - if ( ! a2roffsu(n->string, &su, SCALE_VS)) - SCALE_VS_INIT(&su, strlen(n->string)); + if ( ! a2roffsu(cp, &su, SCALE_VS)) + SCALE_VS_INIT(&su, term_strlen(p, cp)); - return((int)term_vspan(&su)); + return(term_vspan(p, &su)); } static int -a2width(const struct man_node *n) +a2width(const struct termp *p, const char *cp) { struct roffsu su; - assert(MAN_TEXT == n->type); - assert(n->string); - if ( ! a2roffsu(n->string, &su, SCALE_BU)) + if ( ! a2roffsu(cp, &su, SCALE_BU)) return(-1); - return((int)term_hspan(&su)); + return((int)term_hspan(p, &su)); } @@ -253,98 +245,71 @@ pre_I(DECL_ARGS) /* ARGSUSED */ static int -pre_fi(DECL_ARGS) +pre_literal(DECL_ARGS) { - mt->fl &= ~MANT_LITERAL; + term_newln(p); + + if (MAN_nf == n->tok) + mt->fl |= MANT_LITERAL; + else + mt->fl &= ~MANT_LITERAL; + return(1); } - /* ARGSUSED */ static int -pre_nf(DECL_ARGS) -{ - - mt->fl |= MANT_LITERAL; - return(MAN_Vb != n->tok); -} - - -/* ARGSUSED */ -static int -pre_RB(DECL_ARGS) -{ - const struct man_node *nn; - int i; - - for (i = 0, nn = n->child; nn; nn = nn->next, i++) { - if (i % 2 && MAN_RB == n->tok) - term_fontrepl(p, TERMFONT_BOLD); - else if ( ! (i % 2) && MAN_RB != n->tok) - term_fontrepl(p, TERMFONT_BOLD); - else - term_fontrepl(p, TERMFONT_NONE); - - if (i > 0) - p->flags |= TERMP_NOSPACE; - - print_man_node(p, mt, nn, m); - } - return(0); -} - - -/* ARGSUSED */ -static int -pre_RI(DECL_ARGS) -{ - const struct man_node *nn; - int i; - - for (i = 0, nn = n->child; nn; nn = nn->next, i++) { - if (i % 2 && MAN_RI == n->tok) - term_fontrepl(p, TERMFONT_UNDER); - else if ( ! (i % 2) && MAN_RI != n->tok) - term_fontrepl(p, TERMFONT_UNDER); - else - term_fontrepl(p, TERMFONT_NONE); - - if (i > 0) - p->flags |= TERMP_NOSPACE; - - print_man_node(p, mt, nn, m); - } - return(0); -} - - -/* ARGSUSED */ -static int -pre_BI(DECL_ARGS) +pre_alternate(DECL_ARGS) { + enum termfont font[2]; const struct man_node *nn; - int i; + int savelit, i; - for (i = 0, nn = n->child; nn; nn = nn->next, i++) { - if (i % 2 && MAN_BI == n->tok) - term_fontrepl(p, TERMFONT_UNDER); - else if (i % 2) - term_fontrepl(p, TERMFONT_BOLD); - else if (MAN_BI == n->tok) - term_fontrepl(p, TERMFONT_BOLD); - else - term_fontrepl(p, TERMFONT_UNDER); - - if (i) - p->flags |= TERMP_NOSPACE; - - print_man_node(p, mt, nn, m); + switch (n->tok) { + case (MAN_RB): + font[0] = TERMFONT_NONE; + font[1] = TERMFONT_BOLD; + break; + case (MAN_RI): + font[0] = TERMFONT_NONE; + font[1] = TERMFONT_UNDER; + break; + case (MAN_BR): + font[0] = TERMFONT_BOLD; + font[1] = TERMFONT_NONE; + break; + case (MAN_BI): + font[0] = TERMFONT_BOLD; + font[1] = TERMFONT_UNDER; + break; + case (MAN_IR): + font[0] = TERMFONT_UNDER; + font[1] = TERMFONT_NONE; + break; + case (MAN_IB): + font[0] = TERMFONT_UNDER; + font[1] = TERMFONT_BOLD; + break; + default: + abort(); } + + savelit = MANT_LITERAL & mt->fl; + mt->fl &= ~MANT_LITERAL; + + for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) { + term_fontrepl(p, font[i]); + if (savelit && NULL == nn->next) + mt->fl |= MANT_LITERAL; + print_man_node(p, mt, nn, m); + if (nn->next) + p->flags |= TERMP_NOSPACE; + } + return(0); } - /* ARGSUSED */ static int pre_B(DECL_ARGS) @@ -354,19 +319,83 @@ pre_B(DECL_ARGS) return(1); } +/* ARGSUSED */ +static int +pre_ft(DECL_ARGS) +{ + const char *cp; + + if (NULL == n->child) { + term_fontlast(p); + return(0); + } + + cp = n->child->string; + switch (*cp) { + case ('4'): + /* FALLTHROUGH */ + case ('3'): + /* FALLTHROUGH */ + case ('B'): + term_fontrepl(p, TERMFONT_BOLD); + break; + case ('2'): + /* FALLTHROUGH */ + case ('I'): + term_fontrepl(p, TERMFONT_UNDER); + break; + case ('P'): + term_fontlast(p); + break; + case ('1'): + /* FALLTHROUGH */ + case ('C'): + /* FALLTHROUGH */ + case ('R'): + term_fontrepl(p, TERMFONT_NONE); + break; + default: + break; + } + return(0); +} /* ARGSUSED */ static int -pre_sp(DECL_ARGS) +pre_in(DECL_ARGS) { - int i, len; + int len, less; + size_t v; + const char *cp; - len = n->child ? a2height(n->child) : 1; + term_newln(p); - if (0 == len) - term_newln(p); - for (i = 0; i <= len; i++) - term_vspace(p); + if (NULL == n->child) { + p->offset = mt->offset; + return(0); + } + + cp = n->child->string; + less = 0; + + if ('-' == *cp) + less = -1; + else if ('+' == *cp) + less = 1; + else + cp--; + + if ((len = a2width(p, ++cp)) < 0) + return(0); + + v = (size_t)len; + + if (less < 0) + p->offset -= p->offset > v ? v : p->offset; + else if (less > 0) + p->offset += v; + else + p->offset = v; return(0); } @@ -374,10 +403,24 @@ pre_sp(DECL_ARGS) /* ARGSUSED */ static int -pre_br(DECL_ARGS) +pre_sp(DECL_ARGS) { + size_t i, len; + + switch (n->tok) { + case (MAN_br): + len = 0; + break; + default: + len = n->child ? a2height(p, n->child->string) : 1; + break; + } + + if (0 == len) + term_newln(p); + for (i = 0; i < len; i++) + term_vspace(p); - term_newln(p); return(0); } @@ -408,11 +451,11 @@ pre_HP(DECL_ARGS) /* Calculate offset. */ if (NULL != (nn = n->parent->head->child)) - if ((ival = a2width(nn)) >= 0) + if ((ival = a2width(p, nn->string)) >= 0) len = (size_t)ival; if (0 == len) - len = 1; + len = term_len(p, 1); p->offset = mt->offset; p->rmargin = mt->offset + len; @@ -453,7 +496,7 @@ pre_PP(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->lmargin = INDENT; + mt->lmargin = term_len(p, INDENT); print_bvspace(p, n); break; default: @@ -461,7 +504,7 @@ pre_PP(DECL_ARGS) break; } - return(1); + return(MAN_HEAD != n->type); } @@ -471,7 +514,7 @@ pre_IP(DECL_ARGS) { const struct man_node *nn; size_t len; - int ival; + int savelit, ival; switch (n->type) { case (MAN_BODY): @@ -491,21 +534,17 @@ pre_IP(DECL_ARGS) len = mt->lmargin; ival = -1; - /* Calculate offset. */ - + /* Calculate the offset from the optional second argument. */ if (NULL != (nn = n->parent->head->child)) - if (NULL != (nn = nn->next)) { - for ( ; nn->next; nn = nn->next) - /* Do nothing. */ ; - if ((ival = a2width(nn)) >= 0) + if (NULL != (nn = nn->next)) + if ((ival = a2width(p, nn->string)) >= 0) len = (size_t)ival; - } switch (n->type) { case (MAN_HEAD): /* Handle zero-width lengths. */ if (0 == len) - len = 1; + len = term_len(p, 1); p->offset = mt->offset; p->rmargin = mt->offset + len; @@ -515,9 +554,15 @@ pre_IP(DECL_ARGS) /* Set the saved left-margin. */ mt->lmargin = (size_t)ival; - /* Don't print the length value. */ - for (nn = n->child; nn->next; nn = nn->next) - print_man_node(p, mt, nn, m); + savelit = MANT_LITERAL & mt->fl; + mt->fl &= ~MANT_LITERAL; + + if (n->child) + print_man_node(p, mt, n->child, m); + + if (savelit) + mt->fl |= MANT_LITERAL; + return(0); case (MAN_BODY): p->offset = mt->offset + len; @@ -543,7 +588,7 @@ post_IP(DECL_ARGS) p->rmargin = p->maxrmargin; break; case (MAN_BODY): - term_flushln(p); + term_newln(p); p->flags &= ~TERMP_NOLPAD; break; default: @@ -558,12 +603,11 @@ pre_TP(DECL_ARGS) { const struct man_node *nn; size_t len; - int ival; + int savelit, ival; switch (n->type) { case (MAN_HEAD): p->flags |= TERMP_NOBREAK; - p->flags |= TERMP_TWOSPACE; break; case (MAN_BODY): p->flags |= TERMP_NOLPAD; @@ -585,7 +629,7 @@ pre_TP(DECL_ARGS) while (nn && MAN_TEXT != nn->type) nn = nn->next; if (nn && nn->next) - if ((ival = a2width(nn)) >= 0) + if ((ival = a2width(p, nn->string)) >= 0) len = (size_t)ival; } @@ -593,16 +637,22 @@ pre_TP(DECL_ARGS) case (MAN_HEAD): /* Handle zero-length properly. */ if (0 == len) - len = 1; + len = term_len(p, 1); p->offset = mt->offset; p->rmargin = mt->offset + len; + savelit = MANT_LITERAL & mt->fl; + mt->fl &= ~MANT_LITERAL; + /* Don't print same-line elements. */ - for (nn = n->child; nn; nn = nn->next) + for (nn = n->child; nn; nn = nn->next) if (nn->line > n->line) print_man_node(p, mt, nn, m); + if (savelit) + mt->fl |= MANT_LITERAL; + if (ival >= 0) mt->lmargin = (size_t)ival; @@ -632,7 +682,7 @@ post_TP(DECL_ARGS) p->rmargin = p->maxrmargin; break; case (MAN_BODY): - term_flushln(p); + term_newln(p); p->flags &= ~TERMP_NOLPAD; break; default: @@ -648,8 +698,8 @@ pre_SS(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->lmargin = INDENT; - mt->offset = INDENT; + mt->lmargin = term_len(p, INDENT); + mt->offset = term_len(p, INDENT); /* If following a prior empty `SS', no vspace. */ if (n->prev && MAN_SS == n->prev->tok) if (NULL == n->prev->body->child) @@ -660,7 +710,7 @@ pre_SS(DECL_ARGS) break; case (MAN_HEAD): term_fontrepl(p, TERMFONT_BOLD); - p->offset = HALFINDENT; + p->offset = term_len(p, HALFINDENT); break; case (MAN_BODY): p->offset = mt->offset; @@ -698,8 +748,8 @@ pre_SH(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->lmargin = INDENT; - mt->offset = INDENT; + mt->lmargin = term_len(p, INDENT); + mt->offset = term_len(p, INDENT); /* If following a prior empty `SH', no vspace. */ if (n->prev && MAN_SH == n->prev->tok) if (NULL == n->prev->body->child) @@ -760,15 +810,15 @@ pre_RS(DECL_ARGS) } if (NULL == (nn = n->parent->head->child)) { - mt->offset = mt->lmargin + INDENT; + mt->offset = mt->lmargin + term_len(p, INDENT); p->offset = mt->offset; return(1); } - if ((ival = a2width(nn)) < 0) + if ((ival = a2width(p, nn->string)) < 0) return(1); - mt->offset = INDENT + (size_t)ival; + mt->offset = term_len(p, INDENT) + (size_t)ival; p->offset = mt->offset; return(1); @@ -782,13 +832,13 @@ post_RS(DECL_ARGS) switch (n->type) { case (MAN_BLOCK): - mt->offset = mt->lmargin = INDENT; + mt->offset = mt->lmargin = term_len(p, INDENT); break; case (MAN_HEAD): break; default: term_newln(p); - p->offset = INDENT; + p->offset = term_len(p, INDENT); break; } } @@ -819,10 +869,16 @@ 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; } break; + case (MAN_TBL): + if (TBL_SPAN_FIRST & n->span->flags) + term_newln(p); + term_tbl(p, n->span); + break; default: if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) term_fontrepl(p, TERMFONT_NONE); @@ -834,11 +890,17 @@ print_man_node(DECL_ARGS) if (c && n->child) print_man_nodelist(p, mt, n->child, m); - if (MAN_TEXT != n->type) { + switch (n->type) { + case (MAN_TEXT): + /* FALLTHROUGH */ + case (MAN_TBL): + break; + default: if (termacts[n->tok].post) (*termacts[n->tok].post)(p, mt, n, m); if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) term_fontrepl(p, TERMFONT_NONE); + break; } if (MAN_EOS & n->flags) @@ -877,9 +939,13 @@ print_man_foot(struct termp *p, const void *arg) term_vspace(p); p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; - p->rmargin = p->maxrmargin - strlen(buf); + p->rmargin = p->maxrmargin - term_strlen(p, buf); p->offset = 0; + /* term_strlen() can return zero. */ + if (p->rmargin == p->maxrmargin) + p->rmargin--; + if (meta->source) term_word(p, meta->source); if (meta->source) @@ -918,14 +984,15 @@ print_man_head(struct termp *p, const void *arg) if (m->vol) strlcpy(buf, m->vol, BUFSIZ); - buflen = strlen(buf); + buflen = term_strlen(p, buf); snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); - titlen = strlen(title); + titlen = term_strlen(p, title); p->offset = 0; p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ? - (p->maxrmargin - strlen(buf) + 1) / 2 : + (p->maxrmargin - + term_strlen(p, buf) + term_len(p, 1)) / 2 : p->maxrmargin - buflen; p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; diff --git a/usr.bin/mdocml/dist/man_validate.c b/usr.bin/mdocml/dist/man_validate.c new file mode 100644 index 000000000..095ee67df --- /dev/null +++ b/usr.bin/mdocml/dist/man_validate.c @@ -0,0 +1,577 @@ +/* $Vendor-Id: man_validate.c,v 1.57 2011/01/01 12:59:17 kristaps Exp $ */ +/* + * Copyright (c) 2008, 2009, 2010 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 "mandoc.h" +#include "libman.h" +#include "libmandoc.h" + +#define CHKARGS struct man *m, struct man_node *n + +typedef int (*v_check)(CHKARGS); + +struct man_valid { + v_check *pres; + v_check *posts; +}; + +static int check_bline(CHKARGS); +static int check_eq0(CHKARGS); +static int check_ft(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 int check_text(CHKARGS); +static int check_title(CHKARGS); + +static int post_AT(CHKARGS); +static int post_fi(CHKARGS); +static int post_nf(CHKARGS); +static int post_TH(CHKARGS); +static int post_UC(CHKARGS); + +static v_check posts_at[] = { post_AT, NULL }; +static v_check posts_eq0[] = { check_eq0, 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_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_th[] = { check_ge2, check_le5, check_title, post_TH, NULL }; +static v_check posts_uc[] = { post_UC, NULL }; +static v_check pres_bline[] = { check_bline, 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, posts_par }, /* TP */ + { pres_bline, posts_par }, /* LP */ + { pres_bline, posts_par }, /* PP */ + { pres_bline, posts_par }, /* P */ + { pres_bline, posts_par }, /* IP */ + { pres_bline, posts_par }, /* HP */ + { NULL, NULL }, /* SM */ + { NULL, NULL }, /* SB */ + { NULL, NULL }, /* BI */ + { NULL, NULL }, /* IB */ + { NULL, NULL }, /* BR */ + { NULL, NULL }, /* RB */ + { NULL, NULL }, /* R */ + { NULL, NULL }, /* B */ + { 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, NULL }, /* RE */ + { NULL, posts_part }, /* RS */ + { NULL, NULL }, /* DT */ + { NULL, posts_uc }, /* UC */ + { NULL, NULL }, /* PD */ + { NULL, posts_at }, /* AT */ + { NULL, NULL }, /* in */ + { NULL, posts_ft }, /* ft */ +}; + + +int +man_valid_pre(struct man *m, struct man_node *n) +{ + v_check *cp; + + switch (n->type) { + case (MAN_TEXT): + /* FALLTHROUGH */ + case (MAN_ROOT): + /* FALLTHROUGH */ + case (MAN_TBL): + return(1); + default: + break; + } + + if (NULL == (cp = man_valids[n->tok].pres)) + return(1); + for ( ; *cp; cp++) + if ( ! (*cp)(m, n)) + return(0); + return(1); +} + + +int +man_valid_post(struct man *m) +{ + v_check *cp; + + if (MAN_VALID & m->last->flags) + return(1); + m->last->flags |= MAN_VALID; + + switch (m->last->type) { + case (MAN_TEXT): + return(check_text(m, m->last)); + case (MAN_ROOT): + return(check_root(m, m->last)); + case (MAN_TBL): + return(1); + default: + break; + } + + if (NULL == (cp = man_valids[m->last->tok].posts)) + return(1); + for ( ; *cp; cp++) + if ( ! (*cp)(m, m->last)) + return(0); + + return(1); +} + + +static int +check_root(CHKARGS) +{ + + if (MAN_BLINE & m->flags) + man_nmsg(m, n, MANDOCERR_SCOPEEXIT); + else if (MAN_ELINE & m->flags) + man_nmsg(m, n, MANDOCERR_SCOPEEXIT); + + m->flags &= ~MAN_BLINE; + m->flags &= ~MAN_ELINE; + + if (NULL == m->first->child) { + man_nmsg(m, n, MANDOCERR_NODOCBODY); + return(0); + } else if (NULL == m->meta.title) { + man_nmsg(m, n, MANDOCERR_NOTITLE); + + /* + * If a title hasn't been set, do so now (by + * implication, date and section also aren't set). + */ + + m->meta.title = mandoc_strdup("unknown"); + m->meta.date = time(NULL); + m->meta.msec = mandoc_strdup("1"); + } + + return(1); +} + + +static int +check_title(CHKARGS) +{ + const char *p; + + assert(n->child); + /* FIXME: is this sufficient? */ + if ('\0' == *n->child->string) { + man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT); + return(0); + } + + for (p = n->child->string; '\0' != *p; p++) + /* Only warn about this once... */ + if (isalpha((u_char)*p) && ! isupper((u_char)*p)) { + man_nmsg(m, n, MANDOCERR_UPPERCASE); + break; + } + + return(1); +} + + +static int +check_text(CHKARGS) +{ + char *p; + int pos, c; + size_t sz; + + for (p = n->string, pos = n->pos + 1; *p; p++, pos++) { + sz = strcspn(p, "\t\\"); + p += (int)sz; + + if ('\0' == *p) + break; + + pos += (int)sz; + + if ('\t' == *p) { + if (MAN_LITERAL & m->flags) + continue; + if (man_pmsg(m, n->line, pos, MANDOCERR_BADTAB)) + continue; + return(0); + } + + /* Check the special character. */ + + c = mandoc_special(p); + if (c) { + p += c - 1; + pos += c - 1; + } else + man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE); + } + + return(1); +} + + +#define INEQ_DEFINE(x, ineq, name) \ +static int \ +check_##name(CHKARGS) \ +{ \ + if (n->nchild ineq (x)) \ + return(1); \ + man_vmsg(m, MANDOCERR_SYNTARGCOUNT, n->line, n->pos, \ + "line arguments %s %d (have %d)", \ + #ineq, (x), n->nchild); \ + return(0); \ +} + +INEQ_DEFINE(0, ==, eq0) +INEQ_DEFINE(1, <=, le1) +INEQ_DEFINE(2, >=, ge2) +INEQ_DEFINE(5, <=, le5) + +static int +check_ft(CHKARGS) +{ + char *cp; + int ok; + + if (0 == n->nchild) + return(1); + + ok = 0; + cp = n->child->string; + switch (*cp) { + case ('1'): + /* FALLTHROUGH */ + case ('2'): + /* FALLTHROUGH */ + case ('3'): + /* FALLTHROUGH */ + case ('4'): + /* FALLTHROUGH */ + case ('I'): + /* FALLTHROUGH */ + case ('P'): + /* FALLTHROUGH */ + case ('R'): + if ('\0' == cp[1]) + ok = 1; + break; + case ('B'): + if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2])) + ok = 1; + break; + case ('C'): + if ('W' == cp[1] && '\0' == cp[2]) + ok = 1; + break; + default: + break; + } + + if (0 == ok) { + man_vmsg(m, MANDOCERR_BADFONT, + n->line, n->pos, "%s", cp); + *cp = '\0'; + } + + if (1 < n->nchild) + man_vmsg(m, MANDOCERR_ARGCOUNT, n->line, n->pos, + "want one child (have %d)", n->nchild); + + return(1); +} + +static int +check_sec(CHKARGS) +{ + + if (MAN_HEAD == n->type && 0 == n->nchild) { + man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT); + return(0); + } else if (MAN_BODY == n->type && 0 == n->nchild) + man_nmsg(m, n, MANDOCERR_NOBODY); + + return(1); +} + + +static int +check_part(CHKARGS) +{ + + if (MAN_BODY == n->type && 0 == n->nchild) + man_nmsg(m, n, MANDOCERR_NOBODY); + + return(1); +} + + +static int +check_par(CHKARGS) +{ + + if (MAN_BODY == n->type) + switch (n->tok) { + case (MAN_IP): + /* FALLTHROUGH */ + case (MAN_HP): + /* FALLTHROUGH */ + case (MAN_TP): + /* Body-less lists are ok. */ + break; + default: + if (0 == n->nchild) + man_nmsg(m, n, MANDOCERR_NOBODY); + break; + } + if (MAN_HEAD == n->type) + switch (n->tok) { + case (MAN_PP): + /* FALLTHROUGH */ + case (MAN_P): + /* FALLTHROUGH */ + case (MAN_LP): + if (n->nchild) + man_nmsg(m, n, MANDOCERR_ARGSLOST); + break; + default: + break; + } + + return(1); +} + + +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) +{ + + if (m->meta.title) + free(m->meta.title); + if (m->meta.vol) + free(m->meta.vol); + if (m->meta.source) + free(m->meta.source); + if (m->meta.msec) + free(m->meta.msec); + if (m->meta.rawdate) + free(m->meta.rawdate); + + m->meta.title = m->meta.vol = m->meta.rawdate = + m->meta.msec = m->meta.source = NULL; + m->meta.date = 0; + + /* ->TITLE<- MSEC DATE SOURCE VOL */ + + n = n->child; + assert(n); + m->meta.title = mandoc_strdup(n->string); + + /* TITLE ->MSEC<- DATE SOURCE VOL */ + + n = n->next; + assert(n); + m->meta.msec = mandoc_strdup(n->string); + + /* TITLE MSEC ->DATE<- SOURCE VOL */ + + /* + * Try to parse the date. If this works, stash the epoch (this + * is optimal because we can reformat it in the canonical form). + * If it doesn't parse, isn't specified at all, or is an empty + * string, then use the current date. + */ + + n = n->next; + if (n && n->string && *n->string) { + m->meta.date = mandoc_a2time + (MTIME_ISO_8601, n->string); + if (0 == m->meta.date) { + man_nmsg(m, n, MANDOCERR_BADDATE); + m->meta.rawdate = mandoc_strdup(n->string); + } + } else + m->meta.date = time(NULL); + + /* TITLE MSEC DATE ->SOURCE<- VOL */ + + if (n && (n = n->next)) + m->meta.source = mandoc_strdup(n->string); + + /* TITLE MSEC DATE SOURCE ->VOL<- */ + + if (n && (n = n->next)) + m->meta.vol = mandoc_strdup(n->string); + + /* + * Remove the `TH' node after we've processed it for our + * meta-data. + */ + man_node_delete(m, m->last); + return(1); +} + +static int +post_nf(CHKARGS) +{ + + if (MAN_LITERAL & m->flags) + man_nmsg(m, n, MANDOCERR_SCOPEREP); + + m->flags |= MAN_LITERAL; + return(1); +} + +static int +post_fi(CHKARGS) +{ + + if ( ! (MAN_LITERAL & m->flags)) + man_nmsg(m, n, MANDOCERR_NOSCOPE); + + m->flags &= ~MAN_LITERAL; + return(1); +} + +static int +post_UC(CHKARGS) +{ + static const char * const bsd_versions[] = { + "3rd Berkeley Distribution", + "4th Berkeley Distribution", + "4.2 Berkeley Distribution", + "4.3 Berkeley Distribution", + "4.4 Berkeley Distribution", + }; + + const char *p, *s; + + n = n->child; + n = m->last->child; + + if (NULL == n || MAN_TEXT != n->type) + p = bsd_versions[0]; + else { + s = n->string; + if (0 == strcmp(s, "3")) + p = bsd_versions[0]; + else if (0 == strcmp(s, "4")) + p = bsd_versions[1]; + else if (0 == strcmp(s, "5")) + p = bsd_versions[2]; + else if (0 == strcmp(s, "6")) + p = bsd_versions[3]; + else if (0 == strcmp(s, "7")) + p = bsd_versions[4]; + else + p = bsd_versions[0]; + } + + if (m->meta.source) + free(m->meta.source); + + m->meta.source = mandoc_strdup(p); + return(1); +} + +static int +post_AT(CHKARGS) +{ + static const char * const unix_versions[] = { + "7th Edition", + "System III", + "System V", + "System V Release 2", + }; + + const char *p, *s; + struct man_node *nn; + + n = n->child; + + if (NULL == n || MAN_TEXT != n->type) + p = unix_versions[0]; + else { + s = n->string; + if (0 == strcmp(s, "3")) + p = unix_versions[0]; + else if (0 == strcmp(s, "4")) + p = unix_versions[1]; + else if (0 == strcmp(s, "5")) { + nn = n->next; + if (nn && MAN_TEXT == nn->type && nn->string[0]) + p = unix_versions[3]; + else + p = unix_versions[2]; + } else + p = unix_versions[0]; + } + + if (m->meta.source) + free(m->meta.source); + + m->meta.source = mandoc_strdup(p); + return(1); +} diff --git a/usr.bin/mdocml/dist/mandoc b/usr.bin/mdocml/dist/mandoc new file mode 100755 index 000000000..b62a20917 Binary files /dev/null and b/usr.bin/mdocml/dist/mandoc differ diff --git a/commands/mdocml/mandoc.1 b/usr.bin/mdocml/dist/mandoc.1 similarity index 57% rename from commands/mdocml/mandoc.1 rename to usr.bin/mdocml/dist/mandoc.1 index fb22e8d73..4a16a965e 100644 --- a/commands/mdocml/mandoc.1 +++ b/usr.bin/mdocml/dist/mandoc.1 @@ -1,6 +1,6 @@ -.\" $Id: mandoc.1,v 1.63 2010/06/11 07:15:42 kristaps Exp $ +.\" $Vendor-Id: mandoc.1,v 1.84 2011/01/04 23:32:21 kristaps Exp $ .\" -.\" Copyright (c) 2009 Kristaps Dzonsons +.\" Copyright (c) 2009, 2010 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: June 11 2010 $ +.Dd January 4, 2011 .Dt MANDOC 1 .Os .Sh NAME @@ -23,11 +23,10 @@ .Sh SYNOPSIS .Nm mandoc .Op Fl V -.Op Fl f Ns Ar option .Op Fl m Ns Ar format .Op Fl O Ns Ar option .Op Fl T Ns Ar output -.Op Fl W Ns Ar err +.Op Fl W Ns Ar level .Op Ar file... .Sh DESCRIPTION The @@ -37,11 +36,6 @@ utility formats manual pages for display. The arguments are as follows: .Bl -tag -width Ds -.It Fl f Ns Ar option -Comma-separated compiler options. -See -.Sx Compiler Options -for details. .It Fl m Ns Ar format Input format. See @@ -51,9 +45,6 @@ Defaults to .Fl m Ns Cm andoc . .It Fl O Ns Ar option Comma-separated output options. -See -.Sx Output Options -for details. .It Fl T Ns Ar output Output format. See @@ -63,18 +54,41 @@ Defaults to .Fl T Ns Cm ascii . .It Fl V Print version and exit. -.It Fl W Ns Ar err -Comma-separated warning options. -Use +.It Fl W Ns Ar level +Specify the minimum message +.Ar level +to be reported on the standard error output and to affect the exit status. +The +.Ar level +can be +.Cm warning , +.Cm error , +or +.Cm fatal . +The default is +.Fl W Ns Cm fatal ; .Fl W Ns Cm all -to print warnings, -.Fl W Ns Cm error -for warnings to be considered errors and cause utility -termination. -Multiple -.Fl W -arguments may be comma-separated, such as -.Fl W Ns Cm error , Ns Cm all . +is an alias for +.Fl W Ns Cm warning . +See +.Sx EXIT STATUS +and +.Sx DIAGNOSTICS +for details. +.Pp +The special option +.Fl W Ns Cm stop +tells +.Nm +to exit after parsing a file that causes warnings or errors of at least +the requested level. +No formatted output will be produced from that file. +If both a +.Ar level +and +.Cm stop +are requested, they can be joined with a comma, for example +.Fl W Ns Cm error , Ns Cm stop . .It Ar file Read input from zero or more files. If unspecified, reads from stdin. @@ -94,8 +108,6 @@ text from stdin, implying and produces .Fl T Ns Cm ascii output. -.Pp -.Ex -std mandoc .Ss Input Formats The .Nm @@ -144,25 +156,25 @@ The .Nm utility accepts the following .Fl T -arguments (see -.Sx OUTPUT ) : +arguments, which correspond to output modes: .Bl -tag -width Ds .It Fl T Ns Cm ascii -Produce 7-bit ASCII output, backspace-encoded for bold and underline -styles. +Produce 7-bit ASCII output. This is the default. See .Sx ASCII Output . .It Fl T Ns Cm html -Produce strict HTML-4.01 output, with a sane default style. +Produce strict CSS1/HTML-4.01 output. See .Sx HTML Output . .It Fl T Ns Cm lint Parse only: produce no output. Implies -.Fl W Ns Cm all -and -.Fl f Ns Cm strict . +.Fl W Ns Cm warning . +.It Fl T Ns Cm pdf +Produce PDF output. +See +.Sx PDF Output . .It Fl T Ns Cm ps Produce PostScript output. See @@ -170,143 +182,13 @@ See .It Fl T Ns Cm tree Produce an indented parse tree. .It Fl T Ns Cm xhtml -Produce strict XHTML-1.0 output, with a sane default style. +Produce strict CSS1/XHTML-1.0 output. See .Sx XHTML Output . .El .Pp If multiple input files are specified, these will be processed by the corresponding filter in-order. -.Ss Compiler Options -Default compiler behaviour may be overridden with the -.Fl f -flag. -.Bl -tag -width Ds -.It Fl f Ns Cm ign-errors -When parsing multiple files, don't halt when one errors out. -Useful with -.Fl T Ns Cm lint -over a large set of manuals passed on the command line. -.It Fl f Ns Cm ign-escape -Ignore invalid escape sequences. -This is the default, but the option can be used to override an earlier -.Fl f Ns Cm strict . -.It Fl f Ns Cm ign-scope -When rewinding the scope of a block macro, forces the compiler to ignore -scope violations. -This can seriously mangle the resulting tree. -.Pq mdoc only -.It Fl f Ns Cm no-ign-escape -Do not ignore invalid escape sequences. -.It Fl f Ns Cm no-ign-macro -Do not ignore unknown macros at the start of input lines. -.It Fl f Ns Cm strict -Implies -.Fl f Ns Cm no-ign-escape -and -.Fl f Ns Cm no-ign-macro . -.El -.Ss Output Options -The -.Fl T Ns Ar html -and -.Fl T Ns Ar xhtml -modes accept the following output options: -.Bl -tag -width Ds -.It Fl O Ns Cm includes Ns = Ns Ar fmt -The string -.Ar fmt , -for example, -.Ar ../src/%I.html , -is used as a template for linked header files (usually via the -.Sq \&In -macro). -Instances of -.Sq \&%I -are replaced with the include filename. -The default is not to present a -hyperlink. -.It Fl O Ns Cm man Ns = Ns Ar fmt -The string -.Ar fmt , -for example, -.Ar ../html%S/%N.%S.html , -is used as a template for linked manuals (usually via the -.Sq \&Xr -macro). -Instances of -.Sq \&%N -and -.Sq %S -are replaced with the linked manual's name and section, respectively. -If no section is included, section 1 is assumed. -The default is not to -present a hyperlink. -.It Fl O Ns Cm style Ns = Ns Ar style.css -The file -.Ar style.css -is used for an external style-sheet. -This must be a valid absolute or -relative URI. -.El -.Pp -The -.Fl T Ns Ar ascii -mode accepts the following output option: -.Bl -tag -width Ds -.It Fl O Ns Cm width Ns = Ns Ar width -The output width is set to -.Ar width , -which will normalise to \(>=60. -.El -.Sh OUTPUT -This section documents output details of -.Nm . -In general, output conforms to the traditional manual style of a header, -a body composed of sections and sub-sections, and a footer. -.Pp -The text style of output characters (non-macro characters, punctuation, -and white-space) is dictated by context. -.Pp -White-space is generally stripped from input. -This can be changed with -character escapes (specified in -.Xr mandoc_char 7 ) -or literal modes (specified in -.Xr mdoc 7 -and -.Xr man 7 ) . -.Pp -If non-macro punctuation is set apart from words, such as in the phrase -.Dq to be \&, or not to be , -it's processed by -.Nm , -regardless of output format, according to the following rules: opening -punctuation -.Po -.Sq \&( , -.Sq \&[ , -and -.Sq \&{ -.Pc -is not followed by a space; closing punctuation -.Po -.Sq \&. , -.Sq \&, , -.Sq \&; , -.Sq \&: , -.Sq \&? , -.Sq \&! , -.Sq \&) , -.Sq \&] -and -.Sq \&} -.Pc -is not preceded by white-space. -.Pp -If the input is -.Xr mdoc 7 , -however, these rules are also applied to macro arguments when appropriate. .Ss ASCII Output Output produced by .Fl T Ns Cm ascii , @@ -330,28 +212,114 @@ are rendered best-effort in an ASCII equivalent. .Pp Output width is limited to 78 visible columns unless literal input lines exceed this limit. +.Pp +The following +.Fl O +arguments are accepted: +.Bl -tag -width Ds +.It Cm width Ns = Ns Ar width +The output width is set to +.Ar width , +which will normalise to \(>=60. +.El .Ss HTML Output Output produced by .Fl T Ns Cm html conforms to HTML-4.01 strict. .Pp -Font styles and page structure are applied using CSS2. -By default, no font style is applied to any text, -although CSS2 is hard-coded to format -the basic structure of output. -.Pp The .Pa example.style.css -file documents the range of styles applied to output and, if used, will -cause rendered documents to appear as they do in -.Fl T Ns Cm ascii . +file documents style-sheet classes available for customising output. +If a style-sheet is not specified with +.Fl O Ns Ar style , +.Fl T Ns Cm html +defaults to simple output readable in any graphical or text-based web +browser. .Pp Special characters are rendered in decimal-encoded UTF-8. +.Pp +The following +.Fl O +arguments are accepted: +.Bl -tag -width Ds +.It Cm includes Ns = Ns Ar fmt +The string +.Ar fmt , +for example, +.Ar ../src/%I.html , +is used as a template for linked header files (usually via the +.Sq \&In +macro). +Instances of +.Sq \&%I +are replaced with the include filename. +The default is not to present a +hyperlink. +.It Cm man Ns = Ns Ar fmt +The string +.Ar fmt , +for example, +.Ar ../html%S/%N.%S.html , +is used as a template for linked manuals (usually via the +.Sq \&Xr +macro). +Instances of +.Sq \&%N +and +.Sq %S +are replaced with the linked manual's name and section, respectively. +If no section is included, section 1 is assumed. +The default is not to +present a hyperlink. +.It Cm style Ns = Ns Ar style.css +The file +.Ar style.css +is used for an external style-sheet. +This must be a valid absolute or +relative URI. +.El .Ss PostScript Output -PostScript Level 2 pages may be generated by +PostScript +.Qq Adobe-3.0 +Level-2 pages may be generated by .Fl T Ns Cm ps . -Output pages are US-letter sized (215.9 x 279.4 mm) and rendered in -fixed, 10-point Courier font. +Output pages default to letter sized and are rendered in the Times font +family, 11-point. +Margins are calculated as 1/9 the page length and width. +Line-height is 1.4m. +.Pp +Special characters are rendered as in +.Sx ASCII Output . +.Pp +The following +.Fl O +arguments are accepted: +.Bl -tag -width Ds +.It Cm paper Ns = Ns Ar name +The paper size +.Ar name +may be one of +.Ar a3 , +.Ar a4 , +.Ar a5 , +.Ar legal , +or +.Ar letter . +You may also manually specify dimensions as +.Ar NNxNN , +width by height in millimetres. +If an unknown value is encountered, +.Ar letter +is used. +.El +.Ss 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 XHTML Output Output produced by .Fl T Ns Cm xhtml @@ -361,36 +329,138 @@ See .Sx HTML Output for details; beyond generating XHTML tags instead of HTML tags, these output modes are identical. +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values, controlled by the message +.Ar level +associated with the +.Fl W +option: +.Pp +.Bl -tag -width Ds -compact +.It 0 +No warnings or errors occurred, or those that did were ignored because +they were lower than the requested +.Ar level . +.It 2 +At least one warning occurred, but no error, and +.Fl W Ns Cm warning +was specified. +.It 3 +At least one parsing error occurred, but no fatal error, and +.Fl W Ns Cm error +or +.Fl W Ns Cm warning +was specified. +.It 4 +A fatal parsing error 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. +.El +.Pp +Note that selecting +.Fl T Ns Cm lint +output mode implies +.Fl W Ns Cm warning . .Sh EXAMPLES To page manuals to the terminal: .Pp -.D1 $ mandoc \-Wall,error \-fstrict mandoc.1 2\*(Gt&1 | less -.D1 $ mandoc mandoc.1 mdoc.3 mdoc.7 | less +.Dl $ mandoc \-Wall,stop mandoc.1 2\*(Gt&1 | less +.Dl $ mandoc mandoc.1 mdoc.3 mdoc.7 | less .Pp To produce HTML manuals with .Ar style.css as the style-sheet: .Pp -.D1 $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html +.Dl $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html .Pp To check over a large set of manuals: .Pp -.Dl $ mandoc \-Tlint \-fign-errors `find /usr/src -name \e*\e.[1-9]` +.Dl $ mandoc \-Tlint `find /usr/src -name \e*\e.[1-9]` +.Pp +To produce a series of PostScript manuals for A4 paper: +.Pp +.Dl $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt manuals.ps +.Sh DIAGNOSTICS +Standard error messages reporting parsing errors are prefixed by +.Pp +.Sm off +.D1 Ar file : line : column : \ level : +.Sm on +.Pp +where the fields have the following meanings: +.Bl -tag -width "column" +.It Ar file +The name of the input file causing the message. +.It Ar line +The line number in that input file. +Line numbering starts at 1. +.It Ar column +The column number in that input file. +Column numbering starts at 1. +If the issue is caused by a word, the column number usually +points to the first character of the word. +.It Ar level +The message level, printed in capital letters. +.El +.Pp +Message levels have the following meanings: +.Bl -tag -width "warning" +.It Cm fatal +The parser is unable to parse a given input file at all. +No formatted output is produced from that input file. +.It Cm error +An input file contains syntax that cannot be safely interpreted, +either because it is invalid or because +.Nm +does not implement it yet. +By discarding part of the input or inserting missing tokens, +the parser is able to continue, and the error does not prevent +generation of formatted output, but typically, preparing that +output involves information loss, broken document structure +or unintended formatting. +.It Cm warning +An input file uses obsolete, discouraged or non-portable syntax. +All the same, the meaning of the input is unambiguous and a correct +rendering can be produced. +Documents causing warnings may render poorly when using other +formatting tools instead of +.Nm . +.El +.Pp +Messages of the +.Cm warning +and +.Cm error +levels are hidden unless their level, or a lower level, is requested using a +.Fl W +option or +.Fl T Ns Cm lint +output mode. +.Pp +The +.Nm +utility may also print messages related to invalid command line arguments +or operating system errors, for example when memory is exhausted or +input files cannot be read. +Such messages do not carry the prefix described above. .Sh COMPATIBILITY This section summarises .Nm -compatibility with -.Xr groff 1 . +compatibility with GNU troff. Each input and output format is separately noted. .Ss ASCII Compatibility .Bl -bullet -compact .It The -.Sq \e~ -special character doesn't produce expected behaviour in -.Fl T Ns Cm ascii . -.It -The .Sq \&Bd \-literal and .Sq \&Bd \-unfilled @@ -400,9 +470,7 @@ in .Fl T Ns Cm ascii are synonyms, as are \-filled and \-ragged. .It -In -.Xr groff 1 , -the +In GNU troff, the .Sq \&Pa .Xr mdoc 7 macro does not underline when scoped under an @@ -428,11 +496,6 @@ has no effect. .It Words aren't hyphenated. .It -In normal mode (not a literal block), blocks of spaces aren't preserved, -so double spaces following sentence closure are reduced to a single space; -.Xr groff 1 -retains spaces. -.It Sentences are unilaterally monospaced. .El .Ss HTML/XHTML Compatibility @@ -468,22 +531,15 @@ lists render similarly. .Sh SEE ALSO .Xr man 7 , .Xr mandoc_char 7 , -.Xr mdoc 7 +.Xr mdoc 7 , +.Xr roff 7 , +.Xr tbl 7 .Sh AUTHORS The .Nm utility was written by .An Kristaps Dzonsons Aq kristaps@bsd.lv . .Sh CAVEATS -The -.Fl T Ns Cm html -and -.Fl T Ns Cm xhtml -CSS2 styling used for -.Fl m Ns Cm doc -input lists does not render properly in older browsers, such as Internet -Explorer 6 and earlier. -.Pp In .Fl T Ns Cm html and @@ -495,17 +551,6 @@ Be aware of this when setting long link formats such as .Fl O Ns Cm style Ns = Ns Ar really/long/link . .Pp -The -.Fl T Ns Cm html -and -.Fl T Ns Cm xhtml -output modes don't render the -.Sq \es -font size escape documented in -.Xr mdoc 7 -and -.Xr man 7 . -.Pp Nesting elements within next-line element scopes of .Fl m Ns Cm an , such as @@ -519,12 +564,6 @@ and and cause them to forget the formatting of the prior next-line scope. .Pp The -.Sq i -macro in -.Fl m Ns Cm an -should italicise all subsequent text if a line argument is not provided. -This behaviour is not implemented. -The .Sq \(aq control character is an alias for the standard macro control character and does not emit a line-break as stipulated in GNU troff. diff --git a/usr.bin/mdocml/dist/mandoc.c b/usr.bin/mdocml/dist/mandoc.c new file mode 100644 index 000000000..e6f7971c6 --- /dev/null +++ b/usr.bin/mdocml/dist/mandoc.c @@ -0,0 +1,487 @@ +/* $Vendor-Id: mandoc.c,v 1.36 2011/01/03 22:42:37 schwarze Exp $ */ +/* + * Copyright (c) 2008, 2009, 2010 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "libmandoc.h" + +static int a2time(time_t *, const char *, const char *); + + +int +mandoc_special(char *p) +{ + int len, i; + char term; + char *sv; + + len = 0; + term = '\0'; + sv = p; + + assert('\\' == *p); + p++; + + switch (*p++) { +#if 0 + case ('Z'): + /* FALLTHROUGH */ + case ('X'): + /* FALLTHROUGH */ + case ('x'): + /* FALLTHROUGH */ + case ('S'): + /* FALLTHROUGH */ + case ('R'): + /* FALLTHROUGH */ + case ('N'): + /* FALLTHROUGH */ + case ('l'): + /* FALLTHROUGH */ + case ('L'): + /* FALLTHROUGH */ + case ('H'): + /* FALLTHROUGH */ + case ('h'): + /* FALLTHROUGH */ + case ('D'): + /* FALLTHROUGH */ + case ('C'): + /* FALLTHROUGH */ + case ('b'): + /* FALLTHROUGH */ + case ('B'): + /* FALLTHROUGH */ + case ('a'): + /* FALLTHROUGH */ + case ('A'): + if (*p++ != '\'') + return(0); + term = '\''; + break; +#endif + case ('h'): + /* FALLTHROUGH */ + case ('v'): + /* FALLTHROUGH */ + case ('s'): + if (ASCII_HYPH == *p) + *p = '-'; + + i = 0; + if ('+' == *p || '-' == *p) { + p++; + i = 1; + } + + switch (*p++) { + case ('('): + len = 2; + break; + case ('['): + term = ']'; + break; + case ('\''): + term = '\''; + break; + case ('0'): + i = 1; + /* FALLTHROUGH */ + default: + len = 1; + p--; + break; + } + + if (ASCII_HYPH == *p) + *p = '-'; + if ('+' == *p || '-' == *p) { + if (i) + return(0); + p++; + } + + /* Handle embedded numerical subexp or escape. */ + + if ('(' == *p) { + while (*p && ')' != *p) + if ('\\' == *p++) { + i = mandoc_special(--p); + if (0 == i) + return(0); + p += i; + } + + if (')' == *p++) + break; + + return(0); + } else if ('\\' == *p) { + if (0 == (i = mandoc_special(p))) + return(0); + p += i; + } + + break; +#if 0 + case ('Y'): + /* FALLTHROUGH */ + case ('V'): + /* FALLTHROUGH */ + case ('$'): + /* FALLTHROUGH */ + case ('n'): + /* FALLTHROUGH */ +#endif + case ('k'): + /* FALLTHROUGH */ + case ('M'): + /* FALLTHROUGH */ + case ('m'): + /* FALLTHROUGH */ + case ('f'): + /* FALLTHROUGH */ + case ('F'): + /* FALLTHROUGH */ + case ('*'): + switch (*p++) { + case ('('): + len = 2; + break; + case ('['): + term = ']'; + break; + default: + len = 1; + p--; + break; + } + break; + case ('('): + len = 2; + break; + case ('['): + term = ']'; + break; + case ('z'): + len = 1; + if ('\\' == *p) { + if (0 == (i = mandoc_special(p))) + return(0); + p += i; + return(*p ? (int)(p - sv) : 0); + } + break; + case ('o'): + /* FALLTHROUGH */ + case ('w'): + if ('\'' == *p++) { + term = '\''; + break; + } + /* FALLTHROUGH */ + default: + len = 1; + p--; + break; + } + + if (term) { + for ( ; *p && term != *p; p++) + if (ASCII_HYPH == *p) + *p = '-'; + return(*p ? (int)(p - sv) : 0); + } + + for (i = 0; *p && i < len; i++, p++) + if (ASCII_HYPH == *p) + *p = '-'; + return(i == len ? (int)(p - sv) : 0); +} + + +void * +mandoc_calloc(size_t num, size_t size) +{ + void *ptr; + + ptr = calloc(num, size); + if (NULL == ptr) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } + + return(ptr); +} + + +void * +mandoc_malloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + if (NULL == ptr) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } + + return(ptr); +} + + +void * +mandoc_realloc(void *ptr, size_t size) +{ + + ptr = realloc(ptr, size); + if (NULL == ptr) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } + + return(ptr); +} + + +char * +mandoc_strdup(const char *ptr) +{ + char *p; + + p = strdup(ptr); + if (NULL == p) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } + + return(p); +} + +/* + * Parse a quoted or unquoted roff-style request or macro argument. + * Return a pointer to the parsed argument, which is either the original + * pointer or advanced by one byte in case the argument is quoted. + * Null-terminate the argument in place. + * Collapse pairs of quotes inside quoted arguments. + * Advance the argument pointer to the next argument, + * or to the null byte terminating the argument line. + */ +char * +mandoc_getarg(char **cpp, mandocmsg msg, void *data, int ln, int *pos) +{ + char *start, *cp; + int quoted, pairs, white; + + /* Quoting can only start with a new word. */ + start = *cpp; + if ('"' == *start) { + quoted = 1; + start++; + } else + quoted = 0; + + pairs = 0; + white = 0; + for (cp = start; '\0' != *cp; cp++) { + /* Move left after quoted quotes and escaped backslashes. */ + if (pairs) + cp[-pairs] = cp[0]; + if ('\\' == cp[0]) { + if ('\\' == cp[1]) { + /* Poor man's copy mode. */ + pairs++; + cp++; + } else if (0 == quoted && ' ' == cp[1]) + /* Skip escaped blanks. */ + cp++; + } else if (0 == quoted) { + if (' ' == cp[0]) { + /* Unescaped blanks end unquoted args. */ + white = 1; + break; + } + } else if ('"' == cp[0]) { + if ('"' == cp[1]) { + /* Quoted quotes collapse. */ + pairs++; + cp++; + } else { + /* Unquoted quotes end quoted args. */ + quoted = 2; + break; + } + } + } + + /* Quoted argument without a closing quote. */ + if (1 == quoted && msg) + (*msg)(MANDOCERR_BADQUOTE, data, ln, *pos, NULL); + + /* Null-terminate this argument and move to the next one. */ + if (pairs) + cp[-pairs] = '\0'; + if ('\0' != *cp) { + *cp++ = '\0'; + while (' ' == *cp) + cp++; + } + *pos += (cp - start) + (quoted ? 1 : 0); + *cpp = cp; + + if ('\0' == *cp && msg && (white || ' ' == cp[-1])) + (*msg)(MANDOCERR_EOLNSPACE, data, ln, *pos, NULL); + + return(start); +} + + +static int +a2time(time_t *t, const char *fmt, const char *p) +{ + struct tm tm; + char *pp; + + memset(&tm, 0, sizeof(struct tm)); + + pp = strptime(p, fmt, &tm); + if (NULL != pp && '\0' == *pp) { + *t = mktime(&tm); + return(1); + } + + return(0); +} + + +/* + * Convert from a manual date string (see mdoc(7) and man(7)) into a + * date according to the stipulated date type. + */ +time_t +mandoc_a2time(int flags, const char *p) +{ + time_t t; + + if (MTIME_MDOCDATE & flags) { + if (0 == strcmp(p, "$" "Mdocdate$")) + return(time(NULL)); + if (a2time(&t, "$" "Mdocdate: %b %d %Y $", p)) + return(t); + } + + if (MTIME_CANONICAL & flags || MTIME_REDUCED & flags) + if (a2time(&t, "%b %d, %Y", p)) + return(t); + + if (MTIME_ISO_8601 & flags) + if (a2time(&t, "%Y-%m-%d", p)) + return(t); + + if (MTIME_REDUCED & flags) { + if (a2time(&t, "%d, %Y", p)) + return(t); + if (a2time(&t, "%Y", p)) + return(t); + } + + return(0); +} + + +int +mandoc_eos(const char *p, size_t sz, int enclosed) +{ + const char *q; + int found; + + if (0 == sz) + return(0); + + /* + * End-of-sentence recognition must include situations where + * some symbols, such as `)', allow prior EOS punctuation to + * propogate outward. + */ + + found = 0; + for (q = p + (int)sz - 1; q >= p; q--) { + switch (*q) { + case ('\"'): + /* FALLTHROUGH */ + case ('\''): + /* FALLTHROUGH */ + case (']'): + /* FALLTHROUGH */ + case (')'): + if (0 == found) + enclosed = 1; + break; + case ('.'): + /* FALLTHROUGH */ + case ('!'): + /* FALLTHROUGH */ + case ('?'): + found = 1; + break; + default: + return(found && (!enclosed || isalnum((unsigned char)*q))); + } + } + + 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); +} diff --git a/usr.bin/mdocml/dist/mandoc.h b/usr.bin/mdocml/dist/mandoc.h new file mode 100644 index 000000000..315273a77 --- /dev/null +++ b/usr.bin/mdocml/dist/mandoc.h @@ -0,0 +1,315 @@ +/* $Vendor-Id: mandoc.h,v 1.49 2011/01/06 13:45:47 kristaps Exp $ */ +/* + * Copyright (c) 2010, 2011 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef MANDOC_H +#define MANDOC_H + +#define ASCII_NBRSP 31 /* non-breaking space */ +#define ASCII_HYPH 30 /* breakable hyphen */ + +/* + * Status level. This refers to both internal status (i.e., whilst + * running, when warnings/errors are reported) and an indicator of a + * threshold of when to halt (when said internal state exceeds the + * threshold). + */ +enum mandoclevel { + MANDOCLEVEL_OK = 0, + MANDOCLEVEL_RESERVED, + MANDOCLEVEL_WARNING, /* warnings: syntax, whitespace, etc. */ + MANDOCLEVEL_ERROR, /* input has been thrown away */ + MANDOCLEVEL_FATAL, /* input is borked */ + MANDOCLEVEL_BADARG, /* bad argument in invocation */ + MANDOCLEVEL_SYSERR, /* system error */ + MANDOCLEVEL_MAX +}; + +/* + * All possible things that can go wrong within a parse, be it libroff, + * libmdoc, or libman. + */ +enum mandocerr { + MANDOCERR_OK, + + MANDOCERR_WARNING, /* ===== start of warnings ===== */ + + /* related to the prologue */ + MANDOCERR_NOTITLE, /* no title in document */ + MANDOCERR_UPPERCASE, /* document title should be all caps */ + MANDOCERR_BADMSEC, /* unknown manual section */ + MANDOCERR_BADDATE, /* cannot parse date argument */ + MANDOCERR_PROLOGOOO, /* prologue macros out of order */ + MANDOCERR_PROLOGREP, /* duplicate prologue macro */ + MANDOCERR_BADPROLOG, /* macro not allowed in prologue */ + MANDOCERR_BADBODY, /* macro not allowed in body */ + + /* related to document structure */ + MANDOCERR_SO, /* .so is fragile, better use ln(1) */ + MANDOCERR_NAMESECFIRST, /* NAME section must come first */ + MANDOCERR_BADNAMESEC, /* bad NAME section contents */ + MANDOCERR_NONAME, /* manual name not yet set */ + MANDOCERR_SECOOO, /* sections out of conventional order */ + MANDOCERR_SECREP, /* duplicate section name */ + MANDOCERR_SECMSEC, /* section not in conventional manual section */ + + /* related to macros and nesting */ + MANDOCERR_MACROOBS, /* skipping obsolete macro */ + MANDOCERR_IGNPAR, /* skipping paragraph macro */ + MANDOCERR_SCOPENEST, /* blocks badly nested */ + MANDOCERR_CHILD, /* child violates parent syntax */ + MANDOCERR_NESTEDDISP, /* nested displays are not portable */ + MANDOCERR_SCOPEREP, /* already in literal mode */ + + /* related to missing macro arguments */ + MANDOCERR_MACROEMPTY, /* skipping empty macro */ + MANDOCERR_ARGCWARN, /* argument count wrong */ + MANDOCERR_DISPTYPE, /* missing display type */ + MANDOCERR_LISTFIRST, /* list type must come first */ + MANDOCERR_NOWIDTHARG, /* tag lists require a width argument */ + MANDOCERR_FONTTYPE, /* missing font type */ + + /* related to bad macro arguments */ + MANDOCERR_IGNARGV, /* skipping argument */ + MANDOCERR_ARGVREP, /* duplicate argument */ + MANDOCERR_DISPREP, /* duplicate display type */ + MANDOCERR_LISTREP, /* duplicate list type */ + MANDOCERR_BADATT, /* unknown AT&T UNIX version */ + MANDOCERR_BADBOOL, /* bad Boolean value */ + MANDOCERR_BADFONT, /* unknown font */ + MANDOCERR_BADSTANDARD, /* unknown standard specifier */ + MANDOCERR_BADWIDTH, /* bad width argument */ + + /* related to plain text */ + MANDOCERR_NOBLANKLN, /* blank line in non-literal context */ + MANDOCERR_BADTAB, /* tab in non-literal context */ + MANDOCERR_EOLNSPACE, /* end of line whitespace */ + MANDOCERR_BADCOMMENT, /* bad comment style */ + MANDOCERR_BADESCAPE, /* unknown escape sequence */ + MANDOCERR_BADQUOTE, /* unterminated quoted string */ + + /* related to tables */ + MANDOCERR_TBLEXTRADAT, /* extra data cells */ + + MANDOCERR_ERROR, /* ===== start of errors ===== */ + + /* related to tables */ + MANDOCERR_TBL, /* bad table syntax */ + MANDOCERR_TBLOPT, /* bad table option */ + MANDOCERR_TBLLAYOUT, /* bad table layout */ + MANDOCERR_TBLNOLAYOUT, /* no table layout cells specified */ + MANDOCERR_TBLNODATA, /* no table data cells specified */ + MANDOCERR_TBLIGNDATA, /* ignore data in cell */ + MANDOCERR_TBLBLOCK, /* data block still open */ + + MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ + MANDOCERR_BADCHAR, /* skipping bad character */ + MANDOCERR_NOTEXT, /* skipping text before the first section header */ + MANDOCERR_MACRO, /* skipping unknown macro */ + MANDOCERR_REQUEST, /* NOT IMPLEMENTED: skipping request */ + MANDOCERR_LINESCOPE, /* line scope broken */ + MANDOCERR_ARGCOUNT, /* argument count wrong */ + MANDOCERR_NOSCOPE, /* skipping end of block that is not open */ + MANDOCERR_SCOPEBROKEN, /* missing end of block */ + MANDOCERR_SCOPEEXIT, /* scope open on exit */ + MANDOCERR_UNAME, /* uname(3) system call failed */ + /* FIXME: merge following with MANDOCERR_ARGCOUNT */ + MANDOCERR_NOARGS, /* macro requires line argument(s) */ + MANDOCERR_NOBODY, /* macro requires body argument(s) */ + MANDOCERR_NOARGV, /* macro requires argument(s) */ + MANDOCERR_LISTTYPE, /* missing list type */ + MANDOCERR_ARGSLOST, /* line argument(s) will be lost */ + MANDOCERR_BODYLOST, /* body argument(s) will be lost */ + + MANDOCERR_FATAL, /* ===== start of fatal errors ===== */ + + 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 */ + MANDOCERR_SOPATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */ + MANDOCERR_NODOCBODY, /* no document body */ + MANDOCERR_NODOCPROLOG, /* no document prologue */ + MANDOCERR_MEM, /* static buffer exhausted */ + MANDOCERR_MAX +}; + +struct tbl { + char tab; /* cell-separator */ + char decimal; /* decimal point */ + int linesize; + int opts; +#define TBL_OPT_CENTRE (1 << 0) +#define TBL_OPT_EXPAND (1 << 1) +#define TBL_OPT_BOX (1 << 2) +#define TBL_OPT_DBOX (1 << 3) +#define TBL_OPT_ALLBOX (1 << 4) +#define TBL_OPT_NOKEEP (1 << 5) +#define TBL_OPT_NOSPACE (1 << 6) + int cols; /* number of columns */ +}; + +enum tbl_headt { + TBL_HEAD_DATA, /* plug in data from tbl_dat */ + TBL_HEAD_VERT, /* vertical spacer */ + TBL_HEAD_DVERT /* double-vertical spacer */ +}; + +/* + * The head of a table specifies all of its columns. When formatting a + * tbl_span, iterate over these and plug in data from the tbl_span when + * appropriate, using tbl_cell as a guide to placement. + */ +struct tbl_head { + enum tbl_headt pos; + int ident; /* 0 <= unique id < cols */ + struct tbl_head *next; + struct tbl_head *prev; +}; + +enum tbl_cellt { + TBL_CELL_CENTRE, /* c, C */ + TBL_CELL_RIGHT, /* r, R */ + TBL_CELL_LEFT, /* l, L */ + TBL_CELL_NUMBER, /* n, N */ + TBL_CELL_SPAN, /* s, S */ + TBL_CELL_LONG, /* a, A */ + TBL_CELL_DOWN, /* ^ */ + TBL_CELL_HORIZ, /* _, - */ + TBL_CELL_DHORIZ, /* = */ + TBL_CELL_VERT, /* | */ + TBL_CELL_DVERT, /* || */ + TBL_CELL_MAX +}; + +/* + * A cell in a layout row. + */ +struct tbl_cell { + struct tbl_cell *next; + enum tbl_cellt pos; + int spacing; + int flags; +#define TBL_CELL_TALIGN (1 << 0) /* t, T */ +#define TBL_CELL_BALIGN (1 << 1) /* d, D */ +#define TBL_CELL_BOLD (1 << 2) /* fB, B, b */ +#define TBL_CELL_ITALIC (1 << 3) /* fI, I, i */ +#define TBL_CELL_EQUAL (1 << 4) /* e, E */ +#define TBL_CELL_UP (1 << 5) /* u, U */ +#define TBL_CELL_WIGN (1 << 6) /* z, Z */ + struct tbl_head *head; +}; + +/* + * A layout row. + */ +struct tbl_row { + struct tbl_row *next; + struct tbl_cell *first; + struct tbl_cell *last; +}; + +enum tbl_datt { + TBL_DATA_NONE, + TBL_DATA_DATA, + TBL_DATA_HORIZ, + TBL_DATA_DHORIZ, + TBL_DATA_NHORIZ, + TBL_DATA_NDHORIZ +}; + +/* + * A cell within a row of data. The "string" field contains the actual + * string value that's in the cell. The rest is layout. + */ +struct tbl_dat { + struct tbl_cell *layout; /* layout cell: CAN BE NULL */ + struct tbl_dat *next; + char *string; + enum tbl_datt pos; +}; + +enum tbl_spant { + TBL_SPAN_DATA, /* span consists of data */ + TBL_SPAN_HORIZ, /* span is horizontal line */ + TBL_SPAN_DHORIZ /* span is double horizontal line */ +}; + +/* + * A row of data in a table. + */ +struct tbl_span { + struct tbl *tbl; + struct tbl_head *head; + struct tbl_row *layout; /* layout row: CAN BE NULL */ + struct tbl_dat *first; + struct tbl_dat *last; + int flags; +#define TBL_SPAN_FIRST (1 << 0) +#define TBL_SPAN_LAST (1 << 1) + enum tbl_spant pos; + struct tbl_span *next; +}; + +/* + * Available registers (set in libroff, accessed elsewhere). + */ +enum regs { + REG_nS = 0, + REG__MAX +}; + +/* + * A register (struct reg) can consist of many types: this consists of + * normalised types from the original string form. + */ +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 + +/* + * Callback function for warnings, errors, and fatal errors as they + * occur in the compilers libroff, libmdoc, and libman. + */ +typedef int (*mandocmsg)(enum mandocerr, void *, + int, int, const char *); + +__END_DECLS + +#endif /*!MANDOC_H*/ diff --git a/commands/mdocml/mandoc_char.7 b/usr.bin/mdocml/dist/mandoc_char.7 similarity index 99% rename from commands/mdocml/mandoc_char.7 rename to usr.bin/mdocml/dist/mandoc_char.7 index c01826589..a4c5050cf 100644 --- a/commands/mdocml/mandoc_char.7 +++ b/usr.bin/mdocml/dist/mandoc_char.7 @@ -1,4 +1,4 @@ -.\" $Id: mandoc_char.7,v 1.39 2010/05/12 08:29:23 kristaps Exp $ +.\" $Vendor-Id: mandoc_char.7,v 1.40 2010/10/29 00:05:53 schwarze Exp $ .\" .\" Copyright (c) 2009 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: May 12 2010 $ +.Dd October 29, 2010 .Dt MANDOC_CHAR 7 .Os .Sh NAME @@ -516,8 +516,7 @@ implementations: .Sh COMPATIBILITY This section documents compatibility of .Nm -with older or existing versions of -.Xr groff 1 . +with older or existing versions of groff. .Pp The following render differently in .Fl T Ns Ar ascii diff --git a/commands/mdocml/mdoc.3 b/usr.bin/mdocml/dist/mdoc.3 similarity index 67% rename from commands/mdocml/mdoc.3 rename to usr.bin/mdocml/dist/mdoc.3 index da963ca5b..dfafbadc5 100644 --- a/commands/mdocml/mdoc.3 +++ b/usr.bin/mdocml/dist/mdoc.3 @@ -1,6 +1,7 @@ -.\" $Id: mdoc.3,v 1.41 2010/05/30 22:56:02 kristaps Exp $ +.\" $Vendor-Id: mdoc.3,v 1.55 2011/01/07 15:07:21 kristaps Exp $ .\" -.\" Copyright (c) 2009-2010 Kristaps Dzonsons +.\" Copyright (c) 2009, 2010 Kristaps Dzonsons +.\" Copyright (c) 2010 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,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 30 2010 $ +.Dd January 7, 2011 .Dt MDOC 3 .Os .Sh NAME @@ -32,8 +33,17 @@ .In mdoc.h .Vt extern const char * const * mdoc_macronames; .Vt extern const char * const * mdoc_argnames; +.Ft int +.Fo mdoc_addspan +.Fa "struct mdoc *mdoc" +.Fa "const struct tbl_span *span" +.Fc .Ft "struct mdoc *" -.Fn mdoc_alloc "void *data" "int pflags" "mandocmsg msgs" +.Fo mdoc_alloc +.Fa "struct regset *regs" +.Fa "void *data" +.Fa "mandocmsg msgs" +.Fc .Ft int .Fn mdoc_endparse "struct mdoc *mdoc" .Ft void @@ -43,7 +53,11 @@ .Ft "const struct mdoc_node *" .Fn mdoc_node "const struct mdoc *mdoc" .Ft int -.Fn mdoc_parseln "struct mdoc *mdoc" "int line" "char *buf" +.Fo mdoc_parseln +.Fa "struct mdoc *mdoc" +.Fa "int line" +.Fa "char *buf" +.Fc .Ft int .Fn mdoc_reset "struct mdoc *mdoc" .Sh DESCRIPTION @@ -70,56 +84,40 @@ The .Fn mdoc_reset function may be used in order to reset the parser for another input sequence. -See the -.Sx EXAMPLES -section for a simple example. -.Pp -This section further defines the -.Sx Types , -.Sx Functions -and -.Sx Variables -available to programmers. -Following that, the -.Sx Abstract Syntax Tree -section documents the output tree. .Ss Types -Both functions (see -.Sx Functions ) -and variables (see -.Sx Variables ) -may use the following types: .Bl -ohang .It Vt struct mdoc -An opaque type defined in -.Pa mdoc.c . +An opaque type. Its values are only used privately within the library. .It Vt struct mdoc_node A parsed node. -Defined in -.Pa mdoc.h . See .Sx Abstract Syntax Tree for details. -.It Vt mandocmsg -A function callback type defined in -.Pa mandoc.h . .El .Ss Functions -Function descriptions follow: +If +.Fn mdoc_addspan , +.Fn mdoc_parseln , +or +.Fn mdoc_endparse +return 0, calls to any function but +.Fn mdoc_reset +or +.Fn mdoc_free +will raise an assertion. .Bl -ohang +.It Fn mdoc_addspan +Add a table span to the parsing stream. +Returns 0 on failure, 1 on success. .It Fn mdoc_alloc Allocates a parsing structure. The .Fa data pointer is passed to .Fa msgs . -The -.Fa pflags -arguments are defined in -.Pa mdoc.h . -Returns NULL on failure. -If non-NULL, the pointer must be freed with +Always returns a valid pointer. +The pointer must be freed with .Fn mdoc_free . .It Fn mdoc_reset Reset the parser for another parse routine. @@ -139,29 +137,13 @@ The input buffer is modified by this function. .It Fn mdoc_endparse Signals that the parse is complete. -Note that if -.Fn mdoc_endparse -is called subsequent to -.Fn mdoc_node , -the resulting tree is incomplete. Returns 0 on failure, 1 on success. .It Fn mdoc_node Returns the first node of the parse. -Note that if -.Fn mdoc_parseln -or -.Fn mdoc_endparse -return 0, the tree will be incomplete. .It Fn mdoc_meta Returns the document's parsed meta-data. -If this information has not yet been supplied or -.Fn mdoc_parseln -or -.Fn mdoc_endparse -return 0, the data will be incomplete. .El .Ss Variables -The following variables are also defined: .Bl -ohang .It Va mdoc_macronames An array of string-ified token names. @@ -207,10 +189,14 @@ and fields), its position in the tree (the .Va parent , .Va child , +.Va nchild , .Va next and .Va prev -fields) and some type-specific data. +fields) and some type-specific data, in particular, for nodes generated +from macros, the generating macro in the +.Va tok +field. .Pp The tree itself is arranged according to the following normal form, where capitalised non-terminals represent nodes. @@ -225,11 +211,11 @@ where capitalised non-terminals represent nodes. .It ELEMENT \(<- TEXT* .It HEAD -\(<- mnode+ +\(<- mnode* .It BODY -\(<- mnode+ +\(<- mnode* [ENDBODY mnode*] .It TAIL -\(<- mnode+ +\(<- mnode* .It TEXT \(<- [[:printable:],0x1e]* .El @@ -243,20 +229,88 @@ an empty line will produce a zero-length string. Multiple body parts are only found in invocations of .Sq \&Bl \-column , where a new body introduces a new phrase. +.Ss Badly-nested Blocks +The ENDBODY node is available to end the formatting associated +with a given block before the physical end of that block. +It has a non-null +.Va end +field, is of the BODY +.Va type , +has the same +.Va tok +as the BLOCK it is ending, and has a +.Va pending +field pointing to that BLOCK's BODY node. +It is an indirect child of that BODY node +and has no children of its own. +.Pp +An ENDBODY node is generated when a block ends while one of its child +blocks is still open, like in the following example: +.Bd -literal -offset indent +\&.Ao ao +\&.Bo bo ac +\&.Ac bc +\&.Bc end +.Ed +.Pp +This example results in the following block structure: +.Bd -literal -offset indent +BLOCK Ao + HEAD Ao + BODY Ao + TEXT ao + BLOCK Bo, pending -> Ao + HEAD Bo + BODY Bo + TEXT bo + TEXT ac + ENDBODY Ao, pending -> Ao + TEXT bc +TEXT end +.Ed +.Pp +Here, the formatting of the +.Sq \&Ao +block extends from TEXT ao to TEXT ac, +while the formatting of the +.Sq \&Bo +block extends from TEXT bo to TEXT bc. +It renders as follows in +.Fl T Ns Cm ascii +mode: +.Pp +.Dl bc] end +.Pp +Support for badly-nested blocks is only provided for backward +compatibility with some older +.Xr mdoc 7 +implementations. +Using badly-nested blocks is +.Em strongly discouraged : +the +.Fl T Ns Cm html +and +.Fl T Ns Cm xhtml +front-ends are unable to render them in any meaningful way. +Furthermore, behaviour when encountering badly-nested blocks is not +consistent across troff implementations, especially when using multiple +levels of badly-nested blocks. .Sh EXAMPLES The following example reads lines from stdin and parses them, operating on the finished parse tree with .Fn parsed . This example does not error-check nor free memory upon failure. .Bd -literal -offset indent +struct regset regs; struct mdoc *mdoc; const struct mdoc_node *node; char *buf; size_t len; int line; +bzero(®s, sizeof(struct regset)); line = 1; -mdoc = mdoc_alloc(NULL, 0, NULL); +mdoc = mdoc_alloc(®s, NULL, NULL); buf = NULL; alloc_len = 0; @@ -277,9 +331,13 @@ parsed(mdoc, node); mdoc_free(mdoc); .Ed .Pp -Please see +To compile this, execute +.Pp +.Dl % cc main.c libmdoc.a libmandoc.a +.Pp +where .Pa main.c -in the source archive for a rigorous reference. +is the example file. .Sh SEE ALSO .Xr mandoc 1 , .Xr mdoc 7 diff --git a/commands/mdocml/mdoc.7 b/usr.bin/mdocml/dist/mdoc.7 similarity index 58% rename from commands/mdocml/mdoc.7 rename to usr.bin/mdocml/dist/mdoc.7 index bc5f1ee33..7b0c88cf4 100644 --- a/commands/mdocml/mdoc.7 +++ b/usr.bin/mdocml/dist/mdoc.7 @@ -1,6 +1,7 @@ -.\" $Id: mdoc.7,v 1.126 2010/06/12 11:41:50 kristaps Exp $ +.\" $Vendor-Id: mdoc.7,v 1.174 2011/01/04 23:32:21 kristaps Exp $ .\" -.\" Copyright (c) 2009 Kristaps Dzonsons +.\" Copyright (c) 2009, 2010 Kristaps Dzonsons +.\" Copyright (c) 2010 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,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: June 12 2010 $ +.Dd January 4, 2011 .Dt MDOC 7 .Os .Sh NAME @@ -26,8 +27,12 @@ The language is used to format .Bx .Ux -manuals. In this reference document, we describe its syntax, structure, -and usage. Our reference implementation is mandoc; the +manuals. +This reference document describes its syntax, structure, and +usage. +The reference implementation is +.Xr mandoc 1 ; +the .Sx COMPATIBILITY section describes compatibility with other troff \-mdoc implementations. .Pp @@ -36,7 +41,8 @@ An document follows simple rules: lines beginning with the control character .Sq \. -are parsed for macros. Other lines are interpreted within the scope of +are parsed for macros. +Other lines are interpreted within the scope of prior macros: .Bd -literal -offset indent \&.Sh Macro lines change control state. @@ -45,18 +51,20 @@ Other lines are interpreted within the current state. .Sh LANGUAGE SYNTAX .Nm documents may contain only graphable 7-bit ASCII characters, the space -character, and, in certain circumstances, the tab character. All -manuals must have +character, and, in certain circumstances, the tab character. +All manuals must have .Ux line terminators. .Ss Comments Text following a -.Sq \e" , +.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" , -is also ignored. Macro lines with only a control character and optionally -whitespace are stripped from input. +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 Characters Within a macro line, the following characters are reserved: .Pp @@ -87,10 +95,10 @@ Within a macro line, the following characters are reserved: .Pp Use of reserved characters is described in .Sx MACRO SYNTAX . -For general use in macro lines, these characters must either be escaped +For general use in macro lines, these characters can either be escaped with a non-breaking space .Pq Sq \e& -or, if applicable, an appropriate escape sequence used. +or, if applicable, an appropriate escape sequence can be used. .Ss Special Characters Special characters may occur in both macro and free-form lines. Sequences begin with the escape character @@ -101,7 +109,7 @@ for two-character sequences; an open-bracket .Sq \&[ for n-character sequences (terminated at a close-bracket .Sq \&] ) ; -or a single one-character sequence. +or a single one character sequence. See .Xr mandoc_char 7 for a complete list. @@ -114,10 +122,10 @@ and .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 +escape followed by an indicator: B (bold), I (italic), R (Roman), or P (revert to previous mode): .Pp -.D1 \efBbold\efR \efIitalic\efP +.Dl \efBbold\efR \efIitalic\efP .Pp A numerical representation 3, 2, or 1 (bold, italic, and Roman, respectively) may be used instead. @@ -134,35 +142,14 @@ If is specified outside of any font scope, such as in unenclosed, free-form text, it will affect the remainder of the document. .Pp -Text may also be sized with the -.Sq \es -escape, whose syntax is one of -.Sq \es+-n -for one-digit numerals; -.Sq \es(+-nn -or -.Sq \es+-(nn -for two-digit numerals; and -.Sq \es[+-N] , -.Sq \es+-[N] , -.Sq \es'+-N' , -or -.Sq \es+-'N' -for arbitrary-digit numerals: -.Pp -.D1 \es+1bigger\es-1 -.D1 \es[+10]much bigger\es[-10] -.D1 \es+(10much bigger\es-(10 -.D1 \es+'100'much much bigger\es-'100' -.Pp -Note these forms are +Note this form is .Em not recommended for .Nm , which encourages semantic annotation. .Ss Predefined Strings Historically, -.Xr groff 1 +troff also defined a set of package-specific .Dq predefined strings , which, like @@ -187,7 +174,7 @@ and .Pq vertical bar . .Ss Whitespace Whitespace consists of the space character. -In free-form lines, whitespace is preserved within a line; un-escaped +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 whitespace, are only permitted within literal contexts. @@ -195,24 +182,18 @@ within literal contexts. 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 a double-quote to group +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 pair-wise adjacent to another double-quote +The next double-quote not pairwise adjacent to another double-quote terminates the literal, regardless of surrounding whitespace. .Pp -This produces tokens -.Sq a" , -.Sq b c , -.Sq de , -and -.Sq fg" . -Note that any quoted term, be it argument or macro, is indiscriminately -considered literal text. +Note that any quoted text, even if it would cause a macro invocation +when unquoted, is considered literal text. Thus, the following produces -.Sq \&Em a : +.Sq Op "Fl a" : .Bd -literal -offset indent -\&.Em "Em a" +\&.Op "Fl a" .Ed .Pp In free-form mode, quotes are regarded as opaque text. @@ -297,7 +278,7 @@ is necessarily non-portable across output media. See .Sx COMPATIBILITY . .Ss Sentence Spacing -When composing a manual, make sure that your sentences end at the end of +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, @@ -309,22 +290,23 @@ delimiters ( .Sq \&" ) . .Pp The proper spacing is also intelligently preserved if a sentence ends at -the boundary of a macro line, e.g., +the boundary of a macro line. +For example: .Pp -.D1 \&Xr mandoc 1 \. -.D1 \&Fl T \&Ns \&Cm ascii \. +.Dl \&Xr mandoc 1 \. +.Dl \&Fl T \&Ns \&Cm ascii \. .Sh MANUAL STRUCTURE A well-formed .Nm document consists of a document prologue followed by one or more sections. .Pp -The prologue, which consists of (in order) the +The prologue, which consists of the .Sx \&Dd , .Sx \&Dt , and .Sx \&Os -macros, is required for every document. +macros in that order, is required for every document. .Pp The first section (sections are denoted by .Sx \&Sh ) @@ -349,8 +331,9 @@ file: \&.Sh NAME \&.Nm foo \&.Nd a description goes here -\&.\e\*q The next is for sections 2, 3, & 9 only. \&.\e\*q .Sh LIBRARY +\&.\e\*q For sections 2, 3, & 9 only. +\&.\e\*q Not used in OpenBSD. \&.Sh SYNOPSIS \&.Nm foo \&.Op Fl options @@ -360,18 +343,19 @@ The \&.Nm utility processes files ... \&.\e\*q .Sh IMPLEMENTATION NOTES -\&.\e\*q The next is for sections 2, 3, & 9 only. +\&.\e\*q Not used in OpenBSD. \&.\e\*q .Sh RETURN VALUES -\&.\e\*q The next is for sections 1, 6, 7, & 8 only. +\&.\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 The next is for sections 1 & 8 only. \&.\e\*q .Sh EXIT STATUS +\&.\e\*q For sections 1, 6, & 8 only. \&.\e\*q .Sh EXAMPLES -\&.\e\*q The next is for sections 1, 4, 6, 7, & 8 only. \&.\e\*q .Sh DIAGNOSTICS -\&.\e\*q The next is for sections 2, 3, & 9 only. +\&.\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 @@ -380,21 +364,22 @@ utility processes files ... \&.\e\*q .Sh CAVEATS \&.\e\*q .Sh BUGS \&.\e\*q .Sh SECURITY CONSIDERATIONS +\&.\e\*q Not used in OpenBSD. .Ed .Pp -The sections in a +The sections in an .Nm document are conventionally ordered as they appear above. Sections should be composed as follows: .Bl -ohang -offset Ds .It Em NAME -The name(s) and a short description of the documented material. +The name(s) and a one line description of the documented material. The syntax for this as follows: .Bd -literal -offset indent -\&.Nm name0 -\&.Nm name1 +\&.Nm name0 , +\&.Nm name1 , \&.Nm name2 -\&.Nd a short description +\&.Nd a one line description .Ed .Pp The @@ -436,8 +421,8 @@ generally structured as follows: .Pp For the second, function calls (sections 2, 3, 9): .Bd -literal -offset indent -\&.Vt extern const char *global; \&.In header.h +\&.Vt extern const char *global; \&.Ft "char *" \&.Fn foo "const char *src" \&.Ft "char *" @@ -465,8 +450,8 @@ section, particularly .Sx \&Vt , and .Sx \&Ft . -All of these macros are output on their own line. If two such -dissimilar macros are pair-wise invoked (except for +All of these macros are output on their own line. +If two such dissimilar macros are pairwise invoked (except for .Sx \&Ft before .Sx \&Fo @@ -478,10 +463,23 @@ they are separated by a vertical space, unless in the case of and .Sx \&Ft , which are always separated by vertical space. +.Pp +When text and macros following an +.Sx \&Nm +macro starting an input line span multiple output lines, +all output lines but the first will be indented to align +with the text immediately following the +.Sx \&Nm +macro, up to the next +.Sx \&Nm , +.Sx \&Sh , +or +.Sx \&Ss +macro or the end of an enclosing block, whichever comes first. .It Em DESCRIPTION -This expands upon the brief, one-line description in +This expands upon the brief, one line description in .Em NAME . -It usually contains a break-down of the options (if documenting a +It usually contains a breakdown of the options (if documenting a command), such as: .Bd -literal -offset indent The arguments are as follows: @@ -497,31 +495,30 @@ Implementation-specific notes should be kept here. This is useful when implementing standard functions that may have side effects or notable algorithmic implications. .It Em RETURN VALUES -This section is the dual of -.Em EXIT STATUS , -which is used for commands. -It documents the return values of functions in sections 2, 3, and 9. +This section documents the +return values of functions in sections 2, 3, and 9. .Pp See .Sx \&Rv . .It Em ENVIRONMENT -Documents any usages of environment variables, e.g., -.Xr environ 7 . +Lists the environment variables used by the utility, +and explains the syntax and semantics of their values. +The +.Xr environ 7 +manual provides examples of typical content and formatting. .Pp See .Sx \&Ev . .It Em FILES Documents files used. -It's helpful to document both the file and a short description of how +It's helpful to document both the file name and a short description of how the file is used (created, modified, etc.). .Pp See .Sx \&Pa . .It Em EXIT STATUS -Command exit status for section 1, 6, and 8 manuals. -This section is the dual of -.Em RETURN VALUES , -which is used for functions. +This section documents the +command exit status for section 1, 6, and 8 utilities. Historically, this information was described in .Em DIAGNOSTICS , a practise that is now discouraged. @@ -531,7 +528,7 @@ See .It Em EXAMPLES Example usages. This often contains snippets of well-formed, well-tested invocations. -Make doubly sure that your examples work properly! +Make sure that examples work properly! .It Em DIAGNOSTICS Documents error conditions. This is most useful in section 4 manuals. @@ -565,26 +562,25 @@ section should be used instead. See .Sx \&St . .It Em HISTORY -The history of any manual without a -.Em STANDARDS -section should be described in this section. +A brief history of the subject, including where support first appeared. .It Em AUTHORS -Credits to authors, if applicable, should appear in this section. -Authors should generally be noted by both name and an e-mail address. +Credits to the person or persons who wrote the code and/or documentation. +Authors should generally be noted by both name and email address. .Pp See .Sx \&An . .It Em CAVEATS -Explanations of common misuses and misunderstandings should be explained +Common misuses and misunderstandings should be explained in this section. .It Em BUGS -Extant bugs should be described in this section. +Known bugs, limitations, and work-arounds should be described +in this section. .It Em SECURITY CONSIDERATIONS Documents any security precautions that operators should consider. .El .Sh MACRO SYNTAX Macros are one to three three characters in length and begin with a -control character , +control character, .Sq \&. , at the beginning of the line. An arbitrary amount of whitespace may sit between the control character @@ -608,20 +604,21 @@ closes it out. .Pp The .Em Callable -column indicates that the macro may be called subsequent to the initial -line-macro. -If a macro is not callable, then its invocation after the initial line -macro is interpreted as opaque text, such that +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 . .Pp The -.Em Parsable -column indicates whether the macro may be followed by further -(ostensibly callable) macros. -If a macro is not parsable, subsequent macro invocations on the line -will be interpreted as opaque text. +.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 @@ -637,8 +634,8 @@ contains a head. \&.Yc .Ed .Pp -.Bl -column -compact -offset indent "MacroX" "CallableX" "ParsableX" "closed by XXX" -.It Em Macro Ta Em Callable Ta Em Parsable Ta Em Scope +.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 @@ -660,7 +657,9 @@ All macros have bodies; some .Pc don't have heads; only one .Po -.Sx \&It Fl column +.Sx \&It +in +.Sx \&Bl Fl column .Pc has multiple heads. .Bd -literal -offset indent @@ -668,13 +667,24 @@ has multiple heads. \(lBbody...\(rB .Ed .Pp -.Bl -column -compact -offset indent "MacroX" "CallableX" "ParsableX" "closed by XXXXXXXXXXX" -.It Em Macro Ta Em Callable Ta Em Parsable Ta Em Scope +.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 .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 @@ -693,8 +703,8 @@ and/or tail \(lBbody...\(rB \&Yc \(lBtail...\(rB .Ed .Pp -.Bl -column "MacroX" "CallableX" "ParsableX" "closed by XXXX" -compact -offset indent -.It Em Macro Ta Em Callable Ta Em Parsable Ta Em Scope +.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 @@ -728,8 +738,8 @@ or end of line. \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBbody...\(rB \(lBres...\(rB .Ed .Pp -.Bl -column "MacroX" "CallableX" "ParsableX" -compact -offset indent -.It Em Macro Ta Em Callable Ta Em Parsable +.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 @@ -762,15 +772,15 @@ 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 \(lBres...\(rB \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB Yc... \&.Yo \(lB\-arg \(lBval...\(rB\(rB arg0 arg1 argN .Ed .Pp -.Bl -column "MacroX" "CallableX" "ParsableX" "Arguments" -compact -offset indent -.It Em Macro Ta Em Callable Ta Em Parsable Ta Em Arguments +.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 @@ -796,7 +806,7 @@ then the macro accepts an arbitrary number of arguments. .It Sx \&Cd Ta Yes Ta Yes Ta >0 .It Sx \&Cm Ta Yes Ta Yes Ta n .It Sx \&Db Ta \&No Ta \&No Ta 1 -.It Sx \&Dd Ta \&No Ta \&No Ta >0 +.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 n .It Sx \&Dx Ta Yes Ta Yes Ta n @@ -854,28 +864,27 @@ For the scoping of individual macros, see .Ss \&%A Author name of an .Sx \&Rs -block. Multiple authors should each be accorded their own +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. +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 +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. -.Pp -.Em Remarks : -this macro is not implemented in -.Xr groff 1 . .Ss \&%D Publication date of an .Sx \&Rs -block. This should follow the reduced or canonical form syntax -described in +block. +This should follow the reduced or canonical form syntax described in .Sx Dates . .Ss \&%I Publisher or issuer name of an @@ -900,7 +909,8 @@ block. .Ss \&%Q Institutional author (school, government, etc.) of an .Sx \&Rs -block. Multiple institutional authors should each be accorded their own +block. +Multiple institutional authors should each be accorded their own .Sx \&%Q line. .Ss \&%R @@ -910,8 +920,9 @@ 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. +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 @@ -919,70 +930,69 @@ Volume number of an .Sx \&Rs block. .Ss \&Ac -Closes an +Close an .Sx \&Ao -block. Does not have any tail arguments. +block. +Does not have any tail arguments. .Ss \&Ad -Address construct: usually in the context of an computational address in -memory, not a physical (post) address. +Memory address. +Do not use this for postal addresses. .Pp Examples: -.D1 \&.Ad [0,$] -.D1 \&.Ad 0x00000000 +.Dl \&.Ad [0,$] +.Dl \&.Ad 0x00000000 .Ss \&An Author name. -This macro may alternatively accepts the following arguments, although -these may not be specified along with a parameter: -.Bl -tag -width 12n -offset indent +Requires either the name of an author or one of the following arguments: +.Pp +.Bl -tag -width "-nosplitX" -offset indent -compact .It Fl split -Renders a line break before each author listing. +Start a new output line before each subsequent invocation of +.Sx \&An . .It Fl nosplit The opposite of .Fl split . .El .Pp -In the AUTHORS section, the default is not to split the first author -listing, but all subsequent author listings, whether or not they're -interspersed by other macros or text, are split. -Thus, specifying +The default is +.Fl nosplit . +The effect of selecting either of the .Fl split -will cause the first listing also to be split. -If not in the AUTHORS section, the default is not to 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: -.D1 \&.An -nosplit -.D1 \&.An J. D. Ullman . -.Pp -.Em Remarks : -the effects of -.Fl split -or -.Fl nosplit -are re-set when entering the AUTHORS section, so if one specifies -.Sx \&An Fl nosplit -in the general document body, it must be re-specified in the AUTHORS -section. +.Dl \&.An -nosplit +.Dl \&.An Kristaps Dzonsons \&Aq kristaps@bsd.lv .Ss \&Ao -Begins a block enclosed by angled brackets. +Begin a block enclosed by angle brackets. Does not have any head arguments. .Pp Examples: -.D1 \&.Fl -key= \&Ns \&Ao \&Ar val \&Ac +.Dl \&.Fl -key= \&Ns \&Ao \&Ar val \&Ac .Pp See also .Sx \&Aq . .Ss \&Ap -Inserts an apostrophe without any surrounding white-space. +Inserts an apostrophe without any surrounding whitespace. This is generally used as a grammatical device when referring to the verb -form of a function: -.Bd -literal -offset indent -\&.Fn execve Ap d -.Ed -.Ss \&Aq -Encloses its arguments in angled brackets. +form of a function. .Pp Examples: -.D1 \&.Fl -key= \&Ns \&Aq \&Ar val +.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 @@ -999,30 +1009,31 @@ See also .Ss \&Ar Command arguments. If an argument is not provided, the string -.Dq file ... +.Dq file ...\& is used as a default. .Pp Examples: -.D1 \&.Fl o \&Ns \&Ar file1 -.D1 \&.Ar -.D1 \&.Ar arg1 , arg2 . +.Dl \&.Fl o \&Ns \&Ar file1 +.Dl \&.Ar +.Dl \&.Ar arg1 , arg2 . .Ss \&At Formats an AT&T version. -Accepts at most one parameter: -.Bl -tag -width 12n -offset indent +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 system version of -.At . +A version of +.At V . .El .Pp -Note that these parameters do not begin with a hyphen. +Note that these arguments do not begin with a hyphen. .Pp Examples: -.D1 \&.At -.D1 \&.At V.1 +.Dl \&.At +.Dl \&.At V.1 .Pp See also .Sx \&Bsx , @@ -1034,78 +1045,93 @@ See also and .Sx \&Ux . .Ss \&Bc -Closes a +Close a .Sx \&Bo -block. Does not have any tail arguments. +block. +Does not have any tail arguments. .Ss \&Bd -Begins a display block. -A display is collection of macros or text which may be collectively -offset or justified in a manner different from that -of the enclosing context. -By default, the block is preceded by a vertical space. +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 -Each display is associated with a type, which must be one of the -following arguments: -.Bl -tag -width 12n -offset indent -.It Fl ragged -Only left-justify the block. -.It Fl unfilled -Do not justify the block at all. +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 free-form 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 -Alias for -.Fl unfilled . -.It Fl centered -Centre-justify each line. +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 type must be provided first. -Secondary arguments are as follows: -.Bl -tag -width 12n -offset indent +The +.Ar type +must be provided first. +Additional arguments may follow: +.Bl -tag -width 13n -offset indent .It Fl offset Ar width -Offset by the value of +Indent the display by the .Ar width , -which is interpreted as one of the following, specified in order: +which may be one of the following: .Bl -item .It -As one of the pre-defined strings -.Ar indent , +One of the pre-defined strings +.Cm indent , the width of standard indentation; -.Ar indent-two , +.Cm indent-two , twice -.Ar indent ; -.Ar left , -which has no effect ; -.Ar right , -which justifies to the right margin; and -.Ar center , +.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 -As a precalculated width for a named macro. +A macro invocation, which selects a predefined width +associated with that macro. The most popular is the imaginary macro .Ar \&Ds , which resolves to -.Ar 6n . +.Sy 6n . .It -As a scaling unit following the syntax described in +A width using the syntax described in .Sx Scaling Widths . .It -As the calculated string length of the opaque string. +An arbitrary string, which indents by the length of this string. .El .Pp -If not provided an argument, it will be ignored. +When the argument is missing, +.Fl offset +is ignored. .It Fl compact -Do not assert a vertical space before the block. -.It Fl file Ar file -Prepend the file -.Ar file -before any text or macros within the block. +Do not assert vertical space before the display. .El .Pp Examples: .Bd -literal -offset indent -\&.Bd \-unfilled \-offset two-indent \-compact +\&.Bd \-literal \-offset indent \-compact Hello world. \&.Ed .Ed @@ -1115,120 +1141,191 @@ See also 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 +Keep the output generated from each macro input line together +on one single output line. +Line breaks in free-form 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. +.Pp +The following example will not break within each +.Sx \&Op +macro line: +.Bd -literal -offset indent +\&.Bk \-words +\&.Op Fl f Ar flags +\&.Op Fl o Ar output +\&.Ek +.Ed +.Pp +Be careful in using over-long lines within a keep block! +Doing so will clobber the right margin. .Ss \&Bl -Begins a list composed of one or more list entries. -A list is associated with a type, which is a required argument. -Other arguments are -.Fl width , -defined per-type as accepting a literal or -.Sx Scaling Widths -value; -.Fl offset , -also accepting a literal or -.Sx Scaling Widths -value setting the list's global offset; and -.Fl compact , -suppressing the default vertical space printed before each list entry. -A list entry is specified by the +Begin a list. +Lists consist of items specified using the .Sx \&It -macro, which consists of a head and optional body (depending on the list -type). +macro, containing a head or a body or both. +The list syntax is as follows: +.Bd -ragged -offset indent +.Pf \. Sx \&Bl +.Fl Ns Ar type +.Op Fl width Ar val +.Op Fl offset Ar val +.Op Fl compact +.Op HEAD ... +.Ed +.Pp +The list +.Ar type +is mandatory and must be specified first. +The +.Fl width +and +.Fl offset +arguments accept +.Sx Scaling Widths +or use the length of the given string. +The +.Fl offset +is a global indentation for the whole list, affecting both item heads +and bodies. +For those list types supporting it, the +.Fl width +argument requests an additional indentation of item bodies, +to be added to the +.Fl offset . +Unless the +.Fl compact +argument is specified, list entries are separated by vertical space. +.Pp A list must specify one of the following list types: .Bl -tag -width 12n -offset indent .It Fl bullet -A list offset by a bullet. -The head of list entries must be empty. -List entry bodies are positioned after the bullet. -The +No item heads can be specified, but a bullet will be printed at the head +of each item. +Item bodies start on the same output line as the bullet +and are indented according to the .Fl width -argument varies the width of list bodies' left-margins. +argument. .It Fl column A columnated list. The .Fl width -argument has no effect. -The number of columns is specified as parameters to the -.Sx \&Bl -macro. -These dictate the width of columns either as +argument has no effect; instead, each argument specifies the width +of one column, using either the .Sx Scaling Widths -or literal text. -If the initial macro of a +syntax or the string length of the argument. +If the first line of the body of a .Fl column list is not an -.Sx \&It , -an .Sx \&It -context spanning each line is implied until an +macro line, .Sx \&It -line macro is encountered, at which point list bodies are interpreted as +contexts spanning one input line each are implied until an +.Sx \&It +macro line is encountered, at which point items start being interpreted as described in the .Sx \&It documentation. .It Fl dash -A list offset by a dash (hyphen). -The head of list entries must be empty. -List entry bodies are positioned past the dash. -The -.Fl width -argument varies the width of list bodies' left-margins. +Like +.Fl bullet , +except that dashes are used in place of bullets. .It Fl diag Like .Fl inset , -but with additional formatting to the head. -The -.Fl width -argument varies the width of list bodies' left-margins. +except that item heads are not parsed for macro invocations. +.\" but with additional formatting to the head. .It Fl enum -An enumerated list offset by the enumeration from 1. -The head of list entries must be empty. -List entry bodies are positioned after the enumeration. -The -.Fl width -argument varies the width of list bodies' left-margins. +A numbered list. +Formatted like +.Fl bullet , +except that cardinal numbers are used in place of bullets, +starting at 1. .It Fl hang Like .Fl tag , -but instead of list bodies positioned after the head, they trail the -head text. -The -.Fl width -argument varies the width of list bodies' left-margins. +except that the first lines of item bodies are not indented, but follow +the item heads like in +.Fl inset +lists. .It Fl hyphen Synonym for .Fl dash . .It Fl inset -List bodies follow the list head. -The +Item bodies follow items heads on the same line, using normal inter-word +spacing. +Bodies are not indented, and the .Fl width argument is ignored. .It Fl item -This produces blocks of text. -The head of list entries must be empty. -The +No item heads can be specified, and none are printed. +Bodies are not indented, and the .Fl width argument is ignored. .It Fl ohang -List bodies are positioned on the line following the head. +Item bodies start on the line following item heads and are not indented. The .Fl width argument is ignored. .It Fl tag -A list offset by list entry heads. List entry bodies are positioned -after the head as specified by the +Item bodies are indented according to the .Fl width argument. +When an item head fits inside the indentation, the item body follows +this head on the same output line. +Otherwise, the body starts on the output line following the head. .El .Pp See also +.Sx \&El +and .Sx \&It . .Ss \&Bo -Begins a block enclosed by square brackets. +Begin a block enclosed by square brackets. Does not have any head arguments. .Pp Examples: -.Bd -literal -offset indent +.Bd -literal -offset indent -compact \&.Bo 1 , \&.Dv BUFSIZ \&Bc .Ed @@ -1239,7 +1336,7 @@ See also Encloses its arguments in square brackets. .Pp Examples: -.D1 \&.Bq 1 , \&Dv BUFSIZ +.Dl \&.Bq 1 , \&Dv BUFSIZ .Pp .Em Remarks : this macro is sometimes abused to emulate optional arguments for @@ -1252,15 +1349,16 @@ and See also .Sx \&Bo . .Ss \&Brc -Closes a +Close a .Sx \&Bro -block. Does not have any tail arguments. +block. +Does not have any tail arguments. .Ss \&Bro -Begins a block enclosed by curly braces. +Begin a block enclosed by curly braces. Does not have any head arguments. .Pp Examples: -.Bd -literal -offset indent +.Bd -literal -offset indent -compact \&.Bro 1 , ... , \&.Va n \&Brc .Ed @@ -1271,7 +1369,7 @@ See also Encloses its arguments in curly braces. .Pp Examples: -.D1 \&.Brq 1 , ... , \&Va n +.Dl \&.Brq 1 , ... , \&Va n .Pp See also .Sx \&Bro . @@ -1280,8 +1378,8 @@ Format the BSD/OS version provided as an argument, or a default value if no argument is provided. .Pp Examples: -.D1 \&.Bsx 1.0 -.D1 \&.Bsx +.Dl \&.Bsx 1.0 +.Dl \&.Bsx .Pp See also .Sx \&At , @@ -1294,14 +1392,14 @@ 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: -.D1 \&.Bx 4.4 -.D1 \&.Bx +.Dl \&.Bx 4.4 +.Dl \&.Bx .Pp See also .Sx \&At , @@ -1313,16 +1411,16 @@ See also and .Sx \&Ux . .Ss \&Cd -Configuration declaration. +Kernel configuration declaration. This denotes strings accepted by .Xr config 8 . .Pp Examples: -.D1 \&.Cd device le0 at scode? +.Dl \&.Cd device le0 at scode? .Pp .Em Remarks : this macro is commonly abused by using quoted literals to retain -white-space and align consecutive +whitespace and align consecutive .Sx \&Cd declarations. This practise is discouraged. @@ -1331,8 +1429,8 @@ Command modifiers. Useful when specifying configuration options or keys. .Pp Examples: -.D1 \&.Cm ControlPath -.D1 \&.Cm ControlMaster +.Dl \&.Cm ControlPath +.Dl \&.Cm ControlMaster .Pp See also .Sx \&Fl . @@ -1343,22 +1441,25 @@ statements. It is followed by a newline. .Pp Examples: -.D1 \&.D1 \&Fl abcdefgh +.Dl \&.D1 \&Fl abcdefgh .Pp See also .Sx \&Bd and .Sx \&Dl . .Ss \&Db -Start a debugging context. -This macro is parsed, but generally ignored. +Switch debugging mode. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Db Cm on | off +.Pp +This macro is ignored by +.Xr mandoc 1 . .Ss \&Dc -Closes a +Close a .Sx \&Do -block. Does not have any tail arguments. +block. +Does not have any tail arguments. .Ss \&Dd Document date. This is the mandatory first macro of any @@ -1366,22 +1467,22 @@ This is the mandatory first macro of any manual. Its syntax is as follows: .Pp -.D1 Pf \. Sx \&Dd Cm date +.D1 Pf \. Sx \&Dd Op Ar date .Pp The -.Cm date -field may be either +.Ar date +may be either .Ar $\&Mdocdate$ , which signifies the current manual revision date dictated by .Xr cvs 1 , or instead a valid canonical date as specified by .Sx Dates . -If a date does not conform, the current date is used instead. +If a date does not conform or is empty, the current date is used. .Pp Examples: -.D1 \&.Dd $\&Mdocdate$ -.D1 \&.Dd $\&Mdocdate: July 21 2007$ -.D1 \&.Dd July 21, 2007 +.Dl \&.Dd $\&Mdocdate$ +.Dl \&.Dd $\&Mdocdate: July 21 2007$ +.Dl \&.Dd July 21, 2007 .Pp See also .Sx \&Dt @@ -1394,23 +1495,30 @@ invocations. It is followed by a newline. .Pp Examples: -.D1 \&.Dl % mandoc mdoc.7 | less +.Dl \&.Dl % mandoc mdoc.7 \e(ba less .Pp See also .Sx \&Bd and .Sx \&D1 . .Ss \&Do -Begins a block enclosed by double quotes. Does not have any head -arguments. +Begin a block enclosed by double quotes. +Does not have any head arguments. .Pp Examples: -.D1 \&.D1 \&Do April is the cruellest month \&Dc \e(em T.S. Eliot +.Bd -literal -offset indent -compact +\&.Do +April is the cruellest month +\&.Dc +\e(em T.S. Eliot +.Ed .Pp See also .Sx \&Dq . .Ss \&Dq -Encloses its arguments in double quotes. +Encloses its arguments in +.Dq typographic +double-quotes. .Pp Examples: .Bd -literal -offset indent -compact @@ -1419,6 +1527,9 @@ Examples: .Ed .Pp See also +.Sx \&Qq , +.Sx \&Sq , +and .Sx \&Do . .Ss \&Dt Document title. @@ -1429,22 +1540,22 @@ Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Dt .Oo -.Cm title +.Ar title .Oo -.Cm section -.Op Cm volume | arch +.Ar section +.Op Ar volume | arch .Oc .Oc .Ed .Pp Its arguments are as follows: .Bl -tag -width Ds -offset Ds -.It Cm title +.It Ar title The document's title (name), defaulting to -.Qq UNKNOWN +.Dq UNKNOWN if unspecified. It should be capitalised. -.It Cm section +.It Ar section The manual section. This may be one of .Ar 1 @@ -1481,9 +1592,9 @@ or .Ar paper .Pq paper . It should correspond to the manual's filename suffix and defaults to -.Qq 1 +.Dq 1 if unspecified. -.It Cm volume +.It Ar volume This overrides the volume inferred from .Ar section . This field is optional, and if specified, must be one of @@ -1512,10 +1623,10 @@ This field is optional, and if specified, must be one of or .Ar CON .Pq contributed manuals . -.It Cm arch +.It Ar arch This specifies a specific relevant architecture. If -.Cm volume +.Ar volume is not provided, it may be used in its place, else it may be used subsequent that. It, too, is optional. @@ -1536,6 +1647,7 @@ It must be one of .Ar luna88k , .Ar mac68k , .Ar macppc , +.Ar mips64 , .Ar mvme68k , .Ar mvme88k , .Ar mvmeppc , @@ -1551,9 +1663,9 @@ or .El .Pp Examples: -.D1 \&.Dt FOO 1 -.D1 \&.Dt FOO 4 KM -.D1 \&.Dt FOO 9 i386 +.Dl \&.Dt FOO 1 +.Dl \&.Dt FOO 4 KM +.Dl \&.Dt FOO 9 i386 .Pp See also .Sx \&Dd @@ -1563,8 +1675,8 @@ and Defined variables such as preprocessor constants. .Pp Examples: -.D1 \&.Dv BUFSIZ -.D1 \&.Dv STDOUT_FILENO +.Dl \&.Dv BUFSIZ +.Dl \&.Dv STDOUT_FILENO .Pp See also .Sx \&Er . @@ -1573,8 +1685,8 @@ Format the DragonFly BSD version provided as an argument, or a default value if no argument is provided. .Pp Examples: -.D1 \&.Dx 2.4.1 -.D1 \&.Dx +.Dl \&.Dx 2.4.1 +.Dl \&.Dx .Pp See also .Sx \&At , @@ -1586,11 +1698,28 @@ See also and .Sx \&Ux . .Ss \&Ec +Close a scope started by +.Sx \&Eo . +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Ec Op Ar TERM +.Pp +The +.Ar TERM +argument is used as the enclosure tail, for example, specifying \e(rq +will emulate +.Sx \&Dc . .Ss \&Ed +End a display context started by +.Sx \&Bd . .Ss \&Ef +End a font mode context started by +.Sx \&Bf . .Ss \&Ek +End a keep context started by +.Sx \&Bk . .Ss \&El -Ends a list context started by +End a list context started by .Sx \&Bl . .Pp See also @@ -1603,38 +1732,60 @@ Note that this is a presentation term and should not be used for stylistically decorating technical terms. .Pp Examples: -.D1 \&.Em Warnings! -.D1 \&.Em Remarks : +.Dl \&.Em Warnings! +.Dl \&.Em Remarks : +.Pp +See also +.Sx \&Bf , +.Sx \&Sy , +and +.Sx \&Li . .Ss \&En +This macro is obsolete and not implemented in +.Xr mandoc 1 . .Ss \&Eo +An arbitrary enclosure. +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Eo Op Ar TERM +.Pp +The +.Ar TERM +argument is used as the enclosure head, for example, specifying \e(lq +will emulate +.Sx \&Do . .Ss \&Er Display error constants. .Pp Examples: -.D1 \&.Er EPERM -.D1 \&.Er ENOENT +.Dl \&.Er EPERM +.Dl \&.Er ENOENT .Pp See also .Sx \&Dv . .Ss \&Es +This macro is obsolete and not implemented. .Ss \&Ev Environmental variables such as those specified in .Xr environ 7 . .Pp Examples: -.D1 \&.Ev DISPLAY -.D1 \&.Ev PATH +.Dl \&.Ev DISPLAY +.Dl \&.Ev PATH .Ss \&Ex -Inserts text regarding a utility's exit values. -This macro must have first the -.Fl std -argument specified, then an optional -.Ar utility . -If +Insert a standard sentence regarding exit values. +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Ex Fl std Op Ar utility +.Pp +When .Ar utility -is not provided, the document's name as stipulated in +is not specified, the document's name set by .Sx \&Nm -is provided. +is used. +.Pp +See also +.Sx \&Rv . .Ss \&Fa Function argument. Its syntax is as follows: @@ -1660,13 +1811,15 @@ Furthermore, if the following macro is another the last argument will also have a trailing comma. .Pp Examples: -.D1 \&.Fa \(dqconst char *p\(dq -.D1 \&.Fa \(dqint a\(dq \(dqint b\(dq \(dqint c\(dq -.D1 \&.Fa foo +.Dl \&.Fa \(dqconst char *p\(dq +.Dl \&.Fa \(dqint a\(dq \(dqint b\(dq \(dqint c\(dq +.Dl \&.Fa foo .Pp See also .Sx \&Fo . .Ss \&Fc +End a function context started by +.Sx \&Fo . .Ss \&Fd Historically used to document include files. This usage has been deprecated in favour of @@ -1688,10 +1841,10 @@ If the argument is a macro, a hyphen is prefixed to the subsequent macro output. .Pp Examples: -.D1 \&.Fl a b c -.D1 \&.Fl \&Pf a b -.D1 \&.Fl -.D1 \&.Op \&Fl o \&Ns \&Ar file +.Dl \&.Fl a b c +.Dl \&.Fl \&Pf a b +.Dl \&.Fl +.Dl \&.Op \&Fl o \&Ns \&Ar file .Pp See also .Sx \&Cm . @@ -1710,14 +1863,17 @@ are delimited by commas. If no arguments are specified, blank parenthesis are output. .Pp Examples: -.D1 \&.Fn "int funcname" "int arg0" "int arg1" -.D1 \&.Fn funcname "int arg0" -.D1 \&.Fn funcname arg0 +.Dl \&.Fn "int funcname" "int arg0" "int arg1" +.Dl \&.Fn funcname "int arg0" +.Dl \&.Fn funcname arg0 .Bd -literal -offset indent -compact \&.Ft functype \&.Fn funcname .Ed .Pp +When referring to a function documented in another manual page, use +.Sx \&Xr +instead. See also .Sx MANUAL STRUCTURE and @@ -1752,6 +1908,7 @@ See also .Sx \&Fa , .Sx \&Fc , and +.Sx \&Ft . .Ss \&Ft A function type. Its syntax is as follows: @@ -1759,7 +1916,7 @@ Its syntax is as follows: .D1 Pf \. Sx \&Ft Cm functype .Pp Examples: -.D1 \&.Ft int +.Dl \&.Ft int .Bd -literal -offset indent -compact \&.Ft functype \&.Fn funcname @@ -1771,12 +1928,14 @@ See also and .Sx \&Fo . .Ss \&Fx -Format the FreeBSD version provided as an argument, or a default value +Format the +.Fx +version provided as an argument, or a default value if no argument is provided. .Pp Examples: -.D1 \&.Fx 7.1 -.D1 \&.Fx +.Dl \&.Fx 7.1 +.Dl \&.Fx .Pp See also .Sx \&At , @@ -1788,20 +1947,37 @@ See also and .Sx \&Ux . .Ss \&Hf +This macro is obsolete and not implemented. .Ss \&Ic +Designate an internal or interactive command. +This is similar to +.Sx \&Cm +but used for instructions rather than values. +.Pp +Examples: +.Dl \&.Ic hash +.Dl \&.Ic alias +.Pp +Note that using +.Sx \&Bd Fl literal +or +.Sx \&D1 +is preferred for displaying code; the +.Sx \&Ic +macro is used when referring to specific instructions. .Ss \&In An -.Qq include +.Dq include file. In the .Em SYNOPSIS section (only if invoked as the line macro), the first argument is preceded by -.Qq #include , -the arguments is enclosed in angled braces. +.Dq #include , +the arguments is enclosed in angle brackets. .Pp Examples: -.D1 \&.In sys/types +.Dl \&.In sys/types .Pp See also .Sx MANUAL STRUCTURE . @@ -1869,8 +2045,8 @@ 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 +interpreted as a macro). +Note that the tab phrase delimiter may only be used within the .Sx \&It line itself. Subsequent this, only the @@ -1881,7 +2057,7 @@ phrases on an .Sx \&It , for example, .Pp -.D1 .It \(dqcol1 ; col2 ;\(dq \&; +.Dl .It \(dqcol1 ; col2 ;\(dq \&; .Pp will preserve the semicolon whitespace except for the last. .Pp @@ -1908,9 +2084,18 @@ section as described in .Sx MANUAL STRUCTURE . .Pp Examples: -.D1 \&.Lb libz -.D1 \&.Lb mdoc +.Dl \&.Lb libz +.Dl \&.Lb mdoc .Ss \&Li +Denotes text that should be in a literal font mode. +Note that this is a presentation term and should not be used for +stylistically decorating technical terms. +.Pp +See also +.Sx \&Bf , +.Sx \&Sy , +and +.Sx \&Em . .Ss \&Lk Format a hyperlink. Its syntax is as follows: @@ -1918,34 +2103,122 @@ Its syntax is as follows: .D1 Pf \. Sx \&Lk Cm uri Op Cm name .Pp Examples: -.D1 \&.Lk http://bsd.lv "The BSD.lv Project" -.D1 \&.Lk http://bsd.lv +.Dl \&.Lk http://bsd.lv \*qThe BSD.lv Project\*q +.Dl \&.Lk http://bsd.lv .Pp See also .Sx \&Mt . .Ss \&Lp +Synonym for +.Sx \&Pp . .Ss \&Ms +Display a mathematical symbol. +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Ms Cm symbol +.Pp +Examples: +.Dl \&.Ms sigma +.Dl \&.Ms aleph .Ss \&Mt Format a -.Qq mailto: +.Dq mailto: hyperlink. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Mt Cm address .Pp Examples: -.D1 \&.Mt discuss@manpages.bsd.lv +.Dl \&.Mt discuss@manpages.bsd.lv .Ss \&Nd +A one line description of the manual's content. +This may only be invoked in the +.Em SYNOPSIS +section subsequent the +.Sx \&Nm +macro. +.Pp +Examples: +.Dl \&.Sx \&Nd mdoc language reference +.Dl \&.Sx \&Nd format and display UNIX manuals +.Pp +The +.Sx \&Nd +macro technically accepts child macros and terminates with a subsequent +.Sx \&Sh +invocation. +Do not assume this behaviour: some +.Xr whatis 1 +database generators are not smart enough to parse more than the line +arguments and will display macros verbatim. +.Pp +See also +.Sx \&Nm . .Ss \&Nm +The name of the manual page, or \(em in particular in section 1, 6, +and 8 pages \(em of an additional command or feature documented in +the manual page. +When first invoked, the +.Sx \&Nm +macro expects a single argument, the name of the manual page. +Usually, the first invocation happens in the +.Em NAME +section of the page. +The specified name will be remembered and used whenever the macro is +called again without arguments later in the page. +The +.Sx \&Nm +macro uses +.Sx Block full-implicit +semantics when invoked as the first macro on an input line in the +.Em SYNOPSIS +section; otherwise, it uses ordinary +.Sx In-line +semantics. +.Pp +Examples: +.Bd -literal -offset indent +\&.Sh SYNOPSIS +\&.Nm cat +\&.Op Fl benstuv +\&.Op Ar +.Ed +.Pp +In the +.Em SYNOPSIS +of section 2, 3 and 9 manual pages, use the +.Sx \&Fn +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. +.Pp +Examples: +.Dl \&.Sx \&Fl ab \&No cd \&Fl ef .Ss \&Ns +Suppress a space. +Following invocation, text is interpreted as free-form text until a +macro is encountered. +.Pp +Examples: +.Dl \&.Fl o \&Ns \&Ar output +.Pp +See also +.Sx \&No +and +.Sx \&Sm . .Ss \&Nx -Format the NetBSD version provided as an argument, or a default value if +Format the +.Nx +version provided as an argument, or a default value if no argument is provided. .Pp Examples: -.D1 \&.Nx 5.01 -.D1 \&.Nx +.Dl \&.Nx 5.01 +.Dl \&.Nx .Pp See also .Sx \&At , @@ -1957,8 +2230,30 @@ See also and .Sx \&Ux . .Ss \&Oc +Close multi-line +.Sx \&Oo +context. .Ss \&Oo +Multi-line version of +.Sx \&Op . +.Pp +Examples: +.Bd -literal -offset indent -compact +\&.Oo +\&.Op Fl flag Ns Ar value +\&.Oc +.Ed .Ss \&Op +Command-line option. +Used when listing options to command-line utilities. +Prints the argument(s) in brackets. +.Pp +Examples: +.Dl \&.Op \&Fl a \&Ar b +.Dl \&.Op \&Ar a | b +.Pp +See also +.Sx \&Oo . .Ss \&Os Document operating system version. This is the mandatory third macro of @@ -1967,7 +2262,7 @@ any file. Its syntax is as follows: .Pp -.D1 Pf \. Sx \&Os Op Cm system +.D1 Pf \. Sx \&Os Op Cm system Op Cm version .Pp The optional .Cm system @@ -1976,9 +2271,9 @@ Left unspecified, it defaults to the local operating system version. This is the suggested form. .Pp Examples: -.D1 \&.Os -.D1 \&.Os KTH/CSC/TCS -.D1 \&.Os BSD 4.3 +.Dl \&.Os +.Dl \&.Os KTH/CSC/TCS +.Dl \&.Os BSD 4.3 .Pp See also .Sx \&Dd @@ -1990,12 +2285,14 @@ Unknown usage. .Em Remarks : this macro has been deprecated. .Ss \&Ox -Format the OpenBSD version provided as an argument, or a default value +Format the +.Ox +version provided as an argument, or a default value if no argument is provided. .Pp Examples: -.D1 \&.Ox 4.5 -.D1 \&.Ox +.Dl \&.Ox 4.5 +.Dl \&.Ox .Pp See also .Sx \&At , @@ -2007,22 +2304,77 @@ See also and .Sx \&Ux . .Ss \&Pa +A file-system path. +If an argument is not provided, the string +.Dq \(ti +is used as a default. +.Pp +Examples: +.Dl \&.Pa /usr/bin/mandoc +.Dl \&.Pa /usr/share/man/man7/mdoc.7 +.Pp +See also +.Sx \&Lk . .Ss \&Pc +Close parenthesised context opened by +.Sx \&Po . .Ss \&Pf +Removes the space +.Pq Dq prefix +between its arguments. +Its syntax is as follows: +.Pp +.D1 Pf \. \&Pf Cm prefix suffix +.Pp +The +.Cm suffix +argument may be a macro. +.Pp +Examples: +.Dl \&.Pf \e. \&Sx \&Pf \&Cm prefix suffix .Ss \&Po +Multi-line version of +.Sx \&Pq . .Ss \&Pp +Break a paragraph. +This will assert vertical space between prior and subsequent macros +and/or text. .Ss \&Pq +Parenthesised enclosure. +.Pp +See also +.Sx \&Po . .Ss \&Qc +Close quoted context opened by +.Sx \&Qo . .Ss \&Ql +Format a single-quoted literal. +See also +.Sx \&Qq +and +.Sx \&Sq . .Ss \&Qo +Multi-line version of +.Sx \&Qq . .Ss \&Qq +Encloses its arguments in +.Dq typewriter +double-quotes. +Consider using +.Sx \&Dq . +.Pp +See also +.Sx \&Dq , +.Sx \&Sq , +and +.Sx \&Qo . .Ss \&Re -Closes a +Close an .Sx \&Rs block. Does not have any tail arguments. .Ss \&Rs -Begins a bibliographic +Begin a bibliographic .Pq Dq reference block. Does not have any head arguments. @@ -2062,25 +2414,214 @@ 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 . +If +.Ar function +is not provided, the document's name as stipulated by the first +.Sx \&Nm +is provided. +.Pp +See also +.Sx \&Ex . .Ss \&Sc +Close single-quoted context opened by +.Sx \&So . .Ss \&Sh +Begin a new section. +For a list of conventional manual sections, see +.Sx MANUAL STRUCTURE . +These sections should be used unless it's absolutely necessary that +custom sections be used. +.Pp +Section names should be unique so that they may be keyed by +.Sx \&Sx . +.Pp +See also +.Sx \&Pp , +.Sx \&Ss , +and +.Sx \&Sx . .Ss \&Sm +Switches the spacing mode for output generated from macros. +Its syntax is as follows: +.Pp +.D1 Pf \. Sx \&Sm Cm on | off +.Pp +By default, spacing is +.Cm on . +When switched +.Cm off , +no white space is inserted between macro arguments and between the +output generated from adjacent macros, but free-form text lines +still get normal spacing between words and sentences. .Ss \&So +Multi-line version of +.Sx \&Sq . .Ss \&Sq +Encloses its arguments in +.Dq typewriter +single-quotes. +.Pp +See also +.Sx \&Dq , +.Sx \&Qq , +and +.Sx \&So . .Ss \&Ss +Begin a new sub-section. +Unlike with +.Sx \&Sh , +there's no convention for sub-sections. +Conventional sections, as described in +.Sx MANUAL STRUCTURE , +rarely have sub-sections. +.Pp +Sub-section names should be unique so that they may be keyed by +.Sx \&Sx . +.Pp +See also +.Sx \&Pp , +.Sx \&Sh , +and +.Sx \&Sx . .Ss \&St +Replace an abbreviation for a standard with the full form. +The following standards are recognised: +.Pp +.Bl -tag -width "-p1003.1g-2000X" -compact +.It \-p1003.1-88 +.St -p1003.1-88 +.It \-p1003.1-90 +.St -p1003.1-90 +.It \-p1003.1-96 +.St -p1003.1-96 +.It \-p1003.1-2001 +.St -p1003.1-2001 +.It \-p1003.1-2004 +.St -p1003.1-2004 +.It \-p1003.1-2008 +.St -p1003.1-2008 +.It \-p1003.1 +.St -p1003.1 +.It \-p1003.1b +.St -p1003.1b +.It \-p1003.1b-93 +.St -p1003.1b-93 +.It \-p1003.1c-95 +.St -p1003.1c-95 +.It \-p1003.1g-2000 +.St -p1003.1g-2000 +.It \-p1003.1i-95 +.St -p1003.1i-95 +.It \-p1003.2-92 +.St -p1003.2-92 +.It \-p1003.2a-92 +.St -p1003.2a-92 +.It \-p1387.2-95 +.St -p1387.2-95 +.It \-p1003.2 +.St -p1003.2 +.It \-p1387.2 +.St -p1387.2 +.It \-isoC +.St -isoC +.It \-isoC-90 +.St -isoC-90 +.It \-isoC-amd1 +.St -isoC-amd1 +.It \-isoC-tcor1 +.St -isoC-tcor1 +.It \-isoC-tcor2 +.St -isoC-tcor2 +.It \-isoC-99 +.St -isoC-99 +.It \-iso9945-1-90 +.St -iso9945-1-90 +.It \-iso9945-1-96 +.St -iso9945-1-96 +.It \-iso9945-2-93 +.St -iso9945-2-93 +.It \-ansiC +.St -ansiC +.It \-ansiC-89 +.St -ansiC-89 +.It \-ansiC-99 +.St -ansiC-99 +.It \-ieee754 +.St -ieee754 +.It \-iso8802-3 +.St -iso8802-3 +.It \-ieee1275-94 +.St -ieee1275-94 +.It \-xpg3 +.St -xpg3 +.It \-xpg4 +.St -xpg4 +.It \-xpg4.2 +.St -xpg4.2 +.St -xpg4.3 +.It \-xbd5 +.St -xbd5 +.It \-xcu5 +.St -xcu5 +.It \-xsh5 +.St -xsh5 +.It \-xns5 +.St -xns5 +.It \-xns5.2 +.St -xns5.2 +.It \-xns5.2d2.0 +.St -xns5.2d2.0 +.It \-xcurses4.2 +.St -xcurses4.2 +.It \-susv2 +.St -susv2 +.It \-susv3 +.St -susv3 +.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. +.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 \&Li , +and +.Sx \&Em . .Ss \&Tn +Format a tradename. +.Pp +Examples: +.Dl \&.Tn IBM .Ss \&Ud Prints out -.Dq currently under development. +.Dq currently under development . .Ss \&Ux Format the UNIX name. Accepts no argument. .Pp Examples: -.D1 \&.Ux +.Dl \&.Ux .Pp See also .Sx \&At , @@ -2092,6 +2633,11 @@ See also 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 @@ -2110,8 +2656,8 @@ Note that this should not be confused with which is used for function return types. .Pp Examples: -.D1 \&.Vt unsigned char -.D1 \&.Vt extern const char * const sys_signame[] \&; +.Dl \&.Vt unsigned char +.Dl \&.Vt extern const char * const sys_signame[] \&; .Pp See also .Sx MANUAL STRUCTURE @@ -2121,9 +2667,13 @@ and Close a scope opened by .Sx \&Xo . .Ss \&Xo -Open an extension scope. -This macro originally existed to extend the 9-argument limit of troff; -since this limit has been lifted, the macro has been deprecated. +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 . @@ -2142,234 +2692,234 @@ is followed by non-punctuation, an .Sx \&Ns is inserted into the token stream. This behaviour is for compatibility with -.Xr groff 1 . +GNU troff. .Pp Examples: -.D1 \&.Xr mandoc 1 -.D1 \&.Xr mandoc 1 \&; -.D1 \&.Xr mandoc 1 \&Ns s behaviour +.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 Cm height +.Pp +The +.Cm height +argument must be formatted as described in +.Sx Scaling Widths . +If unspecified, +.Sx \&sp +asserts a single vertical space. .Sh COMPATIBILITY This section documents compatibility between mandoc and other other troff implementations, at this time limited to GNU troff .Pq Qq groff . The term .Qq historic groff -refers to groff versions before the +refers to groff versions before 1.17, +which featured a significant update of the .Pa doc.tmac -file re-write -.Pq somewhere between 1.15 and 1.19 . +file. .Pp Heirloom troff, the other significant troff implementation accepting \-mdoc, is similar to historic groff. .Pp +The following problematic behaviour is found in groff: +.ds hist (Historic groff only.) +.Pp .Bl -dash -compact .It -Old groff fails to assert a newline before -.Sx \&Bd Fl ragged compact . -.It -groff behaves inconsistently when encountering -.Pf non- Sx \&Fa -children of -.Sx \&Fo -regarding spacing between arguments. -In mandoc, this is not the case: each argument is consistently followed -by a single space and the trailing -.Sq \&) -suppresses prior spacing. -.It -groff behaves inconsistently when encountering -.Sx \&Ft +Display macros +.Po +.Sx \&Bd , +.Sx \&Dl , and +.Sx \&D1 +.Pc +may not be nested. +\*[hist] +.It +.Sx \&At +with unknown arguments produces no output at all. +\*[hist] +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 +precede tabulator characters, but treats them as normal text and +outputs a space before them. +.It +.Sx \&Bd Fl ragged compact +does not start a new line. +\*[hist] +.It +.Sx \&Dd +without an argument prints +.Dq Epoch . +In mandoc, it resolves to the current date. +.It +.Sx \&Fl +does not print a dash for an empty argument. +\*[hist] +.It .Sx \&Fn +does not start a new line unless invoked as the line macro in the +.Em SYNOPSIS +section. +\*[hist] +.It +.Sx \&Fo +with +.Pf non- Sx \&Fa +children causes inconsistent spacing between arguments. +In mandoc, a single space is always inserted between arguments. +.It +.Sx \&Ft in the -.Em SYNOPSIS : -at times newline(s) are suppressed depending on whether a prior +.Em SYNOPSIS +causes inconsistent vertical spacing, depending on whether a prior .Sx \&Fn has been invoked. -In mandoc, this is not the case. See .Sx \&Ft and .Sx \&Fn -for the normalised behaviour. +for the normalised behaviour in mandoc. .It -Historic groff does not break before an -.Sx \&Fn -when not invoked as the line macro in the -.Em SYNOPSIS -section. -.It -Historic groff formats the .Sx \&In -badly: trailing arguments are trashed and -.Em SYNOPSIS -is not specially treated. +ignores additional arguments and is not treated specially in the +.Em SYNOPSIS . +\*[hist] .It -groff does not accept the -.Sq \&Ta -pseudo-macro as a line macro. -mandoc does. -.It -The comment syntax -.Sq \e." -is no longer accepted. -.It -In groff, the -.Sx \&Pa -macro does not format its arguments when used in the FILES section under -certain list types. -mandoc does. -.It -Historic groff does not print a dash for empty -.Sx \&Fl -arguments. -mandoc and newer groff implementations do. -.It -groff behaves irregularly when specifying -.Sq \ef -.Sx Text Decoration -within line-macro scopes. -mandoc follows a consistent system. -.It -In mandoc, negative scaling units are truncated to zero; groff would -move to prior lines. -Furthermore, the -.Sq f -scaling unit, while accepted, is rendered as the default unit. -.It -In quoted literals, groff allowed pair-wise double-quotes to produce a -standalone double-quote in formatted output. -This idiosyncratic behaviour is not applicable in mandoc. -.It -Display offsets -.Sx \&Bd -.Fl offset Ar center -and -.Fl offset Ar right -are disregarded in mandoc. -Furthermore, the -.Fl file Ar file -argument is not supported in mandoc. -Lastly, since text is not right-justified in mandoc (or even groff), -.Fl ragged -and -.Fl filled -are aliases, as are -.Fl literal -and -.Fl unfilled . -.It -Historic groff has many un-callable macros. -Most of these (excluding some block-level macros) are now callable. -.It -The vertical bar -.Sq \(ba -made historic groff -.Qq go orbital -but has been a proper delimiter since then. -.It -.Sx \&It Fl nested -is assumed for all lists (it wasn't in historic groff): any list may be -nested and +.Sx \&It +sometimes requires a +.Fl nested +flag. +\*[hist] +In new groff and mandoc, any list may be nested by default and .Fl enum lists will restart the sequence only for the sub-list. .It -Some manuals use .Sx \&Li -incorrectly by following it with a reserved character and expecting the -delimiter to render. -This is not supported in mandoc. +followed by a reserved character is incorrectly used in some manuals +instead of properly quoting that character, which sometimes works with +historic groff. .It -In groff, the -.Sx \&Cd , -.Sx \&Er , -.Sx \&Ex , +.Sx \&Lk +only accepts a single link-name argument; the remainder is misformatted. +.It +.Sx \&Pa +does not format its arguments when used in the FILES section under +certain list types. +.It +.Sx \&Ta +can only be called by other macros, but not at the beginning of a line. +.It +.Sx \&%C +is not implemented. +.It +Historic groff only allows up to eight or nine arguments per macro input +line, depending on the exact situation. +Providing more arguments causes garbled output. +The number of arguments on one input line is not limited with mandoc. +.It +Historic groff has many un-callable macros. +Most of these (excluding some block-level macros) are callable +in new groff and mandoc. +.It +.Sq \(ba +(vertical bar) is not fully supported as a delimiter. +\*[hist] +.It +.Sq \ef +.Pq font face and -.Sx \&Rv -macros were stipulated only to occur in certain manual sections. -mandoc does not have these restrictions. +.Sq \ef +.Pq font family face +.Sx Text Decoration +escapes behave irregularly when specified within line-macro scopes. .It -Newer groff and mandoc print -.Qq AT&T UNIX -prior to unknown arguments of -.Sx \&At ; -older groff did nothing. +Negative scaling units return to prior lines. +Instead, mandoc truncates them to zero. +.El +.Pp +The following features are unimplemented in mandoc: +.Pp +.Bl -dash -compact +.It +.Sx \&Bd +.Fl file Ar file . +.It +.Sx \&Bd +.Fl offset Ar center +and +.Fl offset Ar right . +Groff does not implement centered and flush-right rendering either, +but produces large indentations. +.It +The +.Sq \eh +.Pq horizontal position , +.Sq \ev +.Pq vertical position , +.Sq \em +.Pq text colour , +.Sq \eM +.Pq text filling colour , +.Sq \ez +.Pq zero-length character , +.Sq \ew +.Pq string length , +.Sq \ek +.Pq horizontal position marker , +.Sq \eo +.Pq text overstrike , +and +.Sq \es +.Pq text size +escape sequences are all discarded in mandoc. +.It +The +.Sq \ef +scaling unit is accepted by mandoc, but rendered as the default unit. +.It +In quoted literals, groff allows pairwise double-quotes to produce a +standalone double-quote in formatted output. +This is not supported by mandoc. .El .Sh SEE ALSO +.Xr man 1 , .Xr mandoc 1 , +.Xr man 7 , .Xr mandoc_char 7 +.Xr roff 7 , +.Xr tbl 7 +.Sh HISTORY +The +.Nm +language first appeared as a troff macro package in +.Bx 4.4 . +It was later significantly updated by Werner Lemberg and Ruslan Ermilov +in groff-1.17. +The standalone implementation that is part of the +.Xr mandoc 1 +utility written by Kristaps Dzonsons appeared in +.Ox 4.6 . .Sh AUTHORS The .Nm reference was written by .An Kristaps Dzonsons Aq kristaps@bsd.lv . -.\" -.\" XXX: this really isn't the place for these caveats. -.\" . -.\" . -.\" .Sh CAVEATS -.\" There are many ambiguous parts of mdoc. -.\" . -.\" .Pp -.\" .Bl -dash -compact -.\" .It -.\" .Sq \&Fa -.\" should be -.\" .Sq \&Va -.\" as function arguments are variables. -.\" .It -.\" .Sq \&Ft -.\" should be -.\" .Sq \&Vt -.\" as function return types are still types. Furthermore, the -.\" .Sq \&Ft -.\" should be removed and -.\" .Sq \&Fo , -.\" which ostensibly follows it, should follow the same convention as -.\" .Sq \&Va . -.\" .It -.\" .Sq \&Va -.\" should formalise that only one or two arguments are acceptable: a -.\" variable name and optional, preceding type. -.\" .It -.\" .Sq \&Fd -.\" is ambiguous. It's commonly used to indicate an include file in the -.\" synopsis section. -.\" .Sq \&In -.\" should be used, instead. -.\" .It -.\" Only the -.\" .Sq \-literal -.\" argument to -.\" .Sq \&Bd -.\" makes sense. The remaining ones should be removed. -.\" .It -.\" The -.\" .Sq \&Xo -.\" and -.\" .Sq \&Xc -.\" macros should be deprecated. -.\" .It -.\" The -.\" .Sq \&Dt -.\" macro lacks clarity. It should be absolutely clear which title will -.\" render when formatting the manual page. -.\" .It -.\" A -.\" .Sq \&Lx -.\" should be provided for Linux (\(`a la -.\" .Sq \&Ox , -.\" .Sq \&Nx -.\" etc.). -.\" .It -.\" There's no way to refer to references in -.\" .Sq \&Rs/Re -.\" blocks. -.\" .It -.\" The \-split and \-nosplit dictates via -.\" .Sq \&An -.\" are re-set when entering and leaving the AUTHORS section. -.\" .El -.\" . diff --git a/commands/mdocml/mdoc.c b/usr.bin/mdocml/dist/mdoc.c similarity index 76% rename from commands/mdocml/mdoc.c rename to usr.bin/mdocml/dist/mdoc.c index 79624ae83..b6216b5f0 100644 --- a/commands/mdocml/mdoc.c +++ b/usr.bin/mdocml/dist/mdoc.c @@ -1,6 +1,7 @@ -/* $Id: mdoc.c,v 1.146 2010/06/12 11:58:22 kristaps Exp $ */ +/* $Vendor-Id: mdoc.c,v 1.177 2011/01/03 11:27:33 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2010 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 @@ -21,7 +22,6 @@ #include #include -#include #include #include #include @@ -98,15 +98,16 @@ static int node_append(struct mdoc *, struct mdoc_node *); static int mdoc_ptext(struct mdoc *, int, char *, int); static int mdoc_pmacro(struct mdoc *, int, char *, int); -static int macrowarn(struct mdoc *, int, - const char *, int); +static int mdoc_span_alloc(struct mdoc *, + const struct tbl_span *); const struct mdoc_node * mdoc_node(const struct mdoc *m) { - return(MDOC_HALT & m->flags ? NULL : m->first); + assert( ! (MDOC_HALT & m->flags)); + return(m->first); } @@ -114,7 +115,8 @@ const struct mdoc_meta * mdoc_meta(const struct mdoc *m) { - return(MDOC_HALT & m->flags ? NULL : &m->meta); + assert( ! (MDOC_HALT & m->flags)); + return(&m->meta); } @@ -191,7 +193,7 @@ mdoc_free(struct mdoc *mdoc) * Allocate volatile and non-volatile parse resources. */ struct mdoc * -mdoc_alloc(void *data, int pflags, mandocmsg msg) +mdoc_alloc(struct regset *regs, void *data, mandocmsg msg) { struct mdoc *p; @@ -199,7 +201,7 @@ mdoc_alloc(void *data, int pflags, mandocmsg msg) p->msg = msg; p->data = data; - p->pflags = pflags; + p->regs = regs; mdoc_hash_init(); mdoc_alloc1(p); @@ -215,14 +217,30 @@ int mdoc_endparse(struct mdoc *m) { - if (MDOC_HALT & m->flags) - return(0); - else if (mdoc_macroend(m)) + assert( ! (MDOC_HALT & m->flags)); + if (mdoc_macroend(m)) return(1); m->flags |= MDOC_HALT; return(0); } +int +mdoc_addspan(struct mdoc *m, const struct tbl_span *sp) +{ + + assert( ! (MDOC_HALT & m->flags)); + + /* No text before an initial macro. */ + + if (SEC_NONE == m->lastnamed) { + /* FIXME: grab from span. */ + mdoc_pmsg(m, 0, 0, MANDOCERR_NOTEXT); + return(1); + } + + return(mdoc_span_alloc(m, sp)); +} + /* * Main parse routine. Parses a single line -- really just hands off to @@ -232,10 +250,23 @@ int mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs) { - if (MDOC_HALT & m->flags) - return(0); + assert( ! (MDOC_HALT & m->flags)); m->flags |= MDOC_NEWLINE; + + /* + * Let the roff nS register switch SYNOPSIS mode early, + * such that the parser knows at all times + * 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) + m->flags |= MDOC_SYNOPSIS; + else + m->flags &= ~MDOC_SYNOPSIS; + } + return(('.' == buf[offs] || '\'' == buf[offs]) ? mdoc_pmacro(m, ln, buf, offs) : mdoc_ptext(m, ln, buf, offs)); @@ -258,23 +289,25 @@ mdoc_vmsg(struct mdoc *mdoc, enum mandocerr t, int -mdoc_macro(struct mdoc *m, enum mdoct tok, - int ln, int pp, int *pos, char *buf) +mdoc_macro(MACRO_PROT_ARGS) { assert(tok < MDOC_MAX); /* If we're in the body, deny prologue calls. */ if (MDOC_PROLOGUE & mdoc_macros[tok].flags && - MDOC_PBODY & m->flags) - return(mdoc_pmsg(m, ln, pp, MANDOCERR_BADBODY)); + MDOC_PBODY & m->flags) { + mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY); + return(1); + } /* If we're in the prologue, deny "body" macros. */ if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) && ! (MDOC_PBODY & m->flags)) { - if ( ! mdoc_pmsg(m, ln, pp, MANDOCERR_BADPROLOG)) - return(0); + mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG); + if (NULL == m->meta.msec) + m->meta.msec = mandoc_strdup("1"); if (NULL == m->meta.title) m->meta.title = mandoc_strdup("UNKNOWN"); if (NULL == m->meta.vol) @@ -286,7 +319,7 @@ mdoc_macro(struct mdoc *m, enum mdoct tok, m->flags |= MDOC_PBODY; } - return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf)); + return((*mdoc_macros[tok].fp)(m, tok, line, ppos, pos, buf)); } @@ -315,10 +348,25 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p) p->parent->nchild++; + /* + * Copy over the normalised-data pointer of our parent. Not + * everybody has one, but copying a null pointer is fine. + */ + + switch (p->type) { + case (MDOC_BODY): + /* FALLTHROUGH */ + case (MDOC_TAIL): + /* FALLTHROUGH */ + case (MDOC_HEAD): + p->norm = p->parent->norm; + break; + default: + break; + } + if ( ! mdoc_valid_pre(mdoc, p)) return(0); - if ( ! mdoc_action_pre(mdoc, p)) - return(0); switch (p->type) { case (MDOC_HEAD): @@ -330,6 +378,8 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p) p->parent->tail = p; break; case (MDOC_BODY): + if (p->end) + break; assert(MDOC_BLOCK == p->parent->type); p->parent->body = p; break; @@ -340,11 +390,11 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p) mdoc->last = p; switch (p->type) { + case (MDOC_TBL): + /* FALLTHROUGH */ case (MDOC_TEXT): if ( ! mdoc_valid_post(mdoc)) return(0); - if ( ! mdoc_action_post(mdoc)) - return(0); break; default: break; @@ -366,9 +416,17 @@ node_alloc(struct mdoc *m, int line, int pos, p->pos = pos; p->tok = tok; p->type = type; + + /* Flag analysis. */ + + if (MDOC_SYNOPSIS & m->flags) + p->flags |= MDOC_SYNPRETTY; + else + p->flags &= ~MDOC_SYNPRETTY; if (MDOC_NEWLINE & m->flags) p->flags |= MDOC_LINE; m->flags &= ~MDOC_NEWLINE; + return(p); } @@ -415,6 +473,22 @@ mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) } +int +mdoc_endbody_alloc(struct mdoc *m, int line, int pos, enum mdoct tok, + struct mdoc_node *body, enum mdoc_endbody end) +{ + struct mdoc_node *p; + + p = node_alloc(m, line, pos, tok, MDOC_BODY); + p->pending = body; + p->end = end; + if ( ! node_append(m, p)) + return(0); + m->next = MDOC_NEXT_SIBLING; + return(1); +} + + int mdoc_block_alloc(struct mdoc *m, int line, int pos, enum mdoct tok, struct mdoc_arg *args) @@ -425,6 +499,21 @@ mdoc_block_alloc(struct mdoc *m, int line, int pos, p->args = args; if (p->args) (args->refcnt)++; + + switch (tok) { + case (MDOC_Bd): + /* FALLTHROUGH */ + case (MDOC_Bf): + /* FALLTHROUGH */ + case (MDOC_Bl): + /* FALLTHROUGH */ + case (MDOC_Rs): + p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); + break; + default: + break; + } + if ( ! node_append(m, p)) return(0); m->next = MDOC_NEXT_CHILD; @@ -442,12 +531,37 @@ mdoc_elem_alloc(struct mdoc *m, int line, int pos, p->args = args; if (p->args) (args->refcnt)++; + + switch (tok) { + case (MDOC_An): + p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); + break; + default: + break; + } + if ( ! node_append(m, p)) return(0); m->next = MDOC_NEXT_CHILD; return(1); } +static int +mdoc_span_alloc(struct mdoc *m, const struct tbl_span *sp) +{ + struct mdoc_node *n; + + /* FIXME: grab from tbl_span. */ + n = node_alloc(m, 0, 0, MDOC_MAX, MDOC_TBL); + n->span = sp; + + if ( ! node_append(m, n)) + return(0); + + m->next = MDOC_NEXT_SIBLING; + return(1); +} + int mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p) @@ -476,6 +590,8 @@ static void mdoc_node_free(struct mdoc_node *p) { + if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type) + free(p->norm); if (p->string) free(p->string); if (p->args) @@ -501,6 +617,8 @@ mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n) n->parent->nchild--; if (n->parent->child == n) n->parent->child = n->prev ? n->prev : n->next; + if (n->parent->last == n) + n->parent->last = n->prev ? n->prev : NULL; } /* Adjust parse point, if applicable. */ @@ -549,13 +667,17 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) if ('\\' == buf[offs] && '.' == buf[offs + 1] && - '"' == buf[offs + 2]) - return(mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT)); + '"' == buf[offs + 2]) { + mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT); + return(1); + } /* No text before an initial macro. */ - if (SEC_NONE == m->lastnamed) - return(mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT)); + if (SEC_NONE == m->lastnamed) { + mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT); + return(1); + } assert(m->last); n = m->last; @@ -568,7 +690,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) */ if (MDOC_Bl == n->tok && MDOC_BODY == n->type && - LIST_column == n->data.Bl.type) { + LIST_column == n->norm->Bl.type) { /* `Bl' is open without any children. */ m->flags |= MDOC_FREECOL; return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf)); @@ -577,7 +699,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) if (MDOC_It == n->tok && MDOC_BLOCK == n->type && NULL != n->parent && MDOC_Bl == n->parent->tok && - LIST_column == n->parent->data.Bl.type) { + LIST_column == n->parent->norm->Bl.type) { /* `Bl' has block-level `It' children. */ m->flags |= MDOC_FREECOL; return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf)); @@ -630,19 +752,17 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) *end = '\0'; if (ws) - if ( ! mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE)) - return(0); + mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE); if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) { - if ( ! mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN)) - return(0); + mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN); /* - * Insert a `Pp' in the case of a blank line. Technically, + * Insert a `sp' in the case of a blank line. Technically, * blank lines aren't allowed, but enough manuals assume this * behaviour that we want to work around it. */ - if ( ! mdoc_elem_alloc(m, line, offs, MDOC_Pp, NULL)) + if ( ! mdoc_elem_alloc(m, line, offs, MDOC_sp, NULL)) return(0); m->next = MDOC_NEXT_SIBLING; @@ -663,28 +783,13 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) assert(buf < end); - if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) + if (mandoc_eos(buf+offs, (size_t)(end-buf-offs), 0)) m->last->flags |= MDOC_EOS; return(1); } -static int -macrowarn(struct mdoc *m, int ln, const char *buf, int offs) -{ - int rc; - - rc = mdoc_vmsg(m, MANDOCERR_MACRO, ln, offs, - "unknown macro: %s%s", - buf, strlen(buf) > 3 ? "..." : ""); - - /* FIXME: logic should be in driver. */ - /* FIXME: broken, will error out and not omit a message. */ - return(MDOC_IGN_MACRO & m->pflags ? rc : 0); -} - - /* * Parse a macro line, that is, a line beginning with the control * character. @@ -706,11 +811,11 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) i = offs; - /* Accept whitespace after the initial control char. */ + /* Accept tabs/whitespace after the initial control char. */ - if (' ' == buf[i]) { + if (' ' == buf[i] || '\t' == buf[i]) { i++; - while (buf[i] && ' ' == buf[i]) + while (buf[i] && (' ' == buf[i] || '\t' == buf[i])) i++; if ('\0' == buf[i]) return(1); @@ -718,38 +823,28 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) sv = i; - /* Copy the first word into a nil-terminated buffer. */ - - for (j = 0; j < 4; j++, i++) { - if ('\0' == (mac[j] = buf[i])) - break; - else if (' ' == buf[i]) - break; - - /* Check for invalid characters. */ - - if (isgraph((u_char)buf[i])) - continue; - if ( ! mdoc_pmsg(m, ln, i, MANDOCERR_BADCHAR)) - return(0); - i--; - } + /* + * Copy the first word into a nil-terminated buffer. + * Stop copying when a tab, space, or eoln is encountered. + */ + j = 0; + while (j < 4 && '\0' != buf[i] && ' ' != buf[i] && '\t' != buf[i]) + mac[j++] = buf[i++]; mac[j] = '\0'; - if (j == 4 || j < 2) { - if ( ! macrowarn(m, ln, mac, sv)) - goto err; - return(1); - } - - if (MDOC_MAX == (tok = mdoc_hash_find(mac))) { - if ( ! macrowarn(m, ln, mac, sv)) - goto err; + tok = (j > 1 || j < 4) ? mdoc_hash_find(mac) : MDOC_MAX; + if (MDOC_MAX == tok) { + mdoc_vmsg(m, MANDOCERR_MACRO, ln, sv, "%s", buf + sv - 1); return(1); } - /* The macro is sane. Jump to the next word. */ + /* Disregard the first trailing tab, if applicable. */ + + if ('\t' == buf[i]) + i++; + + /* Jump to the next non-whitespace word. */ while (buf[i] && ' ' == buf[i]) i++; @@ -760,8 +855,7 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) */ if ('\0' == buf[i] && ' ' == buf[i - 1]) - if ( ! mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE)) - goto err; + mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE); /* * If an initial macro or a list invocation, divert directly @@ -783,9 +877,9 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) */ if (MDOC_Bl == n->tok && MDOC_BODY == n->type && - LIST_column == n->data.Bl.type) { + LIST_column == n->norm->Bl.type) { m->flags |= MDOC_FREECOL; - if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) + if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) goto err; return(1); } @@ -799,7 +893,7 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) if (MDOC_It == n->tok && MDOC_BLOCK == n->type && NULL != n->parent && MDOC_Bl == n->parent->tok && - LIST_column == n->parent->data.Bl.type) { + LIST_column == n->parent->norm->Bl.type) { m->flags |= MDOC_FREECOL; if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) goto err; diff --git a/commands/mdocml/mdoc.h b/usr.bin/mdocml/dist/mdoc.h similarity index 51% rename from commands/mdocml/mdoc.h rename to usr.bin/mdocml/dist/mdoc.h index 7a84ab16c..7ecc6a525 100644 --- a/commands/mdocml/mdoc.h +++ b/usr.bin/mdocml/dist/mdoc.h @@ -1,6 +1,6 @@ -/* $Id: mdoc.h,v 1.90 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc.h,v 1.114 2011/01/01 12:18:37 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -17,16 +17,9 @@ #ifndef MDOC_H #define MDOC_H -/* - * This library implements a validating scanner/parser for ``mdoc'' roff - * macro documents, a.k.a. BSD manual page documents. The mdoc.c file - * drives the parser, while macro.c describes the macro ontologies. - * validate.c pre- and post-validates parsed macros, and action.c - * performs actions on parsed and validated macros. +/* + * What follows is a list of ALL possible macros. */ - -/* What follows is a list of ALL possible macros. */ - enum mdoct { MDOC_Ap = 0, MDOC_Dd, @@ -153,38 +146,43 @@ enum mdoct { MDOC_MAX }; -/* What follows is a list of ALL possible macro arguments. */ +/* + * What follows is a list of ALL possible macro arguments. + */ +enum mdocargt { + MDOC_Split, + MDOC_Nosplit, + MDOC_Ragged, + MDOC_Unfilled, + MDOC_Literal, + MDOC_File, + MDOC_Offset, + MDOC_Bullet, + MDOC_Dash, + MDOC_Hyphen, + MDOC_Item, + MDOC_Enum, + MDOC_Tag, + MDOC_Diag, + MDOC_Hang, + MDOC_Ohang, + MDOC_Inset, + MDOC_Column, + MDOC_Width, + MDOC_Compact, + MDOC_Std, + MDOC_Filled, + MDOC_Words, + MDOC_Emphasis, + MDOC_Symbolic, + MDOC_Nested, + MDOC_Centred, + MDOC_ARG_MAX +}; -#define MDOC_Split 0 -#define MDOC_Nosplit 1 -#define MDOC_Ragged 2 -#define MDOC_Unfilled 3 -#define MDOC_Literal 4 -#define MDOC_File 5 -#define MDOC_Offset 6 -#define MDOC_Bullet 7 -#define MDOC_Dash 8 -#define MDOC_Hyphen 9 -#define MDOC_Item 10 -#define MDOC_Enum 11 -#define MDOC_Tag 12 -#define MDOC_Diag 13 -#define MDOC_Hang 14 -#define MDOC_Ohang 15 -#define MDOC_Inset 16 -#define MDOC_Column 17 -#define MDOC_Width 18 -#define MDOC_Compact 19 -#define MDOC_Std 20 -#define MDOC_Filled 21 -#define MDOC_Words 22 -#define MDOC_Emphasis 23 -#define MDOC_Symbolic 24 -#define MDOC_Nested 25 -#define MDOC_Centred 26 -#define MDOC_ARG_MAX 27 - -/* Type of a syntax node. */ +/* + * Type of a syntax node. + */ enum mdoc_type { MDOC_TEXT, MDOC_ELEM, @@ -192,12 +190,16 @@ enum mdoc_type { MDOC_TAIL, MDOC_BODY, MDOC_BLOCK, + MDOC_TBL, MDOC_ROOT }; -/* Section (named/unnamed) of `Sh'. */ +/* + * Section (named/unnamed) of `Sh'. Note that these appear in the + * conventional order imposed by mdoc.7. + */ enum mdoc_sec { - SEC_NONE, /* No section, yet. */ + SEC_NONE = 0, /* No section, yet. */ SEC_NAME, SEC_LIBRARY, SEC_SYNOPSIS, @@ -218,36 +220,58 @@ enum mdoc_sec { SEC_CAVEATS, SEC_BUGS, SEC_SECURITY, - SEC_CUSTOM, /* User-defined. */ + SEC_CUSTOM, /* User-defined. */ SEC__MAX }; -/* Information from prologue. */ +/* + * Information from prologue. + */ struct mdoc_meta { - char *msec; - char *vol; - char *arch; - time_t date; - char *title; - char *os; - char *name; + char *msec; /* `Dt' section (1, 3p, etc.) */ + char *vol; /* `Dt' volume (implied) */ + char *arch; /* `Dt' arch (i386, etc.) */ + time_t date; /* `Dd' normalised date */ + char *title; /* `Dt' title (FOO, etc.) */ + char *os; /* `Os' system (OpenBSD, etc.) */ + char *name; /* leading `Nm' name */ }; -/* An argument to a macro (multiple values = `It -column'). */ +/* + * An argument to a macro (multiple values = `-column xxx yyy'). + */ struct mdoc_argv { - int arg; + enum mdocargt arg; /* type of argument */ int line; int pos; - size_t sz; - char **value; + size_t sz; /* elements in "value" */ + char **value; /* argument strings */ }; +/* + * Reference-counted macro arguments. These are refcounted because + * blocks have multiple instances of the same arguments spread across + * the HEAD, BODY, TAIL, and BLOCK node types. + */ struct mdoc_arg { size_t argc; struct mdoc_argv *argv; unsigned int refcnt; }; +/* + * Indicates that a BODY's formatting has ended, but the scope is still + * open. Used for syntax-broken blocks. + */ +enum mdoc_endbody { + ENDBODY_NOT = 0, + ENDBODY_SPACE, /* is broken: append a space */ + ENDBODY_NOSPACE /* is broken: don't append a space */ +}; + +/* + * Normalised `Bl' list type. + */ enum mdoc_list { LIST__NONE = 0, LIST_bullet, @@ -260,9 +284,13 @@ enum mdoc_list { LIST_inset, LIST_item, LIST_ohang, - LIST_tag + LIST_tag, + LIST_MAX }; +/* + * Normalised `Bd' display type. + */ enum mdoc_disp { DISP__NONE = 0, DISP_centred, @@ -272,23 +300,84 @@ enum mdoc_disp { DISP_literal }; +/* + * Normalised `An' splitting argument. + */ +enum mdoc_auth { + AUTH__NONE = 0, + AUTH_split, + AUTH_nosplit +}; + +/* + * Normalised `Bf' font type. + */ +enum mdoc_font { + FONT__NONE = 0, + FONT_Em, + FONT_Li, + FONT_Sy +}; + +/* + * Normalised arguments for `Bd'. + */ struct mdoc_bd { const char *offs; /* -offset */ enum mdoc_disp type; /* -ragged, etc. */ int comp; /* -compact */ }; +/* + * Normalised arguments for `Bl'. + */ struct mdoc_bl { const char *width; /* -width */ const char *offs; /* -offset */ enum mdoc_list type; /* -tag, -enum, etc. */ int comp; /* -compact */ + size_t ncols; /* -column arg count */ + const char **cols; /* -column val ptr */ }; -/* Node in AST. */ +/* + * Normalised arguments for `Bf'. + */ +struct mdoc_bf { + enum mdoc_font font; /* font */ +}; + +/* + * Normalised arguments for `An'. + */ +struct mdoc_an { + enum mdoc_auth auth; /* -split, etc. */ +}; + +struct mdoc_rs { + struct mdoc_node *child_J; /* pointer to %J */ +}; + +/* + * Consists of normalised node arguments. These should be used instead + * of iterating through the mdoc_arg pointers of a node: defaults are + * provided, etc. + */ +union mdoc_data { + struct mdoc_an An; + struct mdoc_bd Bd; + struct mdoc_bf Bf; + struct mdoc_bl Bl; + struct mdoc_rs Rs; +}; + +/* + * Single node in tree-linked AST. + */ struct mdoc_node { struct mdoc_node *parent; /* parent AST node */ struct mdoc_node *child; /* first child AST node */ + struct mdoc_node *last; /* last child AST node */ struct mdoc_node *next; /* sibling AST node */ struct mdoc_node *prev; /* prior sibling AST node */ int nchild; /* number children */ @@ -297,48 +386,50 @@ struct mdoc_node { enum mdoct tok; /* tok or MDOC__MAX if none */ int flags; #define MDOC_VALID (1 << 0) /* has been validated */ -#define MDOC_ACTED (1 << 1) /* has been acted upon */ #define MDOC_EOS (1 << 2) /* at sentence boundary */ #define MDOC_LINE (1 << 3) /* first macro/text on line */ +#define MDOC_SYNPRETTY (1 << 4) /* SYNOPSIS-style formatting */ +#define MDOC_ENDED (1 << 5) /* rendering has been ended */ enum mdoc_type type; /* AST node type */ enum mdoc_sec sec; /* current named section */ - struct mdoc_arg *args; /* BLOCK/ELEM */ -#ifdef UGLY - struct mdoc_node *pending; /* BLOCK */ -#endif - struct mdoc_node *head; /* BLOCK */ - struct mdoc_node *body; /* BLOCK */ - struct mdoc_node *tail; /* BLOCK */ - char *string; /* TEXT */ - - union { - struct mdoc_bl Bl; - struct mdoc_bd Bd; - } data; + union mdoc_data *norm; /* normalised args */ + /* FIXME: these can be union'd to shave a few bytes. */ + struct mdoc_arg *args; /* BLOCK/ELEM */ + struct mdoc_node *pending; /* BLOCK */ + struct mdoc_node *head; /* BLOCK */ + struct mdoc_node *body; /* BLOCK */ + struct mdoc_node *tail; /* BLOCK */ + char *string; /* TEXT */ + const struct tbl_span *span; /* TBL */ + enum mdoc_endbody end; /* BODY */ }; -#define MDOC_IGN_SCOPE (1 << 0) /* Ignore scope violations. */ -#define MDOC_IGN_ESCAPE (1 << 1) /* Ignore bad escape sequences. */ -#define MDOC_IGN_MACRO (1 << 2) /* Ignore unknown macros. */ - -/* See mdoc.3 for documentation. */ - +/* + * Names of macros. Index is enum mdoct. Indexing into this returns + * the normalised name, e.g., mdoc_macronames[MDOC_Sh] -> "Sh". + */ extern const char *const *mdoc_macronames; + +/* + * Names of macro args. Index is enum mdocargt. Indexing into this + * returns the normalised name, e.g., mdoc_argnames[MDOC_File] -> + * "file". + */ extern const char *const *mdoc_argnames; __BEGIN_DECLS struct mdoc; -/* See mdoc.3 for documentation. */ - void mdoc_free(struct mdoc *); -struct mdoc *mdoc_alloc(void *, int, mandocmsg); +struct mdoc *mdoc_alloc(struct regset *, void *, mandocmsg); void mdoc_reset(struct mdoc *); int mdoc_parseln(struct mdoc *, int, char *, int); const struct mdoc_node *mdoc_node(const struct mdoc *); const struct mdoc_meta *mdoc_meta(const struct mdoc *); int mdoc_endparse(struct mdoc *); +int mdoc_addspan(struct mdoc *, + const struct tbl_span *); __END_DECLS diff --git a/commands/mdocml/mdoc_argv.c b/usr.bin/mdocml/dist/mdoc_argv.c similarity index 98% rename from commands/mdocml/mdoc_argv.c rename to usr.bin/mdocml/dist/mdoc_argv.c index 700d558ab..d3098050f 100644 --- a/commands/mdocml/mdoc_argv.c +++ b/usr.bin/mdocml/dist/mdoc_argv.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_argv.c,v 1.54 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc_argv.c,v 1.62 2010/12/24 14:00:40 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -45,7 +45,7 @@ #define MULTI_STEP 5 -static int argv_a2arg(enum mdoct, const char *); +static enum mdocargt argv_a2arg(enum mdoct, const char *); static enum margserr args(struct mdoc *, int, int *, char *, int, char **); static int argv(struct mdoc *, int, @@ -95,7 +95,7 @@ static int mdoc_argflags[MDOC_MAX] = { 0, /* Os */ 0, /* Sh */ 0, /* Ss */ - ARGS_DELIM, /* Pp */ + 0, /* Pp */ ARGS_DELIM, /* D1 */ ARGS_DELIM, /* Dl */ 0, /* Bd */ @@ -196,7 +196,7 @@ static int mdoc_argflags[MDOC_MAX] = { 0, /* Fr */ 0, /* Ud */ 0, /* Lb */ - ARGS_DELIM, /* Lp */ + 0, /* Lp */ ARGS_DELIM, /* Lk */ ARGS_DELIM, /* Mt */ ARGS_DELIM, /* Brq */ @@ -313,9 +313,11 @@ mdoc_argv_free(struct mdoc_arg *p) void mdoc_argn_free(struct mdoc_arg *p, int iarg) { - struct mdoc_argv *arg = &p->argv[iarg]; + struct mdoc_argv *arg; int j; + arg = &p->argv[iarg]; + if (arg->sz && arg->value) { for (j = (int)arg->sz - 1; j >= 0; j--) free(arg->value[j]); @@ -359,7 +361,7 @@ mdoc_args(struct mdoc *m, int line, int *pos, if (MDOC_Bl == n->tok) break; - if (n && LIST_column == n->data.Bl.type) { + if (n && LIST_column == n->norm->Bl.type) { fl |= ARGS_TABSEP; fl &= ~ARGS_DELIM; } @@ -582,7 +584,7 @@ args(struct mdoc *m, int line, int *pos, } -static int +static enum mdocargt argv_a2arg(enum mdoct tok, const char *p) { diff --git a/commands/mdocml/mdoc_hash.c b/usr.bin/mdocml/dist/mdoc_hash.c similarity index 96% rename from commands/mdocml/mdoc_hash.c rename to usr.bin/mdocml/dist/mdoc_hash.c index 3bf29dfd8..ee68246c2 100644 --- a/commands/mdocml/mdoc_hash.c +++ b/usr.bin/mdocml/dist/mdoc_hash.c @@ -1,4 +1,4 @@ -/* $Id: mdoc_hash.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc_hash.c,v 1.16 2010/06/19 20:46:28 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/mdoc_html.c b/usr.bin/mdocml/dist/mdoc_html.c similarity index 63% rename from commands/mdocml/mdoc_html.c rename to usr.bin/mdocml/dist/mdoc_html.c index 82ecccf0c..1201354e1 100644 --- a/commands/mdocml/mdoc_html.c +++ b/usr.bin/mdocml/dist/mdoc_html.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_html.c,v 1.85 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc_html.c,v 1.142 2011/01/07 13:20:58 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -67,23 +67,16 @@ static int mdoc__x_pre(MDOC_ARGS); static int mdoc_ad_pre(MDOC_ARGS); static int mdoc_an_pre(MDOC_ARGS); static int mdoc_ap_pre(MDOC_ARGS); -static void mdoc_aq_post(MDOC_ARGS); -static int mdoc_aq_pre(MDOC_ARGS); static int mdoc_ar_pre(MDOC_ARGS); static int mdoc_bd_pre(MDOC_ARGS); static int mdoc_bf_pre(MDOC_ARGS); -static void mdoc_bl_post(MDOC_ARGS); +static void mdoc_bk_post(MDOC_ARGS); +static int mdoc_bk_pre(MDOC_ARGS); static int mdoc_bl_pre(MDOC_ARGS); -static void mdoc_bq_post(MDOC_ARGS); -static int mdoc_bq_pre(MDOC_ARGS); -static void mdoc_brq_post(MDOC_ARGS); -static int mdoc_brq_pre(MDOC_ARGS); static int mdoc_bt_pre(MDOC_ARGS); static int mdoc_bx_pre(MDOC_ARGS); static int mdoc_cd_pre(MDOC_ARGS); static int mdoc_d1_pre(MDOC_ARGS); -static void mdoc_dq_post(MDOC_ARGS); -static int mdoc_dq_pre(MDOC_ARGS); static int mdoc_dv_pre(MDOC_ARGS); static int mdoc_fa_pre(MDOC_ARGS); static int mdoc_fd_pre(MDOC_ARGS); @@ -97,13 +90,8 @@ static int mdoc_ex_pre(MDOC_ARGS); static void mdoc_fo_post(MDOC_ARGS); static int mdoc_fo_pre(MDOC_ARGS); static int mdoc_ic_pre(MDOC_ARGS); +static int mdoc_igndelim_pre(MDOC_ARGS); static int mdoc_in_pre(MDOC_ARGS); -static int mdoc_it_block_pre(MDOC_ARGS, enum mdoc_list, - int, struct roffsu *, struct roffsu *); -static int mdoc_it_head_pre(MDOC_ARGS, enum mdoc_list, - struct roffsu *); -static int mdoc_it_body_pre(MDOC_ARGS, enum mdoc_list, - struct roffsu *); static int mdoc_it_pre(MDOC_ARGS); static int mdoc_lb_pre(MDOC_ARGS); static int mdoc_li_pre(MDOC_ARGS); @@ -113,19 +101,16 @@ static int mdoc_ms_pre(MDOC_ARGS); static int mdoc_nd_pre(MDOC_ARGS); static int mdoc_nm_pre(MDOC_ARGS); static int mdoc_ns_pre(MDOC_ARGS); -static void mdoc_op_post(MDOC_ARGS); -static int mdoc_op_pre(MDOC_ARGS); static int mdoc_pa_pre(MDOC_ARGS); static void mdoc_pf_post(MDOC_ARGS); -static int mdoc_pf_pre(MDOC_ARGS); -static void mdoc_pq_post(MDOC_ARGS); -static int mdoc_pq_pre(MDOC_ARGS); +static int mdoc_pp_pre(MDOC_ARGS); +static void mdoc_quote_post(MDOC_ARGS); +static int mdoc_quote_pre(MDOC_ARGS); static int mdoc_rs_pre(MDOC_ARGS); static int mdoc_rv_pre(MDOC_ARGS); static int mdoc_sh_pre(MDOC_ARGS); +static int mdoc_sm_pre(MDOC_ARGS); static int mdoc_sp_pre(MDOC_ARGS); -static void mdoc_sq_post(MDOC_ARGS); -static int mdoc_sq_pre(MDOC_ARGS); static int mdoc_ss_pre(MDOC_ARGS); static int mdoc_sx_pre(MDOC_ARGS); static int mdoc_sy_pre(MDOC_ARGS); @@ -142,12 +127,12 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {NULL, NULL}, /* Os */ {mdoc_sh_pre, NULL }, /* Sh */ {mdoc_ss_pre, NULL }, /* Ss */ - {mdoc_sp_pre, NULL}, /* Pp */ + {mdoc_pp_pre, NULL}, /* Pp */ {mdoc_d1_pre, NULL}, /* D1 */ {mdoc_d1_pre, NULL}, /* Dl */ {mdoc_bd_pre, NULL}, /* Bd */ {NULL, NULL}, /* Ed */ - {mdoc_bl_pre, mdoc_bl_post}, /* Bl */ + {mdoc_bl_pre, NULL}, /* Bl */ {NULL, NULL}, /* El */ {mdoc_it_pre, NULL}, /* It */ {mdoc_ad_pre, NULL}, /* Ad */ @@ -169,7 +154,7 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {mdoc_li_pre, NULL}, /* Li */ {mdoc_nd_pre, NULL}, /* Nd */ {mdoc_nm_pre, NULL}, /* Nm */ - {mdoc_op_pre, mdoc_op_post}, /* Op */ + {mdoc_quote_pre, mdoc_quote_post}, /* Op */ {NULL, NULL}, /* Ot */ {mdoc_pa_pre, NULL}, /* Pa */ {mdoc_rv_pre, NULL}, /* Rv */ @@ -189,43 +174,43 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {mdoc__x_pre, mdoc__x_post}, /* %T */ {mdoc__x_pre, mdoc__x_post}, /* %V */ {NULL, NULL}, /* Ac */ - {mdoc_aq_pre, mdoc_aq_post}, /* Ao */ - {mdoc_aq_pre, mdoc_aq_post}, /* Aq */ + {mdoc_quote_pre, mdoc_quote_post}, /* Ao */ + {mdoc_quote_pre, mdoc_quote_post}, /* Aq */ {NULL, NULL}, /* At */ {NULL, NULL}, /* Bc */ {mdoc_bf_pre, NULL}, /* Bf */ - {mdoc_bq_pre, mdoc_bq_post}, /* Bo */ - {mdoc_bq_pre, mdoc_bq_post}, /* Bq */ + {mdoc_quote_pre, mdoc_quote_post}, /* Bo */ + {mdoc_quote_pre, mdoc_quote_post}, /* Bq */ {mdoc_xx_pre, NULL}, /* Bsx */ {mdoc_bx_pre, NULL}, /* Bx */ {NULL, NULL}, /* Db */ {NULL, NULL}, /* Dc */ - {mdoc_dq_pre, mdoc_dq_post}, /* Do */ - {mdoc_dq_pre, mdoc_dq_post}, /* Dq */ + {mdoc_quote_pre, mdoc_quote_post}, /* Do */ + {mdoc_quote_pre, mdoc_quote_post}, /* Dq */ {NULL, NULL}, /* Ec */ /* FIXME: no space */ {NULL, NULL}, /* Ef */ {mdoc_em_pre, NULL}, /* Em */ {NULL, NULL}, /* Eo */ {mdoc_xx_pre, NULL}, /* Fx */ - {mdoc_ms_pre, NULL}, /* Ms */ /* FIXME: convert to symbol? */ - {NULL, NULL}, /* No */ + {mdoc_ms_pre, NULL}, /* Ms */ + {mdoc_igndelim_pre, NULL}, /* No */ {mdoc_ns_pre, NULL}, /* Ns */ {mdoc_xx_pre, NULL}, /* Nx */ {mdoc_xx_pre, NULL}, /* Ox */ {NULL, NULL}, /* Pc */ - {mdoc_pf_pre, mdoc_pf_post}, /* Pf */ - {mdoc_pq_pre, mdoc_pq_post}, /* Po */ - {mdoc_pq_pre, mdoc_pq_post}, /* Pq */ + {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */ + {mdoc_quote_pre, mdoc_quote_post}, /* Po */ + {mdoc_quote_pre, mdoc_quote_post}, /* Pq */ {NULL, NULL}, /* Qc */ - {mdoc_sq_pre, mdoc_sq_post}, /* Ql */ - {mdoc_dq_pre, mdoc_dq_post}, /* Qo */ - {mdoc_dq_pre, mdoc_dq_post}, /* Qq */ + {mdoc_quote_pre, mdoc_quote_post}, /* Ql */ + {mdoc_quote_pre, mdoc_quote_post}, /* Qo */ + {mdoc_quote_pre, mdoc_quote_post}, /* Qq */ {NULL, NULL}, /* Re */ {mdoc_rs_pre, NULL}, /* Rs */ {NULL, NULL}, /* Sc */ - {mdoc_sq_pre, mdoc_sq_post}, /* So */ - {mdoc_sq_pre, mdoc_sq_post}, /* Sq */ - {NULL, NULL}, /* Sm */ /* FIXME - no idea. */ + {mdoc_quote_pre, mdoc_quote_post}, /* So */ + {mdoc_quote_pre, mdoc_quote_post}, /* Sq */ + {mdoc_sm_pre, NULL}, /* Sm */ {mdoc_sx_pre, NULL}, /* Sx */ {mdoc_sy_pre, NULL}, /* Sy */ {NULL, NULL}, /* Tn */ @@ -234,20 +219,20 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {NULL, NULL}, /* Xo */ {mdoc_fo_pre, mdoc_fo_post}, /* Fo */ {NULL, NULL}, /* Fc */ - {mdoc_op_pre, mdoc_op_post}, /* Oo */ + {mdoc_quote_pre, mdoc_quote_post}, /* Oo */ {NULL, NULL}, /* Oc */ - {NULL, NULL}, /* Bk */ + {mdoc_bk_pre, mdoc_bk_post}, /* Bk */ {NULL, NULL}, /* Ek */ {mdoc_bt_pre, NULL}, /* Bt */ {NULL, NULL}, /* Hf */ {NULL, NULL}, /* Fr */ {mdoc_ud_pre, NULL}, /* Ud */ {mdoc_lb_pre, NULL}, /* Lb */ - {mdoc_sp_pre, NULL}, /* Lp */ + {mdoc_pp_pre, NULL}, /* Lp */ {mdoc_lk_pre, NULL}, /* Lk */ {mdoc_mt_pre, NULL}, /* Mt */ - {mdoc_brq_pre, mdoc_brq_post}, /* Brq */ - {mdoc_brq_pre, mdoc_brq_post}, /* Bro */ + {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ + {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ {NULL, NULL}, /* Brc */ {mdoc__x_pre, mdoc__x_post}, /* %C */ {NULL, NULL}, /* Es */ /* TODO */ @@ -260,6 +245,20 @@ static const struct htmlmdoc mdocs[MDOC_MAX] = { {NULL, NULL}, /* Ta */ }; +static const char * const lists[LIST_MAX] = { + NULL, + "list-bul", + "list-col", + "list-dash", + "list-diag", + "list-enum", + "list-hang", + "list-hyph", + "list-inset", + "list-item", + "list-ohang", + "list-tag" +}; void html_mdoc(void *arg, const struct mdoc *m) @@ -288,7 +287,7 @@ a2width(const char *p, struct roffsu *su) { if ( ! a2roffsu(p, su, SCALE_MAX)) { - su->unit = SCALE_EM; + su->unit = SCALE_BU; su->scale = (int)strlen(p); } } @@ -300,21 +299,15 @@ a2width(const char *p, struct roffsu *su) static void synopsis_pre(struct html *h, const struct mdoc_node *n) { - struct roffsu su; - struct htmlpair tag; - if (NULL == n->prev || SEC_SYNOPSIS != n->sec) + if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) return; - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - PAIR_STYLE_INIT(&tag, h); - if (n->prev->tok == n->tok && MDOC_Fo != n->tok && MDOC_Ft != n->tok && MDOC_Fn != n->tok) { - print_otag(h, TAG_DIV, 0, NULL); + print_otag(h, TAG_BR, 0, NULL); return; } @@ -328,16 +321,16 @@ synopsis_pre(struct html *h, const struct mdoc_node *n) case (MDOC_In): /* FALLTHROUGH */ case (MDOC_Vt): - print_otag(h, TAG_DIV, 1, &tag); + print_otag(h, TAG_P, 0, NULL); break; case (MDOC_Ft): if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { - print_otag(h, TAG_DIV, 1, &tag); + print_otag(h, TAG_P, 0, NULL); break; } /* FALLTHROUGH */ default: - print_otag(h, TAG_DIV, 0, NULL); + print_otag(h, TAG_BR, 0, NULL); break; } } @@ -361,7 +354,7 @@ a2offs(const char *p, struct roffsu *su) else if (0 == strcmp(p, "indent-two")) SCALE_HS_INIT(su, INDENT * 2); else if ( ! a2roffsu(p, su, SCALE_MAX)) { - su->unit = SCALE_EM; + su->unit = SCALE_BU; su->scale = (int)strlen(p); } } @@ -371,18 +364,12 @@ static void print_mdoc(MDOC_ARGS) { struct tag *t; - struct htmlpair tag; t = print_otag(h, TAG_HEAD, 0, NULL); print_mdoc_head(m, n, h); print_tagq(h, t); t = print_otag(h, TAG_BODY, 0, NULL); - - tag.key = ATTR_CLASS; - tag.val = "body"; - print_otag(h, TAG_DIV, 1, &tag); - print_mdoc_nodelist(m, n, h); print_tagq(h, t); } @@ -435,12 +422,27 @@ print_mdoc_node(MDOC_ARGS) case (MDOC_TEXT): print_text(h, n->string); return; + case (MDOC_TBL): + print_tbl(h, n->span); + break; default: - if (mdocs[n->tok].pre) + if (mdocs[n->tok].pre && ENDBODY_NOT == n->end) child = (*mdocs[n->tok].pre)(m, n, h); break; } + if (HTML_KEEP & h->flags) { + if (n->prev && n->prev->line != n->line) { + h->flags &= ~HTML_KEEP; + h->flags |= HTML_PREKEEP; + } else if (NULL == n->prev) { + if (n->parent && n->parent->line != n->line) { + h->flags &= ~HTML_KEEP; + h->flags |= HTML_PREKEEP; + } + } + } + if (child && n->child) print_mdoc_nodelist(m, n->child, h); @@ -451,14 +453,15 @@ print_mdoc_node(MDOC_ARGS) case (MDOC_ROOT): mdoc_root_post(m, n, h); break; + case (MDOC_TBL): + break; default: - if (mdocs[n->tok].post) + if (mdocs[n->tok].post && ENDBODY_NOT == n->end) (*mdocs[n->tok].post)(m, n, h); break; } } - /* ARGSUSED */ static void mdoc_root_post(MDOC_ARGS) @@ -469,32 +472,34 @@ mdoc_root_post(MDOC_ARGS) time2a(m->date, b, DATESIZ); - /* - * XXX: this should use divs, but in Firefox, divs with nested - * divs for some reason puke when trying to put a border line - * below. So I use tables, instead. - */ + 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_CLASS_INIT(&tag[0], "footer"); - bufcat_style(h, "width", "100%"); - PAIR_STYLE_INIT(&tag[1], h); - PAIR_SUMMARY_INIT(&tag[2], "footer"); + t = print_otag(h, TAG_TBODY, 0, NULL); - t = print_otag(h, TAG_TABLE, 3, tag); tt = print_otag(h, TAG_TR, 0, NULL); - bufinit(h); - bufcat_style(h, "width", "50%"); - PAIR_STYLE_INIT(&tag[0], h); + PAIR_CLASS_INIT(&tag[0], "foot-date"); print_otag(h, TAG_TD, 1, tag); + print_text(h, b); print_stagq(h, tt); - bufinit(h); - bufcat_style(h, "width", "50%"); - bufcat_style(h, "text-align", "right"); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_TD, 1, tag); + 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); + print_text(h, m->os); print_tagq(h, t); } @@ -508,49 +513,55 @@ mdoc_root_pre(MDOC_ARGS) struct tag *t, *tt; char b[BUFSIZ], title[BUFSIZ]; - (void)strlcpy(b, m->vol, BUFSIZ); + strlcpy(b, m->vol, BUFSIZ); if (m->arch) { - (void)strlcat(b, " (", BUFSIZ); - (void)strlcat(b, m->arch, BUFSIZ); - (void)strlcat(b, ")", BUFSIZ); + strlcat(b, " (", BUFSIZ); + strlcat(b, m->arch, BUFSIZ); + strlcat(b, ")", BUFSIZ); } - (void)snprintf(title, BUFSIZ - 1, - "%s(%s)", m->title, m->msec); + snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec); - /* XXX: see note in mdoc_root_post() about divs. */ + 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_CLASS_INIT(&tag[0], "header"); - bufcat_style(h, "width", "100%"); - PAIR_STYLE_INIT(&tag[1], h); - PAIR_SUMMARY_INIT(&tag[2], "header"); - - t = print_otag(h, TAG_TABLE, 3, tag); + print_otag(h, TAG_TBODY, 0, NULL); tt = print_otag(h, TAG_TR, 0, NULL); - bufinit(h); - bufcat_style(h, "width", "10%"); - PAIR_STYLE_INIT(&tag[0], h); + PAIR_CLASS_INIT(&tag[0], "head-ltitle"); print_otag(h, TAG_TD, 1, tag); + print_text(h, title); print_stagq(h, tt); - bufinit(h); - bufcat_style(h, "text-align", "center"); - bufcat_style(h, "white-space", "nowrap"); - bufcat_style(h, "width", "80%"); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_TD, 1, tag); + 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); + print_text(h, b); print_stagq(h, tt); - bufinit(h); - bufcat_style(h, "text-align", "right"); - bufcat_style(h, "width", "10%"); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_TD, 1, tag); + 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); + print_text(h, title); print_tagq(h, t); return(1); @@ -561,46 +572,25 @@ mdoc_root_pre(MDOC_ARGS) static int mdoc_sh_pre(MDOC_ARGS) { - struct htmlpair tag[2]; - const struct mdoc_node *nn; - char buf[BUFSIZ]; - struct roffsu su; + struct htmlpair tag; + char buf[BUFSIZ]; - if (MDOC_BODY == n->type) { - SCALE_HS_INIT(&su, INDENT); - bufcat_su(h, "margin-left", &su); - PAIR_CLASS_INIT(&tag[0], "sec-body"); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); + if (MDOC_BLOCK == n->type) { + PAIR_CLASS_INIT(&tag, "section"); + print_otag(h, TAG_DIV, 1, &tag); return(1); - } else if (MDOC_BLOCK == n->type) { - PAIR_CLASS_INIT(&tag[0], "sec-block"); - if (n->prev && NULL == n->prev->body->child) { - print_otag(h, TAG_DIV, 1, tag); - return(1); - } - - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - if (NULL == n->next) - bufcat_su(h, "margin-bottom", &su); - - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); + } else if (MDOC_BODY == n->type) return(1); - } buf[0] = '\0'; - for (nn = n->child; nn; nn = nn->next) { - html_idcat(buf, nn->string, BUFSIZ); - if (nn->next) + for (n = n->child; n; n = n->next) { + html_idcat(buf, n->string, BUFSIZ); + if (n->next) html_idcat(buf, " ", BUFSIZ); } - PAIR_CLASS_INIT(&tag[0], "sec-head"); - PAIR_ID_INIT(&tag[1], buf); - - print_otag(h, TAG_DIV, 2, tag); + PAIR_ID_INIT(&tag, buf); + print_otag(h, TAG_H1, 1, &tag); return(1); } @@ -609,51 +599,25 @@ mdoc_sh_pre(MDOC_ARGS) static int mdoc_ss_pre(MDOC_ARGS) { - struct htmlpair tag[3]; - const struct mdoc_node *nn; - char buf[BUFSIZ]; - struct roffsu su; + struct htmlpair tag; + char buf[BUFSIZ]; - SCALE_VS_INIT(&su, 1); - - if (MDOC_BODY == n->type) { - PAIR_CLASS_INIT(&tag[0], "ssec-body"); - if (n->parent->next && n->child) { - bufcat_su(h, "margin-bottom", &su); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); - } else - print_otag(h, TAG_DIV, 1, tag); + if (MDOC_BLOCK == n->type) { + PAIR_CLASS_INIT(&tag, "subsection"); + print_otag(h, TAG_DIV, 1, &tag); return(1); - } else if (MDOC_BLOCK == n->type) { - PAIR_CLASS_INIT(&tag[0], "ssec-block"); - if (n->prev) { - bufcat_su(h, "margin-top", &su); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); - } else - print_otag(h, TAG_DIV, 1, tag); + } else if (MDOC_BODY == n->type) return(1); - } - - /* TODO: see note in mdoc_sh_pre() about duplicates. */ buf[0] = '\0'; - for (nn = n->child; nn; nn = nn->next) { - html_idcat(buf, nn->string, BUFSIZ); - if (nn->next) + for (n = n->child; n; n = n->next) { + html_idcat(buf, n->string, BUFSIZ); + if (n->next) html_idcat(buf, " ", BUFSIZ); } - SCALE_HS_INIT(&su, INDENT - HALFINDENT); - su.scale = -su.scale; - bufcat_su(h, "margin-left", &su); - - PAIR_CLASS_INIT(&tag[0], "ssec-head"); - PAIR_STYLE_INIT(&tag[1], h); - PAIR_ID_INIT(&tag[2], buf); - - print_otag(h, TAG_DIV, 3, tag); + PAIR_ID_INIT(&tag, buf); + print_otag(h, TAG_H2, 1, &tag); return(1); } @@ -665,7 +629,7 @@ mdoc_fl_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "flag"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_B, 1, &tag); /* `Cm' has no leading hyphen. */ @@ -695,57 +659,57 @@ mdoc_nd_pre(MDOC_ARGS) /* XXX: this tag in theory can contain block elements. */ print_text(h, "\\(em"); - PAIR_CLASS_INIT(&tag, "desc-body"); + PAIR_CLASS_INIT(&tag, "desc"); print_otag(h, TAG_SPAN, 1, &tag); return(1); } -/* ARGSUSED */ -static int -mdoc_op_pre(MDOC_ARGS) -{ - struct htmlpair tag; - - if (MDOC_BODY != n->type) - return(1); - - /* XXX: this tag in theory can contain block elements. */ - - print_text(h, "\\(lB"); - h->flags |= HTML_NOSPACE; - PAIR_CLASS_INIT(&tag, "opt"); - print_otag(h, TAG_SPAN, 1, &tag); - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_op_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - h->flags |= HTML_NOSPACE; - print_text(h, "\\(rB"); -} - - static int mdoc_nm_pre(MDOC_ARGS) { - struct htmlpair tag; + struct htmlpair tag; + struct roffsu su; + size_t len; - if (NULL == n->child && NULL == m->name) + switch (n->type) { + case (MDOC_ELEM): + synopsis_pre(h, n); + PAIR_CLASS_INIT(&tag, "name"); + print_otag(h, TAG_B, 1, &tag); + if (NULL == n->child && m->name) + print_text(h, m->name); return(1); + case (MDOC_HEAD): + print_otag(h, TAG_TD, 0, NULL); + if (NULL == n->child && m->name) + print_text(h, m->name); + return(1); + case (MDOC_BODY): + print_otag(h, TAG_TD, 0, NULL); + return(1); + default: + break; + } synopsis_pre(h, n); + PAIR_CLASS_INIT(&tag, "synopsis"); + print_otag(h, TAG_TABLE, 1, &tag); - PAIR_CLASS_INIT(&tag, "name"); - print_otag(h, TAG_SPAN, 1, &tag); - if (NULL == n->child) - print_text(h, m->name); + for (len = 0, n = n->child; n; n = n->next) + if (MDOC_TEXT == n->type) + len += strlen(n->string); + + if (0 == len && m->name) + len = strlen(m->name); + + SCALE_HS_INIT(&su, (double)len); + 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); } @@ -804,7 +768,7 @@ mdoc_ar_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "arg"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_I, 1, &tag); return(1); } @@ -818,7 +782,7 @@ mdoc_xx_pre(MDOC_ARGS) switch (n->tok) { case (MDOC_Bsx): - pp = "BSDI BSD/OS"; + pp = "BSD/OS"; break; case (MDOC_Dx): pp = "DragonFly"; @@ -866,291 +830,209 @@ mdoc_bx_pre(MDOC_ARGS) return(0); } - /* ARGSUSED */ -static int -mdoc_it_block_pre(MDOC_ARGS, enum mdoc_list type, int comp, - struct roffsu *offs, struct roffsu *width) -{ - struct htmlpair tag; - const struct mdoc_node *nn; - struct roffsu su; - - nn = n->parent->parent; - - /* XXX: see notes in mdoc_it_pre(). */ - - if (LIST_column == type) { - /* Don't width-pad on the left. */ - SCALE_HS_INIT(width, 0); - /* Also disallow non-compact. */ - comp = 1; - } - if (LIST_diag == type) - /* Mandate non-compact with empty prior. */ - if (n->prev && NULL == n->prev->body->child) - comp = 1; - - bufcat_style(h, "clear", "both"); - if (offs->scale > 0) - bufcat_su(h, "margin-left", offs); - if (width->scale > 0) - bufcat_su(h, "padding-left", width); - - PAIR_STYLE_INIT(&tag, h); - - /* Mandate compact following `Ss' and `Sh' starts. */ - - for (nn = n; nn && ! comp; nn = nn->parent) { - if (MDOC_BLOCK != nn->type) - continue; - if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok) - comp = 1; - if (nn->prev) - break; - } - - if ( ! comp) { - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "padding-top", &su); - } - - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); - return(1); -} - - -/* ARGSUSED */ -static int -mdoc_it_body_pre(MDOC_ARGS, enum mdoc_list type, struct roffsu *width) -{ - struct htmlpair tag; - struct roffsu su; - - switch (type) { - case (LIST_item): - /* FALLTHROUGH */ - case (LIST_ohang): - /* FALLTHROUGH */ - case (LIST_column): - bufcat_su(h, "min-width", width); - bufcat_style(h, "clear", "none"); - if (n->next) - bufcat_style(h, "float", "left"); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); - break; - default: - /* - * XXX: this tricks CSS into aligning the bodies with - * the right-padding in the head. - */ - SCALE_HS_INIT(&su, 2); - bufcat_su(h, "margin-left", &su); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); - break; - } - - return(1); -} - - -/* ARGSUSED */ -static int -mdoc_it_head_pre(MDOC_ARGS, enum mdoc_list type, struct roffsu *width) -{ - struct htmlpair tag; - struct ord *ord; - char nbuf[BUFSIZ]; - - switch (type) { - case (LIST_item): - return(0); - case (LIST_ohang): - print_otag(h, TAG_DIV, 0, &tag); - return(1); - case (LIST_column): - break; - default: - bufcat_su(h, "min-width", width); - SCALE_INVERT(width); - bufcat_su(h, "margin-left", width); - if (n->next && n->next->child) - bufcat_style(h, "float", "left"); - - /* XXX: buffer if we run into body. */ - SCALE_HS_INIT(width, 1); - bufcat_su(h, "margin-right", width); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); - break; - } - - switch (type) { - case (LIST_diag): - PAIR_CLASS_INIT(&tag, "diag"); - print_otag(h, TAG_SPAN, 1, &tag); - break; - case (LIST_enum): - ord = h->ords.head; - assert(ord); - nbuf[BUFSIZ - 1] = 0; - (void)snprintf(nbuf, BUFSIZ - 1, "%d.", ord->pos++); - print_text(h, nbuf); - return(0); - case (LIST_dash): - print_text(h, "\\(en"); - return(0); - case (LIST_hyphen): - print_text(h, "\\(hy"); - return(0); - case (LIST_bullet): - print_text(h, "\\(bu"); - return(0); - default: - break; - } - - return(1); -} - - static int mdoc_it_pre(MDOC_ARGS) { - int i, wp, comp; - const struct mdoc_node *bl, *nn; - struct roffsu width, offs; - enum mdoc_list type; + struct roffsu su; + enum mdoc_list type; + struct htmlpair tag[2]; + const struct mdoc_node *bl; - /* - * XXX: be very careful in changing anything, here. Lists in - * mandoc have many peculiarities; furthermore, they don't - * translate well into HTML and require a bit of mangling. - */ - - bl = n->parent->parent; - if (MDOC_BLOCK != n->type) + bl = n->parent; + while (bl && MDOC_Bl != bl->tok) bl = bl->parent; - SCALE_HS_INIT(&offs, 0); + assert(bl); - type = bl->data.Bl.type; - comp = bl->data.Bl.comp; + type = bl->norm->Bl.type; - if (bl->data.Bl.offs) - a2offs(bl->data.Bl.offs, &offs); + assert(lists[type]); + PAIR_CLASS_INIT(&tag[0], lists[type]); - switch (type) { - case (LIST_enum): - /* FALLTHROUGH */ - case (LIST_dash): - /* FALLTHROUGH */ - case (LIST_hyphen): - /* FALLTHROUGH */ - case (LIST_bullet): - SCALE_HS_INIT(&width, 2); - break; - default: - SCALE_HS_INIT(&width, INDENT); - break; - } - - if (bl->data.Bl.width) - a2width(bl->data.Bl.width, &width); - - wp = -1; - for (i = 0; bl->args && i < (int)bl->args->argc; i++) - switch (bl->args->argv[i].arg) { - case (MDOC_Column): - wp = i; /* Save for later. */ + if (MDOC_HEAD == n->type) { + switch (type) { + case(LIST_bullet): + /* FALLTHROUGH */ + case(LIST_dash): + /* FALLTHROUGH */ + case(LIST_item): + /* FALLTHROUGH */ + case(LIST_hyphen): + /* FALLTHROUGH */ + case(LIST_enum): + return(0); + case(LIST_diag): + /* FALLTHROUGH */ + case(LIST_hang): + /* FALLTHROUGH */ + case(LIST_inset): + /* FALLTHROUGH */ + case(LIST_ohang): + /* FALLTHROUGH */ + case(LIST_tag): + SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); + bufcat_su(h, "margin-top", &su); + PAIR_STYLE_INIT(&tag[1], h); + print_otag(h, TAG_DT, 2, tag); + if (LIST_diag != type) + break; + PAIR_CLASS_INIT(&tag[0], "diag"); + print_otag(h, TAG_B, 1, tag); + break; + case(LIST_column): + break; + default: + break; + } + } else if (MDOC_BODY == n->type) { + switch (type) { + case(LIST_bullet): + /* FALLTHROUGH */ + case(LIST_hyphen): + /* FALLTHROUGH */ + case(LIST_dash): + /* FALLTHROUGH */ + case(LIST_enum): + /* FALLTHROUGH */ + case(LIST_item): + SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); + bufcat_su(h, "margin-top", &su); + PAIR_STYLE_INIT(&tag[1], h); + print_otag(h, TAG_LI, 2, tag); + break; + case(LIST_diag): + /* FALLTHROUGH */ + case(LIST_hang): + /* FALLTHROUGH */ + case(LIST_inset): + /* FALLTHROUGH */ + case(LIST_ohang): + /* FALLTHROUGH */ + case(LIST_tag): + if (NULL == bl->norm->Bl.width) { + print_otag(h, TAG_DD, 1, tag); + break; + } + a2width(bl->norm->Bl.width, &su); + bufcat_su(h, "margin-left", &su); + PAIR_STYLE_INIT(&tag[1], h); + print_otag(h, TAG_DD, 2, tag); + break; + case(LIST_column): + SCALE_VS_INIT(&su, ! bl->norm->Bl.comp); + bufcat_su(h, "margin-top", &su); + PAIR_STYLE_INIT(&tag[1], h); + print_otag(h, TAG_TD, 2, tag); + break; + default: + break; + } + } else { + switch (type) { + case (LIST_column): + print_otag(h, TAG_TR, 1, tag); break; default: break; } - - /* Override width in some cases. */ - - switch (type) { - case (LIST_ohang): - /* FALLTHROUGH */ - case (LIST_item): - /* FALLTHROUGH */ - case (LIST_inset): - /* FALLTHROUGH */ - case (LIST_diag): - SCALE_HS_INIT(&width, 0); - break; - default: - if (0 == width.scale) - SCALE_HS_INIT(&width, INDENT); - break; } - if (LIST_column == type && MDOC_BODY == n->type) { - nn = n->parent->child; - for (i = 0; nn && nn != n; nn = nn->next) - if (MDOC_BODY == nn->type) - i++; - if (i < (int)bl->args->argv[wp].sz) - a2width(bl->args->argv[wp].value[i], &width); - } - - if (MDOC_HEAD == n->type) - return(mdoc_it_head_pre(m, n, h, type, &width)); - else if (MDOC_BODY == n->type) - return(mdoc_it_body_pre(m, n, h, type, &width)); - - return(mdoc_it_block_pre(m, n, h, type, comp, &offs, &width)); + return(1); } - /* ARGSUSED */ static int mdoc_bl_pre(MDOC_ARGS) { - struct ord *ord; + int i; + struct htmlpair tag[3]; + struct roffsu su; + char buf[BUFSIZ]; - if (MDOC_HEAD == n->type) - return(0); - if (MDOC_BLOCK != n->type) + if (MDOC_BODY == n->type) { + if (LIST_column == n->norm->Bl.type) + print_otag(h, TAG_TBODY, 0, NULL); return(1); - if (LIST_enum != n->data.Bl.type) - return(1); - - ord = malloc(sizeof(struct ord)); - if (NULL == ord) { - perror(NULL); - exit(EXIT_FAILURE); } - ord->cookie = n; - ord->pos = 1; - ord->next = h->ords.head; - h->ords.head = ord; + + if (MDOC_HEAD == n->type) { + if (LIST_column != n->norm->Bl.type) + return(0); + + /* + * For each column, print out the tag with our + * suggested width. The last column gets min-width, as + * in terminal mode it auto-sizes to the width of the + * screen and we want to preserve that behaviour. + */ + + for (i = 0; i < (int)n->norm->Bl.ncols; i++) { + a2width(n->norm->Bl.cols[i], &su); + bufinit(h); + if (i < (int)n->norm->Bl.ncols - 1) + bufcat_su(h, "width", &su); + else + bufcat_su(h, "min-width", &su); + PAIR_STYLE_INIT(&tag[0], h); + print_otag(h, TAG_COL, 1, tag); + } + + return(0); + } + + SCALE_VS_INIT(&su, 0); + bufcat_su(h, "margin-top", &su); + bufcat_su(h, "margin-bottom", &su); + PAIR_STYLE_INIT(&tag[0], h); + + assert(lists[n->norm->Bl.type]); + strlcpy(buf, "list ", BUFSIZ); + strlcat(buf, lists[n->norm->Bl.type], BUFSIZ); + PAIR_INIT(&tag[1], ATTR_CLASS, buf); + + /* Set the block's left-hand margin. */ + + if (n->norm->Bl.offs) { + a2offs(n->norm->Bl.offs, &su); + bufcat_su(h, "margin-left", &su); + } + + switch (n->norm->Bl.type) { + case(LIST_bullet): + /* FALLTHROUGH */ + case(LIST_dash): + /* FALLTHROUGH */ + case(LIST_hyphen): + /* FALLTHROUGH */ + case(LIST_item): + print_otag(h, TAG_UL, 2, tag); + break; + case(LIST_enum): + print_otag(h, TAG_OL, 2, tag); + break; + case(LIST_diag): + /* FALLTHROUGH */ + case(LIST_hang): + /* FALLTHROUGH */ + case(LIST_inset): + /* FALLTHROUGH */ + case(LIST_ohang): + /* FALLTHROUGH */ + case(LIST_tag): + print_otag(h, TAG_DL, 2, tag); + break; + case(LIST_column): + print_otag(h, TAG_TABLE, 2, tag); + break; + default: + abort(); + /* NOTREACHED */ + } + return(1); } - -/* ARGSUSED */ -static void -mdoc_bl_post(MDOC_ARGS) -{ - struct ord *ord; - - if (MDOC_BLOCK != n->type) - return; - if (LIST_enum != n->data.Bl.type) - return; - - ord = h->ords.head; - assert(ord); - h->ords.head = ord->next; - free(ord); -} - - /* ARGSUSED */ static int mdoc_ex_pre(MDOC_ARGS) @@ -1159,11 +1041,14 @@ mdoc_ex_pre(MDOC_ARGS) struct tag *t; struct htmlpair tag; + if (n->prev) + print_otag(h, TAG_BR, 0, NULL); + PAIR_CLASS_INIT(&tag, "utility"); print_text(h, "The"); for (nn = n->child; nn; nn = nn->next) { - t = print_otag(h, TAG_SPAN, 1, &tag); + t = print_otag(h, TAG_B, 1, &tag); print_text(h, nn->string); print_tagq(h, t); @@ -1187,80 +1072,6 @@ mdoc_ex_pre(MDOC_ARGS) } -/* ARGSUSED */ -static int -mdoc_dq_pre(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - print_text(h, "\\(lq"); - h->flags |= HTML_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_dq_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - h->flags |= HTML_NOSPACE; - print_text(h, "\\(rq"); -} - - -/* ARGSUSED */ -static int -mdoc_pq_pre(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - print_text(h, "\\&("); - h->flags |= HTML_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_pq_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - print_text(h, ")"); -} - - -/* ARGSUSED */ -static int -mdoc_sq_pre(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - print_text(h, "\\(oq"); - h->flags |= HTML_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_sq_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - h->flags |= HTML_NOSPACE; - print_text(h, "\\(aq"); -} - - /* ARGSUSED */ static int mdoc_em_pre(MDOC_ARGS) @@ -1283,13 +1094,22 @@ mdoc_d1_pre(MDOC_ARGS) if (MDOC_BLOCK != n->type) return(1); - /* FIXME: D1 shouldn't be literal. */ + SCALE_VS_INIT(&su, 0); + bufcat_su(h, "margin-top", &su); + bufcat_su(h, "margin-bottom", &su); + PAIR_STYLE_INIT(&tag[0], h); + print_otag(h, TAG_BLOCKQUOTE, 1, tag); + + /* BLOCKQUOTE needs a block body. */ + + PAIR_CLASS_INIT(&tag[0], "display"); + print_otag(h, TAG_DIV, 1, tag); + + if (MDOC_Dl == n->tok) { + PAIR_CLASS_INIT(&tag[0], "lit"); + print_otag(h, TAG_CODE, 1, tag); + } - SCALE_VS_INIT(&su, INDENT - 2); - bufcat_su(h, "margin-left", &su); - PAIR_CLASS_INIT(&tag[0], "lit"); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); return(1); } @@ -1312,36 +1132,12 @@ mdoc_sx_pre(MDOC_ARGS) PAIR_CLASS_INIT(&tag[0], "link-sec"); PAIR_HREF_INIT(&tag[1], buf); + print_otag(h, TAG_I, 1, tag); print_otag(h, TAG_A, 2, tag); return(1); } -/* ARGSUSED */ -static int -mdoc_aq_pre(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - print_text(h, "\\(la"); - h->flags |= HTML_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_aq_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - h->flags |= HTML_NOSPACE; - print_text(h, "\\(ra"); -} - - /* ARGSUSED */ static int mdoc_bd_pre(MDOC_ARGS) @@ -1354,18 +1150,8 @@ mdoc_bd_pre(MDOC_ARGS) if (MDOC_HEAD == n->type) return(0); - SCALE_VS_INIT(&su, 0); - - if (n->data.Bd.offs) - a2offs(n->data.Bd.offs, &su); - - comp = n->data.Bd.comp; - - /* FIXME: -centered, etc. formatting. */ - /* FIXME: does not respect -offset ??? */ - if (MDOC_BLOCK == n->type) { - bufcat_su(h, "margin-left", &su); + comp = n->norm->Bd.comp; for (nn = n; nn && ! comp; nn = nn->parent) { if (MDOC_BLOCK != nn->type) continue; @@ -1374,36 +1160,62 @@ mdoc_bd_pre(MDOC_ARGS) if (nn->prev) break; } - if (comp) { - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_DIV, 1, tag); - return(1); - } - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - PAIR_STYLE_INIT(&tag[0], h); - print_otag(h, TAG_DIV, 1, tag); + if ( ! comp) + print_otag(h, TAG_P, 0, NULL); return(1); } - if (DISP_unfilled != n->data.Bd.type && - DISP_literal != n->data.Bd.type) - return(1); + SCALE_HS_INIT(&su, 0); + if (n->norm->Bd.offs) + a2offs(n->norm->Bd.offs, &su); - PAIR_CLASS_INIT(&tag[0], "lit"); - bufcat_style(h, "white-space", "pre"); - PAIR_STYLE_INIT(&tag[1], h); - print_otag(h, TAG_DIV, 2, tag); + bufcat_su(h, "margin-left", &su); + PAIR_STYLE_INIT(&tag[0], h); + + if (DISP_unfilled != n->norm->Bd.type && + DISP_literal != n->norm->Bd.type) { + PAIR_CLASS_INIT(&tag[1], "display"); + print_otag(h, TAG_DIV, 2, tag); + return(1); + } + + PAIR_CLASS_INIT(&tag[1], "lit display"); + print_otag(h, TAG_PRE, 2, tag); for (nn = n->child; nn; nn = nn->next) { - h->flags |= HTML_NOSPACE; print_mdoc_node(m, nn, h); - if (NULL == nn->next) + /* + * If the printed node flushes its own line, then we + * needn't do it here as well. This is hacky, but the + * notion of selective eoln whitespace is pretty dumb + * anyway, so don't sweat it. + */ + switch (nn->tok) { + case (MDOC_Sm): + /* FALLTHROUGH */ + case (MDOC_br): + /* FALLTHROUGH */ + case (MDOC_sp): + /* FALLTHROUGH */ + case (MDOC_Bl): + /* FALLTHROUGH */ + case (MDOC_D1): + /* FALLTHROUGH */ + case (MDOC_Dl): + /* FALLTHROUGH */ + case (MDOC_Lp): + /* FALLTHROUGH */ + case (MDOC_Pp): continue; - if (nn->prev && nn->prev->line < nn->line) - print_text(h, "\n"); - else if (NULL == nn->prev) + default: + break; + } + if (nn->next && nn->next->line == nn->line) + continue; + else if (nn->next) print_text(h, "\n"); + + h->flags |= HTML_NOSPACE; } return(0); @@ -1417,7 +1229,7 @@ mdoc_pa_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "file"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_I, 1, &tag); return(1); } @@ -1429,7 +1241,7 @@ mdoc_ad_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "addr"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_I, 1, &tag); return(1); } @@ -1456,7 +1268,7 @@ mdoc_cd_pre(MDOC_ARGS) synopsis_pre(h, n); PAIR_CLASS_INIT(&tag, "config"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_B, 1, &tag); return(1); } @@ -1507,12 +1319,12 @@ mdoc_fa_pre(MDOC_ARGS) PAIR_CLASS_INIT(&tag, "farg"); if (n->parent->tok != MDOC_Fo) { - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_I, 1, &tag); return(1); } for (nn = n->child; nn; nn = nn->next) { - t = print_otag(h, TAG_SPAN, 1, &tag); + t = print_otag(h, TAG_I, 1, &tag); print_text(h, nn->string); print_tagq(h, t); if (nn->next) @@ -1535,7 +1347,7 @@ mdoc_fd_pre(MDOC_ARGS) synopsis_pre(h, n); PAIR_CLASS_INIT(&tag, "macro"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_B, 1, &tag); return(1); } @@ -1568,7 +1380,7 @@ mdoc_ft_pre(MDOC_ARGS) synopsis_pre(h, n); PAIR_CLASS_INIT(&tag, "ftype"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_I, 1, &tag); return(1); } @@ -1593,7 +1405,7 @@ mdoc_fn_pre(MDOC_ARGS) ep = strchr(sp, ' '); if (NULL != ep) { PAIR_CLASS_INIT(&tag[0], "ftype"); - t = print_otag(h, TAG_SPAN, 1, tag); + t = print_otag(h, TAG_I, 1, tag); while (ep) { sz = MIN((int)(ep - sp), BUFSIZ - 1); @@ -1613,7 +1425,7 @@ mdoc_fn_pre(MDOC_ARGS) */ #if 0 - if (SEC_SYNOPSIS == n->sec) { + if (MDOC_SYNPRETTY & n->flags) { nbuf[0] = '\0'; html_idcat(nbuf, sp, BUFSIZ); PAIR_ID_INIT(&tag[1], nbuf); @@ -1624,7 +1436,7 @@ mdoc_fn_pre(MDOC_ARGS) } #endif - t = print_otag(h, TAG_SPAN, 1, tag); + t = print_otag(h, TAG_B, 1, tag); if (sp) { strlcpy(nbuf, sp, BUFSIZ); @@ -1643,9 +1455,9 @@ mdoc_fn_pre(MDOC_ARGS) for (nn = n->child->next; nn; nn = nn->next) { i = 1; - if (SEC_SYNOPSIS == n->sec) + if (MDOC_SYNPRETTY & n->flags) i = 2; - t = print_otag(h, TAG_SPAN, i, tag); + t = print_otag(h, TAG_I, i, tag); print_text(h, nn->string); print_tagq(h, t); if (nn->next) @@ -1653,38 +1465,67 @@ mdoc_fn_pre(MDOC_ARGS) } print_text(h, ")"); - if (SEC_SYNOPSIS == n->sec) + if (MDOC_SYNPRETTY & n->flags) print_text(h, ";"); return(0); } +/* ARGSUSED */ +static int +mdoc_sm_pre(MDOC_ARGS) +{ + + assert(n->child && MDOC_TEXT == n->child->type); + if (0 == strcmp("on", n->child->string)) { + /* + * FIXME: no p->col to check. Thus, if we have + * .Bd -literal + * .Sm off + * 1 2 + * .Sm on + * 3 + * .Ed + * the "3" is preceded by a space. + */ + h->flags &= ~HTML_NOSPACE; + h->flags &= ~HTML_NONOSPACE; + } else + h->flags |= HTML_NONOSPACE; + + return(0); +} + +/* ARGSUSED */ +static int +mdoc_pp_pre(MDOC_ARGS) +{ + + print_otag(h, TAG_P, 0, NULL); + return(0); + +} + /* ARGSUSED */ static int mdoc_sp_pre(MDOC_ARGS) { - int len; - struct htmlpair tag; struct roffsu su; + struct htmlpair tag; - switch (n->tok) { - case (MDOC_sp): - /* FIXME: can this have a scaling indicator? */ - len = n->child ? atoi(n->child->string) : 1; - break; - case (MDOC_br): - len = 0; - break; - default: - len = 1; - break; - } + SCALE_VS_INIT(&su, 1); + + if (MDOC_sp == n->tok) { + if (n->child) + a2roffsu(n->child->string, &su, SCALE_VS); + } else + su.scale = 0; - SCALE_VS_INIT(&su, len); bufcat_su(h, "height", &su); PAIR_STYLE_INIT(&tag, h); print_otag(h, TAG_DIV, 1, &tag); + /* So the div isn't empty: */ print_text(h, "\\~"); @@ -1692,32 +1533,6 @@ mdoc_sp_pre(MDOC_ARGS) } - -/* ARGSUSED */ -static int -mdoc_brq_pre(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - print_text(h, "\\(lC"); - h->flags |= HTML_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_brq_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - h->flags |= HTML_NOSPACE; - print_text(h, "\\(rC"); -} - - /* ARGSUSED */ static int mdoc_lk_pre(MDOC_ARGS) @@ -1731,7 +1546,7 @@ mdoc_lk_pre(MDOC_ARGS) PAIR_HREF_INIT(&tag[1], nn->string); print_otag(h, TAG_A, 2, tag); - if (NULL == nn->next) + if (NULL == nn || NULL == nn->next) return(1); for (nn = nn->next; nn; nn = nn->next) @@ -1788,7 +1603,7 @@ mdoc_fo_pre(MDOC_ARGS) assert(n->child->string); PAIR_CLASS_INIT(&tag, "fname"); - t = print_otag(h, TAG_SPAN, 1, &tag); + t = print_otag(h, TAG_B, 1, &tag); print_text(h, n->child->string); print_tagq(h, t); return(0); @@ -1802,9 +1617,7 @@ mdoc_fo_post(MDOC_ARGS) if (MDOC_BODY != n->type) return; - h->flags |= HTML_NOSPACE; print_text(h, ")"); - h->flags |= HTML_NOSPACE; print_text(h, ";"); } @@ -1821,9 +1634,9 @@ mdoc_in_pre(MDOC_ARGS) synopsis_pre(h, n); PAIR_CLASS_INIT(&tag[0], "includes"); - print_otag(h, TAG_SPAN, 1, tag); + print_otag(h, TAG_B, 1, tag); - if (SEC_SYNOPSIS == n->sec && MDOC_LINE & n->flags) + if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) print_text(h, "#include"); print_text(h, "<"); @@ -1857,7 +1670,7 @@ mdoc_ic_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "cmd"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_B, 1, &tag); return(1); } @@ -1870,12 +1683,14 @@ mdoc_rv_pre(MDOC_ARGS) struct htmlpair tag; struct tag *t; - print_otag(h, TAG_DIV, 0, NULL); + if (n->prev) + print_otag(h, TAG_BR, 0, NULL); + print_text(h, "The"); for (nn = n->child; nn; nn = nn->next) { PAIR_CLASS_INIT(&tag, "fname"); - t = print_otag(h, TAG_SPAN, 1, &tag); + t = print_otag(h, TAG_B, 1, &tag); print_text(h, nn->string); print_tagq(h, t); @@ -1897,7 +1712,7 @@ mdoc_rv_pre(MDOC_ARGS) "-1 is returned and the global variable"); PAIR_CLASS_INIT(&tag, "var"); - t = print_otag(h, TAG_SPAN, 1, &tag); + t = print_otag(h, TAG_B, 1, &tag); print_text(h, "errno"); print_tagq(h, t); print_text(h, "is set to indicate the error."); @@ -1912,36 +1727,11 @@ mdoc_va_pre(MDOC_ARGS) struct htmlpair tag; PAIR_CLASS_INIT(&tag, "var"); - print_otag(h, TAG_SPAN, 1, &tag); + print_otag(h, TAG_B, 1, &tag); return(1); } -/* ARGSUSED */ -static int -mdoc_bq_pre(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - print_text(h, "\\(lB"); - h->flags |= HTML_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -mdoc_bq_post(MDOC_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - h->flags |= HTML_NOSPACE; - print_text(h, "\\(rB"); -} - - /* ARGSUSED */ static int mdoc_ap_pre(MDOC_ARGS) @@ -1958,46 +1748,31 @@ mdoc_ap_pre(MDOC_ARGS) static int mdoc_bf_pre(MDOC_ARGS) { - int i; struct htmlpair tag[2]; struct roffsu su; if (MDOC_HEAD == n->type) return(0); - else if (MDOC_BLOCK != n->type) + else if (MDOC_BODY != n->type) return(1); - PAIR_CLASS_INIT(&tag[0], "lit"); - - if (n->head->child) { - if ( ! strcmp("Em", n->head->child->string)) - PAIR_CLASS_INIT(&tag[0], "emph"); - else if ( ! strcmp("Sy", n->head->child->string)) - PAIR_CLASS_INIT(&tag[0], "symb"); - else if ( ! strcmp("Li", n->head->child->string)) - PAIR_CLASS_INIT(&tag[0], "lit"); - } else { - for (i = 0; n->args && i < (int)n->args->argc; i++) - switch (n->args->argv[i].arg) { - case (MDOC_Symbolic): - PAIR_CLASS_INIT(&tag[0], "symb"); - break; - case (MDOC_Literal): - PAIR_CLASS_INIT(&tag[0], "lit"); - break; - case (MDOC_Emphasis): - PAIR_CLASS_INIT(&tag[0], "emph"); - break; - default: - break; - } - } - - /* FIXME: div's have spaces stripped--we want them. */ + if (FONT_Em == n->norm->Bf.font) + PAIR_CLASS_INIT(&tag[0], "emph"); + else if (FONT_Sy == n->norm->Bf.font) + PAIR_CLASS_INIT(&tag[0], "symb"); + else if (FONT_Li == n->norm->Bf.font) + PAIR_CLASS_INIT(&tag[0], "lit"); + else + PAIR_CLASS_INIT(&tag[0], "none"); + /* + * We want this to be inline-formatted, but needs to be div to + * accept block children. + */ bufcat_style(h, "display", "inline"); SCALE_HS_INIT(&su, 1); - bufcat_su(h, "margin-right", &su); + /* Needs a left-margin for spacing. */ + bufcat_su(h, "margin-left", &su); PAIR_STYLE_INIT(&tag[1], h); print_otag(h, TAG_DIV, 2, tag); return(1); @@ -2018,7 +1793,7 @@ mdoc_ms_pre(MDOC_ARGS) /* ARGSUSED */ static int -mdoc_pf_pre(MDOC_ARGS) +mdoc_igndelim_pre(MDOC_ARGS) { h->flags |= HTML_IGNDELIM; @@ -2031,7 +1806,6 @@ static void mdoc_pf_post(MDOC_ARGS) { - h->flags &= ~HTML_IGNDELIM; h->flags |= HTML_NOSPACE; } @@ -2041,17 +1815,12 @@ static int mdoc_rs_pre(MDOC_ARGS) { struct htmlpair tag; - struct roffsu su; if (MDOC_BLOCK != n->type) return(1); - if (n->prev && SEC_SEE_ALSO == n->sec) { - SCALE_VS_INIT(&su, 1); - bufcat_su(h, "margin-top", &su); - PAIR_STYLE_INIT(&tag, h); - print_otag(h, TAG_DIV, 1, &tag); - } + if (n->prev && SEC_SEE_ALSO == n->sec) + print_otag(h, TAG_P, 0, NULL); PAIR_CLASS_INIT(&tag, "ref"); print_otag(h, TAG_SPAN, 1, &tag); @@ -2110,8 +1879,9 @@ mdoc_lb_pre(MDOC_ARGS) { struct htmlpair tag; - if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags) - print_otag(h, TAG_DIV, 0, NULL); + if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev) + print_otag(h, TAG_BR, 0, NULL); + PAIR_CLASS_INIT(&tag, "lib"); print_otag(h, TAG_SPAN, 1, &tag); return(1); @@ -2123,13 +1893,20 @@ static int mdoc__x_pre(MDOC_ARGS) { struct htmlpair tag[2]; + enum htmltag t; + + t = TAG_SPAN; switch (n->tok) { case(MDOC__A): PAIR_CLASS_INIT(&tag[0], "ref-auth"); + if (n->prev && MDOC__A == n->prev->tok) + if (NULL == n->next || MDOC__A != n->next->tok) + print_text(h, "and"); break; case(MDOC__B): PAIR_CLASS_INIT(&tag[0], "ref-book"); + t = TAG_I; break; case(MDOC__C): PAIR_CLASS_INIT(&tag[0], "ref-city"); @@ -2139,9 +1916,11 @@ mdoc__x_pre(MDOC_ARGS) break; case(MDOC__I): PAIR_CLASS_INIT(&tag[0], "ref-issue"); + t = TAG_I; break; case(MDOC__J): PAIR_CLASS_INIT(&tag[0], "ref-jrnl"); + t = TAG_I; break; case(MDOC__N): PAIR_CLASS_INIT(&tag[0], "ref-num"); @@ -2173,12 +1952,13 @@ mdoc__x_pre(MDOC_ARGS) } if (MDOC__U != n->tok) { - print_otag(h, TAG_SPAN, 1, tag); + print_otag(h, t, 1, tag); return(1); } PAIR_HREF_INIT(&tag[1], n->child->string); print_otag(h, TAG_A, 2, tag); + return(1); } @@ -2188,8 +1968,172 @@ static void mdoc__x_post(MDOC_ARGS) { + if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) + if (NULL == n->next->next || MDOC__A != n->next->next->tok) + if (NULL == n->prev || MDOC__A != n->prev->tok) + return; + /* TODO: %U */ - h->flags |= HTML_NOSPACE; + if (NULL == n->parent || MDOC_Rs != n->parent->tok) + return; + print_text(h, n->next ? "," : "."); } + + +/* ARGSUSED */ +static int +mdoc_bk_pre(MDOC_ARGS) +{ + + switch (n->type) { + case (MDOC_BLOCK): + break; + case (MDOC_HEAD): + return(0); + case (MDOC_BODY): + if (n->parent->args || 0 == n->prev->nchild) + h->flags |= HTML_PREKEEP; + break; + default: + abort(); + /* NOTREACHED */ + } + + return(1); +} + + +/* ARGSUSED */ +static void +mdoc_bk_post(MDOC_ARGS) +{ + + if (MDOC_BODY == n->type) + h->flags &= ~(HTML_KEEP | HTML_PREKEEP); +} + + +/* ARGSUSED */ +static int +mdoc_quote_pre(MDOC_ARGS) +{ + struct htmlpair tag; + + if (MDOC_BODY != n->type) + return(1); + + switch (n->tok) { + case (MDOC_Ao): + /* FALLTHROUGH */ + case (MDOC_Aq): + print_text(h, "\\(la"); + break; + case (MDOC_Bro): + /* FALLTHROUGH */ + case (MDOC_Brq): + print_text(h, "\\(lC"); + break; + case (MDOC_Bo): + /* FALLTHROUGH */ + case (MDOC_Bq): + print_text(h, "\\(lB"); + break; + case (MDOC_Oo): + /* FALLTHROUGH */ + case (MDOC_Op): + print_text(h, "\\(lB"); + h->flags |= HTML_NOSPACE; + PAIR_CLASS_INIT(&tag, "opt"); + print_otag(h, TAG_SPAN, 1, &tag); + break; + case (MDOC_Do): + /* FALLTHROUGH */ + case (MDOC_Dq): + /* FALLTHROUGH */ + case (MDOC_Qo): + /* FALLTHROUGH */ + case (MDOC_Qq): + print_text(h, "\\(lq"); + break; + case (MDOC_Po): + /* FALLTHROUGH */ + case (MDOC_Pq): + print_text(h, "("); + break; + case (MDOC_Ql): + /* FALLTHROUGH */ + case (MDOC_So): + /* FALLTHROUGH */ + case (MDOC_Sq): + print_text(h, "\\(oq"); + break; + default: + abort(); + /* NOTREACHED */ + } + + h->flags |= HTML_NOSPACE; + return(1); +} + + +/* ARGSUSED */ +static void +mdoc_quote_post(MDOC_ARGS) +{ + + if (MDOC_BODY != n->type) + return; + + h->flags |= HTML_NOSPACE; + + switch (n->tok) { + case (MDOC_Ao): + /* FALLTHROUGH */ + case (MDOC_Aq): + print_text(h, "\\(ra"); + break; + case (MDOC_Bro): + /* FALLTHROUGH */ + case (MDOC_Brq): + print_text(h, "\\(rC"); + break; + case (MDOC_Oo): + /* FALLTHROUGH */ + case (MDOC_Op): + /* FALLTHROUGH */ + case (MDOC_Bo): + /* FALLTHROUGH */ + case (MDOC_Bq): + print_text(h, "\\(rB"); + break; + case (MDOC_Qo): + /* FALLTHROUGH */ + case (MDOC_Qq): + /* FALLTHROUGH */ + case (MDOC_Do): + /* FALLTHROUGH */ + case (MDOC_Dq): + print_text(h, "\\(rq"); + break; + case (MDOC_Po): + /* FALLTHROUGH */ + case (MDOC_Pq): + print_text(h, ")"); + break; + case (MDOC_Ql): + /* FALLTHROUGH */ + case (MDOC_So): + /* FALLTHROUGH */ + case (MDOC_Sq): + print_text(h, "\\(aq"); + break; + default: + abort(); + /* NOTREACHED */ + } +} + + diff --git a/commands/mdocml/mdoc_macro.c b/usr.bin/mdocml/dist/mdoc_macro.c similarity index 78% rename from commands/mdocml/mdoc_macro.c rename to usr.bin/mdocml/dist/mdoc_macro.c index 0b0fe4080..b47a59143 100644 --- a/commands/mdocml/mdoc_macro.c +++ b/usr.bin/mdocml/dist/mdoc_macro.c @@ -1,6 +1,7 @@ -/* $Id: mdoc_macro.c,v 1.80 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc_macro.c,v 1.99 2010/12/15 23:39:40 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2010 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 @@ -29,10 +30,13 @@ #include "libmdoc.h" #include "libmandoc.h" -enum rew { - REWIND_REWIND, - REWIND_NOHALT, - REWIND_HALT +enum rew { /* see rew_dohalt() */ + REWIND_NONE, + REWIND_THIS, + REWIND_MORE, + REWIND_FORCE, + REWIND_LATER, + REWIND_ERROR }; static int blk_full(MACRO_PROT_ARGS); @@ -50,10 +54,10 @@ static int append_delims(struct mdoc *, int, int *, char *); static enum mdoct lookup(enum mdoct, const char *); static enum mdoct lookup_raw(const char *); +static int make_pending(struct mdoc_node *, enum mdoct, + struct mdoc *, int, int); static int phrase(struct mdoc *, int, int, char *); static enum mdoct rew_alt(enum mdoct); -static int rew_dobreak(enum mdoct, - const struct mdoc_node *); static enum rew rew_dohalt(enum mdoct, enum mdoc_type, const struct mdoc_node *); static int rew_elem(struct mdoc *, enum mdoct); @@ -61,8 +65,6 @@ static int rew_last(struct mdoc *, const struct mdoc_node *); static int rew_sub(enum mdoc_type, struct mdoc *, enum mdoct, int, int); -static int swarn(struct mdoc *, enum mdoc_type, int, - int, const struct mdoc_node *); const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */ @@ -97,7 +99,7 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */ { blk_full, 0 }, /* Nd */ - { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */ + { ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */ { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */ { obsolete, 0 }, /* Ot */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */ @@ -137,8 +139,8 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */ { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ms */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* No */ - { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* No */ + { in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Ns */ { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */ { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */ { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */ @@ -192,53 +194,6 @@ const struct mdoc_macro __mdoc_macros[MDOC_MAX] = { const struct mdoc_macro * const mdoc_macros = __mdoc_macros; -static int -swarn(struct mdoc *mdoc, enum mdoc_type type, - int line, int pos, const struct mdoc_node *p) -{ - const char *n, *t, *tt; - enum mandocerr ec; - - n = t = ""; - tt = "block"; - - switch (type) { - case (MDOC_BODY): - tt = "multi-line"; - break; - case (MDOC_HEAD): - tt = "line"; - break; - default: - break; - } - - switch (p->type) { - case (MDOC_BLOCK): - n = mdoc_macronames[p->tok]; - t = "block"; - break; - case (MDOC_BODY): - n = mdoc_macronames[p->tok]; - t = "multi-line"; - break; - case (MDOC_HEAD): - n = mdoc_macronames[p->tok]; - t = "line"; - break; - default: - break; - } - - ec = (MDOC_IGN_SCOPE & mdoc->pflags) ? - MANDOCERR_SCOPE : MANDOCERR_SYNTSCOPE; - - return(mdoc_vmsg(mdoc, ec, line, pos, - "%s scope breaks %s of %s", - tt, t, n)); -} - - /* * This is called at the end of parsing. It must traverse up the tree, * closing out open [implicit] scopes. Obviously, open explicit scopes @@ -253,14 +208,10 @@ mdoc_macroend(struct mdoc *m) n = MDOC_VALID & m->last->flags ? m->last->parent : m->last; - for ( ; n; n = n->parent) { - if (MDOC_BLOCK != n->type) - continue; - if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags)) - continue; - mdoc_nmsg(m, n, MANDOCERR_SYNTSCOPE); - return(0); - } + for ( ; n; n = n->parent) + if (MDOC_BLOCK == n->type && + MDOC_EXPLICIT & mdoc_macros[n->tok].flags) + mdoc_nmsg(m, n, MANDOCERR_SCOPEEXIT); /* Rewind to the first. */ @@ -301,6 +252,7 @@ lookup_raw(const char *p) static int rew_last(struct mdoc *mdoc, const struct mdoc_node *to) { + struct mdoc_node *n; assert(to); mdoc->next = MDOC_NEXT_SIBLING; @@ -309,21 +261,19 @@ rew_last(struct mdoc *mdoc, const struct mdoc_node *to) while (mdoc->last != to) { if ( ! mdoc_valid_post(mdoc)) return(0); - if ( ! mdoc_action_post(mdoc)) - return(0); + n = mdoc->last; mdoc->last = mdoc->last->parent; assert(mdoc->last); + mdoc->last->last = n; } - if ( ! mdoc_valid_post(mdoc)) - return(0); - return(mdoc_action_post(mdoc)); + return(mdoc_valid_post(mdoc)); } /* - * Return the opening macro of a closing one, e.g., `Ec' has `Eo' as its - * matching pair. + * For a block closing macro, return the corresponding opening one. + * Otherwise, return the macro itself. */ static enum mdoct rew_alt(enum mdoct tok) @@ -362,202 +312,119 @@ rew_alt(enum mdoct tok) case (MDOC_Xc): return(MDOC_Xo); default: - break; + return(tok); } - abort(); /* NOTREACHED */ } -/* - * Rewind rules. This indicates whether to stop rewinding - * (REWIND_HALT) without touching our current scope, stop rewinding and - * close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT). - * The scope-closing and so on occurs in the various rew_* routines. +/* + * Rewinding to tok, how do we have to handle *p? + * REWIND_NONE: *p would delimit tok, but no tok scope is open + * inside *p, so there is no need to rewind anything at all. + * REWIND_THIS: *p matches tok, so rewind *p and nothing else. + * REWIND_MORE: *p is implicit, rewind it and keep searching for tok. + * REWIND_FORCE: *p is explicit, but tok is full, force rewinding *p. + * REWIND_LATER: *p is explicit and still open, postpone rewinding. + * REWIND_ERROR: No tok block is open at all. */ static enum rew rew_dohalt(enum mdoct tok, enum mdoc_type type, const struct mdoc_node *p) { + /* + * No matching token, no delimiting block, no broken block. + * This can happen when full implicit macros are called for + * the first time but try to rewind their previous + * instance anyway. + */ if (MDOC_ROOT == p->type) - return(REWIND_HALT); - if (MDOC_VALID & p->flags) - return(REWIND_NOHALT); + return(MDOC_BLOCK == type && + MDOC_EXPLICIT & mdoc_macros[tok].flags ? + REWIND_ERROR : REWIND_NONE); - switch (tok) { - case (MDOC_Aq): - /* FALLTHROUGH */ - case (MDOC_Bq): - /* FALLTHROUGH */ - case (MDOC_Brq): - /* FALLTHROUGH */ - case (MDOC_D1): - /* FALLTHROUGH */ - case (MDOC_Dl): - /* FALLTHROUGH */ - case (MDOC_Dq): - /* FALLTHROUGH */ - case (MDOC_Op): - /* FALLTHROUGH */ - case (MDOC_Pq): - /* FALLTHROUGH */ - case (MDOC_Ql): - /* FALLTHROUGH */ - case (MDOC_Qq): - /* FALLTHROUGH */ - case (MDOC_Sq): - /* FALLTHROUGH */ - case (MDOC_Vt): - assert(MDOC_TAIL != type); - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - break; - case (MDOC_It): - assert(MDOC_TAIL != type); - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - if (MDOC_BODY == p->type && MDOC_Bl == p->tok) - return(REWIND_HALT); - break; - case (MDOC_Sh): - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - break; - case (MDOC_Nd): - /* FALLTHROUGH */ - case (MDOC_Ss): - assert(MDOC_TAIL != type); - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - if (MDOC_BODY == p->type && MDOC_Sh == p->tok) - return(REWIND_HALT); - break; - case (MDOC_Ao): - /* FALLTHROUGH */ - case (MDOC_Bd): - /* FALLTHROUGH */ - case (MDOC_Bf): - /* FALLTHROUGH */ - case (MDOC_Bk): - /* FALLTHROUGH */ - case (MDOC_Bl): - /* FALLTHROUGH */ - case (MDOC_Bo): - /* FALLTHROUGH */ - case (MDOC_Bro): - /* FALLTHROUGH */ - case (MDOC_Do): - /* FALLTHROUGH */ - case (MDOC_Eo): - /* FALLTHROUGH */ - case (MDOC_Fo): - /* FALLTHROUGH */ - case (MDOC_Oo): - /* FALLTHROUGH */ - case (MDOC_Po): - /* FALLTHROUGH */ - case (MDOC_Qo): - /* FALLTHROUGH */ - case (MDOC_Rs): - /* FALLTHROUGH */ - case (MDOC_So): - /* FALLTHROUGH */ - case (MDOC_Xo): - if (type == p->type && tok == p->tok) - return(REWIND_REWIND); - break; - /* Multi-line explicit scope close. */ - case (MDOC_Ac): - /* FALLTHROUGH */ - case (MDOC_Bc): - /* FALLTHROUGH */ - case (MDOC_Brc): - /* FALLTHROUGH */ - case (MDOC_Dc): - /* FALLTHROUGH */ - case (MDOC_Ec): - /* FALLTHROUGH */ - case (MDOC_Ed): - /* FALLTHROUGH */ - case (MDOC_Ek): - /* FALLTHROUGH */ - case (MDOC_El): - /* FALLTHROUGH */ - case (MDOC_Fc): - /* FALLTHROUGH */ - case (MDOC_Ef): - /* FALLTHROUGH */ - case (MDOC_Oc): - /* FALLTHROUGH */ - case (MDOC_Pc): - /* FALLTHROUGH */ - case (MDOC_Qc): - /* FALLTHROUGH */ - case (MDOC_Re): - /* FALLTHROUGH */ - case (MDOC_Sc): - /* FALLTHROUGH */ - case (MDOC_Xc): - if (type == p->type && rew_alt(tok) == p->tok) - return(REWIND_REWIND); - break; - default: - abort(); - /* NOTREACHED */ - } + /* + * When starting to rewind, skip plain text + * and nodes that have already been rewound. + */ + if (MDOC_TEXT == p->type || MDOC_VALID & p->flags) + return(REWIND_MORE); - return(REWIND_NOHALT); -} + /* + * The easiest case: Found a matching token. + * This applies to both blocks and elements. + */ + tok = rew_alt(tok); + if (tok == p->tok) + return(p->end ? REWIND_NONE : + type == p->type ? REWIND_THIS : REWIND_MORE); - -/* - * See if we can break an encountered scope (the rew_dohalt has returned - * REWIND_NOHALT). - */ -static int -rew_dobreak(enum mdoct tok, const struct mdoc_node *p) -{ - - assert(MDOC_ROOT != p->type); + /* + * While elements do require rewinding for themselves, + * they never affect rewinding of other nodes. + */ if (MDOC_ELEM == p->type) - return(1); - if (MDOC_TEXT == p->type) - return(1); - if (MDOC_VALID & p->flags) - return(1); + return(REWIND_MORE); + /* + * Blocks delimited by our target token get REWIND_MORE. + * Blocks delimiting our target token get REWIND_NONE. + */ switch (tok) { - case (MDOC_It): - return(MDOC_It == p->tok); - case (MDOC_Nd): - return(MDOC_Nd == p->tok); - case (MDOC_Ss): - return(MDOC_Ss == p->tok); - case (MDOC_Sh): - if (MDOC_Nd == p->tok) - return(1); - if (MDOC_Ss == p->tok) - return(1); - return(MDOC_Sh == p->tok); - case (MDOC_El): + case (MDOC_Bl): if (MDOC_It == p->tok) - return(1); + return(REWIND_MORE); break; - case (MDOC_Oc): + case (MDOC_It): + if (MDOC_BODY == p->type && MDOC_Bl == p->tok) + return(REWIND_NONE); + break; + /* + * XXX Badly nested block handling still fails badly + * when one block is breaking two blocks of the same type. + * This is an incomplete and extremely ugly workaround, + * required to let the OpenBSD tree build. + */ + case (MDOC_Oo): if (MDOC_Op == p->tok) - return(1); + return(REWIND_MORE); + break; + case (MDOC_Nm): + return(REWIND_NONE); + case (MDOC_Nd): + /* FALLTHROUGH */ + case (MDOC_Ss): + if (MDOC_BODY == p->type && MDOC_Sh == p->tok) + return(REWIND_NONE); + /* FALLTHROUGH */ + case (MDOC_Sh): + if (MDOC_Nd == p->tok || MDOC_Ss == p->tok || + MDOC_Sh == p->tok) + return(REWIND_MORE); break; default: break; } - if (MDOC_EXPLICIT & mdoc_macros[tok].flags) - return(p->tok == rew_alt(tok)); - else if (MDOC_BLOCK == p->type) - return(1); + /* + * Default block rewinding rules. + * In particular, always skip block end markers, + * and let all blocks rewind Nm children. + */ + if (ENDBODY_NOT != p->end || MDOC_Nm == p->tok || + (MDOC_BLOCK == p->type && + ! (MDOC_EXPLICIT & mdoc_macros[tok].flags))) + return(REWIND_MORE); - return(tok == p->tok); + /* + * By default, closing out full blocks + * forces closing of broken explicit blocks, + * while closing out partial blocks + * allows delayed rewinding by default. + */ + return (&blk_full == mdoc_macros[tok].fp ? + REWIND_FORCE : REWIND_LATER); } @@ -576,51 +443,131 @@ rew_elem(struct mdoc *mdoc, enum mdoct tok) } +/* + * We are trying to close a block identified by tok, + * but the child block *broken is still open. + * Thus, postpone closing the tok block + * until the rew_sub call closing *broken. + */ +static int +make_pending(struct mdoc_node *broken, enum mdoct tok, + struct mdoc *m, int line, int ppos) +{ + struct mdoc_node *breaker; + + /* + * Iterate backwards, searching for the block matching tok, + * that is, the block breaking the *broken block. + */ + for (breaker = broken->parent; breaker; breaker = breaker->parent) { + + /* + * If the *broken block had already been broken before + * and we encounter its breaker, make the tok block + * pending on the inner breaker. + * Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]" + * becomes "[A broken=[B [C->B B] tok=A] C]" + * and finally "[A [B->A [C->B B] A] C]". + */ + if (breaker == broken->pending) { + broken = breaker; + continue; + } + + if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker)) + continue; + if (MDOC_BODY == broken->type) + broken = broken->parent; + + /* + * Found the breaker. + * If another, outer breaker is already pending on + * the *broken block, we must not clobber the link + * to the outer breaker, but make it pending on the + * new, now inner breaker. + * Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]" + * becomes "[A breaker=[B->A broken=[C A] tok=B] C]" + * and finally "[A [B->A [C->B A] B] C]". + */ + if (broken->pending) { + struct mdoc_node *taker; + + /* + * If the breaker had also been broken before, + * it cannot take on the outer breaker itself, + * but must hand it on to its own breakers. + * Graphically, this is the following situation: + * "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]" + * "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]" + */ + taker = breaker; + while (taker->pending) + taker = taker->pending; + taker->pending = broken->pending; + } + broken->pending = breaker; + mdoc_vmsg(m, MANDOCERR_SCOPENEST, line, ppos, + "%s breaks %s", mdoc_macronames[tok], + mdoc_macronames[broken->tok]); + return(1); + } + + /* + * Found no matching block for tok. + * Are you trying to close a block that is not open? + */ + return(0); +} + + static int rew_sub(enum mdoc_type t, struct mdoc *m, enum mdoct tok, int line, int ppos) { struct mdoc_node *n; - enum rew c; - /* LINTED */ - for (n = m->last; n; n = n->parent) { - c = rew_dohalt(tok, t, n); - if (REWIND_HALT == c) { - if (MDOC_BLOCK != t) - return(1); - if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) - return(1); - /* FIXME: shouldn't raise an error */ - mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTNOSCOPE); - return(0); - } - if (REWIND_REWIND == c) + n = m->last; + while (n) { + switch (rew_dohalt(tok, t, n)) { + case (REWIND_NONE): + return(1); + case (REWIND_THIS): break; - else if (rew_dobreak(tok, n)) + case (REWIND_FORCE): + mdoc_vmsg(m, MANDOCERR_SCOPEBROKEN, line, ppos, + "%s breaks %s", mdoc_macronames[tok], + mdoc_macronames[n->tok]); + /* FALLTHROUGH */ + case (REWIND_MORE): + n = n->parent; continue; - if ( ! swarn(m, t, line, ppos, n)) - return(0); + case (REWIND_LATER): + if (make_pending(n, tok, m, line, ppos) || + MDOC_BLOCK != t) + return(1); + /* FALLTHROUGH */ + case (REWIND_ERROR): + mdoc_pmsg(m, line, ppos, MANDOCERR_NOSCOPE); + return(1); + } + break; } assert(n); if ( ! rew_last(m, n)) return(0); -#ifdef UGLY /* - * The current block extends an enclosing block beyond a line - * break. Now that the current block ends, close the enclosing - * block, too. + * The current block extends an enclosing block. + * Now that the current block ends, close the enclosing block, too. */ - if (NULL != (n = n->pending)) { - assert(MDOC_HEAD == n->type); + while (NULL != (n = n->pending)) { if ( ! rew_last(m, n)) return(0); - if ( ! mdoc_body_alloc(m, n->line, n->pos, n->tok)) + if (MDOC_HEAD == n->type && + ! mdoc_body_alloc(m, n->line, n->pos, n->tok)) return(0); } -#endif return(1); } @@ -660,7 +607,7 @@ append_delims(struct mdoc *m, int line, int *pos, char *buf) * knowing which symbols break this behaviour, for * example, `. ;' shouldn't propogate the double-space. */ - if (mandoc_eos(p, strlen(p))) + if (mandoc_eos(p, strlen(p), 0)) m->last->flags |= MDOC_EOS; } @@ -674,9 +621,13 @@ append_delims(struct mdoc *m, int line, int *pos, char *buf) static int blk_exp_close(MACRO_PROT_ARGS) { + struct mdoc_node *body; /* Our own body. */ + struct mdoc_node *later; /* A sub-block starting later. */ + struct mdoc_node *n; /* For searching backwards. */ + int j, lastarg, maxargs, flushed, nl; enum margserr ac; - enum mdoct ntok; + enum mdoct atok, ntok; char *p; nl = MDOC_NEWLINE & m->flags; @@ -690,6 +641,65 @@ blk_exp_close(MACRO_PROT_ARGS) break; } + /* + * Search backwards for beginnings of blocks, + * both of our own and of pending sub-blocks. + */ + atok = rew_alt(tok); + body = later = NULL; + for (n = m->last; n; n = n->parent) { + if (MDOC_VALID & n->flags) + continue; + + /* Remember the start of our own body. */ + if (MDOC_BODY == n->type && atok == n->tok) { + if (ENDBODY_NOT == n->end) + body = n; + continue; + } + + if (MDOC_BLOCK != n->type || MDOC_Nm == n->tok) + continue; + if (atok == n->tok) { + assert(body); + + /* + * Found the start of our own block. + * When there is no pending sub block, + * just proceed to closing out. + */ + if (NULL == later) + break; + + /* + * When there is a pending sub block, + * postpone closing out the current block + * until the rew_sub() closing out the sub-block. + */ + make_pending(later, tok, m, line, ppos); + + /* + * Mark the place where the formatting - but not + * the scope - of the current block ends. + */ + if ( ! mdoc_endbody_alloc(m, line, ppos, + atok, body, ENDBODY_SPACE)) + return(0); + break; + } + + /* + * When finding an open sub block, remember the last + * open explicit block, or, in case there are only + * implicit ones, the first open implicit block. + */ + if (later && + MDOC_EXPLICIT & mdoc_macros[later->tok].flags) + continue; + if (MDOC_CALLABLE & mdoc_macros[n->tok].flags) + later = n; + } + if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) { /* FIXME: do this in validate */ if (buf[*pos]) @@ -704,7 +714,7 @@ blk_exp_close(MACRO_PROT_ARGS) if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); - if (maxargs > 0) + if (NULL == later && maxargs > 0) if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok))) return(0); @@ -778,7 +788,7 @@ in_line(MACRO_PROT_ARGS) /* FALLTHROUGH */ case (MDOC_Fl): /* FALLTHROUGH */ - case (MDOC_Lk): + case (MDOC_Mt): /* FALLTHROUGH */ case (MDOC_Nm): /* FALLTHROUGH */ @@ -936,9 +946,7 @@ blk_full(MACRO_PROT_ARGS) struct mdoc_arg *arg; struct mdoc_node *head; /* save of head macro */ struct mdoc_node *body; /* save of body macro */ -#ifdef UGLY struct mdoc_node *n; -#endif enum mdoc_type mtt; enum mdoct ntok; enum margserr ac, lac; @@ -1012,6 +1020,9 @@ blk_full(MACRO_PROT_ARGS) lac = ARGS_ERROR == ac ? ARGS_PEND : ac; ac = mdoc_args(m, line, pos, buf, tok, &p); + if (ARGS_PUNCT == ac) + break; + if (ARGS_ERROR == ac) return(0); @@ -1120,7 +1131,6 @@ blk_full(MACRO_PROT_ARGS) if (NULL != body) goto out; -#ifdef UGLY /* * If there is an open (i.e., unvalidated) sub-block requiring * explicit close-out, postpone switching the current block from @@ -1131,12 +1141,10 @@ blk_full(MACRO_PROT_ARGS) if (MDOC_BLOCK == n->type && MDOC_EXPLICIT & mdoc_macros[n->tok].flags && ! (MDOC_VALID & n->flags)) { - assert( ! (MDOC_ACTED & n->flags)); n->pending = head; return(1); } } -#endif /* Close out scopes to remain in a consistent state. */ @@ -1251,7 +1259,7 @@ blk_part_imp(MACRO_PROT_ARGS) */ if (n && MDOC_TEXT == n->type && n->string) - if (mandoc_eos(n->string, strlen(n->string))) + if (mandoc_eos(n->string, strlen(n->string), 1)) n->flags |= MDOC_EOS; /* Up-propogate the end-of-space flag. */ @@ -1261,22 +1269,34 @@ blk_part_imp(MACRO_PROT_ARGS) body->parent->flags |= MDOC_EOS; } + /* + * If there is an open sub-block requiring explicit close-out, + * postpone closing out the current block + * until the rew_sub() call closing out the sub-block. + */ + for (n = m->last; n && n != body && n != blk->parent; n = n->parent) { + if (MDOC_BLOCK == n->type && + MDOC_EXPLICIT & mdoc_macros[n->tok].flags && + ! (MDOC_VALID & n->flags)) { + make_pending(n, tok, m, line, ppos); + if ( ! mdoc_endbody_alloc(m, line, ppos, + tok, body, ENDBODY_NOSPACE)) + return(0); + return(1); + } + } + /* * If we can't rewind to our body, then our scope has already * been closed by another macro (like `Oc' closing `Op'). This * is ugly behaviour nodding its head to OpenBSD's overwhelming * crufty use of `Op' breakage. - * - * FIXME - this should be ifdef'd OpenBSD? */ - for (n = m->last; n; n = n->parent) - if (body == n) - break; - - if (NULL == n && ! mdoc_nmsg(m, body, MANDOCERR_SCOPE)) + if (n != body && ! mdoc_vmsg(m, MANDOCERR_SCOPENEST, + line, ppos, "%s broken", mdoc_macronames[tok])) return(0); - if (n && ! rew_last(m, body)) + if (n && ! rew_sub(MDOC_BODY, m, tok, line, ppos)) return(0); /* Standard appending of delimiters. */ @@ -1286,7 +1306,7 @@ blk_part_imp(MACRO_PROT_ARGS) /* Rewind scope, if applicable. */ - if (n && ! rew_last(m, blk)) + if (n && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos)) return(0); return(1); @@ -1554,6 +1574,9 @@ in_line_eoln(MACRO_PROT_ARGS) assert( ! (MDOC_PARSED & mdoc_macros[tok].flags)); + if (tok == MDOC_Pp) + rew_sub(MDOC_BLOCK, m, MDOC_Nm, line, ppos); + /* Parse macro arguments. */ for (arg = NULL; ; ) { @@ -1617,7 +1640,7 @@ ctx_synopsis(MACRO_PROT_ARGS) nl = MDOC_NEWLINE & m->flags; /* If we're not in the SYNOPSIS, go straight to in-line. */ - if (SEC_SYNOPSIS != m->lastsec) + if ( ! (MDOC_SYNOPSIS & m->flags)) return(in_line(m, tok, line, ppos, pos, buf)); /* If we're a nested call, same place. */ @@ -1629,7 +1652,9 @@ ctx_synopsis(MACRO_PROT_ARGS) * up formatting the block scope, then child nodes will inherit * the formatting. Be careful. */ - + if (MDOC_Nm == tok) + return(blk_full(m, tok, line, ppos, pos, buf)); + assert(MDOC_Vt == tok); return(blk_part_imp(m, tok, line, ppos, pos, buf)); } diff --git a/commands/mdocml/mdoc_strings.c b/usr.bin/mdocml/dist/mdoc_strings.c similarity index 96% rename from commands/mdocml/mdoc_strings.c rename to usr.bin/mdocml/dist/mdoc_strings.c index c05807f1b..d15bb4a85 100644 --- a/commands/mdocml/mdoc_strings.c +++ b/usr.bin/mdocml/dist/mdoc_strings.c @@ -1,6 +1,6 @@ -/* $Id: mdoc_strings.c,v 1.23 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc_strings.c,v 1.24 2010/07/31 23:52:58 schwarze Exp $ */ /* - * Copyright (c) 2008 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 diff --git a/commands/mdocml/mdoc_term.c b/usr.bin/mdocml/dist/mdoc_term.c similarity index 73% rename from commands/mdocml/mdoc_term.c rename to usr.bin/mdocml/dist/mdoc_term.c index c3dc634a3..38b7e2857 100644 --- a/commands/mdocml/mdoc_term.c +++ b/usr.bin/mdocml/dist/mdoc_term.c @@ -1,6 +1,7 @@ -/* $Id: mdoc_term.c,v 1.156 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: mdoc_term.c,v 1.208 2011/01/06 14:05:12 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2010 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 @@ -52,12 +53,10 @@ struct termact { void (*post)(DECL_ARGS); }; -static size_t a2width(const char *); -static size_t a2height(const struct mdoc_node *); -static size_t a2offs(const char *); +static size_t a2width(const struct termp *, const char *); +static size_t a2height(const struct termp *, const char *); +static size_t a2offs(const struct termp *, const char *); -static int arg_hasattr(int, const struct mdoc_node *); -static int arg_getattr(int, const struct mdoc_node *); static void print_bvspace(struct termp *, const struct mdoc_node *, const struct mdoc_node *); @@ -69,47 +68,43 @@ static void synopsis_pre(struct termp *, const struct mdoc_node *); static void termp____post(DECL_ARGS); +static void termp__t_post(DECL_ARGS); static void termp_an_post(DECL_ARGS); -static void termp_aq_post(DECL_ARGS); static void termp_bd_post(DECL_ARGS); +static void termp_bk_post(DECL_ARGS); static void termp_bl_post(DECL_ARGS); -static void termp_bq_post(DECL_ARGS); -static void termp_brq_post(DECL_ARGS); static void termp_bx_post(DECL_ARGS); static void termp_d1_post(DECL_ARGS); -static void termp_dq_post(DECL_ARGS); -static int termp_fd_pre(DECL_ARGS); static void termp_fo_post(DECL_ARGS); static void termp_in_post(DECL_ARGS); static void termp_it_post(DECL_ARGS); static void termp_lb_post(DECL_ARGS); -static void termp_op_post(DECL_ARGS); +static void termp_nm_post(DECL_ARGS); static void termp_pf_post(DECL_ARGS); -static void termp_pq_post(DECL_ARGS); -static void termp_qq_post(DECL_ARGS); +static void termp_quote_post(DECL_ARGS); static void termp_sh_post(DECL_ARGS); -static void termp_sq_post(DECL_ARGS); static void termp_ss_post(DECL_ARGS); +static int termp__a_pre(DECL_ARGS); +static int termp__t_pre(DECL_ARGS); static int termp_an_pre(DECL_ARGS); static int termp_ap_pre(DECL_ARGS); -static int termp_aq_pre(DECL_ARGS); static int termp_bd_pre(DECL_ARGS); static int termp_bf_pre(DECL_ARGS); +static int termp_bk_pre(DECL_ARGS); static int termp_bl_pre(DECL_ARGS); static int termp_bold_pre(DECL_ARGS); -static int termp_bq_pre(DECL_ARGS); -static int termp_brq_pre(DECL_ARGS); static int termp_bt_pre(DECL_ARGS); static int termp_cd_pre(DECL_ARGS); static int termp_d1_pre(DECL_ARGS); -static int termp_dq_pre(DECL_ARGS); static int termp_ex_pre(DECL_ARGS); static int termp_fa_pre(DECL_ARGS); +static int termp_fd_pre(DECL_ARGS); static int termp_fl_pre(DECL_ARGS); static int termp_fn_pre(DECL_ARGS); static int termp_fo_pre(DECL_ARGS); static int termp_ft_pre(DECL_ARGS); +static int termp_igndelim_pre(DECL_ARGS); static int termp_in_pre(DECL_ARGS); static int termp_it_pre(DECL_ARGS); static int termp_li_pre(DECL_ARGS); @@ -117,16 +112,12 @@ static int termp_lk_pre(DECL_ARGS); static int termp_nd_pre(DECL_ARGS); static int termp_nm_pre(DECL_ARGS); static int termp_ns_pre(DECL_ARGS); -static int termp_op_pre(DECL_ARGS); -static int termp_pf_pre(DECL_ARGS); -static int termp_pq_pre(DECL_ARGS); -static int termp_qq_pre(DECL_ARGS); +static int termp_quote_pre(DECL_ARGS); static int termp_rs_pre(DECL_ARGS); static int termp_rv_pre(DECL_ARGS); static int termp_sh_pre(DECL_ARGS); static int termp_sm_pre(DECL_ARGS); static int termp_sp_pre(DECL_ARGS); -static int termp_sq_pre(DECL_ARGS); static int termp_ss_pre(DECL_ARGS); static int termp_under_pre(DECL_ARGS); static int termp_ud_pre(DECL_ARGS); @@ -149,7 +140,7 @@ static const struct termact termacts[MDOC_MAX] = { { termp_bl_pre, termp_bl_post }, /* Bl */ { NULL, NULL }, /* El */ { termp_it_pre, termp_it_post }, /* It */ - { NULL, NULL }, /* Ad */ + { termp_under_pre, NULL }, /* Ad */ { termp_an_pre, termp_an_post }, /* An */ { termp_under_pre, NULL }, /* Ar */ { termp_cd_pre, NULL }, /* Cd */ @@ -167,8 +158,8 @@ static const struct termact termacts[MDOC_MAX] = { { termp_in_pre, termp_in_post }, /* In */ { termp_li_pre, NULL }, /* Li */ { termp_nd_pre, NULL }, /* Nd */ - { termp_nm_pre, NULL }, /* Nm */ - { termp_op_pre, termp_op_post }, /* Op */ + { termp_nm_pre, termp_nm_post }, /* Nm */ + { termp_quote_pre, termp_quote_post }, /* Op */ { NULL, NULL }, /* Ot */ { termp_under_pre, NULL }, /* Pa */ { termp_rv_pre, NULL }, /* Rv */ @@ -176,7 +167,7 @@ static const struct termact termacts[MDOC_MAX] = { { termp_under_pre, NULL }, /* Va */ { termp_vt_pre, NULL }, /* Vt */ { termp_xr_pre, NULL }, /* Xr */ - { NULL, termp____post }, /* %A */ + { termp__a_pre, termp____post }, /* %A */ { termp_under_pre, termp____post }, /* %B */ { NULL, termp____post }, /* %D */ { termp_under_pre, termp____post }, /* %I */ @@ -185,45 +176,45 @@ static const struct termact termacts[MDOC_MAX] = { { NULL, termp____post }, /* %O */ { NULL, termp____post }, /* %P */ { NULL, termp____post }, /* %R */ - { termp_under_pre, termp____post }, /* %T */ + { termp__t_pre, termp__t_post }, /* %T */ { NULL, termp____post }, /* %V */ { NULL, NULL }, /* Ac */ - { termp_aq_pre, termp_aq_post }, /* Ao */ - { termp_aq_pre, termp_aq_post }, /* Aq */ + { termp_quote_pre, termp_quote_post }, /* Ao */ + { termp_quote_pre, termp_quote_post }, /* Aq */ { NULL, NULL }, /* At */ { NULL, NULL }, /* Bc */ { termp_bf_pre, NULL }, /* Bf */ - { termp_bq_pre, termp_bq_post }, /* Bo */ - { termp_bq_pre, termp_bq_post }, /* Bq */ + { termp_quote_pre, termp_quote_post }, /* Bo */ + { termp_quote_pre, termp_quote_post }, /* Bq */ { termp_xx_pre, NULL }, /* Bsx */ { NULL, termp_bx_post }, /* Bx */ { NULL, NULL }, /* Db */ { NULL, NULL }, /* Dc */ - { termp_dq_pre, termp_dq_post }, /* Do */ - { termp_dq_pre, termp_dq_post }, /* Dq */ + { termp_quote_pre, termp_quote_post }, /* Do */ + { termp_quote_pre, termp_quote_post }, /* Dq */ { NULL, NULL }, /* Ec */ /* FIXME: no space */ { NULL, NULL }, /* Ef */ { termp_under_pre, NULL }, /* Em */ { NULL, NULL }, /* Eo */ { termp_xx_pre, NULL }, /* Fx */ - { termp_bold_pre, NULL }, /* Ms */ /* FIXME: convert to symbol? */ - { NULL, NULL }, /* No */ + { termp_bold_pre, NULL }, /* Ms */ + { termp_igndelim_pre, NULL }, /* No */ { termp_ns_pre, NULL }, /* Ns */ { termp_xx_pre, NULL }, /* Nx */ { termp_xx_pre, NULL }, /* Ox */ { NULL, NULL }, /* Pc */ - { termp_pf_pre, termp_pf_post }, /* Pf */ - { termp_pq_pre, termp_pq_post }, /* Po */ - { termp_pq_pre, termp_pq_post }, /* Pq */ + { termp_igndelim_pre, termp_pf_post }, /* Pf */ + { termp_quote_pre, termp_quote_post }, /* Po */ + { termp_quote_pre, termp_quote_post }, /* Pq */ { NULL, NULL }, /* Qc */ - { termp_sq_pre, termp_sq_post }, /* Ql */ - { termp_qq_pre, termp_qq_post }, /* Qo */ - { termp_qq_pre, termp_qq_post }, /* Qq */ + { termp_quote_pre, termp_quote_post }, /* Ql */ + { termp_quote_pre, termp_quote_post }, /* Qo */ + { termp_quote_pre, termp_quote_post }, /* Qq */ { NULL, NULL }, /* Re */ { termp_rs_pre, NULL }, /* Rs */ { NULL, NULL }, /* Sc */ - { termp_sq_pre, termp_sq_post }, /* So */ - { termp_sq_pre, termp_sq_post }, /* Sq */ + { termp_quote_pre, termp_quote_post }, /* So */ + { termp_quote_pre, termp_quote_post }, /* Sq */ { termp_sm_pre, NULL }, /* Sm */ { termp_under_pre, NULL }, /* Sx */ { termp_bold_pre, NULL }, /* Sy */ @@ -233,9 +224,9 @@ static const struct termact termacts[MDOC_MAX] = { { NULL, NULL }, /* Xo */ { termp_fo_pre, termp_fo_post }, /* Fo */ { NULL, NULL }, /* Fc */ - { termp_op_pre, termp_op_post }, /* Oo */ + { termp_quote_pre, termp_quote_post }, /* Oo */ { NULL, NULL }, /* Oc */ - { NULL, NULL }, /* Bk */ + { termp_bk_pre, termp_bk_post }, /* Bk */ { NULL, NULL }, /* Ek */ { termp_bt_pre, NULL }, /* Bt */ { NULL, NULL }, /* Hf */ @@ -245,8 +236,8 @@ static const struct termact termacts[MDOC_MAX] = { { termp_sp_pre, NULL }, /* Lp */ { termp_lk_pre, NULL }, /* Lk */ { termp_under_pre, NULL }, /* Mt */ - { termp_brq_pre, termp_brq_post }, /* Brq */ - { termp_brq_pre, termp_brq_post }, /* Bro */ + { termp_quote_pre, termp_quote_post }, /* Brq */ + { termp_quote_pre, termp_quote_post }, /* Bro */ { NULL, NULL }, /* Brc */ { NULL, termp____post }, /* %C */ { NULL, NULL }, /* Es */ /* TODO */ @@ -271,7 +262,7 @@ terminal_mdoc(void *arg, const struct mdoc *mdoc) p->overstep = 0; p->maxrmargin = p->defrmargin; - p->tabwidth = 5; + p->tabwidth = term_len(p, 5); if (NULL == p->symtab) switch (p->enc) { @@ -321,21 +312,83 @@ print_mdoc_node(DECL_ARGS) memset(&npair, 0, sizeof(struct termpair)); npair.ppair = pair; + + switch (n->type) { + case (MDOC_TEXT): + term_word(p, n->string); + break; + case (MDOC_TBL): + term_tbl(p, n->span); + break; + default: + if (termacts[n->tok].pre && ENDBODY_NOT == n->end) + chld = (*termacts[n->tok].pre) + (p, &npair, m, n); + break; + } - if (MDOC_TEXT != n->type) { - if (termacts[n->tok].pre) - chld = (*termacts[n->tok].pre)(p, &npair, m, n); - } else - term_word(p, n->string); + /* + * Keeps only work until the end of a line. If a keep was + * invoked in a prior line, revert it to PREKEEP. + * + * Also let SYNPRETTY sections behave as if they were wrapped + * in a `Bk' block. + */ + + if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) { + if (n->prev && n->prev->line != n->line) { + p->flags &= ~TERMP_KEEP; + p->flags |= TERMP_PREKEEP; + } else if (NULL == n->prev) { + if (n->parent && n->parent->line != n->line) { + p->flags &= ~TERMP_KEEP; + p->flags |= TERMP_PREKEEP; + } + } + } + + /* + * Since SYNPRETTY sections aren't "turned off" with `Ek', + * we have to intuit whether we should disable formatting. + */ + + if ( ! (MDOC_SYNPRETTY & n->flags) && + ((n->prev && MDOC_SYNPRETTY & n->prev->flags) || + (n->parent && MDOC_SYNPRETTY & n->parent->flags))) + p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); if (chld && n->child) print_mdoc_nodelist(p, &npair, m, n->child); term_fontpopq(p, font); - if (MDOC_TEXT != n->type) - if (termacts[n->tok].post) - (*termacts[n->tok].post)(p, &npair, m, n); + switch (n->type) { + case (MDOC_TEXT): + break; + case (MDOC_TBL): + break; + default: + if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags) + break; + (void)(*termacts[n->tok].post)(p, &npair, m, n); + + /* + * Explicit end tokens not only call the post + * handler, but also tell the respective block + * that it must not call the post handler again. + */ + if (ENDBODY_NOT != n->end) + n->pending->flags |= MDOC_ENDED; + + /* + * End of line terminating an implicit block + * while an explicit block is still open. + * Continue the explicit block without spacing. + */ + if (ENDBODY_NOSPACE == n->end) + p->flags |= TERMP_NOSPACE; + break; + } if (MDOC_EOS & n->flags) p->flags |= TERMP_SENTENCE; @@ -369,14 +422,15 @@ print_mdoc_foot(struct termp *p, const void *arg) term_vspace(p); p->offset = 0; - p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2; + p->rmargin = (p->maxrmargin - + term_strlen(p, buf) + term_len(p, 1)) / 2; p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; term_word(p, os); term_flushln(p); p->offset = p->rmargin; - p->rmargin = p->maxrmargin - strlen(os); + p->rmargin = p->maxrmargin - term_strlen(p, os); p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; term_word(p, buf); @@ -432,14 +486,15 @@ print_mdoc_head(struct termp *p, const void *arg) snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec); p->offset = 0; - p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2; + p->rmargin = (p->maxrmargin - + term_strlen(p, buf) + term_len(p, 1)) / 2; p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; term_word(p, title); term_flushln(p); p->offset = p->rmargin; - p->rmargin = p->maxrmargin - strlen(title); + p->rmargin = p->maxrmargin - term_strlen(p, title); p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; term_word(p, buf); @@ -460,34 +515,33 @@ print_mdoc_head(struct termp *p, const void *arg) static size_t -a2height(const struct mdoc_node *n) +a2height(const struct termp *p, const char *v) { struct roffsu su; - assert(MDOC_TEXT == n->type); - assert(n->string); - if ( ! a2roffsu(n->string, &su, SCALE_VS)) - SCALE_VS_INIT(&su, strlen(n->string)); + assert(v); + if ( ! a2roffsu(v, &su, SCALE_VS)) + SCALE_VS_INIT(&su, term_len(p, 1)); - return(term_vspan(&su)); + return(term_vspan(p, &su)); } static size_t -a2width(const char *v) +a2width(const struct termp *p, const char *v) { struct roffsu su; assert(v); if ( ! a2roffsu(v, &su, SCALE_MAX)) - SCALE_HS_INIT(&su, strlen(v)); + SCALE_HS_INIT(&su, term_strlen(p, v)); - return(term_hspan(&su)); + return(term_hspan(p, &su)); } static size_t -a2offs(const char *v) +a2offs(const struct termp *p, const char *v) { struct roffsu su; @@ -496,45 +550,13 @@ a2offs(const char *v) else if (0 == strcmp(v, "left")) return(0); else if (0 == strcmp(v, "indent")) - return(INDENT + 1); + return(term_len(p, INDENT + 1)); else if (0 == strcmp(v, "indent-two")) - return((INDENT + 1) * 2); + return(term_len(p, (INDENT + 1) * 2)); else if ( ! a2roffsu(v, &su, SCALE_MAX)) - SCALE_HS_INIT(&su, strlen(v)); + SCALE_HS_INIT(&su, term_strlen(p, v)); - return(term_hspan(&su)); -} - - -/* - * Return 1 if an argument has a particular argument value or 0 if it - * does not. See arg_getattr(). - */ -static int -arg_hasattr(int arg, const struct mdoc_node *n) -{ - - return(-1 != arg_getattr(arg, n)); -} - - -/* - * Get the index of an argument in a node's argument list or -1 if it - * does not exist. - */ -static int -arg_getattr(int v, const struct mdoc_node *n) -{ - int i; - - if (NULL == n->args) - return(0); - - for (i = 0; i < (int)n->args->argc; i++) - if (n->args->argv[i].arg == v) - return(i); - - return(-1); + return(term_hspan(p, &su)); } @@ -552,9 +574,9 @@ print_bvspace(struct termp *p, term_newln(p); - if (MDOC_Bd == bl->tok && bl->data.Bd.comp) + if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) return; - if (MDOC_Bl == bl->tok && bl->data.Bl.comp) + if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) return; /* Do not vspace directly after Ss/Sh. */ @@ -573,13 +595,13 @@ print_bvspace(struct termp *p, /* A `-column' does not assert vspace within the list. */ - if (MDOC_Bl == bl->tok && LIST_column == bl->data.Bl.type) + if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) if (n->prev && MDOC_It == n->prev->tok) return; /* A `-diag' without body does not vspace. */ - if (MDOC_Bl == bl->tok && LIST_diag == bl->data.Bl.type) + if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) if (n->prev && MDOC_It == n->prev->tok) { assert(n->prev->body); if (NULL == n->prev->body->child) @@ -590,40 +612,13 @@ print_bvspace(struct termp *p, } -/* ARGSUSED */ -static int -termp_dq_pre(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - - term_word(p, "\\(lq"); - p->flags |= TERMP_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -termp_dq_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - - p->flags |= TERMP_NOSPACE; - term_word(p, "\\(rq"); -} - - /* ARGSUSED */ static int termp_it_pre(DECL_ARGS) { const struct mdoc_node *bl, *nn; char buf[7]; - int i, col; + int i; size_t width, offset, ncols, dcol; enum mdoc_list type; @@ -633,7 +628,7 @@ termp_it_pre(DECL_ARGS) } bl = n->parent->parent->parent; - type = bl->data.Bl.type; + type = bl->norm->Bl.type; /* * First calculate width and offset. This is pretty easy unless @@ -643,16 +638,14 @@ termp_it_pre(DECL_ARGS) width = offset = 0; - if (bl->data.Bl.offs) - offset = a2offs(bl->data.Bl.offs); + if (bl->norm->Bl.offs) + offset = a2offs(p, bl->norm->Bl.offs); switch (type) { case (LIST_column): if (MDOC_HEAD == n->type) break; - col = arg_getattr(MDOC_Column, bl); - /* * Imitate groff's column handling: * - For each earlier column, add its width. @@ -662,9 +655,11 @@ termp_it_pre(DECL_ARGS) * column. * - For more than 5 columns, add only one column. */ - ncols = bl->args->argv[col].sz; + ncols = bl->norm->Bl.ncols; + /* LINTED */ - dcol = ncols < 5 ? 4 : ncols == 5 ? 3 : 1; + dcol = ncols < 5 ? term_len(p, 4) : + ncols == 5 ? term_len(p, 3) : term_len(p, 1); /* * Calculate the offset by applying all prior MDOC_BODY, @@ -675,7 +670,7 @@ termp_it_pre(DECL_ARGS) nn->prev && i < (int)ncols; nn = nn->prev, i++) offset += dcol + a2width - (bl->args->argv[col].value[i]); + (p, bl->norm->Bl.cols[i]); /* * When exceeding the declared number of columns, leave @@ -690,10 +685,10 @@ termp_it_pre(DECL_ARGS) * Use the declared column widths, extended as explained * in the preceding paragraph. */ - width = a2width(bl->args->argv[col].value[i]) + dcol; + width = a2width(p, bl->norm->Bl.cols[i]) + dcol; break; default: - if (NULL == bl->data.Bl.width) + if (NULL == bl->norm->Bl.width) break; /* @@ -701,8 +696,8 @@ termp_it_pre(DECL_ARGS) * number for buffering single arguments. See the above * handling for column for how this changes. */ - assert(bl->data.Bl.width); - width = a2width(bl->data.Bl.width) + 2; + assert(bl->norm->Bl.width); + width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); break; } @@ -718,22 +713,22 @@ termp_it_pre(DECL_ARGS) case (LIST_dash): /* FALLTHROUGH */ case (LIST_hyphen): - if (width < 4) - width = 4; + if (width < term_len(p, 4)) + width = term_len(p, 4); break; case (LIST_enum): - if (width < 5) - width = 5; + if (width < term_len(p, 5)) + width = term_len(p, 5); break; case (LIST_hang): if (0 == width) - width = 8; + width = term_len(p, 8); break; case (LIST_column): /* FALLTHROUGH */ case (LIST_tag): if (0 == width) - width = 10; + width = term_len(p, 10); break; default: break; @@ -964,7 +959,7 @@ termp_it_post(DECL_ARGS) if (MDOC_BLOCK == n->type) return; - type = n->parent->parent->parent->data.Bl.type; + type = n->parent->parent->parent->norm->Bl.type; switch (type) { case (LIST_item): @@ -1003,10 +998,41 @@ static int termp_nm_pre(DECL_ARGS) { - if (NULL == n->child && NULL == m->name) + if (MDOC_BLOCK == n->type) return(1); - synopsis_pre(p, n); + if (MDOC_BODY == n->type) { + if (NULL == n->child) + return(0); + p->flags |= TERMP_NOLPAD | TERMP_NOSPACE; + p->offset += term_len(p, 1) + + (NULL == n->prev->child ? term_strlen(p, m->name) : + MDOC_TEXT == n->prev->child->type ? + term_strlen(p, n->prev->child->string) : + term_len(p, 5)); + return(1); + } + + if (NULL == n->child && NULL == m->name) + return(0); + + if (MDOC_HEAD == n->type) + synopsis_pre(p, n->parent); + + if (MDOC_HEAD == n->type && n->next->child) { + p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; + p->rmargin = p->offset + term_len(p, 1); + if (NULL == n->child) { + p->rmargin += term_strlen(p, m->name); + } else if (MDOC_TEXT == n->child->type) { + p->rmargin += term_strlen(p, n->child->string); + if (n->child->next) + p->flags |= TERMP_HANG; + } else { + p->rmargin += term_len(p, 5); + p->flags |= TERMP_HANG; + } + } term_fontpush(p, TERMFONT_BOLD); if (NULL == n->child) @@ -1015,6 +1041,21 @@ termp_nm_pre(DECL_ARGS) } +/* ARGSUSED */ +static void +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) { + term_flushln(p); + p->flags &= ~TERMP_NOLPAD; + } +} + + /* ARGSUSED */ static int termp_fl_pre(DECL_ARGS) @@ -1032,6 +1073,19 @@ termp_fl_pre(DECL_ARGS) } +/* ARGSUSED */ +static int +termp__a_pre(DECL_ARGS) +{ + + if (n->prev && MDOC__A == n->prev->tok) + if (NULL == n->next || MDOC__A != n->next->tok) + term_word(p, "and"); + + return(1); +} + + /* ARGSUSED */ static int termp_an_pre(DECL_ARGS) @@ -1079,10 +1133,10 @@ termp_an_post(DECL_ARGS) return; } - if (arg_hasattr(MDOC_Split, n)) { + if (AUTH_split == n->norm->An.auth) { p->flags &= ~TERMP_NOSPLIT; p->flags |= TERMP_SPLIT; - } else { + } else if (AUTH_nosplit == n->norm->An.auth) { p->flags &= ~TERMP_SPLIT; p->flags |= TERMP_NOSPLIT; } @@ -1223,18 +1277,6 @@ termp_bl_post(DECL_ARGS) } -/* ARGSUSED */ -static void -termp_op_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - p->flags |= TERMP_NOSPACE; - term_word(p, "\\(rB"); -} - - /* ARGSUSED */ static int termp_xr_pre(DECL_ARGS) @@ -1252,9 +1294,7 @@ termp_xr_pre(DECL_ARGS) return(0); p->flags |= TERMP_NOSPACE; term_word(p, "("); - p->flags |= TERMP_NOSPACE; term_word(p, nn->string); - p->flags |= TERMP_NOSPACE; term_word(p, ")"); return(0); @@ -1273,7 +1313,7 @@ synopsis_pre(struct termp *p, const struct mdoc_node *n) * Obviously, if we're not in a SYNOPSIS or no prior macros * exist, do nothing. */ - if (NULL == n->prev || SEC_SYNOPSIS != n->sec) + if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) return; /* @@ -1374,7 +1414,7 @@ termp_sh_pre(DECL_ARGS) term_fontpush(p, TERMFONT_BOLD); break; case (MDOC_BODY): - p->offset = INDENT; + p->offset = term_len(p, INDENT); break; default: break; @@ -1402,23 +1442,6 @@ termp_sh_post(DECL_ARGS) } -/* ARGSUSED */ -static int -termp_op_pre(DECL_ARGS) -{ - - switch (n->type) { - case (MDOC_BODY): - term_word(p, "\\(lB"); - p->flags |= TERMP_NOSPACE; - break; - default: - break; - } - return(1); -} - - /* ARGSUSED */ static int termp_bt_pre(DECL_ARGS) @@ -1459,7 +1482,7 @@ termp_d1_pre(DECL_ARGS) if (MDOC_BLOCK != n->type) return(1); term_newln(p); - p->offset += (INDENT + 1); + p->offset += term_len(p, (INDENT + 1)); return(1); } @@ -1475,31 +1498,6 @@ termp_d1_post(DECL_ARGS) } -/* ARGSUSED */ -static int -termp_aq_pre(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - term_word(p, "\\(la"); - p->flags |= TERMP_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -termp_aq_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - p->flags |= TERMP_NOSPACE; - term_word(p, "\\(ra"); -} - - /* ARGSUSED */ static int termp_ft_pre(DECL_ARGS) @@ -1538,7 +1536,7 @@ termp_fn_pre(DECL_ARGS) term_word(p, ")"); - if (SEC_SYNOPSIS == n->sec) + if (MDOC_SYNPRETTY & n->flags) term_word(p, ";"); return(0); @@ -1576,8 +1574,7 @@ termp_fa_pre(DECL_ARGS) static int termp_bd_pre(DECL_ARGS) { - size_t tabwidth; - size_t rm, rmax; + size_t tabwidth, rm, rmax; const struct mdoc_node *nn; if (MDOC_BLOCK == n->type) { @@ -1586,8 +1583,8 @@ termp_bd_pre(DECL_ARGS) } else if (MDOC_HEAD == n->type) return(0); - if (n->data.Bd.offs) - p->offset += a2offs(n->data.Bd.offs); + if (n->norm->Bd.offs) + p->offset += a2offs(p, n->norm->Bd.offs); /* * If -ragged or -filled are specified, the block does nothing @@ -1597,23 +1594,50 @@ termp_bd_pre(DECL_ARGS) * lines are allowed. */ - if (DISP_literal != n->data.Bd.type && - DISP_unfilled != n->data.Bd.type) + if (DISP_literal != n->norm->Bd.type && + DISP_unfilled != n->norm->Bd.type) return(1); tabwidth = p->tabwidth; - p->tabwidth = 8; + if (DISP_literal == n->norm->Bd.type) + p->tabwidth = term_len(p, 8); + rm = p->rmargin; rmax = p->maxrmargin; p->rmargin = p->maxrmargin = TERM_MAXMARGIN; for (nn = n->child; nn; nn = nn->next) { - p->flags |= TERMP_NOSPACE; print_mdoc_node(p, pair, m, nn); - if (NULL == nn->prev || - nn->prev->line < nn->line || - NULL == nn->next) - term_flushln(p); + /* + * If the printed node flushes its own line, then we + * needn't do it here as well. This is hacky, but the + * notion of selective eoln whitespace is pretty dumb + * anyway, so don't sweat it. + */ + switch (nn->tok) { + case (MDOC_Sm): + /* FALLTHROUGH */ + case (MDOC_br): + /* FALLTHROUGH */ + case (MDOC_sp): + /* FALLTHROUGH */ + case (MDOC_Bl): + /* FALLTHROUGH */ + case (MDOC_D1): + /* FALLTHROUGH */ + case (MDOC_Dl): + /* FALLTHROUGH */ + case (MDOC_Lp): + /* FALLTHROUGH */ + case (MDOC_Pp): + continue; + default: + break; + } + if (nn->next && nn->next->line == nn->line) + continue; + term_flushln(p); + p->flags |= TERMP_NOSPACE; } p->tabwidth = tabwidth; @@ -1635,8 +1659,8 @@ termp_bd_post(DECL_ARGS) rm = p->rmargin; rmax = p->maxrmargin; - if (DISP_literal == n->data.Bd.type || - DISP_unfilled == n->data.Bd.type) + if (DISP_literal == n->norm->Bd.type || + DISP_unfilled == n->norm->Bd.type) p->rmargin = p->maxrmargin = TERM_MAXMARGIN; p->flags |= TERMP_NOSPACE; @@ -1647,31 +1671,6 @@ termp_bd_post(DECL_ARGS) } -/* ARGSUSED */ -static int -termp_qq_pre(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - term_word(p, "\""); - p->flags |= TERMP_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -termp_qq_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - p->flags |= TERMP_NOSPACE; - term_word(p, "\""); -} - - /* ARGSUSED */ static void termp_bx_post(DECL_ARGS) @@ -1692,7 +1691,7 @@ termp_xx_pre(DECL_ARGS) pp = NULL; switch (n->tok) { case (MDOC_Bsx): - pp = "BSDI BSD/OS"; + pp = "BSD/OS"; break; case (MDOC_Dx): pp = "DragonFly"; @@ -1721,32 +1720,7 @@ termp_xx_pre(DECL_ARGS) /* ARGSUSED */ static int -termp_sq_pre(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - term_word(p, "\\(oq"); - p->flags |= TERMP_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -termp_sq_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - p->flags |= TERMP_NOSPACE; - term_word(p, "\\(aq"); -} - - -/* ARGSUSED */ -static int -termp_pf_pre(DECL_ARGS) +termp_igndelim_pre(DECL_ARGS) { p->flags |= TERMP_IGNDELIM; @@ -1759,7 +1733,6 @@ static void termp_pf_post(DECL_ARGS) { - p->flags &= ~TERMP_IGNDELIM; p->flags |= TERMP_NOSPACE; } @@ -1777,7 +1750,7 @@ termp_ss_pre(DECL_ARGS) break; case (MDOC_HEAD): term_fontpush(p, TERMFONT_BOLD); - p->offset = HALFINDENT; + p->offset = term_len(p, HALFINDENT); break; default: break; @@ -1815,7 +1788,7 @@ termp_in_pre(DECL_ARGS) synopsis_pre(p, n); - if (SEC_SYNOPSIS == n->sec && MDOC_LINE & n->flags) { + if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) { term_fontpush(p, TERMFONT_BOLD); term_word(p, "#include"); term_word(p, "<"); @@ -1834,13 +1807,13 @@ static void termp_in_post(DECL_ARGS) { - if (SEC_SYNOPSIS == n->sec) + if (MDOC_SYNPRETTY & n->flags) term_fontpush(p, TERMFONT_BOLD); p->flags |= TERMP_NOSPACE; term_word(p, ">"); - if (SEC_SYNOPSIS == n->sec) + if (MDOC_SYNPRETTY & n->flags) term_fontpop(p); } @@ -1853,7 +1826,7 @@ termp_sp_pre(DECL_ARGS) switch (n->tok) { case (MDOC_sp): - len = n->child ? a2height(n->child) : 1; + len = n->child ? a2height(p, n->child->string) : 1; break; case (MDOC_br): len = 0; @@ -1874,12 +1847,61 @@ termp_sp_pre(DECL_ARGS) /* ARGSUSED */ static int -termp_brq_pre(DECL_ARGS) +termp_quote_pre(DECL_ARGS) { - if (MDOC_BODY != n->type) + if (MDOC_BODY != n->type && MDOC_ELEM != n->type) return(1); - term_word(p, "\\(lC"); + + switch (n->tok) { + case (MDOC_Ao): + /* FALLTHROUGH */ + case (MDOC_Aq): + term_word(p, "<"); + break; + case (MDOC_Bro): + /* FALLTHROUGH */ + case (MDOC_Brq): + term_word(p, "{"); + break; + case (MDOC_Oo): + /* FALLTHROUGH */ + case (MDOC_Op): + /* FALLTHROUGH */ + case (MDOC_Bo): + /* FALLTHROUGH */ + case (MDOC_Bq): + term_word(p, "["); + break; + case (MDOC_Do): + /* FALLTHROUGH */ + case (MDOC_Dq): + term_word(p, "``"); + break; + case (MDOC_Po): + /* FALLTHROUGH */ + case (MDOC_Pq): + term_word(p, "("); + break; + case (MDOC__T): + /* FALLTHROUGH */ + case (MDOC_Qo): + /* FALLTHROUGH */ + case (MDOC_Qq): + term_word(p, "\""); + break; + case (MDOC_Ql): + /* FALLTHROUGH */ + case (MDOC_So): + /* FALLTHROUGH */ + case (MDOC_Sq): + term_word(p, "`"); + break; + default: + abort(); + /* NOTREACHED */ + } + p->flags |= TERMP_NOSPACE; return(1); } @@ -1887,62 +1909,62 @@ termp_brq_pre(DECL_ARGS) /* ARGSUSED */ static void -termp_brq_post(DECL_ARGS) +termp_quote_post(DECL_ARGS) { - if (MDOC_BODY != n->type) + if (MDOC_BODY != n->type && MDOC_ELEM != n->type) return; + p->flags |= TERMP_NOSPACE; - term_word(p, "\\(rC"); -} - -/* ARGSUSED */ -static int -termp_bq_pre(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - term_word(p, "\\(lB"); - p->flags |= TERMP_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -termp_bq_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - p->flags |= TERMP_NOSPACE; - term_word(p, "\\(rB"); -} - - -/* ARGSUSED */ -static int -termp_pq_pre(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return(1); - term_word(p, "\\&("); - p->flags |= TERMP_NOSPACE; - return(1); -} - - -/* ARGSUSED */ -static void -termp_pq_post(DECL_ARGS) -{ - - if (MDOC_BODY != n->type) - return; - term_word(p, ")"); + switch (n->tok) { + case (MDOC_Ao): + /* FALLTHROUGH */ + case (MDOC_Aq): + term_word(p, ">"); + break; + case (MDOC_Bro): + /* FALLTHROUGH */ + case (MDOC_Brq): + term_word(p, "}"); + break; + case (MDOC_Oo): + /* FALLTHROUGH */ + case (MDOC_Op): + /* FALLTHROUGH */ + case (MDOC_Bo): + /* FALLTHROUGH */ + case (MDOC_Bq): + term_word(p, "]"); + break; + case (MDOC_Do): + /* FALLTHROUGH */ + case (MDOC_Dq): + term_word(p, "''"); + break; + case (MDOC_Po): + /* FALLTHROUGH */ + case (MDOC_Pq): + term_word(p, ")"); + break; + case (MDOC__T): + /* FALLTHROUGH */ + case (MDOC_Qo): + /* FALLTHROUGH */ + case (MDOC_Qq): + term_word(p, "\""); + break; + case (MDOC_Ql): + /* FALLTHROUGH */ + case (MDOC_So): + /* FALLTHROUGH */ + case (MDOC_Sq): + term_word(p, "'"); + break; + default: + abort(); + /* NOTREACHED */ + } } @@ -1957,13 +1979,14 @@ termp_fo_pre(DECL_ARGS) } else if (MDOC_BODY == n->type) { p->flags |= TERMP_NOSPACE; term_word(p, "("); - p->flags |= TERMP_NOSPACE; return(1); } + if (NULL == n->child) + return(0); + /* XXX: we drop non-initial arguments as per groff. */ - assert(n->child); assert(n->child->string); term_fontpush(p, TERMFONT_BOLD); term_word(p, n->child->string); @@ -1979,13 +2002,10 @@ termp_fo_post(DECL_ARGS) if (MDOC_BODY != n->type) return; - p->flags |= TERMP_NOSPACE; term_word(p, ")"); - if (SEC_SYNOPSIS == n->sec) { - p->flags |= TERMP_NOSPACE; + if (MDOC_SYNPRETTY & n->flags) term_word(p, ";"); - } } @@ -1993,30 +2013,17 @@ termp_fo_post(DECL_ARGS) static int termp_bf_pre(DECL_ARGS) { - const struct mdoc_node *nn; if (MDOC_HEAD == n->type) return(0); else if (MDOC_BLOCK != n->type) return(1); - if (NULL == (nn = n->head->child)) { - if (arg_hasattr(MDOC_Emphasis, n)) - term_fontpush(p, TERMFONT_UNDER); - else if (arg_hasattr(MDOC_Symbolic, n)) - term_fontpush(p, TERMFONT_BOLD); - else - term_fontpush(p, TERMFONT_NONE); - - return(1); - } - - assert(MDOC_TEXT == nn->type); - if (0 == strcmp("Em", nn->string)) + if (FONT_Em == n->norm->Bf.font) term_fontpush(p, TERMFONT_UNDER); - else if (0 == strcmp("Sy", nn->string)) + else if (FONT_Sy == n->norm->Bf.font) term_fontpush(p, TERMFONT_BOLD); - else + else term_fontpush(p, TERMFONT_NONE); return(1); @@ -2029,9 +2036,11 @@ termp_sm_pre(DECL_ARGS) { assert(n->child && MDOC_TEXT == n->child->type); - if (0 == strcmp("on", n->child->string)) + if (0 == strcmp("on", n->child->string)) { + if (p->col) + p->flags &= ~TERMP_NOSPACE; p->flags &= ~TERMP_NONOSPACE; - else + } else p->flags |= TERMP_NONOSPACE; return(0); @@ -2044,7 +2053,7 @@ termp_ap_pre(DECL_ARGS) { p->flags |= TERMP_NOSPACE; - term_word(p, "\\(aq"); + term_word(p, "'"); p->flags |= TERMP_NOSPACE; return(1); } @@ -2055,10 +2064,26 @@ static void termp____post(DECL_ARGS) { + /* + * Handle lists of authors. In general, print each followed by + * a comma. Don't print the comma if there are only two + * authors. + */ + if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) + if (NULL == n->next->next || MDOC__A != n->next->next->tok) + if (NULL == n->prev || MDOC__A != n->prev->tok) + return; + /* TODO: %U. */ - p->flags |= TERMP_NOSPACE; - term_word(p, n->next ? "," : "."); + if (NULL == n->parent || MDOC_Rs != n->parent->tok) + return; + + if (NULL == n->next) { + term_word(p, "."); + p->flags |= TERMP_SENTENCE; + } else + term_word(p, ","); } @@ -2076,29 +2101,95 @@ termp_li_pre(DECL_ARGS) static int termp_lk_pre(DECL_ARGS) { - const struct mdoc_node *nn; + const struct mdoc_node *nn, *sv; term_fontpush(p, TERMFONT_UNDER); - nn = n->child; - if (NULL == nn->next) + nn = sv = n->child; + + if (NULL == nn || NULL == nn->next) return(1); - term_word(p, nn->string); + for (nn = nn->next; nn; nn = nn->next) + term_word(p, nn->string); + term_fontpop(p); - p->flags |= TERMP_NOSPACE; term_word(p, ":"); term_fontpush(p, TERMFONT_BOLD); - for (nn = nn->next; nn; nn = nn->next) - term_word(p, nn->string); + term_word(p, sv->string); term_fontpop(p); return(0); } +/* ARGSUSED */ +static int +termp_bk_pre(DECL_ARGS) +{ + + switch (n->type) { + case (MDOC_BLOCK): + break; + case (MDOC_HEAD): + return(0); + case (MDOC_BODY): + if (n->parent->args || 0 == n->prev->nchild) + p->flags |= TERMP_PREKEEP; + break; + default: + abort(); + /* NOTREACHED */ + } + + return(1); +} + + +/* ARGSUSED */ +static void +termp_bk_post(DECL_ARGS) +{ + + if (MDOC_BODY == n->type) + p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); +} + +/* ARGSUSED */ +static void +termp__t_post(DECL_ARGS) +{ + + /* + * If we're in an `Rs' and there's a journal present, then quote + * us instead of underlining us (for disambiguation). + */ + if (n->parent && MDOC_Rs == n->parent->tok && + n->parent->norm->Rs.child_J) + termp_quote_post(p, pair, m, n); + + termp____post(p, pair, m, n); +} + +/* ARGSUSED */ +static int +termp__t_pre(DECL_ARGS) +{ + + /* + * If we're in an `Rs' and there's a journal present, then quote + * us instead of underlining us (for disambiguation). + */ + if (n->parent && MDOC_Rs == n->parent->tok && + n->parent->norm->Rs.child_J) + return(termp_quote_pre(p, pair, m, n)); + + term_fontpush(p, TERMFONT_UNDER); + return(1); +} + /* ARGSUSED */ static int termp_under_pre(DECL_ARGS) diff --git a/usr.bin/mdocml/dist/mdoc_validate.c b/usr.bin/mdocml/dist/mdoc_validate.c new file mode 100644 index 000000000..784b73313 --- /dev/null +++ b/usr.bin/mdocml/dist/mdoc_validate.c @@ -0,0 +1,2239 @@ +/* $Vendor-Id: mdoc_validate.c,v 1.151 2011/01/03 23:53:51 schwarze Exp $ */ +/* + * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2010, 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 + +#ifndef OSNAME +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "libmdoc.h" +#include "libmandoc.h" + +/* FIXME: .Bl -diag can't have non-text children in HEAD. */ + +#define PRE_ARGS struct mdoc *mdoc, struct mdoc_node *n +#define POST_ARGS struct mdoc *mdoc + +#define NUMSIZ 32 +#define DATESIZE 32 + +enum check_ineq { + CHECK_LT, + CHECK_GT, + CHECK_EQ +}; + +enum check_lvl { + CHECK_WARN, + CHECK_ERROR, +}; + +typedef int (*v_pre)(PRE_ARGS); +typedef int (*v_post)(POST_ARGS); + +struct valids { + v_pre *pre; + v_post *post; +}; + +static int check_count(struct mdoc *, enum mdoc_type, + enum check_lvl, enum check_ineq, int); +static int check_parent(PRE_ARGS, enum mdoct, enum mdoc_type); +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 ebool(POST_ARGS); +static int berr_ge1(POST_ARGS); +static int bwarn_ge1(POST_ARGS); +static int eerr_ge1(POST_ARGS); +static int ewarn_eq0(POST_ARGS); +static int ewarn_eq1(POST_ARGS); +static int ewarn_ge1(POST_ARGS); +static int ewarn_le1(POST_ARGS); +static int hwarn_eq0(POST_ARGS); +static int hwarn_eq1(POST_ARGS); +static int hwarn_ge1(POST_ARGS); +static int hwarn_le1(POST_ARGS); + +static int post_an(POST_ARGS); +static int post_at(POST_ARGS); +static int post_bf(POST_ARGS); +static int post_bl(POST_ARGS); +static int post_bl_block(POST_ARGS); +static int post_bl_block_width(POST_ARGS); +static int post_bl_block_tag(POST_ARGS); +static int post_bl_head(POST_ARGS); +static int post_dd(POST_ARGS); +static int post_dt(POST_ARGS); +static int post_defaults(POST_ARGS); +static int post_literal(POST_ARGS); +static int post_eoln(POST_ARGS); +static int post_it(POST_ARGS); +static int post_lb(POST_ARGS); +static int post_nm(POST_ARGS); +static int post_os(POST_ARGS); +static int post_ignpar(POST_ARGS); +static int post_prol(POST_ARGS); +static int post_root(POST_ARGS); +static int post_rs(POST_ARGS); +static int post_sh(POST_ARGS); +static int post_sh_body(POST_ARGS); +static int post_sh_head(POST_ARGS); +static int post_st(POST_ARGS); +static int post_std(POST_ARGS); +static int post_vt(POST_ARGS); +static int pre_an(PRE_ARGS); +static int pre_bd(PRE_ARGS); +static int pre_bl(PRE_ARGS); +static int pre_dd(PRE_ARGS); +static int pre_display(PRE_ARGS); +static int pre_dt(PRE_ARGS); +static int pre_it(PRE_ARGS); +static int pre_literal(PRE_ARGS); +static int pre_os(PRE_ARGS); +static int pre_par(PRE_ARGS); +static int pre_sh(PRE_ARGS); +static int pre_ss(PRE_ARGS); +static int pre_std(PRE_ARGS); + +static v_post posts_an[] = { post_an, NULL }; +static v_post posts_at[] = { post_at, post_defaults, NULL }; +static v_post posts_bd[] = { post_literal, hwarn_eq0, bwarn_ge1, NULL }; +static v_post posts_bf[] = { hwarn_le1, post_bf, NULL }; +static v_post posts_bk[] = { hwarn_eq0, bwarn_ge1, NULL }; +static v_post posts_bl[] = { bwarn_ge1, post_bl, NULL }; +static v_post posts_bool[] = { ebool, NULL }; +static v_post posts_eoln[] = { post_eoln, NULL }; +static v_post posts_defaults[] = { post_defaults, NULL }; +static v_post posts_dd[] = { ewarn_ge1, post_dd, post_prol, NULL }; +static v_post posts_dl[] = { post_literal, bwarn_ge1, NULL }; +static v_post posts_dt[] = { post_dt, post_prol, NULL }; +static v_post posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL }; +static v_post posts_it[] = { post_it, NULL }; +static v_post posts_lb[] = { post_lb, NULL }; +static v_post posts_nd[] = { berr_ge1, NULL }; +static v_post posts_nm[] = { post_nm, NULL }; +static v_post posts_notext[] = { ewarn_eq0, NULL }; +static v_post posts_os[] = { post_os, post_prol, NULL }; +static v_post posts_rs[] = { post_rs, NULL }; +static v_post posts_sh[] = { post_ignpar, hwarn_ge1, bwarn_ge1, post_sh, NULL }; +static v_post posts_sp[] = { ewarn_le1, NULL }; +static v_post posts_ss[] = { post_ignpar, hwarn_ge1, bwarn_ge1, NULL }; +static v_post posts_st[] = { post_st, NULL }; +static v_post posts_std[] = { post_std, NULL }; +static v_post posts_text[] = { eerr_ge1, NULL }; +static v_post posts_text1[] = { ewarn_eq1, NULL }; +static v_post posts_vt[] = { post_vt, NULL }; +static v_post posts_wline[] = { bwarn_ge1, NULL }; +static v_post posts_wtext[] = { ewarn_ge1, NULL }; +static v_pre pres_an[] = { pre_an, NULL }; +static v_pre pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL }; +static v_pre pres_bl[] = { pre_bl, pre_par, NULL }; +static v_pre pres_d1[] = { pre_display, NULL }; +static v_pre pres_dl[] = { pre_literal, pre_display, NULL }; +static v_pre pres_dd[] = { pre_dd, NULL }; +static v_pre pres_dt[] = { pre_dt, NULL }; +static v_pre pres_er[] = { NULL, NULL }; +static v_pre pres_fd[] = { NULL, NULL }; +static v_pre pres_it[] = { pre_it, pre_par, NULL }; +static v_pre pres_os[] = { pre_os, NULL }; +static v_pre pres_pp[] = { pre_par, NULL }; +static v_pre pres_sh[] = { pre_sh, NULL }; +static v_pre pres_ss[] = { pre_ss, NULL }; +static v_pre pres_std[] = { pre_std, NULL }; + +const struct valids mdoc_valids[MDOC_MAX] = { + { NULL, NULL }, /* Ap */ + { pres_dd, posts_dd }, /* Dd */ + { pres_dt, posts_dt }, /* Dt */ + { pres_os, posts_os }, /* Os */ + { pres_sh, posts_sh }, /* Sh */ + { pres_ss, posts_ss }, /* Ss */ + { pres_pp, posts_notext }, /* Pp */ + { pres_d1, posts_wline }, /* D1 */ + { pres_dl, posts_dl }, /* Dl */ + { pres_bd, posts_bd }, /* Bd */ + { NULL, NULL }, /* Ed */ + { pres_bl, posts_bl }, /* Bl */ + { NULL, NULL }, /* El */ + { pres_it, posts_it }, /* It */ + { NULL, posts_text }, /* Ad */ + { pres_an, posts_an }, /* An */ + { NULL, posts_defaults }, /* Ar */ + { NULL, posts_text }, /* Cd */ + { NULL, NULL }, /* Cm */ + { NULL, NULL }, /* Dv */ + { pres_er, posts_text }, /* Er */ + { NULL, NULL }, /* Ev */ + { pres_std, posts_std }, /* Ex */ + { NULL, NULL }, /* Fa */ + { pres_fd, posts_wtext }, /* Fd */ + { NULL, NULL }, /* Fl */ + { NULL, posts_text }, /* Fn */ + { NULL, posts_wtext }, /* Ft */ + { NULL, posts_text }, /* Ic */ + { NULL, posts_text1 }, /* In */ + { NULL, posts_defaults }, /* Li */ + { NULL, posts_nd }, /* Nd */ + { NULL, posts_nm }, /* Nm */ + { NULL, NULL }, /* Op */ + { NULL, NULL }, /* Ot */ + { NULL, posts_defaults }, /* Pa */ + { pres_std, posts_std }, /* Rv */ + { NULL, posts_st }, /* St */ + { NULL, NULL }, /* Va */ + { NULL, posts_vt }, /* Vt */ + { NULL, posts_wtext }, /* Xr */ + { NULL, posts_text }, /* %A */ + { NULL, posts_text }, /* %B */ /* FIXME: can be used outside Rs/Re. */ + { NULL, posts_text }, /* %D */ /* FIXME: check date with mandoc_a2time(). */ + { NULL, posts_text }, /* %I */ + { NULL, posts_text }, /* %J */ + { NULL, posts_text }, /* %N */ + { NULL, posts_text }, /* %O */ + { NULL, posts_text }, /* %P */ + { NULL, posts_text }, /* %R */ + { NULL, posts_text }, /* %T */ /* FIXME: can be used outside Rs/Re. */ + { NULL, posts_text }, /* %V */ + { NULL, NULL }, /* Ac */ + { NULL, NULL }, /* Ao */ + { NULL, NULL }, /* Aq */ + { NULL, posts_at }, /* At */ + { NULL, NULL }, /* Bc */ + { NULL, posts_bf }, /* Bf */ + { NULL, NULL }, /* Bo */ + { NULL, NULL }, /* Bq */ + { NULL, NULL }, /* Bsx */ + { NULL, NULL }, /* Bx */ + { NULL, posts_bool }, /* Db */ + { NULL, NULL }, /* Dc */ + { NULL, NULL }, /* Do */ + { NULL, NULL }, /* Dq */ + { NULL, NULL }, /* Ec */ + { NULL, NULL }, /* Ef */ + { NULL, NULL }, /* Em */ + { NULL, NULL }, /* Eo */ + { NULL, NULL }, /* Fx */ + { NULL, posts_text }, /* Ms */ + { NULL, posts_notext }, /* No */ + { NULL, posts_notext }, /* Ns */ + { NULL, NULL }, /* Nx */ + { NULL, NULL }, /* Ox */ + { NULL, NULL }, /* Pc */ + { NULL, posts_text1 }, /* Pf */ + { NULL, NULL }, /* Po */ + { NULL, NULL }, /* Pq */ + { NULL, NULL }, /* Qc */ + { NULL, NULL }, /* Ql */ + { NULL, NULL }, /* Qo */ + { NULL, NULL }, /* Qq */ + { NULL, NULL }, /* Re */ + { NULL, posts_rs }, /* Rs */ + { NULL, NULL }, /* Sc */ + { NULL, NULL }, /* So */ + { NULL, NULL }, /* Sq */ + { NULL, posts_bool }, /* Sm */ + { NULL, posts_text }, /* Sx */ + { NULL, posts_text }, /* Sy */ + { NULL, posts_text }, /* Tn */ + { NULL, NULL }, /* Ux */ + { NULL, NULL }, /* Xc */ + { NULL, NULL }, /* Xo */ + { NULL, posts_fo }, /* Fo */ + { NULL, NULL }, /* Fc */ + { NULL, NULL }, /* Oo */ + { NULL, NULL }, /* Oc */ + { NULL, posts_bk }, /* Bk */ + { NULL, NULL }, /* Ek */ + { NULL, posts_eoln }, /* Bt */ + { NULL, NULL }, /* Hf */ + { NULL, NULL }, /* Fr */ + { NULL, posts_eoln }, /* Ud */ + { NULL, posts_lb }, /* Lb */ + { NULL, posts_notext }, /* Lp */ + { NULL, posts_text }, /* Lk */ + { NULL, posts_defaults }, /* Mt */ + { NULL, NULL }, /* Brq */ + { NULL, NULL }, /* Bro */ + { NULL, NULL }, /* Brc */ + { NULL, posts_text }, /* %C */ + { NULL, NULL }, /* Es */ + { NULL, NULL }, /* En */ + { NULL, NULL }, /* Dx */ + { NULL, posts_text }, /* %Q */ + { NULL, posts_notext }, /* br */ + { pres_pp, posts_sp }, /* sp */ + { NULL, posts_text1 }, /* %U */ + { NULL, NULL }, /* Ta */ +}; + +#define RSORD_MAX 14 /* Number of `Rs' blocks. */ + +static const enum mdoct rsord[RSORD_MAX] = { + MDOC__A, + MDOC__T, + MDOC__B, + MDOC__I, + MDOC__J, + MDOC__R, + MDOC__N, + MDOC__V, + MDOC__P, + MDOC__Q, + MDOC__D, + MDOC__O, + MDOC__C, + MDOC__U +}; + + +int +mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n) +{ + v_pre *p; + int line, pos; + char *tp; + + switch (n->type) { + case (MDOC_TEXT): + tp = n->string; + line = n->line; + pos = n->pos; + check_text(mdoc, line, pos, tp); + /* FALLTHROUGH */ + case (MDOC_TBL): + /* FALLTHROUGH */ + case (MDOC_ROOT): + return(1); + default: + break; + } + + check_args(mdoc, n); + + if (NULL == mdoc_valids[n->tok].pre) + return(1); + for (p = mdoc_valids[n->tok].pre; *p; p++) + if ( ! (*p)(mdoc, n)) + return(0); + return(1); +} + + +int +mdoc_valid_post(struct mdoc *mdoc) +{ + v_post *p; + + if (MDOC_VALID & mdoc->last->flags) + return(1); + mdoc->last->flags |= MDOC_VALID; + + switch (mdoc->last->type) { + case (MDOC_TEXT): + /* FALLTHROUGH */ + case (MDOC_TBL): + return(1); + case (MDOC_ROOT): + return(post_root(mdoc)); + default: + break; + } + + if (NULL == mdoc_valids[mdoc->last->tok].post) + return(1); + for (p = mdoc_valids[mdoc->last->tok].post; *p; p++) + if ( ! (*p)(mdoc)) + return(0); + + return(1); +} + +static int +check_count(struct mdoc *m, enum mdoc_type type, + enum check_lvl lvl, enum check_ineq ineq, int val) +{ + const char *p; + enum mandocerr t; + + if (m->last->type != type) + return(1); + + switch (ineq) { + case (CHECK_LT): + p = "less than "; + if (m->last->nchild < val) + return(1); + break; + case (CHECK_GT): + p = "more than "; + if (m->last->nchild > val) + return(1); + break; + case (CHECK_EQ): + p = ""; + if (val == m->last->nchild) + return(1); + break; + default: + abort(); + /* NOTREACHED */ + } + + t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT; + + return(mdoc_vmsg(m, t, m->last->line, m->last->pos, + "want %s%d children (have %d)", + p, val, m->last->nchild)); +} + +static int +berr_ge1(POST_ARGS) +{ + + return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0)); +} + +static int +bwarn_ge1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0)); +} + +static int +eerr_ge1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_ELEM, CHECK_ERROR, CHECK_GT, 0)); +} + +static int +ewarn_eq0(POST_ARGS) +{ + return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0)); +} + +static int +ewarn_eq1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1)); +} + +static int +ewarn_ge1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0)); +} + +static int +ewarn_le1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2)); +} + +static int +hwarn_eq0(POST_ARGS) +{ + return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0)); +} + +static int +hwarn_eq1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1)); +} + +static int +hwarn_ge1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0)); +} + +static int +hwarn_le1(POST_ARGS) +{ + return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_LT, 2)); +} + +static void +check_args(struct mdoc *m, struct mdoc_node *n) +{ + int i; + + if (NULL == n->args) + return; + + assert(n->args->argc); + for (i = 0; i < (int)n->args->argc; i++) + check_argv(m, n, &n->args->argv[i]); +} + +static void +check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v) +{ + int i; + + for (i = 0; i < (int)v->sz; i++) + check_text(m, v->line, v->pos, v->value[i]); + + /* FIXME: move to post_std(). */ + + if (MDOC_Std == v->arg) + if ( ! (v->sz || m->meta.name)) + mdoc_nmsg(m, n, MANDOCERR_NONAME); +} + +static void +check_text(struct mdoc *m, int ln, int pos, char *p) +{ + int c; + size_t sz; + + for ( ; *p; p++, pos++) { + sz = strcspn(p, "\t\\"); + p += (int)sz; + + if ('\0' == *p) + break; + + pos += (int)sz; + + if ('\t' == *p) { + if ( ! (MDOC_LITERAL & m->flags)) + mdoc_pmsg(m, ln, pos, MANDOCERR_BADTAB); + continue; + } + + if (0 == (c = mandoc_special(p))) { + mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE); + continue; + } + + p += c - 1; + pos += c - 1; + } +} + +static int +check_parent(PRE_ARGS, enum mdoct tok, enum mdoc_type t) +{ + + assert(n->parent); + if ((MDOC_ROOT == t || tok == n->parent->tok) && + (t == n->parent->type)) + return(1); + + mdoc_vmsg(mdoc, MANDOCERR_SYNTCHILD, + n->line, n->pos, "want parent %s", + MDOC_ROOT == t ? "" : + mdoc_macronames[tok]); + return(0); +} + + +static int +pre_display(PRE_ARGS) +{ + struct mdoc_node *node; + + if (MDOC_BLOCK != n->type) + return(1); + + for (node = mdoc->last->parent; node; node = node->parent) + if (MDOC_BLOCK == node->type) + if (MDOC_Bd == node->tok) + break; + + if (node) + mdoc_nmsg(mdoc, n, MANDOCERR_NESTEDDISP); + + return(1); +} + + +static int +pre_bl(PRE_ARGS) +{ + int i, comp, dup; + const char *offs, *width; + enum mdoc_list lt; + struct mdoc_node *np; + + if (MDOC_BLOCK != n->type) { + if (ENDBODY_NOT != n->end) { + assert(n->pending); + np = n->pending->parent; + } else + np = n->parent; + + assert(np); + assert(MDOC_BLOCK == np->type); + assert(MDOC_Bl == np->tok); + return(1); + } + + /* + * First figure out which kind of list to use: bind ourselves to + * the first mentioned list type and warn about any remaining + * ones. If we find no list type, we default to LIST_item. + */ + + /* LINTED */ + for (i = 0; n->args && i < (int)n->args->argc; i++) { + lt = LIST__NONE; + dup = comp = 0; + width = offs = NULL; + switch (n->args->argv[i].arg) { + /* Set list types. */ + case (MDOC_Bullet): + lt = LIST_bullet; + break; + case (MDOC_Dash): + lt = LIST_dash; + break; + case (MDOC_Enum): + lt = LIST_enum; + break; + case (MDOC_Hyphen): + lt = LIST_hyphen; + break; + case (MDOC_Item): + lt = LIST_item; + break; + case (MDOC_Tag): + lt = LIST_tag; + break; + case (MDOC_Diag): + lt = LIST_diag; + break; + case (MDOC_Hang): + lt = LIST_hang; + break; + case (MDOC_Ohang): + lt = LIST_ohang; + break; + case (MDOC_Inset): + lt = LIST_inset; + break; + case (MDOC_Column): + lt = LIST_column; + break; + /* Set list arguments. */ + case (MDOC_Compact): + dup = n->norm->Bl.comp; + comp = 1; + break; + case (MDOC_Width): + dup = (NULL != n->norm->Bl.width); + width = n->args->argv[i].value[0]; + break; + case (MDOC_Offset): + /* NB: this can be empty! */ + if (n->args->argv[i].sz) { + offs = n->args->argv[i].value[0]; + dup = (NULL != n->norm->Bl.offs); + break; + } + mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); + break; + default: + continue; + } + + /* Check: duplicate auxiliary arguments. */ + + if (dup) + mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); + + if (comp && ! dup) + n->norm->Bl.comp = comp; + if (offs && ! dup) + n->norm->Bl.offs = offs; + if (width && ! dup) + n->norm->Bl.width = width; + + /* Check: multiple list types. */ + + if (LIST__NONE != lt && n->norm->Bl.type != LIST__NONE) + mdoc_nmsg(mdoc, n, MANDOCERR_LISTREP); + + /* Assign list type. */ + + if (LIST__NONE != lt && n->norm->Bl.type == LIST__NONE) { + n->norm->Bl.type = lt; + /* Set column information, too. */ + if (LIST_column == lt) { + n->norm->Bl.ncols = + n->args->argv[i].sz; + n->norm->Bl.cols = (const char **) + n->args->argv[i].value; + } + } + + /* The list type should come first. */ + + if (n->norm->Bl.type == LIST__NONE) + if (n->norm->Bl.width || + n->norm->Bl.offs || + n->norm->Bl.comp) + mdoc_nmsg(mdoc, n, MANDOCERR_LISTFIRST); + + continue; + } + + /* Allow lists to default to LIST_item. */ + + if (LIST__NONE == n->norm->Bl.type) { + mdoc_nmsg(mdoc, n, MANDOCERR_LISTTYPE); + n->norm->Bl.type = LIST_item; + } + + /* + * Validate the width field. Some list types don't need width + * types and should be warned about them. Others should have it + * and must also be warned. + */ + + switch (n->norm->Bl.type) { + case (LIST_tag): + if (n->norm->Bl.width) + break; + mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG); + break; + case (LIST_column): + /* FALLTHROUGH */ + case (LIST_diag): + /* FALLTHROUGH */ + case (LIST_ohang): + /* FALLTHROUGH */ + case (LIST_inset): + /* FALLTHROUGH */ + case (LIST_item): + if (n->norm->Bl.width) + mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); + break; + default: + break; + } + + return(1); +} + + +static int +pre_bd(PRE_ARGS) +{ + int i, dup, comp; + enum mdoc_disp dt; + const char *offs; + struct mdoc_node *np; + + if (MDOC_BLOCK != n->type) { + if (ENDBODY_NOT != n->end) { + assert(n->pending); + np = n->pending->parent; + } else + np = n->parent; + + assert(np); + assert(MDOC_BLOCK == np->type); + assert(MDOC_Bd == np->tok); + return(1); + } + + /* LINTED */ + for (i = 0; n->args && i < (int)n->args->argc; i++) { + dt = DISP__NONE; + dup = comp = 0; + offs = NULL; + + switch (n->args->argv[i].arg) { + case (MDOC_Centred): + dt = DISP_centred; + break; + case (MDOC_Ragged): + dt = DISP_ragged; + break; + case (MDOC_Unfilled): + dt = DISP_unfilled; + break; + case (MDOC_Filled): + dt = DISP_filled; + break; + case (MDOC_Literal): + dt = DISP_literal; + break; + case (MDOC_File): + mdoc_nmsg(mdoc, n, MANDOCERR_BADDISP); + return(0); + case (MDOC_Offset): + /* NB: this can be empty! */ + if (n->args->argv[i].sz) { + offs = n->args->argv[i].value[0]; + dup = (NULL != n->norm->Bd.offs); + break; + } + mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV); + break; + case (MDOC_Compact): + comp = 1; + dup = n->norm->Bd.comp; + break; + default: + abort(); + /* NOTREACHED */ + } + + /* Check whether we have duplicates. */ + + if (dup) + mdoc_nmsg(mdoc, n, MANDOCERR_ARGVREP); + + /* Make our auxiliary assignments. */ + + if (offs && ! dup) + n->norm->Bd.offs = offs; + if (comp && ! dup) + n->norm->Bd.comp = comp; + + /* Check whether a type has already been assigned. */ + + if (DISP__NONE != dt && n->norm->Bd.type != DISP__NONE) + mdoc_nmsg(mdoc, n, MANDOCERR_DISPREP); + + /* Make our type assignment. */ + + if (DISP__NONE != dt && n->norm->Bd.type == DISP__NONE) + n->norm->Bd.type = dt; + } + + if (DISP__NONE == n->norm->Bd.type) { + mdoc_nmsg(mdoc, n, MANDOCERR_DISPTYPE); + n->norm->Bd.type = DISP_ragged; + } + + return(1); +} + + +static int +pre_ss(PRE_ARGS) +{ + + if (MDOC_BLOCK != n->type) + return(1); + return(check_parent(mdoc, n, MDOC_Sh, MDOC_BODY)); +} + + +static int +pre_sh(PRE_ARGS) +{ + + if (MDOC_BLOCK != n->type) + return(1); + + mdoc->regs->regs[(int)REG_nS].set = 0; + return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT)); +} + + +static int +pre_it(PRE_ARGS) +{ + + if (MDOC_BLOCK != n->type) + return(1); + + return(check_parent(mdoc, n, MDOC_Bl, MDOC_BODY)); +} + + +static int +pre_an(PRE_ARGS) +{ + int i; + + if (NULL == n->args) + return(1); + + for (i = 1; i < (int)n->args->argc; i++) + mdoc_pmsg(mdoc, n->args->argv[i].line, + n->args->argv[i].pos, MANDOCERR_IGNARGV); + + if (MDOC_Split == n->args->argv[0].arg) + n->norm->An.auth = AUTH_split; + else if (MDOC_Nosplit == n->args->argv[0].arg) + n->norm->An.auth = AUTH_nosplit; + else + abort(); + + return(1); +} + +static int +pre_std(PRE_ARGS) +{ + + if (n->args && 1 == n->args->argc) + if (MDOC_Std == n->args->argv[0].arg) + return(1); + + mdoc_nmsg(mdoc, n, MANDOCERR_NOARGV); + return(1); +} + +static int +pre_dt(PRE_ARGS) +{ + + if (0 == mdoc->meta.date || mdoc->meta.os) + mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); + + if (mdoc->meta.title) + mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); + + return(1); +} + +static int +pre_os(PRE_ARGS) +{ + + if (NULL == mdoc->meta.title || 0 == mdoc->meta.date) + mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); + + if (mdoc->meta.os) + mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); + + return(1); +} + +static int +pre_dd(PRE_ARGS) +{ + + if (mdoc->meta.title || mdoc->meta.os) + mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGOOO); + + if (mdoc->meta.date) + mdoc_nmsg(mdoc, n, MANDOCERR_PROLOGREP); + + return(1); +} + + +static int +post_bf(POST_ARGS) +{ + struct mdoc_node *np; + enum mdocargt arg; + + /* + * Unlike other data pointers, these are "housed" by the HEAD + * element, which contains the goods. + */ + + if (MDOC_HEAD != mdoc->last->type) { + if (ENDBODY_NOT != mdoc->last->end) { + assert(mdoc->last->pending); + np = mdoc->last->pending->parent->head; + } else if (MDOC_BLOCK != mdoc->last->type) { + np = mdoc->last->parent->head; + } else + np = mdoc->last->head; + + assert(np); + assert(MDOC_HEAD == np->type); + assert(MDOC_Bf == np->tok); + return(1); + } + + np = mdoc->last; + assert(MDOC_BLOCK == np->parent->type); + assert(MDOC_Bf == np->parent->tok); + + /* + * Cannot have both argument and parameter. + * If neither is specified, let it through with a warning. + */ + + if (np->parent->args && np->child) { + mdoc_nmsg(mdoc, np, MANDOCERR_SYNTARGVCOUNT); + return(0); + } else if (NULL == np->parent->args && NULL == np->child) { + mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); + return(1); + } + + /* Extract argument into data. */ + + if (np->parent->args) { + arg = np->parent->args->argv[0].arg; + if (MDOC_Emphasis == arg) + np->norm->Bf.font = FONT_Em; + else if (MDOC_Literal == arg) + np->norm->Bf.font = FONT_Li; + else if (MDOC_Symbolic == arg) + np->norm->Bf.font = FONT_Sy; + else + abort(); + return(1); + } + + /* Extract parameter into data. */ + + if (0 == strcmp(np->child->string, "Em")) + np->norm->Bf.font = FONT_Em; + else if (0 == strcmp(np->child->string, "Li")) + np->norm->Bf.font = FONT_Li; + else if (0 == strcmp(np->child->string, "Sy")) + np->norm->Bf.font = FONT_Sy; + else + mdoc_nmsg(mdoc, np, MANDOCERR_FONTTYPE); + + return(1); +} + +static int +post_lb(POST_ARGS) +{ + const char *p; + char *buf; + size_t sz; + + check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); + + assert(mdoc->last->child); + assert(MDOC_TEXT == mdoc->last->child->type); + + p = mdoc_a2lib(mdoc->last->child->string); + + /* If lookup ok, replace with table value. */ + + if (p) { + free(mdoc->last->child->string); + mdoc->last->child->string = mandoc_strdup(p); + return(1); + } + + /* If not, use "library ``xxxx''. */ + + sz = strlen(mdoc->last->child->string) + + 2 + strlen("\\(lqlibrary\\(rq"); + buf = mandoc_malloc(sz); + snprintf(buf, sz, "library \\(lq%s\\(rq", + mdoc->last->child->string); + free(mdoc->last->child->string); + mdoc->last->child->string = buf; + return(1); +} + +static int +post_eoln(POST_ARGS) +{ + + if (mdoc->last->child) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); + return(1); +} + + +static int +post_vt(POST_ARGS) +{ + const struct mdoc_node *n; + + /* + * The Vt macro comes in both ELEM and BLOCK form, both of which + * have different syntaxes (yet more context-sensitive + * behaviour). ELEM types must have a child; BLOCK types, + * specifically the BODY, should only have TEXT children. + */ + + if (MDOC_ELEM == mdoc->last->type) + return(eerr_ge1(mdoc)); + if (MDOC_BODY != mdoc->last->type) + return(1); + + for (n = mdoc->last->child; n; n = n->next) + if (MDOC_TEXT != n->type) + mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); + + return(1); +} + + +static int +post_nm(POST_ARGS) +{ + char buf[BUFSIZ]; + + /* If no child specified, make sure we have the meta name. */ + + if (NULL == mdoc->last->child && NULL == mdoc->meta.name) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME); + return(1); + } else if (mdoc->meta.name) + return(1); + + /* If no meta name, set it from the child. */ + + if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ)) + return(0); + + mdoc->meta.name = mandoc_strdup(buf); + + return(1); +} + +static int +post_literal(POST_ARGS) +{ + + /* + * The `Dl' (note "el" not "one") and `Bd' macros unset the + * MDOC_LITERAL flag as they leave. Note that `Bd' only sets + * this in literal mode, but it doesn't hurt to just switch it + * off in general since displays can't be nested. + */ + + if (MDOC_BODY == mdoc->last->type) + mdoc->flags &= ~MDOC_LITERAL; + + return(1); +} + +static int +post_defaults(POST_ARGS) +{ + struct mdoc_node *nn; + + /* + * The `Ar' defaults to "file ..." if no value is provided as an + * argument; the `Mt' and `Pa' macros use "~"; the `Li' just + * gets an empty string. + */ + + if (mdoc->last->child) + return(1); + + nn = mdoc->last; + mdoc->next = MDOC_NEXT_CHILD; + + switch (nn->tok) { + case (MDOC_Ar): + if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file")) + return(0); + if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "...")) + return(0); + break; + case (MDOC_At): + if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "AT&T")) + return(0); + if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "UNIX")) + return(0); + break; + case (MDOC_Li): + if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "")) + return(0); + break; + case (MDOC_Pa): + /* FALLTHROUGH */ + case (MDOC_Mt): + if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~")) + return(0); + break; + default: + abort(); + /* NOTREACHED */ + } + + mdoc->last = nn; + return(1); +} + +static int +post_at(POST_ARGS) +{ + const char *p, *q; + char *buf; + size_t sz; + + /* + * If we have a child, look it up in the standard keys. If a + * key exist, use that instead of the child; if it doesn't, + * prefix "AT&T UNIX " to the existing data. + */ + + if (NULL == mdoc->last->child) + return(1); + + assert(MDOC_TEXT == mdoc->last->child->type); + p = mdoc_a2att(mdoc->last->child->string); + + if (p) { + free(mdoc->last->child->string); + mdoc->last->child->string = mandoc_strdup(p); + } else { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADATT); + p = "AT&T UNIX "; + q = mdoc->last->child->string; + sz = strlen(p) + strlen(q) + 1; + buf = mandoc_malloc(sz); + strlcpy(buf, p, sz); + strlcat(buf, q, sz); + free(mdoc->last->child->string); + mdoc->last->child->string = buf; + } + + return(1); +} + +static int +post_an(POST_ARGS) +{ + struct mdoc_node *np; + + np = mdoc->last; + if (AUTH__NONE != np->norm->An.auth && np->child) { + check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0); + return(1); + } + + /* + * FIXME: make this ewarn and make sure that the front-ends + * don't print the arguments. + */ + if (AUTH__NONE != np->norm->An.auth || np->child) + return(1); + + mdoc_nmsg(mdoc, np, MANDOCERR_NOARGS); + return(1); +} + + +static int +post_it(POST_ARGS) +{ + int i, cols, rc; + enum mdoc_list lt; + struct mdoc_node *n, *c; + enum mandocerr er; + + if (MDOC_BLOCK != mdoc->last->type) + return(1); + + n = mdoc->last->parent->parent; + lt = n->norm->Bl.type; + + if (LIST__NONE == lt) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_LISTTYPE); + return(1); + } + + switch (lt) { + case (LIST_tag): + if (mdoc->last->head->child) + break; + /* FIXME: give this a dummy value. */ + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); + break; + case (LIST_hang): + /* FALLTHROUGH */ + case (LIST_ohang): + /* FALLTHROUGH */ + case (LIST_inset): + /* FALLTHROUGH */ + case (LIST_diag): + if (NULL == mdoc->last->head->child) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOARGS); + break; + case (LIST_bullet): + /* FALLTHROUGH */ + case (LIST_dash): + /* FALLTHROUGH */ + case (LIST_enum): + /* FALLTHROUGH */ + case (LIST_hyphen): + if (NULL == mdoc->last->body->child) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); + /* FALLTHROUGH */ + case (LIST_item): + if (mdoc->last->head->child) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_ARGSLOST); + break; + case (LIST_column): + cols = (int)n->norm->Bl.ncols; + + assert(NULL == mdoc->last->head->child); + + if (NULL == mdoc->last->body->child) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NOBODY); + + for (i = 0, c = mdoc->last->child; c; c = c->next) + if (MDOC_BODY == c->type) + i++; + + if (i < cols) + er = MANDOCERR_ARGCOUNT; + else if (i == cols || i == cols + 1) + break; + else + er = MANDOCERR_SYNTARGCOUNT; + + rc = mdoc_vmsg(mdoc, er, + mdoc->last->line, mdoc->last->pos, + "columns == %d (have %d)", cols, i); + return(rc); + default: + break; + } + + return(1); +} + +static int +post_bl_block(POST_ARGS) +{ + struct mdoc_node *n; + + /* + * These are fairly complicated, so we've broken them into two + * functions. post_bl_block_tag() is called when a -tag is + * specified, but no -width (it must be guessed). The second + * when a -width is specified (macro indicators must be + * rewritten into real lengths). + */ + + n = mdoc->last; + + if (LIST_tag == n->norm->Bl.type && + NULL == n->norm->Bl.width) { + if ( ! post_bl_block_tag(mdoc)) + return(0); + } else if (NULL != n->norm->Bl.width) { + if ( ! post_bl_block_width(mdoc)) + return(0); + } else + return(1); + + assert(n->norm->Bl.width); + return(1); +} + +static int +post_bl_block_width(POST_ARGS) +{ + size_t width; + int i; + enum mdoct tok; + struct mdoc_node *n; + char buf[NUMSIZ]; + + n = mdoc->last; + + /* + * Calculate the real width of a list from the -width string, + * which may contain a macro (with a known default width), a + * literal string, or a scaling width. + * + * If the value to -width is a macro, then we re-write it to be + * the macro's width as set in share/tmac/mdoc/doc-common. + */ + + if (0 == strcmp(n->norm->Bl.width, "Ds")) + width = 6; + else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width))) + return(1); + else if (0 == (width = mdoc_macro2len(tok))) { + mdoc_nmsg(mdoc, n, MANDOCERR_BADWIDTH); + return(1); + } + + /* The value already exists: free and reallocate it. */ + + assert(n->args); + + for (i = 0; i < (int)n->args->argc; i++) + if (MDOC_Width == n->args->argv[i].arg) + break; + + assert(i < (int)n->args->argc); + + snprintf(buf, NUMSIZ, "%zun", width); + free(n->args->argv[i].value[0]); + n->args->argv[i].value[0] = mandoc_strdup(buf); + + /* Set our width! */ + n->norm->Bl.width = n->args->argv[i].value[0]; + return(1); +} + +static int +post_bl_block_tag(POST_ARGS) +{ + struct mdoc_node *n, *nn; + size_t sz, ssz; + int i; + char buf[NUMSIZ]; + + /* + * Calculate the -width for a `Bl -tag' list if it hasn't been + * provided. Uses the first head macro. NOTE AGAIN: this is + * ONLY if the -width argument has NOT been provided. See + * post_bl_block_width() for converting the -width string. + */ + + sz = 10; + n = mdoc->last; + + for (nn = n->body->child; nn; nn = nn->next) { + if (MDOC_It != nn->tok) + continue; + + assert(MDOC_BLOCK == nn->type); + nn = nn->head->child; + + if (nn == NULL) + break; + + if (MDOC_TEXT == nn->type) { + sz = strlen(nn->string) + 1; + break; + } + + if (0 != (ssz = mdoc_macro2len(nn->tok))) + sz = ssz; + + break; + } + + /* Defaults to ten ens. */ + + snprintf(buf, NUMSIZ, "%zun", sz); + + /* + * We have to dynamically add this to the macro's argument list. + * We're guaranteed that a MDOC_Width doesn't already exist. + */ + + assert(n->args); + i = (int)(n->args->argc)++; + + n->args->argv = mandoc_realloc(n->args->argv, + n->args->argc * sizeof(struct mdoc_argv)); + + n->args->argv[i].arg = MDOC_Width; + n->args->argv[i].line = n->line; + n->args->argv[i].pos = n->pos; + n->args->argv[i].sz = 1; + n->args->argv[i].value = mandoc_malloc(sizeof(char *)); + n->args->argv[i].value[0] = mandoc_strdup(buf); + + /* Set our width! */ + n->norm->Bl.width = n->args->argv[i].value[0]; + return(1); +} + + +static int +post_bl_head(POST_ARGS) +{ + struct mdoc_node *np, *nn, *nnp; + int i, j; + + if (LIST_column != mdoc->last->norm->Bl.type) + /* FIXME: this should be ERROR class... */ + return(hwarn_eq0(mdoc)); + + /* + * Convert old-style lists, where the column width specifiers + * trail as macro parameters, to the new-style ("normal-form") + * lists where they're argument values following -column. + */ + + /* First, disallow both types and allow normal-form. */ + + /* + * TODO: technically, we can accept both and just merge the two + * lists, but I'll leave that for another day. + */ + + if (mdoc->last->norm->Bl.ncols && mdoc->last->nchild) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_COLUMNS); + return(0); + } else if (NULL == mdoc->last->child) + return(1); + + np = mdoc->last->parent; + assert(np->args); + + for (j = 0; j < (int)np->args->argc; j++) + if (MDOC_Column == np->args->argv[j].arg) + break; + + assert(j < (int)np->args->argc); + assert(0 == np->args->argv[j].sz); + + /* + * Accomodate for new-style groff column syntax. Shuffle the + * child nodes, all of which must be TEXT, as arguments for the + * column field. Then, delete the head children. + */ + + np->args->argv[j].sz = (size_t)mdoc->last->nchild; + np->args->argv[j].value = mandoc_malloc + ((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; + + for (i = 0, nn = mdoc->last->child; nn; i++) { + np->args->argv[j].value[i] = nn->string; + nn->string = NULL; + nnp = nn; + nn = nn->next; + mdoc_node_delete(NULL, nnp); + } + + mdoc->last->nchild = 0; + mdoc->last->child = NULL; + + return(1); +} + +static int +post_bl(POST_ARGS) +{ + struct mdoc_node *n; + + if (MDOC_HEAD == mdoc->last->type) + return(post_bl_head(mdoc)); + if (MDOC_BLOCK == mdoc->last->type) + return(post_bl_block(mdoc)); + if (MDOC_BODY != mdoc->last->type) + return(1); + + for (n = mdoc->last->child; n; n = n->next) { + switch (n->tok) { + case (MDOC_Lp): + /* FALLTHROUGH */ + case (MDOC_Pp): + mdoc_nmsg(mdoc, n, MANDOCERR_CHILD); + /* FALLTHROUGH */ + case (MDOC_It): + /* FALLTHROUGH */ + case (MDOC_Sm): + continue; + default: + break; + } + + mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD); + return(0); + } + + return(1); +} + +static int +ebool(struct mdoc *mdoc) +{ + + if (NULL == mdoc->last->child) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); + mdoc_node_delete(mdoc, mdoc->last); + return(1); + } + check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1); + + assert(MDOC_TEXT == mdoc->last->child->type); + + if (0 == strcmp(mdoc->last->child->string, "on")) + return(1); + if (0 == strcmp(mdoc->last->child->string, "off")) + return(1); + + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL); + return(1); +} + +static int +post_root(POST_ARGS) +{ + int erc; + struct mdoc_node *n; + + erc = 0; + + /* Check that we have a finished prologue. */ + + if ( ! (MDOC_PBODY & mdoc->flags)) { + erc++; + mdoc_nmsg(mdoc, mdoc->first, MANDOCERR_NODOCPROLOG); + } + + n = mdoc->first; + assert(n); + + /* Check that we begin with a proper `Sh'. */ + + if (NULL == n->child) { + erc++; + mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); + } else if (MDOC_BLOCK != n->child->type || + MDOC_Sh != n->child->tok) { + erc++; + /* Can this be lifted? See rxdebug.1 for example. */ + mdoc_nmsg(mdoc, n, MANDOCERR_NODOCBODY); + } + + return(erc ? 0 : 1); +} + +static int +post_st(POST_ARGS) +{ + struct mdoc_node *ch; + const char *p; + + if (NULL == (ch = mdoc->last->child)) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_MACROEMPTY); + mdoc_node_delete(mdoc, mdoc->last); + return(1); + } + + assert(MDOC_TEXT == ch->type); + + if (NULL == (p = mdoc_a2st(ch->string))) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADSTANDARD); + mdoc_node_delete(mdoc, mdoc->last); + } else { + free(ch->string); + ch->string = mandoc_strdup(p); + } + + return(1); +} + +static int +post_rs(POST_ARGS) +{ + struct mdoc_node *nn, *next, *prev; + int i, j; + + switch (mdoc->last->type) { + case (MDOC_HEAD): + check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0); + return(1); + case (MDOC_BODY): + if (mdoc->last->child) + break; + check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0); + return(1); + default: + return(1); + } + + /* + * Make sure only certain types of nodes are allowed within the + * the `Rs' body. Delete offending nodes and raise a warning. + * Do this before re-ordering for the sake of clarity. + */ + + next = NULL; + for (nn = mdoc->last->child; nn; nn = next) { + for (i = 0; i < RSORD_MAX; i++) + if (nn->tok == rsord[i]) + break; + + if (i < RSORD_MAX) { + if (MDOC__J == rsord[i]) + mdoc->last->norm->Rs.child_J = nn; + next = nn->next; + continue; + } + + next = nn->next; + mdoc_nmsg(mdoc, nn, MANDOCERR_CHILD); + mdoc_node_delete(mdoc, nn); + } + + /* + * The full `Rs' block needs special handling to order the + * sub-elements according to `rsord'. Pick through each element + * and correctly order it. This is a insertion sort. + */ + + next = NULL; + for (nn = mdoc->last->child->next; nn; nn = next) { + /* Determine order of `nn'. */ + for (i = 0; i < RSORD_MAX; i++) + if (rsord[i] == nn->tok) + break; + + /* + * Remove `nn' from the chain. This somewhat + * repeats mdoc_node_unlink(), but since we're + * just re-ordering, there's no need for the + * full unlink process. + */ + + if (NULL != (next = nn->next)) + next->prev = nn->prev; + + if (NULL != (prev = nn->prev)) + prev->next = nn->next; + + nn->prev = nn->next = NULL; + + /* + * Scan back until we reach a node that's + * ordered before `nn'. + */ + + for ( ; prev ; prev = prev->prev) { + /* Determine order of `prev'. */ + for (j = 0; j < RSORD_MAX; j++) + if (rsord[j] == prev->tok) + break; + + if (j <= i) + break; + } + + /* + * Set `nn' back into its correct place in front + * of the `prev' node. + */ + + nn->prev = prev; + + if (prev) { + if (prev->next) + prev->next->prev = nn; + nn->next = prev->next; + prev->next = nn; + } else { + mdoc->last->child->prev = nn; + nn->next = mdoc->last->child; + mdoc->last->child = nn; + } + } + + return(1); +} + +static int +post_sh(POST_ARGS) +{ + + if (MDOC_HEAD == mdoc->last->type) + return(post_sh_head(mdoc)); + if (MDOC_BODY == mdoc->last->type) + return(post_sh_body(mdoc)); + + return(1); +} + +static int +post_sh_body(POST_ARGS) +{ + struct mdoc_node *n; + + if (SEC_NAME != mdoc->lastsec) + return(1); + + /* + * Warn if the NAME section doesn't contain the `Nm' and `Nd' + * macros (can have multiple `Nm' and one `Nd'). Note that the + * children of the BODY declaration can also be "text". + */ + + if (NULL == (n = mdoc->last->child)) { + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); + return(1); + } + + for ( ; n && n->next; n = n->next) { + if (MDOC_ELEM == n->type && MDOC_Nm == n->tok) + continue; + if (MDOC_TEXT == n->type) + continue; + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); + } + + assert(n); + if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok) + return(1); + + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADNAMESEC); + return(1); +} + +static int +post_sh_head(POST_ARGS) +{ + char buf[BUFSIZ]; + enum mdoc_sec sec; + + /* + * Process a new section. Sections are either "named" or + * "custom". Custom sections are user-defined, while named ones + * follow a conventional order and may only appear in certain + * manual sections. + */ + + if ( ! concat(mdoc, buf, mdoc->last->child, BUFSIZ)) + return(0); + + sec = mdoc_str2sec(buf); + + /* The NAME should be first. */ + + if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NAMESECFIRST); + + /* The SYNOPSIS gets special attention in other areas. */ + + if (SEC_SYNOPSIS == sec) + mdoc->flags |= MDOC_SYNOPSIS; + else + mdoc->flags &= ~MDOC_SYNOPSIS; + + /* Mark our last section. */ + + mdoc->lastsec = sec; + + /* We don't care about custom sections after this. */ + + if (SEC_CUSTOM == sec) + return(1); + + /* + * Check whether our non-custom section is being repeated or is + * out of order. + */ + + if (sec == mdoc->lastnamed) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECREP); + + if (sec < mdoc->lastnamed) + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECOOO); + + /* Mark the last named section. */ + + mdoc->lastnamed = sec; + + /* Check particular section/manual conventions. */ + + assert(mdoc->meta.msec); + + switch (sec) { + case (SEC_RETURN_VALUES): + /* FALLTHROUGH */ + case (SEC_ERRORS): + /* FALLTHROUGH */ + case (SEC_LIBRARY): + if (*mdoc->meta.msec == '2') + break; + if (*mdoc->meta.msec == '3') + break; + if (*mdoc->meta.msec == '9') + break; + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC); + break; + default: + break; + } + + return(1); +} + +static int +post_ignpar(POST_ARGS) +{ + struct mdoc_node *np; + + if (MDOC_BODY != mdoc->last->type) + return(1); + + if (NULL != (np = mdoc->last->child)) + if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { + mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); + mdoc_node_delete(mdoc, np); + } + + if (NULL != (np = mdoc->last->last)) + if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) { + mdoc_nmsg(mdoc, np, MANDOCERR_IGNPAR); + mdoc_node_delete(mdoc, np); + } + + return(1); +} + +static int +pre_par(PRE_ARGS) +{ + + if (NULL == mdoc->last) + return(1); + if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type) + return(1); + + /* + * Don't allow prior `Lp' or `Pp' prior to a paragraph-type + * block: `Lp', `Pp', or non-compact `Bd' or `Bl'. + */ + + if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok) + return(1); + if (MDOC_Bl == n->tok && n->norm->Bl.comp) + return(1); + if (MDOC_Bd == n->tok && n->norm->Bd.comp) + return(1); + if (MDOC_It == n->tok && n->parent->norm->Bl.comp) + return(1); + + mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR); + mdoc_node_delete(mdoc, mdoc->last); + return(1); +} + +static int +pre_literal(PRE_ARGS) +{ + + if (MDOC_BODY != n->type) + return(1); + + /* + * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd + * -unfilled' macros set MDOC_LITERAL on entrance to the body. + */ + + switch (n->tok) { + case (MDOC_Dl): + mdoc->flags |= MDOC_LITERAL; + break; + case (MDOC_Bd): + if (DISP_literal == n->norm->Bd.type) + mdoc->flags |= MDOC_LITERAL; + if (DISP_unfilled == n->norm->Bd.type) + mdoc->flags |= MDOC_LITERAL; + break; + default: + abort(); + /* NOTREACHED */ + } + + return(1); +} + +static int +post_dd(POST_ARGS) +{ + char buf[DATESIZE]; + struct mdoc_node *n; + + n = mdoc->last; + + if (NULL == n->child) { + mdoc->meta.date = time(NULL); + return(1); + } + + if ( ! concat(mdoc, buf, n->child, DATESIZE)) + return(0); + + mdoc->meta.date = mandoc_a2time + (MTIME_MDOCDATE | MTIME_CANONICAL, buf); + + if (0 == mdoc->meta.date) { + mdoc_nmsg(mdoc, n, MANDOCERR_BADDATE); + mdoc->meta.date = time(NULL); + } + + return(1); +} + +static int +post_dt(POST_ARGS) +{ + struct mdoc_node *nn, *n; + const char *cp; + char *p; + + n = mdoc->last; + + if (mdoc->meta.title) + free(mdoc->meta.title); + if (mdoc->meta.vol) + free(mdoc->meta.vol); + if (mdoc->meta.arch) + free(mdoc->meta.arch); + + mdoc->meta.title = mdoc->meta.vol = mdoc->meta.arch = NULL; + + /* First make all characters uppercase. */ + + if (NULL != (nn = n->child)) + for (p = nn->string; *p; p++) { + if (toupper((u_char)*p) == *p) + continue; + + /* + * FIXME: don't be lazy: have this make all + * characters be uppercase and just warn once. + */ + mdoc_nmsg(mdoc, nn, MANDOCERR_UPPERCASE); + break; + } + + /* Handles: `.Dt' + * --> title = unknown, volume = local, msec = 0, arch = NULL + */ + + if (NULL == (nn = n->child)) { + /* XXX: make these macro values. */ + /* FIXME: warn about missing values. */ + mdoc->meta.title = mandoc_strdup("UNKNOWN"); + mdoc->meta.vol = mandoc_strdup("LOCAL"); + mdoc->meta.msec = mandoc_strdup("1"); + return(1); + } + + /* Handles: `.Dt TITLE' + * --> title = TITLE, volume = local, msec = 0, arch = NULL + */ + + mdoc->meta.title = mandoc_strdup + ('\0' == nn->string[0] ? "UNKNOWN" : nn->string); + + if (NULL == (nn = nn->next)) { + /* FIXME: warn about missing msec. */ + /* XXX: make this a macro value. */ + mdoc->meta.vol = mandoc_strdup("LOCAL"); + mdoc->meta.msec = mandoc_strdup("1"); + return(1); + } + + /* Handles: `.Dt TITLE SEC' + * --> title = TITLE, volume = SEC is msec ? + * format(msec) : SEC, + * msec = SEC is msec ? atoi(msec) : 0, + * arch = NULL + */ + + cp = mdoc_a2msec(nn->string); + if (cp) { + mdoc->meta.vol = mandoc_strdup(cp); + mdoc->meta.msec = mandoc_strdup(nn->string); + } else { + mdoc_nmsg(mdoc, n, MANDOCERR_BADMSEC); + mdoc->meta.vol = mandoc_strdup(nn->string); + mdoc->meta.msec = mandoc_strdup(nn->string); + } + + if (NULL == (nn = nn->next)) + return(1); + + /* Handles: `.Dt TITLE SEC VOL' + * --> title = TITLE, volume = VOL is vol ? + * format(VOL) : + * VOL is arch ? format(arch) : + * VOL + */ + + cp = mdoc_a2vol(nn->string); + if (cp) { + free(mdoc->meta.vol); + mdoc->meta.vol = mandoc_strdup(cp); + } else { + /* FIXME: warn about bad arch. */ + cp = mdoc_a2arch(nn->string); + if (NULL == cp) { + free(mdoc->meta.vol); + mdoc->meta.vol = mandoc_strdup(nn->string); + } else + mdoc->meta.arch = mandoc_strdup(cp); + } + + /* Ignore any subsequent parameters... */ + /* FIXME: warn about subsequent parameters. */ + + return(1); +} + +static int +post_prol(POST_ARGS) +{ + /* + * Remove prologue macros from the document after they're + * processed. The final document uses mdoc_meta for these + * values and discards the originals. + */ + + mdoc_node_delete(mdoc, mdoc->last); + if (mdoc->meta.title && mdoc->meta.date && mdoc->meta.os) + mdoc->flags |= MDOC_PBODY; + + return(1); +} + +static int +post_os(POST_ARGS) +{ + struct mdoc_node *n; + char buf[BUFSIZ]; +#ifndef OSNAME + struct utsname utsname; +#endif + + n = mdoc->last; + + /* + * Set the operating system by way of the `Os' macro. Note that + * if an argument isn't provided and -DOSNAME="\"foo\"" is + * provided during compilation, this value will be used instead + * of filling in "sysname release" from uname(). + */ + + if (mdoc->meta.os) + free(mdoc->meta.os); + + if ( ! concat(mdoc, buf, n->child, BUFSIZ)) + return(0); + + /* XXX: yes, these can all be dynamically-adjusted buffers, but + * it's really not worth the extra hackery. + */ + + if ('\0' == buf[0]) { +#ifdef OSNAME + if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) { + mdoc_nmsg(mdoc, n, MANDOCERR_MEM); + return(0); + } +#else /*!OSNAME */ + if (uname(&utsname)) { + mdoc_nmsg(mdoc, n, MANDOCERR_UNAME); + mdoc->meta.os = mandoc_strdup("UNKNOWN"); + return(post_prol(mdoc)); + } + + if (strlcat(buf, utsname.sysname, BUFSIZ) >= BUFSIZ) { + mdoc_nmsg(mdoc, n, MANDOCERR_MEM); + return(0); + } + if (strlcat(buf, " ", BUFSIZ) >= BUFSIZ) { + mdoc_nmsg(mdoc, n, MANDOCERR_MEM); + return(0); + } + if (strlcat(buf, utsname.release, BUFSIZ) >= BUFSIZ) { + mdoc_nmsg(mdoc, n, MANDOCERR_MEM); + return(0); + } +#endif /*!OSNAME*/ + } + + mdoc->meta.os = mandoc_strdup(buf); + return(1); +} + +static int +post_std(POST_ARGS) +{ + struct mdoc_node *nn, *n; + + n = mdoc->last; + + /* + * Macros accepting `-std' as an argument have the name of the + * current document (`Nm') filled in as the argument if it's not + * provided. + */ + + if (n->child) + return(1); + + if (NULL == mdoc->meta.name) + return(1); + + nn = n; + mdoc->next = MDOC_NEXT_CHILD; + + if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name)) + return(0); + + mdoc->last = nn; + return(1); +} + +static int +concat(struct mdoc *m, 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); + return(0); + } + } + + return(1); +} + diff --git a/commands/mdocml/msec.c b/usr.bin/mdocml/dist/msec.c similarity index 94% rename from commands/mdocml/msec.c rename to usr.bin/mdocml/dist/msec.c index ba5e8d783..3e0ad56fd 100644 --- a/commands/mdocml/msec.c +++ b/usr.bin/mdocml/dist/msec.c @@ -1,4 +1,4 @@ -/* $Id: msec.c,v 1.8 2010/05/17 22:11:42 kristaps Exp $ */ +/* $Vendor-Id: msec.c,v 1.8 2010/05/17 22:11:42 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/msec.in b/usr.bin/mdocml/dist/msec.in similarity index 71% rename from commands/mdocml/msec.in rename to usr.bin/mdocml/dist/msec.in index f3aebb46a..a7f333576 100644 --- a/commands/mdocml/msec.in +++ b/usr.bin/mdocml/dist/msec.in @@ -1,4 +1,4 @@ -/* $Id: msec.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: msec.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -22,16 +22,16 @@ * Be sure to escape strings. */ -LINE("1", "General Commands Manual") -LINE("2", "System Calls Manual") -LINE("3", "Library Functions Manual") +LINE("1", "NetBSD General Commands Manual") +LINE("2", "NetBSD System Calls Manual") +LINE("3", "NetBSD Library Functions Manual") LINE("3p", "Perl Library Functions Manual") -LINE("4", "Kernel Interfaces Manual") -LINE("5", "File Formats Manual") -LINE("6", "Games Manual") -LINE("7", "Miscellaneous Information Manual") -LINE("8", "System Manager\'s Manual") -LINE("9", "Kernel Developer\'s Manual") +LINE("4", "NetBSD Kernel Interfaces Manual") +LINE("5", "NetBSD File Formats Manual") +LINE("6", "NetBSD Games Manual") +LINE("7", "NetBSD Miscellaneous Information Manual") +LINE("8", "NetBSD System Manager\'s Manual") +LINE("9", "NetBSD Kernel Developer\'s Manual") LINE("X11", "X11 Developer\'s Manual") LINE("X11R6", "X11 Developer\'s Manual") LINE("unass", "Unassociated") diff --git a/usr.bin/mdocml/dist/out.c b/usr.bin/mdocml/dist/out.c new file mode 100644 index 000000000..eb1b78437 --- /dev/null +++ b/usr.bin/mdocml/dist/out.c @@ -0,0 +1,562 @@ +/* $Vendor-Id: out.c,v 1.30 2011/01/05 15:37:23 kristaps Exp $ */ +/* + * Copyright (c) 2009, 2010 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 "mandoc.h" +#include "out.h" + +static void tblcalc_data(struct rofftbl *, struct roffcol *, + const struct tbl *, const struct tbl_dat *); +static void tblcalc_literal(struct rofftbl *, struct roffcol *, + const struct tbl_dat *); +static void tblcalc_number(struct rofftbl *, struct roffcol *, + const struct tbl *, const struct tbl_dat *); + +/* + * Convert a `scaling unit' to a consistent form, or fail. Scaling + * units are documented in groff.7, mdoc.7, man.7. + */ +int +a2roffsu(const char *src, struct roffsu *dst, enum roffscale def) +{ + char buf[BUFSIZ], hasd; + int i; + enum roffscale unit; + + if ('\0' == *src) + return(0); + + i = hasd = 0; + + switch (*src) { + case ('+'): + src++; + break; + case ('-'): + buf[i++] = *src++; + break; + default: + break; + } + + if ('\0' == *src) + return(0); + + while (i < BUFSIZ) { + if ( ! isdigit((u_char)*src)) { + if ('.' != *src) + break; + else if (hasd) + break; + else + hasd = 1; + } + buf[i++] = *src++; + } + + if (BUFSIZ == i || (*src && *(src + 1))) + return(0); + + buf[i] = '\0'; + + switch (*src) { + case ('c'): + unit = SCALE_CM; + break; + case ('i'): + unit = SCALE_IN; + break; + case ('P'): + unit = SCALE_PC; + break; + case ('p'): + unit = SCALE_PT; + break; + case ('f'): + unit = SCALE_FS; + break; + case ('v'): + unit = SCALE_VS; + break; + case ('m'): + unit = SCALE_EM; + break; + case ('\0'): + if (SCALE_MAX == def) + return(0); + unit = SCALE_BU; + break; + case ('u'): + unit = SCALE_BU; + break; + case ('M'): + unit = SCALE_MM; + break; + case ('n'): + unit = SCALE_EN; + break; + default: + return(0); + } + + /* FIXME: do this in the caller. */ + if ((dst->scale = atof(buf)) < 0) + dst->scale = 0; + dst->unit = unit; + 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); +} + + +int +a2roffdeco(enum roffdeco *d, const char **word, size_t *sz) +{ + int i, j, lim; + char term, c; + const char *wp; + enum roffdeco dd; + + *d = DECO_NONE; + lim = i = 0; + term = '\0'; + wp = *word; + + switch ((c = wp[i++])) { + case ('('): + *d = DECO_SPECIAL; + lim = 2; + break; + case ('F'): + /* FALLTHROUGH */ + case ('f'): + *d = 'F' == c ? DECO_FFONT : DECO_FONT; + + switch (wp[i++]) { + case ('('): + lim = 2; + break; + case ('['): + term = ']'; + break; + case ('3'): + /* FALLTHROUGH */ + case ('B'): + *d = DECO_BOLD; + return(i); + case ('2'): + /* FALLTHROUGH */ + case ('I'): + *d = DECO_ITALIC; + return(i); + case ('P'): + *d = DECO_PREVIOUS; + return(i); + case ('1'): + /* FALLTHROUGH */ + case ('R'): + *d = DECO_ROMAN; + return(i); + default: + i--; + lim = 1; + break; + } + break; + case ('k'): + /* FALLTHROUGH */ + case ('M'): + /* FALLTHROUGH */ + case ('m'): + /* FALLTHROUGH */ + case ('*'): + if ('*' == c) + *d = DECO_RESERVED; + + switch (wp[i++]) { + case ('('): + lim = 2; + break; + case ('['): + term = ']'; + break; + default: + i--; + lim = 1; + break; + } + break; + case ('h'): + /* FALLTHROUGH */ + case ('v'): + /* FALLTHROUGH */ + case ('s'): + j = 0; + if ('+' == wp[i] || '-' == wp[i]) { + i++; + j = 1; + } + + switch (wp[i++]) { + case ('('): + lim = 2; + break; + case ('['): + term = ']'; + break; + case ('\''): + term = '\''; + break; + case ('0'): + j = 1; + /* FALLTHROUGH */ + default: + i--; + lim = 1; + break; + } + + if ('+' == wp[i] || '-' == wp[i]) { + if (j) + return(i); + i++; + } + + /* Handle embedded numerical subexp or escape. */ + + if ('(' == wp[i]) { + while (wp[i] && ')' != wp[i]) + if ('\\' == wp[i++]) { + /* Handle embedded escape. */ + *word = &wp[i]; + i += a2roffdeco(&dd, word, sz); + } + + if (')' == wp[i++]) + break; + + *d = DECO_NONE; + return(i - 1); + } else if ('\\' == wp[i]) { + *word = &wp[++i]; + i += a2roffdeco(&dd, word, sz); + } + + break; + case ('['): + *d = DECO_SPECIAL; + term = ']'; + break; + case ('c'): + *d = DECO_NOSPACE; + return(i); + case ('z'): + *d = DECO_NONE; + if ('\\' == wp[i]) { + *word = &wp[++i]; + return(i + a2roffdeco(&dd, word, sz)); + } else + lim = 1; + break; + case ('o'): + /* FALLTHROUGH */ + case ('w'): + if ('\'' == wp[i++]) { + term = '\''; + break; + } + /* FALLTHROUGH */ + default: + *d = DECO_SSPECIAL; + i--; + lim = 1; + break; + } + + assert(term || lim); + *word = &wp[i]; + + if (term) { + j = i; + while (wp[i] && wp[i] != term) + i++; + if ('\0' == wp[i]) { + *d = DECO_NONE; + return(i); + } + + assert(i >= j); + *sz = (size_t)(i - j); + + return(i + 1); + } + + assert(lim > 0); + *sz = (size_t)lim; + + for (j = 0; wp[i] && j < lim; j++) + i++; + if (j < lim) + *d = DECO_NONE; + + return(i); +} + +/* + * Calculate the abstract widths and decimal positions of columns in a + * table. This routine allocates the columns structures then runs over + * all rows and cells in the table. The function pointers in "tbl" are + * used for the actual width calculations. + */ +void +tblcalc(struct rofftbl *tbl, const struct tbl_span *sp) +{ + const struct tbl_dat *dp; + const struct tbl_head *hp; + struct roffcol *col; + + /* + * Allocate the master column specifiers. These will hold the + * widths and decimal positions for all cells in the column. It + * must be freed and nullified by the caller. + */ + + assert(NULL == tbl->cols); + tbl->cols = calloc(sp->tbl->cols, sizeof(struct roffcol)); + + hp = sp->head; + + for ( ; sp; sp = sp->next) { + if (TBL_SPAN_DATA != sp->pos) + continue; + /* + * 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) { + if (NULL == dp->layout) + continue; + col = &tbl->cols[dp->layout->head->ident]; + tblcalc_data(tbl, col, sp->tbl, dp); + } + } + + /* + * Calculate width of the spanners. These get one space for a + * vertical line, two for a double-vertical line. + */ + + for ( ; hp; hp = hp->next) { + col = &tbl->cols[hp->ident]; + switch (hp->pos) { + case (TBL_HEAD_VERT): + col->width = (*tbl->len)(1, tbl->arg); + break; + case (TBL_HEAD_DVERT): + col->width = (*tbl->len)(2, tbl->arg); + break; + default: + break; + } + } +} + +static void +tblcalc_data(struct rofftbl *tbl, struct roffcol *col, + const struct tbl *tp, const struct tbl_dat *dp) +{ + size_t sz; + + /* Branch down into data sub-types. */ + + switch (dp->layout->pos) { + case (TBL_CELL_HORIZ): + /* FALLTHROUGH */ + case (TBL_CELL_DHORIZ): + sz = (*tbl->len)(1, tbl->arg); + if (col->width < sz) + col->width = sz; + break; + case (TBL_CELL_LONG): + /* FALLTHROUGH */ + case (TBL_CELL_CENTRE): + /* FALLTHROUGH */ + case (TBL_CELL_LEFT): + /* FALLTHROUGH */ + case (TBL_CELL_RIGHT): + tblcalc_literal(tbl, col, dp); + break; + case (TBL_CELL_NUMBER): + tblcalc_number(tbl, col, tp, dp); + break; + default: + abort(); + /* NOTREACHED */ + } +} + +static void +tblcalc_literal(struct rofftbl *tbl, struct roffcol *col, + const struct tbl_dat *dp) +{ + size_t sz, bufsz, spsz; + + /* + * 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). + */ + + sz = bufsz = spsz = 0; + if (dp->string) + sz = (*tbl->slen)(dp->string, tbl->arg); + + assert(dp->layout); + switch (dp->layout->pos) { + case (TBL_CELL_LONG): + /* FALLTHROUGH */ + case (TBL_CELL_CENTRE): + bufsz = (*tbl->len)(2, 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; +} + +static void +tblcalc_number(struct rofftbl *tbl, struct roffcol *col, + const struct tbl *tp, const struct tbl_dat *dp) +{ + int i; + size_t sz, psz, ssz, d; + char *cp; + const char *str; + char buf[2]; + + /* TODO: use spacing modifier. */ + + /* + * First calculate number width and decimal place (last + 1 for + * no-decimal numbers). If the stored decimal is subsequent + * ours, make our size longer by that difference + * (right-"shifting"); similarly, if ours is subsequent the + * stored, then extend the stored size by the difference. + * Finally, re-assign the stored values. + */ + + str = ""; + if (dp->string) + str = dp->string; + + sz = (*tbl->slen)(str, tbl->arg); + + buf[0] = tp->decimal; + buf[1] = '\0'; + + psz = (*tbl->slen)(buf, tbl->arg); + + if (NULL != (cp = strchr(str, tp->decimal))) { + buf[1] = '\0'; + for (ssz = 0, i = 0; cp != &str[i]; i++) { + buf[0] = str[i]; + ssz += (*tbl->slen)(buf, tbl->arg); + } + d = ssz + psz; + } 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) { + sz += col->decimal - d; + d = col->decimal; + } else + col->width += d - col->decimal; + + if (sz > col->width) + col->width = sz; + if (d > col->decimal) + col->decimal = d; +} + + diff --git a/commands/mdocml/out.h b/usr.bin/mdocml/dist/out.h similarity index 55% rename from commands/mdocml/out.h rename to usr.bin/mdocml/dist/out.h index 39dde4203..4cba1bf5e 100644 --- a/commands/mdocml/out.h +++ b/usr.bin/mdocml/dist/out.h @@ -1,4 +1,4 @@ -/* $Id: out.h,v 1.11 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: out.h,v 1.15 2011/01/05 15:37:23 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -21,6 +21,21 @@ __BEGIN_DECLS +struct roffcol { + size_t width; /* width of cell */ + size_t decimal; /* decimal position in cell */ +}; + +typedef size_t (*tbl_strlen)(const char *, void *); +typedef size_t (*tbl_len)(size_t, void *); + +struct rofftbl { + tbl_strlen slen; /* calculate string length */ + tbl_len len; /* produce width of empty space */ + struct roffcol *cols; /* master column specifiers */ + void *arg; /* passed to slen and len */ +}; + enum roffscale { SCALE_CM, SCALE_IN, @@ -37,14 +52,14 @@ enum roffscale { enum roffdeco { DECO_NONE, - DECO_SPECIAL, - DECO_RESERVED, - DECO_BOLD, - DECO_ITALIC, - DECO_ROMAN, - DECO_PREVIOUS, - DECO_SIZE, - DECO_NOSPACE, + DECO_SPECIAL, /* special character */ + DECO_SSPECIAL, /* single-char special */ + DECO_RESERVED, /* reserved word */ + DECO_BOLD, /* bold font */ + DECO_ITALIC, /* italic font */ + DECO_ROMAN, /* "normal" undecorated font */ + DECO_PREVIOUS, /* revert to previous font */ + DECO_NOSPACE, /* suppress spacing */ DECO_FONT, /* font */ DECO_FFONT, /* font family */ DECO_MAX @@ -53,30 +68,23 @@ enum roffdeco { struct roffsu { enum roffscale unit; double scale; - int pt; }; -#define SCALE_INVERT(p) \ - do { (p)->scale = -(p)->scale; } \ - while (/* CONSTCOND */ 0) - #define SCALE_VS_INIT(p, v) \ do { (p)->unit = SCALE_VS; \ - (p)->scale = (v); \ - (p)->pt = 0; } \ + (p)->scale = (v); } \ while (/* CONSTCOND */ 0) #define SCALE_HS_INIT(p, v) \ do { (p)->unit = SCALE_BU; \ - (p)->scale = (v); \ - (p)->pt = 0; } \ + (p)->scale = (v); } \ while (/* CONSTCOND */ 0) -int a2roffsu(const char *, - struct roffsu *, enum roffscale); -int a2roffdeco(enum roffdeco *, const char **, size_t *); -void time2a(time_t, char *, size_t); +int a2roffsu(const char *, struct roffsu *, enum roffscale); +int a2roffdeco(enum roffdeco *, const char **, size_t *); +void time2a(time_t, char *, size_t); +void tblcalc(struct rofftbl *tbl, const struct tbl_span *); __END_DECLS -#endif /*!HTML_H*/ +#endif /*!OUT_H*/ diff --git a/commands/mdocml/roff.3 b/usr.bin/mdocml/dist/roff.3 similarity index 82% rename from commands/mdocml/roff.3 rename to usr.bin/mdocml/dist/roff.3 index fcc47395b..554fb1938 100644 --- a/commands/mdocml/roff.3 +++ b/usr.bin/mdocml/dist/roff.3 @@ -1,4 +1,4 @@ -.\" $Id: roff.3,v 1.1 2010/05/25 22:16:59 kristaps Exp $ +.\" $Vendor-Id: roff.3,v 1.10 2011/01/01 16:18:39 kristaps Exp $ .\" .\" Copyright (c) 2010 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: May 25 2010 $ +.Dd January 1, 2011 .Dt ROFF 3 .Os .Sh NAME @@ -23,14 +23,19 @@ .Nm roff_endparse , .Nm roff_free , .Nm roff_parseln , -.Nm roff_reset +.Nm roff_reset , +.Nm roff_span .Nd roff macro compiler library .Sh SYNOPSIS .In mandoc.h .In roff.h .Ft "struct roff *" -.Fn roff_alloc "mandocmsg msgs" "void *data" -.Ft int +.Fo roff_alloc +.Fa "struct regset *regs" +.Fa "void *data" +.Fa "mandocmsg msgs" +.Fc +.Ft void .Fn roff_endparse "struct roff *roff" .Ft void .Fn roff_free "struct roff *roff" @@ -45,6 +50,8 @@ .Fc .Ft void .Fn roff_reset "struct roff *roff" +.Ft "const struct tbl_span *" +.Fn roff_span "const struct roff *roff" .Sh DESCRIPTION The .Nm @@ -107,10 +114,6 @@ The .Fa data pointer is passed to .Fa msgs . -The -.Fa pflags -arguments are defined in -.Pa roff.h . Returns NULL on failure. If non-NULL, the pointer must be freed with .Fn roff_free . @@ -138,7 +141,13 @@ This line should not contain the trailing newline. Returns 0 on failure, 1 on success. .It Fn roff_endparse Signals that the parse is complete. -Returns 0 on failure, 1 on success. +.It Fn roff_span +If +.Fn roff_parseln +returned +.Va ROFF_TBL , +return the last parsed table row. +Returns NULL otherwise. .El .Sh EXAMPLES See @@ -154,3 +163,15 @@ The .Nm library was written by .An Kristaps Dzonsons Aq kristaps@bsd.lv . +.Sh BUGS +The implementation of user-defined strings needs improvement: +.Bl -dash +.It +String values are taken literally and are not interpreted. +.It +Parsing of quoted strings is incomplete. +.It +The stings are stored internally using a singly linked list, +which is fine for small numbers of strings, +but ineffient when handling many strings. +.El diff --git a/usr.bin/mdocml/dist/roff.7 b/usr.bin/mdocml/dist/roff.7 new file mode 100644 index 000000000..34a9fb5bb --- /dev/null +++ b/usr.bin/mdocml/dist/roff.7 @@ -0,0 +1,619 @@ +.\" $Vendor-Id: roff.7,v 1.23 2011/01/04 23:32:21 kristaps Exp $ +.\" +.\" Copyright (c) 2010 Kristaps Dzonsons +.\" Copyright (c) 2010 Ingo Schwarze +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd January 4, 2011 +.Dt ROFF 7 +.Os +.Sh NAME +.Nm roff +.Nd roff language reference for mandoc +.Sh DESCRIPTION +The +.Nm roff +language is a general purpose text formatting language. +In particular, it serves as the basis for the +.Xr mdoc 7 +and +.Xr man 7 +manual formatting macro languages. +This manual describes the subset of the +.Nm +language accepted by the +.Xr mandoc 1 +utility. +.Pp +Input lines beginning with the control characters +.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. +.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 +.Xr mandoc_char 7 +manual. +.Pp +All manuals must have +.Ux +line terminators. +.Sh REQUEST SYNTAX +A request or macro line consists of: +.Pp +.Bl -enum -compact +.It +the control character +.Sq \&. +or +.Sq \(aq +at the beginning of the line, +.It +optionally an arbitrary amount of whitespace, +.It +the name of the request or the macro, which is one word of arbitrary +length, terminated by whitespace, +.It +and zero or more arguments delimited by whitespace. +.El +.Pp +Thus, the following request lines are all equivalent: +.Bd -literal -offset indent +\&.ig end +\&.ig end +\&. ig end +.Ed +.Sh REQUEST REFERENCE +The +.Xr mandoc 1 +.Nm +parser recognizes the following requests. +Note that the +.Nm +language defines many more requests not implemented in +.Xr mandoc 1 . +.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. +Currently, it is ignored including its arguments, +and the number of arguments is not checked. +.Ss \&am +Append to a macro definition. +The syntax of this request is the same as that of +.Sx \&de . +It is currently ignored by +.Xr mandoc 1 , +as are its children. +.Ss \&ami +Append to a macro definition, specifying the macro name indirectly. +The syntax of this request is the same as that of +.Sx \&dei . +It is currently ignored by +.Xr mandoc 1 , +as are its children. +.Ss \&am1 +Append to a macro definition, switching roff compatibility mode off +during macro execution. +The syntax of this request is the same as that of +.Sx \&de1 . +It is currently ignored by +.Xr mandoc 1 , +as are its children. +.Ss \&de +Define a +.Nm +macro. +Its syntax can be either +.Bd -literal -offset indent +.Pf . Cm \&de Ar name +.Ar macro definition +\&.. +.Ed +.Pp +or +.Bd -literal -offset indent +.Pf . Cm \&de Ar name Ar end +.Ar macro definition +.Pf . Ar end +.Ed +.Pp +Both forms define or redefine the macro +.Ar name +to represent the +.Ar macro definition , +which may consist of one or more input lines, including the newline +characters terminating each line, optionally containing calls to +.Nm +requests, +.Nm +macros or high-level macros like +.Xr man 7 +or +.Xr mdoc 7 +macros, whichever applies to the document in question. +.Pp +Specifying a custom +.Ar end +macro works in the same way as for +.Sx \&ig ; +namely, the call to +.Sq Pf . Ar end +first ends the +.Ar macro definition , +and after that, it is also evaluated as a +.Nm +request or +.Nm +macro, but not as a high-level macro. +.Pp +The macro can be invoked later using the syntax +.Pp +.D1 Pf . Ar name Op Ar argument Op Ar argument ... +.Pp +Arguments are separated by blank characters and can be quoted +using double-quotes +.Pq Sq \(dq +to allow inclusion of blank characters into arguments. +To include the double-quote character into a quoted argument, +escape it from ending the argument by doubling it. +.Pp +The line invoking the macro will be replaced +in the input stream by the +.Ar macro definition , +replacing all occurrences of +.No \e\e$ Ns Ar N , +where +.Ar N +is a digit, by the +.Ar N Ns th Ar argument . +For example, +.Bd -literal -offset indent +\&.de ZN +\efI\e^\e\e$1\e^\efP\e\e$2 +\&.. +\&.ZN XtFree . +.Ed +.Pp +produces +.Pp +.D1 \efI\e^XtFree\e^\efP. +.Pp +in the input stream, and thus in the output: \fI\^XtFree\^\fP. +.Pp +Since macros and user-defined strings share a common string table, +defining a macro +.Ar name +clobbers the user-defined string +.Ar name , +and the +.Ar macro definition +can also be printed using the +.Sq \e* +string interpolation syntax described below +.Sx ds , +but this is rarely useful because every macro definition contains at least +one explicit newline character. +.Pp +In order to prevent endless recursion, both groff and +.Xr mandoc 1 +limit the stack depth for expanding macros and strings +to a large, but finite number. +Do not rely on the exact value of this limit. +.Ss \&dei +Define a +.Nm +macro, specifying the macro name indirectly. +The syntax of this request is the same as that of +.Sx \&de . +It is currently ignored by +.Xr mandoc 1 , +as are its children. +.Ss \&de1 +Define a +.Nm +macro that will be executed with +.Nm +compatibility mode switched off during macro execution. +This is a GNU extension not available in traditional +.Nm +implementations and not even in older versions of groff. +Since +.Xr mandoc 1 +does not implement +.Nm +compatibility mode at all, it handles this request as an alias for +.Sx \&de . +.Ss \&ds +Define a user-defined string. +Its syntax is as follows: +.Pp +.D1 Pf . Cm \&ds Ar name Oo \(dq Oc Ns Ar string +.Pp +The +.Ar name +and +.Ar string +arguments are space-separated. +If the +.Ar string +begins with a double-quote character, that character will not be part +of the string. +All remaining characters on the input line form the +.Ar string , +including whitespace and double-quote characters, even trailing ones. +.Pp +The +.Ar string +can be interpolated into subsequent text by using +.No \e* Ns Bq Ar name +for a +.Ar name +of arbitrary length, or \e*(NN or \e*N if the length of +.Ar name +is two or one characters, respectively. +Interpolation can be prevented by escaping the leading backslash; +that is, an asterisk preceded by an even number of backslashes +does not trigger string interpolation. +.Pp +Since user-defined strings and macros share a common string table, +defining a string +.Ar name +clobbers the macro +.Ar name , +and the +.Ar name +used for defining a string can also be invoked as a macro, +in which case the following input line will be appended to the +.Ar string , +forming a new input line passed to the +.Nm +parser. +For example, +.Bd -literal -offset indent +\&.ds badidea .S +\&.badidea +H SYNOPSIS +.Ed +.Pp +invokes the +.Cm SH +macro when used in a +.Xr man 7 +document. +Such abuse is of course strongly discouraged. +.Ss \&el +The +.Qq else +half of an if/else conditional. +Pops a result off the stack of conditional evaluations pushed by +.Sx \&ie +and uses it as its conditional. +If no stack entries are present (e.g., due to no prior +.Sx \&ie +calls) +then false is assumed. +The syntax of this request is similar to +.Sx \&if +except that the conditional is missing. +.Ss \&hy +Set automatic hyphenation mode. +This line-scoped request is currently ignored. +.Ss \&ie +The +.Qq if +half of an if/else conditional. +The result of the conditional is pushed into a stack used by subsequent +invocations of +.Sx \&el , +which may be separated by any intervening input (or not exist at all). +Its syntax is equivalent to +.Sx \&if . +.Ss \&if +Begins a conditional. +Right now, the conditional evaluates to true +if and only if it starts with the letter +.Sy n , +indicating processing in nroff style as opposed to troff style. +If a conditional is false, its children are not processed, but are +syntactically interpreted to preserve the integrity of the input +document. +Thus, +.Pp +.D1 \&.if t .ig +.Pp +will discard the +.Sq \&.ig , +which may lead to interesting results, but +.Pp +.D1 \&.if t .if t \e{\e +.Pp +will continue to syntactically interpret to the block close of the final +conditional. +Sub-conditionals, in this case, obviously inherit the truth value of +the parent. +This request has the following syntax: +.Bd -literal -offset indent +\&.if COND \e{\e +BODY... +\&.\e} +.Ed +.Bd -literal -offset indent +\&.if COND \e{ BODY +BODY... \e} +.Ed +.Bd -literal -offset indent +\&.if COND \e{ BODY +BODY... +\&.\e} +.Ed +.Bd -literal -offset indent +\&.if COND \e +BODY +.Ed +.Pp +COND is a conditional statement. +roff allows for complicated conditionals; mandoc is much simpler. +At this time, mandoc supports only +.Sq n , +evaluating to true; +and +.Sq t , +.Sq e , +and +.Sq o , +evaluating to false. +All other invocations are read up to the next end of line or space and +evaluate as false. +.Pp +If the BODY section is begun by an escaped brace +.Sq \e{ , +scope continues until a closing-brace escape sequence +.Sq \.\e} . +If the BODY is not enclosed in braces, scope continues until +the end of the line. +If the COND is followed by a BODY on the same line, whether after a +brace or not, then requests and macros +.Em must +begin with a control character. +It is generally more intuitive, in this case, to write +.Bd -literal -offset indent +\&.if COND \e{\e +\&.foo +bar +\&.\e} +.Ed +.Pp +than having the request or macro follow as +.Pp +.D1 \&.if COND \e{ .foo +.Pp +The scope of a conditional is always parsed, but only executed if the +conditional evaluates to true. +.Pp +Note that text following an +.Sq \&.\e} +escape sequence is discarded. +Furthermore, if an explicit closing sequence +.Sq \e} +is specified in a free-form line, the entire line is accepted within the +scope of the prior request, not only the text preceding the close, with the +.Sq \e} +collapsing into a zero-width space. +.Ss \&ig +Ignore input. +Its syntax can be either +.Bd -literal -offset indent +.Pf . Cm \&ig +.Ar ignored text +\&.. +.Ed +.Pp +or +.Bd -literal -offset indent +.Pf . Cm \&ig Ar end +.Ar ignored text +.Pf . Ar end +.Ed +.Pp +In the first case, input is ignored until a +.Sq \&.. +request is encountered on its own line. +In the second case, input is ignored until the specified +.Sq Pf . Ar end +macro is encountered. +Do not use the escape character +.Sq \e +anywhere in the definition of +.Ar end ; +it would cause very strange behaviour. +.Pp +When the +.Ar end +macro is a roff request or a roff macro, like in +.Pp +.D1 \&.ig if +.Pp +the subsequent invocation of +.Sx \&if +will first terminate the +.Ar ignored text , +then be invoked as usual. +Otherwise, it only terminates the +.Ar ignored text , +and arguments following it or the +.Sq \&.. +request are discarded. +.Ss \&ne +Declare the need for the specified minimum vertical space +before the next trap or the bottom of the page. +This line-scoped request is currently ignored. +.Ss \&nh +Turn off automatic hyphenation mode. +This line-scoped request is currently ignored. +.Ss \&rm +Remove a request, macro or string. +This request is intended to have one argument, +the name of the request, macro or string to be undefined. +Currently, it is ignored including its arguments, +and the number of arguments is not checked. +.Ss \&nr +Define a register. +A register is an arbitrary string value that defines some sort of state, +which influences parsing and/or formatting. +Its syntax is as follows: +.Pp +.D1 Pf \. Cm \&nr Ar name Ar value +.Pp +The +.Ar value +may, at the moment, only be an integer. +So far, only the following register +.Ar name +is recognised: +.Bl -tag -width Ds +.It Cm nS +If set to a positive integer value, certain +.Xr mdoc 7 +macros will behave in the same way as in the +.Em SYNOPSIS +section. +If set to 0, these macros will behave in the same way as outside the +.Em SYNOPSIS +section, even when called within the +.Em SYNOPSIS +section itself. +Note that starting a new +.Xr mdoc 7 +section with the +.Cm \&Sh +macro will reset this register. +.El +.Ss \&so +Include a source file. +Its syntax is as follows: +.Pp +.D1 Pf \. Cm \&so Ar file +.Pp +The +.Ar file +will be read and its contents processed as input in place of the +.Sq \&.so +request line. +To avoid inadvertant inclusion of unrelated files, +.Xr mandoc 1 +only accepts relative paths not containing the strings +.Qq ../ +and +.Qq /.. . +.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. +.Ss \&T& +Re-start a table layout, retaining the options of the prior table +invocation. +See +.Sx \&TS . +.Ss \&TE +End a table context. +See +.Sx \&TS . +.Ss \&TS +Begin a table, which formats input in aligned rows and columns. +See +.Xr tbl 7 +for a description of the tbl language. +.Sh COMPATIBILITY +This section documents compatibility between mandoc and other other +.Nm +implementations, at this time limited to GNU troff +.Pq Qq groff . +The term +.Qq historic groff +refers to groff version 1.15. +.Pp +.Bl -dash -compact +.It +The +.Cm nS +register is only compatible with OpenBSD's groff-1.15. +.It +Historic groff did not accept white-space before a custom +.Ar end +macro for the +.Sx \&ig +request. +.It +The +.Sx \&if +and family would print funny white-spaces with historic groff when +using the next-line syntax. +.El +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr man 7 , +.Xr mandoc_char 7 , +.Xr mdoc 7 , +.Xr tbl 7 +.Rs +.%A Joseph F. Ossanna +.%A Brian W. Kernighan +.%I AT&T Bell Laboratories +.%T Troff User's Manual +.%R Computing Science Technical Report +.%N 54 +.%C Murray Hill, New Jersey +.%D 1976 and 1992 +.%U http://www.kohala.com/start/troff/cstr54.ps +.Re +.Rs +.%A Joseph F. Ossanna +.%A Brian W. Kernighan +.%A Gunnar Ritter +.%T Heirloom Documentation Tools Nroff/Troff User's Manual +.%D September 17, 2007 +.%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 +.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. +.Sh AUTHORS +.An -nosplit +This partial +.Nm +reference was written by +.An Kristaps Dzonsons Aq kristaps@bsd.lv +and +.An Ingo Schwarze Aq schwarze@openbsd.org . diff --git a/usr.bin/mdocml/dist/roff.c b/usr.bin/mdocml/dist/roff.c new file mode 100644 index 000000000..f70cfa3a1 --- /dev/null +++ b/usr.bin/mdocml/dist/roff.c @@ -0,0 +1,1356 @@ +/* $Vendor-Id: roff.c,v 1.120 2011/01/03 23:24:16 schwarze Exp $ */ +/* + * Copyright (c) 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2010, 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 AUTHORS DISCLAIM ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "roff.h" +#include "libroff.h" +#include "libmandoc.h" + +#define RSTACK_MAX 128 + +#define ROFF_CTL(c) \ + ('.' == (c) || '\'' == (c)) + +enum rofft { + ROFF_ad, + ROFF_am, + ROFF_ami, + ROFF_am1, + ROFF_de, + ROFF_dei, + ROFF_de1, + ROFF_ds, + ROFF_el, + ROFF_hy, + ROFF_ie, + ROFF_if, + ROFF_ig, + ROFF_ne, + ROFF_nh, + ROFF_nr, + ROFF_rm, + ROFF_so, + ROFF_tr, + ROFF_TS, + ROFF_TE, + ROFF_T_, + ROFF_cblock, + ROFF_ccond, /* FIXME: remove this. */ + ROFF_USERDEF, + ROFF_MAX +}; + +enum roffrule { + ROFFRULE_ALLOW, + ROFFRULE_DENY +}; + +struct roffstr { + char *name; /* key of symbol */ + char *string; /* current value */ + struct roffstr *next; /* next in list */ +}; + +struct roff { + struct roffnode *last; /* leaf of stack */ + mandocmsg msg; /* err/warn/fatal messages */ + void *data; /* privdata for messages */ + 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 */ + const char *current_string; /* value of last called user macro */ + struct tbl_node *first_tbl; /* first table parsed */ + struct tbl_node *last_tbl; /* last table parsed */ + struct tbl_node *tbl; /* current table being parsed */ +}; + +struct roffnode { + enum rofft tok; /* type of node */ + struct roffnode *parent; /* up one in stack */ + int line; /* parse line */ + int col; /* parse col */ + char *name; /* node name, e.g. macro name */ + char *end; /* end-rules: custom token */ + int endspan; /* end-rules: next-line or infty */ + enum roffrule rule; /* current evaluation rule */ +}; + +#define ROFF_ARGS struct roff *r, /* parse ctx */ \ + enum rofft tok, /* tok of macro */ \ + char **bufp, /* input buffer */ \ + size_t *szp, /* size of input buffer */ \ + int ln, /* parse line */ \ + int ppos, /* original pos in buffer */ \ + int pos, /* current pos in buffer */ \ + int *offs /* reset offset of buffer data */ + +typedef enum rofferr (*roffproc)(ROFF_ARGS); + +struct roffmac { + const char *name; /* macro name */ + roffproc proc; /* process new macro */ + roffproc text; /* process as child text of macro */ + roffproc sub; /* process as child of macro */ + int flags; +#define ROFFMAC_STRUCT (1 << 0) /* always interpret */ + struct roffmac *next; +}; + +static enum rofferr roff_block(ROFF_ARGS); +static enum rofferr roff_block_text(ROFF_ARGS); +static enum rofferr roff_block_sub(ROFF_ARGS); +static enum rofferr roff_cblock(ROFF_ARGS); +static enum rofferr roff_ccond(ROFF_ARGS); +static enum rofferr roff_cond(ROFF_ARGS); +static enum rofferr roff_cond_text(ROFF_ARGS); +static enum rofferr roff_cond_sub(ROFF_ARGS); +static enum rofferr roff_ds(ROFF_ARGS); +static enum roffrule roff_evalcond(const char *, int *); +static void roff_freestr(struct roff *); +static const char *roff_getstrn(const struct roff *, + const char *, size_t); +static enum rofferr roff_line_ignore(ROFF_ARGS); +static enum rofferr roff_line_error(ROFF_ARGS); +static enum rofferr roff_nr(ROFF_ARGS); +static int roff_res(struct roff *, + char **, size_t *, int); +static void roff_setstr(struct roff *, + const char *, const char *, int); +static enum rofferr roff_so(ROFF_ARGS); +static enum rofferr roff_TE(ROFF_ARGS); +static enum rofferr roff_TS(ROFF_ARGS); +static enum rofferr roff_T_(ROFF_ARGS); +static enum rofferr roff_userdef(ROFF_ARGS); + +/* See roff_hash_find() */ + +#define ASCII_HI 126 +#define ASCII_LO 33 +#define HASHWIDTH (ASCII_HI - ASCII_LO + 1) + +static struct roffmac *hash[HASHWIDTH]; + +static struct roffmac roffs[ROFF_MAX] = { + { "ad", roff_line_ignore, NULL, NULL, 0, NULL }, + { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "ds", roff_ds, NULL, NULL, 0, NULL }, + { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, + { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, + { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, + { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, + { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "ne", roff_line_ignore, NULL, NULL, 0, NULL }, + { "nh", roff_line_ignore, NULL, NULL, 0, NULL }, + { "nr", roff_nr, NULL, NULL, 0, NULL }, + { "rm", roff_line_error, NULL, NULL, 0, NULL }, + { "so", roff_so, NULL, NULL, 0, NULL }, + { "tr", roff_line_ignore, 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 }, + { ".", roff_cblock, NULL, NULL, 0, NULL }, + { "\\}", roff_ccond, NULL, NULL, 0, NULL }, + { NULL, roff_userdef, NULL, NULL, 0, NULL }, +}; + +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 *); +static int roff_parse_nat(const char *, unsigned int *); + +/* See roff_hash_find() */ +#define ROFF_HASH(p) (p[0] - ASCII_LO) + +static void +roff_hash_init(void) +{ + struct roffmac *n; + int buc, i; + + for (i = 0; i < (int)ROFF_USERDEF; i++) { + assert(roffs[i].name[0] >= ASCII_LO); + assert(roffs[i].name[0] <= ASCII_HI); + + buc = ROFF_HASH(roffs[i].name); + + if (NULL != (n = hash[buc])) { + for ( ; n->next; n = n->next) + /* Do nothing. */ ; + n->next = &roffs[i]; + } else + hash[buc] = &roffs[i]; + } +} + + +/* + * Look up a roff token by its name. Returns ROFF_MAX if no macro by + * the nil-terminated string name could be found. + */ +static enum rofft +roff_hash_find(const char *p, size_t s) +{ + int buc; + struct roffmac *n; + + /* + * libroff has an extremely simple hashtable, for the time + * being, which simply keys on the first character, which must + * be printable, then walks a chain. It works well enough until + * optimised. + */ + + if (p[0] < ASCII_LO || p[0] > ASCII_HI) + return(ROFF_MAX); + + buc = ROFF_HASH(p); + + if (NULL == (n = hash[buc])) + return(ROFF_MAX); + for ( ; n; n = n->next) + if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s]) + return((enum rofft)(n - roffs)); + + return(ROFF_MAX); +} + + +/* + * Pop the current node off of the stack of roff instructions currently + * pending. + */ +static void +roffnode_pop(struct roff *r) +{ + struct roffnode *p; + + assert(r->last); + p = r->last; + + if (ROFF_el == p->tok) + if (r->rstackpos > -1) + r->rstackpos--; + + r->last = r->last->parent; + free(p->name); + free(p->end); + free(p); +} + + +/* + * Push a roff node onto the instruction stack. This must later be + * removed with roffnode_pop(). + */ +static void +roffnode_push(struct roff *r, enum rofft tok, const char *name, + int line, int col) +{ + struct roffnode *p; + + p = mandoc_calloc(1, sizeof(struct roffnode)); + p->tok = tok; + if (name) + p->name = mandoc_strdup(name); + p->parent = r->last; + p->line = line; + p->col = col; + p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY; + + r->last = p; +} + + +static void +roff_free1(struct roff *r) +{ + struct tbl_node *t; + + while (r->first_tbl) { + t = r->first_tbl; + r->first_tbl = t->next; + tbl_free(t); + } + + r->first_tbl = r->last_tbl = r->tbl = NULL; + + while (r->last) + roffnode_pop(r); + + roff_freestr(r); +} + + +void +roff_reset(struct roff *r) +{ + + roff_free1(r); +} + + +void +roff_free(struct roff *r) +{ + + roff_free1(r); + free(r); +} + + +struct roff * +roff_alloc(struct regset *regs, void *data, const mandocmsg msg) +{ + struct roff *r; + + r = mandoc_calloc(1, sizeof(struct roff)); + r->regs = regs; + r->msg = msg; + r->data = data; + r->rstackpos = -1; + + roff_hash_init(); + 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. + */ +static int +roff_res(struct roff *r, char **bufp, size_t *szp, int pos) +{ + 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; + size_t nsz; + char *n; + + /* Search for a leading backslash and save a pointer to it. */ + + cp = *bufp + pos; + while (NULL != (cp = strchr(cp, '\\'))) { + stesc = cp++; + + /* + * The second character must be an asterisk. + * If it isn't, skip it anyway: It is escaped, + * so it can't start another escape sequence. + */ + + if ('\0' == *cp) + return(1); + if ('*' != *cp++) + continue; + + /* + * The third character decides the length + * of the name of the string. + * Save a pointer to the name. + */ + + switch (*cp) { + case ('\0'): + return(1); + case ('('): + cp++; + maxl = 2; + break; + case ('['): + cp++; + maxl = 0; + break; + default: + maxl = 1; + break; + } + stnam = cp; + + /* Advance to the end of the name. */ + + for (i = 0; 0 == maxl || i < maxl; i++, cp++) { + if ('\0' == *cp) + return(1); /* Error. */ + if (0 == maxl && ']' == *cp) + break; + } + + /* + * Retrieve the replacement string; if it is + * undefined, resume searching for escapes. + */ + + res = roff_getstrn(r, stnam, (size_t)i); + + if (NULL == res) { + cp -= maxl ? 1 : 0; + continue; + } + + /* Replace the escape sequence by the string. */ + + nsz = *szp + strlen(res) + 1; + n = mandoc_malloc(nsz); + + strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1)); + strlcat(n, res, nsz); + strlcat(n, cp + (maxl ? 0 : 1), nsz); + + free(*bufp); + + *bufp = n; + *szp = nsz; + return(0); + } + + return(1); +} + + +enum rofferr +roff_parseln(struct roff *r, int ln, char **bufp, + size_t *szp, int pos, int *offs) +{ + enum rofft t; + enum rofferr e; + int ppos; + + /* + * Run the reserved-word filter only if we have some reserved + * words to fill in. + */ + + if (r->first_string && ! roff_res(r, bufp, szp, pos)) + return(ROFF_REPARSE); + + /* + * First, if a scope is open and we're not a macro, pass the + * text through the macro's filter. If a scope isn't open and + * we're not a macro, just let it through. + */ + + if (r->last && ! ROFF_CTL((*bufp)[pos])) { + t = r->last->tok; + assert(roffs[t].text); + e = (*roffs[t].text) + (r, t, bufp, szp, ln, pos, pos, offs); + assert(ROFF_IGN == e || ROFF_CONT == e); + if (ROFF_CONT == e && r->tbl) + return(tbl_read(r->tbl, ln, *bufp, *offs)); + return(e); + } else if ( ! ROFF_CTL((*bufp)[pos])) { + if (r->tbl) + return(tbl_read(r->tbl, ln, *bufp, *offs)); + return(ROFF_CONT); + } + + /* + * If a scope is open, go to the child handler for that macro, + * as it may want to preprocess before doing anything with it. + */ + + if (r->last) { + t = r->last->tok; + assert(roffs[t].sub); + return((*roffs[t].sub) + (r, t, bufp, szp, + ln, pos, pos, offs)); + } + + /* + * Lastly, as we've no scope open, try to look up and execute + * the new macro. If no macro is found, simply return and let + * the compilers handle it. + */ + + ppos = pos; + if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) + return(ROFF_CONT); + + assert(roffs[t].proc); + return((*roffs[t].proc) + (r, t, bufp, szp, + ln, ppos, pos, offs)); +} + + +void +roff_endparse(struct roff *r) +{ + + if (r->last) + (*r->msg)(MANDOCERR_SCOPEEXIT, r->data, + r->last->line, r->last->col, NULL); + + if (r->tbl) { + (*r->msg)(MANDOCERR_SCOPEEXIT, r->data, + r->tbl->line, r->tbl->pos, NULL); + tbl_end(r->tbl); + r->tbl = NULL; + } +} + + +/* + * Parse a roff node's type from the input buffer. This must be in the + * form of ".foo xxx" in the usual way. + */ +static enum rofft +roff_parse(struct roff *r, const char *buf, int *pos) +{ + const char *mac; + size_t maclen; + enum rofft t; + + assert(ROFF_CTL(buf[*pos])); + (*pos)++; + + while (' ' == buf[*pos] || '\t' == buf[*pos]) + (*pos)++; + + if ('\0' == buf[*pos]) + return(ROFF_MAX); + + mac = buf + *pos; + maclen = strcspn(mac, " \\\t\0"); + + t = (r->current_string = roff_getstrn(r, mac, maclen)) + ? ROFF_USERDEF : roff_hash_find(mac, maclen); + + *pos += maclen; + while (buf[*pos] && ' ' == buf[*pos]) + (*pos)++; + + return(t); +} + + +static int +roff_parse_nat(const char *buf, unsigned int *res) +{ + 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); + + *res = (unsigned int)lval; + return(1); +} + + +/* ARGSUSED */ +static enum rofferr +roff_cblock(ROFF_ARGS) +{ + + /* + * A block-close `..' should only be invoked as a child of an + * ignore macro, otherwise raise a warning and just ignore it. + */ + + if (NULL == r->last) { + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + switch (r->last->tok) { + case (ROFF_am): + /* FALLTHROUGH */ + case (ROFF_ami): + /* FALLTHROUGH */ + case (ROFF_am1): + /* FALLTHROUGH */ + case (ROFF_de): + /* ROFF_de1 is remapped to ROFF_de in roff_block(). */ + /* FALLTHROUGH */ + case (ROFF_dei): + /* FALLTHROUGH */ + case (ROFF_ig): + break; + default: + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + if ((*bufp)[pos]) + (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL); + + roffnode_pop(r); + roffnode_cleanscope(r); + return(ROFF_IGN); + +} + + +static void +roffnode_cleanscope(struct roff *r) +{ + + while (r->last) { + if (--r->last->endspan < 0) + break; + roffnode_pop(r); + } +} + + +/* ARGSUSED */ +static enum rofferr +roff_ccond(ROFF_ARGS) +{ + + if (NULL == r->last) { + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + switch (r->last->tok) { + case (ROFF_el): + /* FALLTHROUGH */ + case (ROFF_ie): + /* FALLTHROUGH */ + case (ROFF_if): + break; + default: + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + if (r->last->endspan > -1) { + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + if ((*bufp)[pos]) + (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL); + + roffnode_pop(r); + roffnode_cleanscope(r); + return(ROFF_IGN); +} + + +/* ARGSUSED */ +static enum rofferr +roff_block(ROFF_ARGS) +{ + int sv; + size_t sz; + char *name; + + name = NULL; + + if (ROFF_ig != tok) { + if ('\0' == (*bufp)[pos]) { + (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + /* + * Re-write `de1', since we don't really care about + * groff's strange compatibility mode, into `de'. + */ + + if (ROFF_de1 == tok) + tok = ROFF_de; + if (ROFF_de == tok) + name = *bufp + pos; + else + (*r->msg)(MANDOCERR_REQUEST, r->data, ln, ppos, + roffs[tok].name); + + while ((*bufp)[pos] && ' ' != (*bufp)[pos]) + pos++; + + while (' ' == (*bufp)[pos]) + (*bufp)[pos++] = '\0'; + } + + roffnode_push(r, tok, name, ln, ppos); + + /* + * At the beginning of a `de' macro, clear the existing string + * with the same name, if there is one. New content will be + * added from roff_block_text() in multiline mode. + */ + + if (ROFF_de == tok) + roff_setstr(r, name, "", 0); + + if ('\0' == (*bufp)[pos]) + return(ROFF_IGN); + + /* If present, process the custom end-of-line marker. */ + + sv = pos; + while ((*bufp)[pos] && + ' ' != (*bufp)[pos] && + '\t' != (*bufp)[pos]) + pos++; + + /* + * Note: groff does NOT like escape characters in the input. + * Instead of detecting this, we're just going to let it fly and + * to hell with it. + */ + + assert(pos > sv); + sz = (size_t)(pos - sv); + + if (1 == sz && '.' == (*bufp)[sv]) + return(ROFF_IGN); + + r->last->end = mandoc_malloc(sz + 1); + + memcpy(r->last->end, *bufp + sv, sz); + r->last->end[(int)sz] = '\0'; + + if ((*bufp)[pos]) + (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL); + + return(ROFF_IGN); +} + + +/* ARGSUSED */ +static enum rofferr +roff_block_sub(ROFF_ARGS) +{ + enum rofft t; + int i, j; + + /* + * First check whether a custom macro exists at this level. If + * it does, then check against it. This is some of groff's + * stranger behaviours. If we encountered a custom end-scope + * tag and that tag also happens to be a "real" macro, then we + * need to try interpreting it again as a real macro. If it's + * not, then return ignore. Else continue. + */ + + if (r->last->end) { + i = pos + 1; + while (' ' == (*bufp)[i] || '\t' == (*bufp)[i]) + i++; + + for (j = 0; r->last->end[j]; j++, i++) + if ((*bufp)[i] != r->last->end[j]) + break; + + if ('\0' == r->last->end[j] && + ('\0' == (*bufp)[i] || + ' ' == (*bufp)[i] || + '\t' == (*bufp)[i])) { + roffnode_pop(r); + roffnode_cleanscope(r); + + if (ROFF_MAX != roff_parse(r, *bufp, &pos)) + return(ROFF_RERUN); + return(ROFF_IGN); + } + } + + /* + * If we have no custom end-query or lookup failed, then try + * pulling it out of the hashtable. + */ + + ppos = pos; + t = roff_parse(r, *bufp, &pos); + + /* + * Macros other than block-end are only significant + * in `de' blocks; elsewhere, simply throw them away. + */ + if (ROFF_cblock != t) { + if (ROFF_de == tok) + roff_setstr(r, r->last->name, *bufp + ppos, 1); + return(ROFF_IGN); + } + + assert(roffs[t].proc); + return((*roffs[t].proc)(r, t, bufp, szp, + ln, ppos, pos, offs)); +} + + +/* ARGSUSED */ +static enum rofferr +roff_block_text(ROFF_ARGS) +{ + + if (ROFF_de == tok) + roff_setstr(r, r->last->name, *bufp + pos, 1); + + return(ROFF_IGN); +} + + +/* ARGSUSED */ +static enum rofferr +roff_cond_sub(ROFF_ARGS) +{ + enum rofft t; + enum roffrule rr; + + ppos = pos; + rr = r->last->rule; + + /* + * Clean out scope. If we've closed ourselves, then don't + * continue. + */ + + roffnode_cleanscope(r); + + if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) { + if ('\\' == (*bufp)[pos] && '}' == (*bufp)[pos + 1]) + return(roff_ccond + (r, ROFF_ccond, bufp, szp, + ln, pos, pos + 2, offs)); + return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + } + + /* + * A denied conditional must evaluate its children if and only + * if they're either structurally required (such as loops and + * conditionals) or a closing macro. + */ + if (ROFFRULE_DENY == rr) + if ( ! (ROFFMAC_STRUCT & roffs[t].flags)) + if (ROFF_ccond != t) + return(ROFF_IGN); + + assert(roffs[t].proc); + return((*roffs[t].proc)(r, t, bufp, szp, + ln, ppos, pos, offs)); +} + + +/* ARGSUSED */ +static enum rofferr +roff_cond_text(ROFF_ARGS) +{ + char *ep, *st; + enum roffrule rr; + + rr = r->last->rule; + + /* + * We display the value of the text if out current evaluation + * scope permits us to do so. + */ + + /* FIXME: use roff_ccond? */ + + st = &(*bufp)[pos]; + if (NULL == (ep = strstr(st, "\\}"))) { + roffnode_cleanscope(r); + return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + } + + if (ep == st || (ep > st && '\\' != *(ep - 1))) + roffnode_pop(r); + + roffnode_cleanscope(r); + return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); +} + + +static enum roffrule +roff_evalcond(const char *v, int *pos) +{ + + switch (v[*pos]) { + case ('n'): + (*pos)++; + return(ROFFRULE_ALLOW); + case ('e'): + /* FALLTHROUGH */ + case ('o'): + /* FALLTHROUGH */ + case ('t'): + (*pos)++; + return(ROFFRULE_DENY); + default: + break; + } + + while (v[*pos] && ' ' != v[*pos]) + (*pos)++; + return(ROFFRULE_DENY); +} + +/* ARGSUSED */ +static enum rofferr +roff_line_ignore(ROFF_ARGS) +{ + + return(ROFF_IGN); +} + +/* ARGSUSED */ +static enum rofferr +roff_line_error(ROFF_ARGS) +{ + + (*r->msg)(MANDOCERR_REQUEST, r->data, ln, ppos, roffs[tok].name); + return(ROFF_IGN); +} + +/* ARGSUSED */ +static enum rofferr +roff_cond(ROFF_ARGS) +{ + int sv; + enum roffrule rule; + + /* Stack overflow! */ + + if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) { + (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL); + return(ROFF_ERR); + } + + /* First, evaluate the conditional. */ + + if (ROFF_el == tok) { + /* + * An `.el' will get the value of the current rstack + * entry set in prior `ie' calls or defaults to DENY. + */ + if (r->rstackpos < 0) + rule = ROFFRULE_DENY; + else + rule = r->rstack[r->rstackpos]; + } else + rule = roff_evalcond(*bufp, &pos); + + sv = pos; + + while (' ' == (*bufp)[pos]) + pos++; + + /* + * Roff is weird. If we have just white-space after the + * conditional, it's considered the BODY and we exit without + * really doing anything. Warn about this. It's probably + * wrong. + */ + + if ('\0' == (*bufp)[pos] && sv != pos) { + (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL); + return(ROFF_IGN); + } + + roffnode_push(r, tok, NULL, ln, ppos); + + r->last->rule = rule; + + if (ROFF_ie == tok) { + /* + * An if-else will put the NEGATION of the current + * evaluated conditional into the stack. + */ + r->rstackpos++; + if (ROFFRULE_DENY == r->last->rule) + r->rstack[r->rstackpos] = ROFFRULE_ALLOW; + else + r->rstack[r->rstackpos] = ROFFRULE_DENY; + } + + /* If the parent has false as its rule, then so do we. */ + + if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) + r->last->rule = ROFFRULE_DENY; + + /* + * Determine scope. If we're invoked with "\{" trailing the + * conditional, then we're in a multiline scope. Else our scope + * expires on the next line. + */ + + r->last->endspan = 1; + + if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) { + r->last->endspan = -1; + pos += 2; + } + + /* + * If there are no arguments on the line, the next-line scope is + * assumed. + */ + + if ('\0' == (*bufp)[pos]) + return(ROFF_IGN); + + /* Otherwise re-run the roff parser after recalculating. */ + + *offs = pos; + return(ROFF_RERUN); +} + + +/* ARGSUSED */ +static enum rofferr +roff_ds(ROFF_ARGS) +{ + char *name, *string; + + /* + * A symbol is named by the first word following the macro + * invocation up to a space. Its value is anything after the + * name's trailing whitespace and optional double-quote. Thus, + * + * [.ds foo "bar " ] + * + * will have `bar " ' as its value. + */ + + name = *bufp + pos; + if ('\0' == *name) + return(ROFF_IGN); + + string = name; + /* Read until end of name. */ + while (*string && ' ' != *string) + string++; + + /* Nil-terminate name. */ + if (*string) + *(string++) = '\0'; + + /* Read past spaces. */ + while (*string && ' ' == *string) + string++; + + /* Read passed initial double-quote. */ + if (*string && '"' == *string) + string++; + + /* The rest is the value. */ + roff_setstr(r, name, string, 0); + return(ROFF_IGN); +} + + +/* ARGSUSED */ +static enum rofferr +roff_nr(ROFF_ARGS) +{ + const char *key, *val; + struct reg *rg; + + key = &(*bufp)[pos]; + rg = r->regs->regs; + + /* Parse register request. */ + while ((*bufp)[pos] && ' ' != (*bufp)[pos]) + pos++; + + /* + * Set our nil terminator. Because this line is going to be + * ignored anyway, we can munge it as we please. + */ + if ((*bufp)[pos]) + (*bufp)[pos++] = '\0'; + + /* Skip whitespace to register token. */ + while ((*bufp)[pos] && ' ' == (*bufp)[pos]) + pos++; + + val = &(*bufp)[pos]; + + /* Process register token. */ + + if (0 == strcmp(key, "nS")) { + rg[(int)REG_nS].set = 1; + if ( ! roff_parse_nat(val, &rg[(int)REG_nS].v.u)) + rg[(int)REG_nS].v.u = 0; + } + + return(ROFF_IGN); +} + +/* ARGSUSED */ +static enum rofferr +roff_TE(ROFF_ARGS) +{ + + if (NULL == r->tbl) + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + else + tbl_end(r->tbl); + + r->tbl = NULL; + return(ROFF_IGN); +} + +/* ARGSUSED */ +static enum rofferr +roff_T_(ROFF_ARGS) +{ + + if (NULL == r->tbl) + (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL); + else + tbl_restart(ppos, ln, r->tbl); + + return(ROFF_IGN); +} + +/* ARGSUSED */ +static enum rofferr +roff_TS(ROFF_ARGS) +{ + struct tbl_node *t; + + if (r->tbl) { + (*r->msg)(MANDOCERR_SCOPEBROKEN, r->data, ln, ppos, NULL); + tbl_end(r->tbl); + } + + t = tbl_alloc(ppos, ln, r->data, r->msg); + + if (r->last_tbl) + r->last_tbl->next = t; + else + r->first_tbl = r->last_tbl = t; + + r->tbl = r->last_tbl = t; + return(ROFF_IGN); +} + +/* ARGSUSED */ +static enum rofferr +roff_so(ROFF_ARGS) +{ + char *name; + + (*r->msg)(MANDOCERR_SO, r->data, ln, ppos, NULL); + + /* + * Handle `so'. Be EXTREMELY careful, as we shouldn't be + * opening anything that's not in our cwd or anything beneath + * it. Thus, explicitly disallow traversing up the file-system + * or using absolute paths. + */ + + name = *bufp + pos; + if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) { + (*r->msg)(MANDOCERR_SOPATH, r->data, ln, pos, NULL); + return(ROFF_ERR); + } + + *offs = pos; + return(ROFF_SO); +} + +/* ARGSUSED */ +static enum rofferr +roff_userdef(ROFF_ARGS) +{ + const char *arg[9]; + char *cp, *n1, *n2; + int i; + + /* + * Collect pointers to macro argument strings + * and null-terminate them. + */ + cp = *bufp + pos; + for (i = 0; i < 9; i++) + arg[i] = '\0' == *cp ? "" : + mandoc_getarg(&cp, r->msg, r->data, ln, &pos); + + /* + * Expand macro arguments. + */ + *szp = 0; + n1 = cp = mandoc_strdup(r->current_string); + while (NULL != (cp = strstr(cp, "\\$"))) { + i = cp[2] - '1'; + if (0 > i || 8 < i) { + /* Not an argument invocation. */ + cp += 2; + continue; + } + + *szp = strlen(n1) - 3 + strlen(arg[i]) + 1; + n2 = mandoc_malloc(*szp); + + strlcpy(n2, n1, (size_t)(cp - n1 + 1)); + strlcat(n2, arg[i], *szp); + strlcat(n2, cp + 3, *szp); + + cp = n2 + (cp - n1); + free(n1); + n1 = n2; + } + + /* + * Replace the macro invocation + * by the expanded macro. + */ + free(*bufp); + *bufp = n1; + if (0 == *szp) + *szp = strlen(*bufp) + 1; + + return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ? + ROFF_REPARSE : ROFF_APPEND); +} + +/* + * Store *string into the user-defined string called *name. + * In multiline mode, append to an existing entry and append '\n'; + * else replace the existing entry, if there is one. + * To clear an existing entry, call with (*r, *name, NULL, 0). + */ +static void +roff_setstr(struct roff *r, const char *name, const char *string, + int multiline) +{ + struct roffstr *n; + char *c; + size_t oldch, newch; + + /* Search for an existing string with the same name. */ + n = r->first_string; + while (n && strcmp(name, n->name)) + 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; + } else if (0 == multiline) { + /* In multiline mode, append; else replace. */ + free(n->string); + n->string = NULL; + } + + if (NULL == string) + return; + + /* + * One additional byte for the '\n' in multiline mode, + * and one for the terminating '\0'. + */ + newch = strlen(string) + (multiline ? 2 : 1); + if (NULL == n->string) { + n->string = mandoc_malloc(newch); + *n->string = '\0'; + oldch = 0; + } else { + oldch = strlen(n->string); + n->string = mandoc_realloc(n->string, oldch + newch); + } + + /* Skip existing content in the destination buffer. */ + c = n->string + oldch; + + /* Append new content to the destination buffer. */ + while (*string) { + /* + * Rudimentary roff copy mode: + * Handle escaped backslashes. + */ + if ('\\' == *string && '\\' == *(string + 1)) + string++; + *c++ = *string++; + } + + /* Append terminating bytes. */ + if (multiline) + *c++ = '\n'; + *c = '\0'; +} + + +static const char * +roff_getstrn(const struct roff *r, const char *name, size_t len) +{ + const struct roffstr *n; + + n = r->first_string; + while (n && (strncmp(name, n->name, len) || '\0' != n->name[(int)len])) + n = n->next; + + return(n ? n->string : NULL); +} + + +static void +roff_freestr(struct roff *r) +{ + struct roffstr *n, *nn; + + for (n = r->first_string; n; n = nn) { + free(n->name); + free(n->string); + nn = n->next; + free(n); + } + + r->first_string = NULL; +} + +const struct tbl_span * +roff_span(const struct roff *r) +{ + + return(r->tbl ? tbl_span(r->tbl) : NULL); +} diff --git a/commands/mdocml/roff.h b/usr.bin/mdocml/dist/roff.h similarity index 74% rename from commands/mdocml/roff.h rename to usr.bin/mdocml/dist/roff.h index 84859ec13..d7c35831a 100644 --- a/commands/mdocml/roff.h +++ b/usr.bin/mdocml/dist/roff.h @@ -1,4 +1,4 @@ -/* $Id: roff.h,v 1.15 2010/05/17 00:06:36 kristaps Exp $ */ +/* $Vendor-Id: roff.h,v 1.22 2011/01/01 16:18:39 kristaps Exp $ */ /* * Copyright (c) 2010 Kristaps Dzonsons * @@ -20,7 +20,11 @@ enum rofferr { ROFF_CONT, /* continue processing line */ ROFF_RERUN, /* re-run roff interpreter with offset */ + ROFF_APPEND, /* re-run main parser, appending next line */ + ROFF_REPARSE, /* re-run main parser on the result */ + ROFF_SO, /* include another file */ ROFF_IGN, /* ignore current line */ + ROFF_TBL, /* a table row was successfully parsed */ ROFF_ERR /* badness: puke and stop */ }; @@ -29,11 +33,12 @@ __BEGIN_DECLS struct roff; void roff_free(struct roff *); -struct roff *roff_alloc(mandocmsg, void *); +struct roff *roff_alloc(struct regset *, void *, mandocmsg); void roff_reset(struct roff *); enum rofferr roff_parseln(struct roff *, int, char **, size_t *, int, int *); -int roff_endparse(struct roff *); +void roff_endparse(struct roff *); +const struct tbl_span *roff_span(const struct roff *); __END_DECLS diff --git a/commands/mdocml/st.c b/usr.bin/mdocml/dist/st.c similarity index 94% rename from commands/mdocml/st.c rename to usr.bin/mdocml/dist/st.c index 5c6798e58..05f28a38a 100644 --- a/commands/mdocml/st.c +++ b/usr.bin/mdocml/dist/st.c @@ -1,4 +1,4 @@ -/* $Id: st.c,v 1.8 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: st.c,v 1.8 2010/06/19 20:46:28 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/st.in b/usr.bin/mdocml/dist/st.in similarity index 95% rename from commands/mdocml/st.in rename to usr.bin/mdocml/dist/st.in index 113ccbacb..5ca9a64ed 100644 --- a/commands/mdocml/st.in +++ b/usr.bin/mdocml/dist/st.in @@ -1,6 +1,6 @@ -/* $Id: st.in,v 1.13 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: st.in,v 1.15 2010/07/31 23:52:58 schwarze Exp $ */ /* - * Copyright (c) 2009 Kristaps Dzonsons + * Copyright (c) 2009, 2010 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 @@ -22,6 +22,8 @@ * the formatted output string. * * Be sure to escape strings. + * + * REMEMBER TO ADD NEW STANDARDS TO MDOC.7! */ LINE("-p1003.1-88", "IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)") diff --git a/usr.bin/mdocml/dist/tbl.7 b/usr.bin/mdocml/dist/tbl.7 new file mode 100644 index 000000000..0bd343ee0 --- /dev/null +++ b/usr.bin/mdocml/dist/tbl.7 @@ -0,0 +1,322 @@ +.\" $Vendor-Id: tbl.7,v 1.4 2011/01/07 14:59:52 kristaps Exp $ +.\" +.\" Copyright (c) 2010 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 January 7, 2011 +.Dt TBL 7 +.Os +.Sh NAME +.Nm tbl +.Nd tbl language reference for mandoc +.Sh DESCRIPTION +The +.Nm tbl +language is a table-formatting language. +It is used within +.Xr mdoc 7 +and +.Xr man 7 +.Ux +manual pages. +This manual describes the subset of the +.Nm +language accepted by the +.Xr mandoc 1 +utility. +.Pp +Tables within +.Xr mdoc 7 +or +.Xr man 7 +are enclosed by the +.Sq TS +and +.Sq TE +macro tags, whose precise syntax is documented in +.Xr roff 7 . +Tables consist of a series of options on a single line, followed by the +table layout, followed by data. +.Pp +For example, the following creates a boxed table with digits centered in +the cells. +.Bd -literal -offset indent +\&.TS +tab(:) box; +c5 c5 c5. +1:2:3 +4:5:6 +\&.TE +.Ed +.Pp +When formatted, the following output is produced: +.Bd -filled -offset indent -compact +.TS +tab(:) box; +c5 c5 c5. +1:2:3 +4:5:6 +.TE +.Ed +.Sh TABLE STRUCTURE +Tables are enclosed by the +.Sq TS +and +.Sq TE +.Xr roff 7 +macros. +A table consists of an optional single line of table +.Sx Options +terminated by a semicolon, followed by one or more lines of +.Sx Layout +specifications terminated by a period, then +.Sx Data . +All input must be 7-bit ASCII. +Example: +.Bd -literal -offset indent +\&.TS +box tab(:); +c | c +| c | c. +1:2 +3:4 +\&.TE +.Ed +.Pp +Table data is +.Em pre-processed , +that is, data rows are parsed then inserted into the underlying stream +of input data. +This allows data rows to be interspersed by arbitrary +.Xr roff 7 , +.Xr mdoc 7 , +and +.Xr man 7 +macros such as +.Bd -literal -offset indent +\&.TS +tab(:); +c c c. +1:2:3 +\&.Ao +3:2:1 +\&.Ac +\&.TE +.Ed +.Pp +in the case of +.Xr mdoc 7 +or +.Bd -literal -offset indent +\&.TS +tab(:); +c c c. +\&.ds ab 2 +1:\e*(ab:3 +\&.I +3:2:1 +\&.TE +.Ed +.Pp +in the case of +.Xr man 7 . +.Ss Options +The first line of a table consists of space-separated option keys and +modifiers terminated by a semicolon. +If the first line does not have a terminating semicolon, it is assumed +that no options are specified and instead a +.Sx Layout +is processed. +Some options accept arguments enclosed by parenthesis. +The following case-insensitive options are available: +.Bl -tag -width Ds +.It Cm center +This option is not supported by +.Xr mandoc 1 . +This may also be invoked with +.Cm centre . +.It Cm delim +Accepts a two-character argument. +This option is not supported by +.Xr mandoc 1 . +.It Cm expand +This option is not supported by +.Xr mandoc 1 . +.It Cm box +Draw a single-line box around the table. +This may also be invoked with +.Cm frame . +.It Cm doublebox +Draw a double-line box around the table. +This may also be invoked with +.Cm doubleframe . +.It Cm allbox +This option is not supported by +.Xr mandoc 1 . +.It Cm tab +Accepts a single-character argument. +This character is used as a delimiter between data cells, which otherwise +defaults to the tab character. +.It Cm linesize +Accepts a natural number (all digits). +This option is not supported by +.Xr mandoc 1 . +.It Cm nokeep +This option is not supported by +.Xr mandoc 1 . +.It Cm decimalpoint +Accepts a single-character argument. +This character will be used as the decimal point with the +.Cm n +layout key. +This option is not supported by +.Xr mandoc 1 . +.It Cm nospaces +This option is not supported by +.Xr mandoc 1 . +.El +.Ss Layout +The table layout follows +.Sx Options +or a +.Sq \&T& +macro invocation. +Layout specifies how data rows are displayed on output. +Each layout line corresponds to a line of data; the last layout line +applies to all remaining data lines. +Layout lines may also be separated by a comma. +Each layout cell consists of one of the following case-insensitive keys: +.Bl -tag -width Ds +.It Cm c +Centre a literal string within its column. +.It Cm r +Right-justify a literal string within its column. +.It Cm l +Left-justify a literal string within its column. +.It Cm n +Justify a number around its decimal point. +If the decimal point is not found on the number, it's assumed to trail +the number. +.It Cm s +This option is not supported by +.Xr mandoc 1 . +.It Cm a +This option is not supported by +.Xr mandoc 1 . +.It Cm ^ +This option is not supported by +.Xr mandoc 1 . +.It Cm \- +Replace the data cell (its contents will be lost) with a single +horizontal line. +This may also be invoked with +.Cm _ . +.It Cm = +Replace the data cell (its contents will be lost) with a double +horizontal line. +.It Cm \(ba +Emit a vertical bar instead of data. +.It Cm \(ba\(ba +Emit a double-vertical bar instead of data. +.El +.Pp +For example, the following layout specifies a centre-justified column of +minimum width 10, followed by vertical bar, followed by a left-justified +column of minimum width 10, another vertical bar, then a column +justified about the decimal point in numbers: +.Pp +.Dl c10 | l10 | n +.Pp +Keys may be followed by a set of modifiers. +A modifier is either a modifier key or a natural number for specifying +spacing. +The following case-insensitive modifier keys are available: +.Cm z , +.Cm u , +.Cm e , +.Cm t , +.Cm d , +.Cm f , +.Cm b , +.Cm i , +.Cm b , +and +.Cm i . +All of these are ignored by +.Xr mandoc 1 . +.Ss Data +The data section follows the last layout row. +By default, cells in a data section are delimited by a tab. +This behaviour may be changed with the +.Cm tab +option. +If +.Cm _ +or +.Cm = +is specified, a single or double line, respectively, is drawn across the +data field. +If +.Cm \e- +or +.Cm \e= +is specified, a line is drawn within the data field (i.e. terminating +within the cell and not draw to the border). +If the last cell of a line is +.Cm T{ , +all subsequent lines are included as part of the cell until +.Cm T} +is specified as its own data cell. +It may then be followed by a tab +.Pq or as designated by Cm tab +or an end-of-line to terminate the row. +.Sh COMPATIBILITY +This section documents compatibility between mandoc and other +.Nm +implementations, at this time limited to GNU tbl. +.Pp +.Bl -dash -compact +.It +In GNU tbl, comments and macros are disallowed prior to the data block +of a table. +The +.Xr mandoc 1 +implementation allows them. +.El +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr man 7 , +.Xr mandoc_char 7 , +.Xr mdoc 7 , +.Xr roff 7 +.Rs +.%A M. E. Lesk +.%T Tbl\(emA Program to Format Tables +.%D June 11, 1976 +.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. +.Sh AUTHORS +This partial +.Nm +reference was written by +.An Kristaps Dzonsons Aq kristaps@bsd.lv . diff --git a/usr.bin/mdocml/dist/tbl.c b/usr.bin/mdocml/dist/tbl.c new file mode 100644 index 000000000..34bcfb153 --- /dev/null +++ b/usr.bin/mdocml/dist/tbl.c @@ -0,0 +1,159 @@ +/* $Vendor-Id: tbl.c,v 1.21 2011/01/04 15:02:00 kristaps Exp $ */ +/* + * Copyright (c) 2009, 2010 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. + */ +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "roff.h" +#include "libmandoc.h" +#include "libroff.h" + +enum rofferr +tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs) +{ + int len; + const char *cp; + + cp = &p[offs]; + len = (int)strlen(cp); + + /* + * If we're in the options section and we don't have a + * terminating semicolon, assume we've moved directly into the + * layout section. No need to report a warning: this is, + * apparently, standard behaviour. + */ + + if (TBL_PART_OPTS == tbl->part && len) + if (';' != cp[len - 1]) + tbl->part = TBL_PART_LAYOUT; + + /* Now process each logical section of the table. */ + + switch (tbl->part) { + case (TBL_PART_OPTS): + return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR); + case (TBL_PART_LAYOUT): + return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR); + case (TBL_PART_CDATA): + return(tbl_cdata(tbl, ln, p) ? ROFF_TBL : ROFF_IGN); + default: + break; + } + + /* + * This only returns zero if the line is empty, so we ignore it + * and continue on. + */ + return(tbl_data(tbl, ln, p) ? ROFF_TBL : ROFF_IGN); +} + +struct tbl_node * +tbl_alloc(int pos, int line, void *data, const mandocmsg msg) +{ + struct tbl_node *p; + + p = mandoc_calloc(1, sizeof(struct tbl_node)); + p->line = line; + p->pos = pos; + p->data = data; + p->msg = msg; + p->part = TBL_PART_OPTS; + p->opts.tab = '\t'; + p->opts.linesize = 12; + p->opts.decimal = '.'; + return(p); +} + +void +tbl_free(struct tbl_node *p) +{ + struct tbl_row *rp; + struct tbl_cell *cp; + struct tbl_span *sp; + struct tbl_dat *dp; + struct tbl_head *hp; + + while (NULL != (rp = p->first_row)) { + p->first_row = rp->next; + while (rp->first) { + cp = rp->first; + rp->first = cp->next; + free(cp); + } + free(rp); + } + + while (NULL != (sp = p->first_span)) { + p->first_span = sp->next; + while (sp->first) { + dp = sp->first; + sp->first = dp->next; + if (dp->string) + free(dp->string); + free(dp); + } + free(sp); + } + + while (NULL != (hp = p->first_head)) { + p->first_head = hp->next; + free(hp); + } + + free(p); +} + +void +tbl_restart(int line, int pos, struct tbl_node *tbl) +{ + if (TBL_PART_CDATA == tbl->part) + TBL_MSG(tbl, MANDOCERR_TBLBLOCK, tbl->line, tbl->pos); + + tbl->part = TBL_PART_LAYOUT; + tbl->line = line; + tbl->pos = pos; + + if (NULL == tbl->first_span || NULL == tbl->first_span->first) + TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos); +} + +const struct tbl_span * +tbl_span(const struct tbl_node *tbl) +{ + + assert(tbl); + return(tbl->last_span); +} + +void +tbl_end(struct tbl_node *tbl) +{ + + if (NULL == tbl->first_span || NULL == tbl->first_span->first) + TBL_MSG(tbl, MANDOCERR_TBLNODATA, tbl->line, tbl->pos); + + if (tbl->last_span) + tbl->last_span->flags |= TBL_SPAN_LAST; + + if (TBL_PART_CDATA == tbl->part) + TBL_MSG(tbl, MANDOCERR_TBLBLOCK, tbl->line, tbl->pos); +} + diff --git a/usr.bin/mdocml/dist/tbl_data.c b/usr.bin/mdocml/dist/tbl_data.c new file mode 100644 index 000000000..53d198985 --- /dev/null +++ b/usr.bin/mdocml/dist/tbl_data.c @@ -0,0 +1,218 @@ +/* $Vendor-Id: tbl_data.c,v 1.15 2011/01/09 23:14:41 kristaps Exp $ */ +/* + * Copyright (c) 2009, 2010 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 "mandoc.h" +#include "libmandoc.h" +#include "libroff.h" + +static int data(struct tbl_node *, struct tbl_span *, + int, const char *, int *); + +static int +data(struct tbl_node *tbl, struct tbl_span *dp, + int ln, const char *p, int *pos) +{ + struct tbl_dat *dat; + struct tbl_cell *cp; + int sv; + + cp = NULL; + if (dp->last && dp->last->layout) + cp = dp->last->layout->next; + else if (NULL == dp->last) + cp = dp->layout->first; + + /* + * Skip over spanners and vertical lines to data formats, since + * we want to match data with data layout cells in the header. + */ + + while (cp && (TBL_CELL_VERT == cp->pos || + TBL_CELL_DVERT == cp->pos || + TBL_CELL_SPAN == cp->pos)) + cp = cp->next; + + dat = mandoc_calloc(1, sizeof(struct tbl_dat)); + dat->layout = cp; + dat->pos = TBL_DATA_NONE; + + if (NULL == dat->layout) + TBL_MSG(tbl, MANDOCERR_TBLEXTRADAT, ln, *pos); + + if (dp->last) { + dp->last->next = dat; + dp->last = dat; + } else + dp->last = dp->first = dat; + + sv = *pos; + while (p[*pos] && p[*pos] != tbl->opts.tab) + (*pos)++; + + /* + * Check for a continued-data scope opening. This consists of a + * trailing `T{' at the end of the line. Subsequent lines, + * until a standalone `T}', are included in our cell. + */ + + if (*pos - sv == 2 && 'T' == p[sv] && '{' == p[sv + 1]) { + tbl->part = TBL_PART_CDATA; + return(0); + } + + dat->string = mandoc_malloc(*pos - sv + 1); + memcpy(dat->string, &p[sv], *pos - sv); + dat->string[*pos - sv] = '\0'; + + if (p[*pos]) + (*pos)++; + + if ( ! strcmp(dat->string, "_")) + dat->pos = TBL_DATA_HORIZ; + else if ( ! strcmp(dat->string, "=")) + dat->pos = TBL_DATA_DHORIZ; + else if ( ! strcmp(dat->string, "\\_")) + dat->pos = TBL_DATA_NHORIZ; + else if ( ! strcmp(dat->string, "\\=")) + dat->pos = TBL_DATA_NDHORIZ; + else + dat->pos = TBL_DATA_DATA; + + if (NULL == dat->layout) + return(1); + + if (TBL_CELL_HORIZ == dat->layout->pos || + TBL_CELL_DHORIZ == dat->layout->pos) + if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string) + TBL_MSG(tbl, MANDOCERR_TBLIGNDATA, ln, sv); + + return(1); +} + +/* ARGSUSED */ +int +tbl_cdata(struct tbl_node *tbl, int ln, const char *p) +{ + struct tbl_dat *dat; + size_t sz; + int pos; + + pos = 0; + + dat = tbl->last_span->last; + dat->pos = TBL_DATA_DATA; + + if (p[pos] == 'T' && p[pos + 1] == '}') { + pos += 2; + if (p[pos] == tbl->opts.tab) { + tbl->part = TBL_PART_DATA; + pos++; + return(data(tbl, tbl->last_span, ln, p, &pos)); + } else if ('\0' == p[pos]) { + tbl->part = TBL_PART_DATA; + return(1); + } + + /* Fallthrough: T} is part of a word. */ + } + + if (dat->string) { + sz = strlen(p) + strlen(dat->string) + 2; + dat->string = mandoc_realloc(dat->string, sz); + strlcat(dat->string, " ", sz); + strlcat(dat->string, p, sz); + } else + dat->string = mandoc_strdup(p); + + return(0); +} + +int +tbl_data(struct tbl_node *tbl, int ln, const char *p) +{ + struct tbl_span *dp; + struct tbl_row *rp; + int pos; + + pos = 0; + + if ('\0' == p[pos]) { + TBL_MSG(tbl, MANDOCERR_TBL, ln, pos); + return(0); + } + + /* + * Choose a layout row: take the one following the last parsed + * span's. If that doesn't exist, use the last parsed span's. + * If there's no last parsed span, use the first row. Lastly, + * if the last span was a horizontal line, use the same layout + * (it doesn't "consume" the layout). + * + * In the end, this can be NULL! + */ + + if (tbl->last_span) { + assert(tbl->last_span->layout); + if (tbl->last_span->pos == TBL_SPAN_DATA) + rp = tbl->last_span->layout->next; + else + rp = tbl->last_span->layout; + if (NULL == rp) + rp = tbl->last_span->layout; + } else + rp = tbl->first_row; + + dp = mandoc_calloc(1, sizeof(struct tbl_span)); + dp->tbl = &tbl->opts; + dp->layout = rp; + dp->head = tbl->first_head; + + if (tbl->last_span) { + tbl->last_span->next = dp; + tbl->last_span = dp; + } else { + tbl->last_span = tbl->first_span = dp; + dp->flags |= TBL_SPAN_FIRST; + } + + if ( ! strcmp(p, "_")) { + dp->pos = TBL_SPAN_HORIZ; + return(1); + } else if ( ! strcmp(p, "=")) { + dp->pos = TBL_SPAN_DHORIZ; + return(1); + } + + dp->pos = TBL_SPAN_DATA; + + /* This returns 0 when TBL_PART_CDATA is entered. */ + + while ('\0' != p[pos]) + if ( ! data(tbl, dp, ln, p, &pos)) + return(0); + + return(1); +} diff --git a/usr.bin/mdocml/dist/tbl_html.c b/usr.bin/mdocml/dist/tbl_html.c new file mode 100644 index 000000000..85c669895 --- /dev/null +++ b/usr.bin/mdocml/dist/tbl_html.c @@ -0,0 +1,126 @@ +/* $Vendor-Id: tbl_html.c,v 1.5 2011/01/06 12:31:39 kristaps Exp $ */ +/* + * Copyright (c) 2009 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 size_t html_tbl_len(size_t, void *); +static size_t html_tbl_strlen(const char *, void *); + +/* ARGSUSED */ +static size_t +html_tbl_len(size_t sz, void *arg) +{ + + return(sz); +} + +/* ARGSUSED */ +static size_t +html_tbl_strlen(const char *p, void *arg) +{ + + return(strlen(p)); +} + +void +print_tbl(struct html *h, const struct tbl_span *sp) +{ + const struct tbl_head *hp; + const struct tbl_dat *dp; + struct tag *tt; + struct htmlpair tag; + struct roffsu su; + struct roffcol *col; + + /* Inhibit printing of spaces: we do padding ourselves. */ + + h->flags |= HTML_NONOSPACE; + h->flags |= HTML_NOSPACE; + + /* First pass: calculate widths. */ + + if (TBL_SPAN_FIRST & sp->flags) { + h->tbl.len = html_tbl_len; + h->tbl.slen = html_tbl_strlen; + tblcalc(&h->tbl, sp); + } + + switch (sp->pos) { + case (TBL_SPAN_HORIZ): + /* FALLTHROUGH */ + case (TBL_SPAN_DHORIZ): + break; + default: + PAIR_CLASS_INIT(&tag, "tbl"); + print_otag(h, TAG_TABLE, 1, &tag); + print_otag(h, TAG_TR, 0, NULL); + + /* Iterate over template headers. */ + + dp = sp->first; + for (hp = sp->head; hp; hp = hp->next) { + switch (hp->pos) { + case (TBL_HEAD_VERT): + /* FALLTHROUGH */ + case (TBL_HEAD_DVERT): + continue; + case (TBL_HEAD_DATA): + break; + } + + /* + * For the time being, use the simplest possible + * table styling: setting the widths of data + * columns. + */ + + col = &h->tbl.cols[hp->ident]; + SCALE_HS_INIT(&su, col->width); + bufcat_su(h, "width", &su); + PAIR_STYLE_INIT(&tag, h); + tt = print_otag(h, TAG_TD, 1, &tag); + + if (dp && dp->string) + print_text(h, dp->string); + if (dp) + dp = dp->next; + + print_tagq(h, tt); + } + break; + } + + h->flags &= ~HTML_NONOSPACE; + + /* Close out column specifiers on the last span. */ + + if (TBL_SPAN_LAST & sp->flags) { + assert(h->tbl.cols); + free(h->tbl.cols); + h->tbl.cols = NULL; + } +} diff --git a/usr.bin/mdocml/dist/tbl_layout.c b/usr.bin/mdocml/dist/tbl_layout.c new file mode 100644 index 000000000..6dea8418d --- /dev/null +++ b/usr.bin/mdocml/dist/tbl_layout.c @@ -0,0 +1,408 @@ +/* $Vendor-Id: tbl_layout.c,v 1.13 2011/01/09 05:38:23 joerg Exp $ */ +/* + * Copyright (c) 2009, 2010 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. + */ +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "libmandoc.h" +#include "libroff.h" + +struct tbl_phrase { + char name; + enum tbl_cellt key; +}; + +/* + * FIXME: we can make this parse a lot nicer by, when an error is + * encountered in a layout key, bailing to the next key (i.e. to the + * next whitespace then continuing). + */ + +#define KEYS_MAX 11 + +static const struct tbl_phrase keys[KEYS_MAX] = { + { 'c', TBL_CELL_CENTRE }, + { 'r', TBL_CELL_RIGHT }, + { 'l', TBL_CELL_LEFT }, + { 'n', TBL_CELL_NUMBER }, + { 's', TBL_CELL_SPAN }, + { 'a', TBL_CELL_LONG }, + { '^', TBL_CELL_DOWN }, + { '-', TBL_CELL_HORIZ }, + { '_', TBL_CELL_HORIZ }, + { '=', TBL_CELL_DHORIZ }, + { '|', TBL_CELL_VERT } +}; + +static int mods(struct tbl_node *, struct tbl_cell *, + int, const char *, int *); +static int cell(struct tbl_node *, struct tbl_row *, + int, const char *, int *); +static void row(struct tbl_node *, int, const char *, int *); +static struct tbl_cell *cell_alloc(struct tbl_node *, + struct tbl_row *, enum tbl_cellt); +static void head_adjust(const struct tbl_cell *, + struct tbl_head *); + +static int +mods(struct tbl_node *tbl, struct tbl_cell *cp, + int ln, const char *p, int *pos) +{ + char buf[5]; + int i; + +mod: + /* + * XXX: since, at least for now, modifiers are non-conflicting + * (are separable by value, regardless of position), we let + * modifiers come in any order. The existing tbl doesn't let + * this happen. + */ + switch (p[*pos]) { + case ('\0'): + /* FALLTHROUGH */ + case (' '): + /* FALLTHROUGH */ + case ('\t'): + /* FALLTHROUGH */ + case (','): + /* FALLTHROUGH */ + case ('.'): + return(1); + default: + break; + } + + /* Throw away parenthesised expression. */ + + if ('(' == p[*pos]) { + (*pos)++; + while (p[*pos] && ')' != p[*pos]) + (*pos)++; + if (')' == p[*pos]) { + (*pos)++; + goto mod; + } + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); + } + + /* Parse numerical spacing from modifier string. */ + + if (isdigit((unsigned char)p[*pos])) { + for (i = 0; i < 4; i++) { + if ( ! isdigit((unsigned char)p[*pos + i])) + break; + buf[i] = p[*pos + i]; + } + buf[i] = '\0'; + + /* No greater than 4 digits. */ + + if (4 == i) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); + } + + *pos += i; + cp->spacing = atoi(buf); + + goto mod; + /* NOTREACHED */ + } + + /* TODO: GNU has many more extensions. */ + + switch (tolower((unsigned char)p[(*pos)++])) { + case ('z'): + cp->flags |= TBL_CELL_WIGN; + goto mod; + case ('u'): + cp->flags |= TBL_CELL_UP; + goto mod; + case ('e'): + cp->flags |= TBL_CELL_EQUAL; + goto mod; + case ('t'): + cp->flags |= TBL_CELL_TALIGN; + goto mod; + case ('d'): + cp->flags |= TBL_CELL_BALIGN; + goto mod; + case ('w'): /* XXX for now, ignore minimal column width */ + goto mod; + case ('f'): + break; + case ('b'): + /* FALLTHROUGH */ + case ('i'): + (*pos)--; + break; + default: + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); + } + + switch (tolower((unsigned char)p[(*pos)++])) { + case ('b'): + cp->flags |= TBL_CELL_BOLD; + goto mod; + case ('i'): + cp->flags |= TBL_CELL_ITALIC; + goto mod; + default: + break; + } + + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); +} + +static int +cell(struct tbl_node *tbl, struct tbl_row *rp, + int ln, const char *p, int *pos) +{ + int i; + enum tbl_cellt c; + + /* Parse the column position (`r', `R', `|', ...). */ + + for (i = 0; i < KEYS_MAX; i++) + if (tolower((unsigned char)p[*pos]) == keys[i].name) + break; + + if (KEYS_MAX == i) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); + } + + c = keys[i].key; + + /* + * If a span cell is found first, raise a warning and abort the + * parse. FIXME: recover from this somehow? + */ + + if (NULL == rp->first && TBL_CELL_SPAN == c) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos); + return(0); + } + + (*pos)++; + + /* Extra check for the double-vertical. */ + + if (TBL_CELL_VERT == c && '|' == p[*pos]) { + (*pos)++; + c = TBL_CELL_DVERT; + } + + /* Disallow adjacent spacers. */ + + if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) && + (TBL_CELL_VERT == rp->last->pos || + TBL_CELL_DVERT == rp->last->pos)) { + TBL_MSG(tbl, MANDOCERR_TBLLAYOUT, ln, *pos - 1); + return(0); + } + + /* Allocate cell then parse its modifiers. */ + + return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos)); +} + + +static void +row(struct tbl_node *tbl, int ln, const char *p, int *pos) +{ + struct tbl_row *rp; + +row: /* + * EBNF describing this section: + * + * row ::= row_list [:space:]* [.]?[\n] + * row_list ::= [:space:]* row_elem row_tail + * row_tail ::= [:space:]*[,] row_list | + * epsilon + * row_elem ::= [\t\ ]*[:alpha:]+ + */ + + rp = mandoc_calloc(1, sizeof(struct tbl_row)); + if (tbl->last_row) { + tbl->last_row->next = rp; + tbl->last_row = rp; + } else + tbl->last_row = tbl->first_row = rp; + +cell: + while (isspace((unsigned char)p[*pos])) + (*pos)++; + + /* Safely exit layout context. */ + + if ('.' == p[*pos]) { + tbl->part = TBL_PART_DATA; + if (NULL == tbl->first_row) + TBL_MSG(tbl, MANDOCERR_TBLNOLAYOUT, ln, *pos); + (*pos)++; + return; + } + + /* End (and possibly restart) a row. */ + + if (',' == p[*pos]) { + (*pos)++; + goto row; + } else if ('\0' == p[*pos]) + return; + + if ( ! cell(tbl, rp, ln, p, pos)) + return; + + goto cell; + /* NOTREACHED */ +} + +int +tbl_layout(struct tbl_node *tbl, int ln, const char *p) +{ + int pos; + + pos = 0; + row(tbl, ln, p, &pos); + + /* Always succeed. */ + return(1); +} + +static struct tbl_cell * +cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos) +{ + struct tbl_cell *p, *pp; + struct tbl_head *h, *hp; + + p = mandoc_calloc(1, sizeof(struct tbl_cell)); + + if (NULL != (pp = rp->last)) { + rp->last->next = p; + rp->last = p; + } else + rp->last = rp->first = p; + + p->pos = pos; + + /* + * This is a little bit complicated. Here we determine the + * header the corresponds to a cell. We add headers dynamically + * when need be or re-use them, otherwise. As an example, given + * the following: + * + * 1 c || l + * 2 | c | l + * 3 l l + * 3 || c | l |. + * + * We first add the new headers (as there are none) in (1); then + * in (2) we insert the first spanner (as it doesn't match up + * with the header); then we re-use the prior data headers, + * skipping over the spanners; then we re-use everything and add + * a last spanner. Note that VERT headers are made into DVERT + * ones. + */ + + h = pp ? pp->head->next : tbl->first_head; + + if (h) { + /* Re-use data header. */ + if (TBL_HEAD_DATA == h->pos && + (TBL_CELL_VERT != p->pos && + TBL_CELL_DVERT != p->pos)) { + p->head = h; + return(p); + } + + /* Re-use spanner header. */ + if (TBL_HEAD_DATA != h->pos && + (TBL_CELL_VERT == p->pos || + TBL_CELL_DVERT == p->pos)) { + head_adjust(p, h); + p->head = h; + return(p); + } + + /* Right-shift headers with a new spanner. */ + if (TBL_HEAD_DATA == h->pos && + (TBL_CELL_VERT == p->pos || + TBL_CELL_DVERT == p->pos)) { + hp = mandoc_calloc(1, sizeof(struct tbl_head)); + hp->ident = tbl->opts.cols++; + hp->prev = h->prev; + if (h->prev) + h->prev->next = hp; + if (h == tbl->first_head) + tbl->first_head = hp; + h->prev = hp; + hp->next = h; + head_adjust(p, hp); + p->head = hp; + return(p); + } + + if (NULL != (h = h->next)) { + head_adjust(p, h); + p->head = h; + return(p); + } + + /* Fall through to default case... */ + } + + hp = mandoc_calloc(1, sizeof(struct tbl_head)); + hp->ident = tbl->opts.cols++; + + if (tbl->last_head) { + hp->prev = tbl->last_head; + tbl->last_head->next = hp; + tbl->last_head = hp; + } else + tbl->last_head = tbl->first_head = hp; + + head_adjust(p, hp); + p->head = hp; + return(p); +} + +static void +head_adjust(const struct tbl_cell *cell, struct tbl_head *head) +{ + if (TBL_CELL_VERT != cell->pos && + TBL_CELL_DVERT != cell->pos) { + head->pos = TBL_HEAD_DATA; + return; + } + + if (TBL_CELL_VERT == cell->pos) + if (TBL_HEAD_DVERT != head->pos) + head->pos = TBL_HEAD_VERT; + + if (TBL_CELL_DVERT == cell->pos) + head->pos = TBL_HEAD_DVERT; +} + diff --git a/usr.bin/mdocml/dist/tbl_opts.c b/usr.bin/mdocml/dist/tbl_opts.c new file mode 100644 index 000000000..ff32bcc49 --- /dev/null +++ b/usr.bin/mdocml/dist/tbl_opts.c @@ -0,0 +1,260 @@ +/* $Vendor-Id: tbl_opts.c,v 1.8 2011/01/09 05:38:23 joerg Exp $ */ +/* + * Copyright (c) 2009, 2010 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. + */ +#include +#include +#include +#include + +#include "mandoc.h" +#include "libroff.h" + +enum tbl_ident { + KEY_CENTRE = 0, + KEY_DELIM, + KEY_EXPAND, + KEY_BOX, + KEY_DBOX, + KEY_ALLBOX, + KEY_TAB, + KEY_LINESIZE, + KEY_NOKEEP, + KEY_DPOINT, + KEY_NOSPACE, + KEY_FRAME, + KEY_DFRAME, + KEY_MAX +}; + +struct tbl_phrase { + const char *name; + int key; + enum tbl_ident ident; +}; + +/* Handle Commonwealth/American spellings. */ +#define KEY_MAXKEYS 14 + +/* Maximum length of key name string. */ +#define KEY_MAXNAME 13 + +/* Maximum length of key number size. */ +#define KEY_MAXNUMSZ 10 + +static const struct tbl_phrase keys[KEY_MAXKEYS] = { + { "center", TBL_OPT_CENTRE, KEY_CENTRE}, + { "centre", TBL_OPT_CENTRE, KEY_CENTRE}, + { "delim", 0, KEY_DELIM}, + { "expand", TBL_OPT_EXPAND, KEY_EXPAND}, + { "box", TBL_OPT_BOX, KEY_BOX}, + { "doublebox", TBL_OPT_DBOX, KEY_DBOX}, + { "allbox", TBL_OPT_ALLBOX, KEY_ALLBOX}, + { "frame", TBL_OPT_BOX, KEY_FRAME}, + { "doubleframe", TBL_OPT_DBOX, KEY_DFRAME}, + { "tab", 0, KEY_TAB}, + { "linesize", 0, KEY_LINESIZE}, + { "nokeep", TBL_OPT_NOKEEP, KEY_NOKEEP}, + { "decimalpoint", 0, KEY_DPOINT}, + { "nospaces", TBL_OPT_NOSPACE, KEY_NOSPACE}, +}; + +static int arg(struct tbl_node *, int, + const char *, int *, enum tbl_ident); +static void opt(struct tbl_node *, int, + const char *, int *); + +static int +arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key) +{ + int i; + char buf[KEY_MAXNUMSZ]; + + while (isspace((unsigned char)p[*pos])) + (*pos)++; + + /* Arguments always begin with a parenthesis. */ + + if ('(' != p[*pos]) { + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos); + return(0); + } + + (*pos)++; + + /* + * The arguments can be ANY value, so we can't just stop at the + * next close parenthesis (the argument can be a closed + * parenthesis itself). + */ + + switch (key) { + case (KEY_DELIM): + if ('\0' == p[(*pos)++]) { + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); + return(0); + } + + if ('\0' == p[(*pos)++]) { + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); + return(0); + } + break; + case (KEY_TAB): + if ('\0' != (tbl->opts.tab = p[(*pos)++])) + break; + + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); + return(0); + case (KEY_LINESIZE): + for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) { + buf[i] = p[*pos]; + if ( ! isdigit((unsigned char)buf[i])) + break; + } + + if (i < KEY_MAXNUMSZ) { + buf[i] = '\0'; + tbl->opts.linesize = atoi(buf); + break; + } + + (*tbl->msg)(MANDOCERR_TBL, tbl->data, ln, *pos, NULL); + return(0); + case (KEY_DPOINT): + if ('\0' != (tbl->opts.decimal = p[(*pos)++])) + break; + + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); + return(0); + default: + abort(); + /* NOTREACHED */ + } + + /* End with a close parenthesis. */ + + if (')' == p[(*pos)++]) + return(1); + + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1); + return(0); +} + +static void +opt(struct tbl_node *tbl, int ln, const char *p, int *pos) +{ + int i, sv; + char buf[KEY_MAXNAME]; + + /* + * Parse individual options from the stream as surrounded by + * this goto. Each pass through the routine parses out a single + * option and registers it. Option arguments are processed in + * the arg() function. + */ + +again: /* + * EBNF describing this section: + * + * options ::= option_list [:space:]* [;][\n] + * option_list ::= option option_tail + * option_tail ::= [:space:]+ option_list | + * ::= epsilon + * option ::= [:alpha:]+ args + * args ::= [:space:]* [(] [:alpha:]+ [)] + */ + + while (isspace((unsigned char)p[*pos])) + (*pos)++; + + /* Safe exit point. */ + + if (';' == p[*pos]) + return; + + /* Copy up to first non-alpha character. */ + + for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) { + buf[i] = tolower((unsigned char)p[*pos]); + if ( ! isalpha((unsigned char)buf[i])) + break; + } + + /* Exit if buffer is empty (or overrun). */ + + if (KEY_MAXNAME == i || 0 == i) { + TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos); + return; + } + + buf[i] = '\0'; + + while (isspace((unsigned char)p[*pos])) + (*pos)++; + + /* + * Look through all of the available keys to find one that + * matches the input. FIXME: hashtable this. + */ + + for (i = 0; i < KEY_MAXKEYS; i++) { + if (strcmp(buf, keys[i].name)) + continue; + + /* + * Note: this is more difficult to recover from, as we + * can be anywhere in the option sequence and it's + * harder to jump to the next. Meanwhile, just bail out + * of the sequence altogether. + */ + + if (keys[i].key) + tbl->opts.opts |= keys[i].key; + else if ( ! arg(tbl, ln, p, pos, keys[i].ident)) + return; + + break; + } + + /* + * Allow us to recover from bad options by continuing to another + * parse sequence. + */ + + if (KEY_MAXKEYS == i) + TBL_MSG(tbl, MANDOCERR_TBLOPT, ln, sv); + + goto again; + /* NOTREACHED */ +} + +int +tbl_option(struct tbl_node *tbl, int ln, const char *p) +{ + int pos; + + /* + * Table options are always on just one line, so automatically + * switch into the next input mode here. + */ + tbl->part = TBL_PART_LAYOUT; + + pos = 0; + opt(tbl, ln, p, &pos); + + /* Always succeed. */ + return(1); +} diff --git a/usr.bin/mdocml/dist/tbl_term.c b/usr.bin/mdocml/dist/tbl_term.c new file mode 100644 index 000000000..7607adef2 --- /dev/null +++ b/usr.bin/mdocml/dist/tbl_term.c @@ -0,0 +1,425 @@ +/* $Vendor-Id: tbl_term.c,v 1.13 2011/01/07 14:59:52 kristaps Exp $ */ +/* + * Copyright (c) 2009 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" + +/* FIXME: `n' modifier doesn't always do the right thing. */ +/* FIXME: `n' modifier doesn't use the cell-spacing buffer. */ + +static size_t term_tbl_len(size_t, void *); +static size_t term_tbl_strlen(const char *, void *); +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 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 *); + + +static size_t +term_tbl_strlen(const char *p, void *arg) +{ + + return(term_strlen((const struct termp *)arg, p)); +} + +static size_t +term_tbl_len(size_t sz, void *arg) +{ + + return(term_len((const struct termp *)arg, sz)); +} + +void +term_tbl(struct termp *tp, const struct tbl_span *sp) +{ + const struct tbl_head *hp; + const struct tbl_dat *dp; + struct roffcol *col; + size_t rmargin, maxrmargin; + + rmargin = tp->rmargin; + maxrmargin = tp->maxrmargin; + + tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN; + + /* Inhibit printing of spaces: we do padding ourselves. */ + + tp->flags |= TERMP_NONOSPACE; + tp->flags |= TERMP_NOSPACE; + + /* + * The first time we're invoked for a given table block, + * calculate the table widths and decimal positions. + */ + + if (TBL_SPAN_FIRST & sp->flags) { + term_flushln(tp); + + tp->tbl.len = term_tbl_len; + tp->tbl.slen = term_tbl_strlen; + tp->tbl.arg = tp; + + tblcalc(&tp->tbl, sp); + } + + /* Horizontal frame at the start of boxed tables. */ + + if (TBL_SPAN_FIRST & sp->flags) + tbl_hframe(tp, sp); + + /* Vertical frame at the start of each row. */ + + tbl_vframe(tp, sp->tbl); + + /* + * Now print the actual data itself depending on the span type. + * Spanner spans get a horizontal rule; data spanners have their + * data printed by matching data to header. + */ + + switch (sp->pos) { + case (TBL_SPAN_HORIZ): + /* FALLTHROUGH */ + case (TBL_SPAN_DHORIZ): + tbl_hrule(tp, sp); + break; + case (TBL_SPAN_DATA): + /* Iterate over template headers. */ + dp = sp->first; + for (hp = sp->head; hp; hp = hp->next) { + switch (hp->pos) { + case (TBL_HEAD_VERT): + /* FALLTHROUGH */ + case (TBL_HEAD_DVERT): + tbl_vrule(tp, hp); + continue; + case (TBL_HEAD_DATA): + break; + } + + col = &tp->tbl.cols[hp->ident]; + tbl_data(tp, sp->tbl, dp, col); + + /* Go to the next data cell. */ + if (dp) + dp = dp->next; + } + break; + } + + tbl_vframe(tp, sp->tbl); + term_flushln(tp); + + /* + * If we're the last row, clean up after ourselves: clear the + * existing table configuration and set it to NULL. + */ + + if (TBL_SPAN_LAST & sp->flags) { + tbl_hframe(tp, sp); + assert(tp->tbl.cols); + free(tp->tbl.cols); + tp->tbl.cols = NULL; + } + + tp->flags &= ~TERMP_NONOSPACE; + tp->rmargin = rmargin; + tp->maxrmargin = maxrmargin; + +} + +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): + 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 */ + } + } +} + +static void +tbl_hframe(struct termp *tp, const struct tbl_span *sp) +{ + 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; + } + } + term_word(tp, "+"); + term_flushln(tp); +} + +static void +tbl_data(struct termp *tp, const struct tbl *tbl, + const struct tbl_dat *dp, + const struct roffcol *col) +{ + enum tbl_cellt pos; + + if (NULL == dp) { + tbl_char(tp, ASCII_NBRSP, col->width); + return; + } + + switch (dp->pos) { + case (TBL_DATA_NONE): + tbl_char(tp, ASCII_NBRSP, col->width); + return; + case (TBL_DATA_HORIZ): + /* FALLTHROUGH */ + case (TBL_DATA_NHORIZ): + tbl_char(tp, '-', col->width); + return; + case (TBL_DATA_NDHORIZ): + /* FALLTHROUGH */ + case (TBL_DATA_DHORIZ): + tbl_char(tp, '=', col->width); + return; + default: + break; + } + + pos = dp && dp->layout ? dp->layout->pos : TBL_CELL_LEFT; + + switch (pos) { + case (TBL_CELL_HORIZ): + tbl_char(tp, '-', col->width); + break; + case (TBL_CELL_DHORIZ): + tbl_char(tp, '=', col->width); + break; + case (TBL_CELL_LONG): + /* FALLTHROUGH */ + case (TBL_CELL_CENTRE): + /* FALLTHROUGH */ + case (TBL_CELL_LEFT): + /* FALLTHROUGH */ + case (TBL_CELL_RIGHT): + tbl_literal(tp, dp, col); + break; + case (TBL_CELL_NUMBER): + tbl_number(tp, tbl, dp, col); + break; + default: + abort(); + /* NOTREACHED */ + } +} + +static void +tbl_vrule(struct termp *tp, const struct tbl_head *hp) +{ + + switch (hp->pos) { + case (TBL_HEAD_VERT): + term_word(tp, "|"); + break; + case (TBL_HEAD_DVERT): + term_word(tp, "||"); + break; + default: + break; + } +} + +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) +{ + size_t i, sz; + char cp[2]; + + cp[0] = c; + cp[1] = '\0'; + + sz = term_strlen(tp, cp); + + for (i = 0; i < len; i += sz) + term_word(tp, cp); +} + +static void +tbl_literal(struct termp *tp, const struct tbl_dat *dp, + const struct roffcol *col) +{ + size_t padl, padr, ssz; + enum tbl_cellt pos; + const char *str; + + padl = padr = 0; + + pos = dp && dp->layout ? dp->layout->pos : TBL_CELL_LEFT; + str = dp && dp->string ? dp->string : ""; + + ssz = term_len(tp, 1); + + switch (pos) { + case (TBL_CELL_LONG): + padl = ssz; + padr = col->width - term_strlen(tp, str) - ssz; + break; + case (TBL_CELL_CENTRE): + padl = col->width - term_strlen(tp, str); + if (padl % 2) + padr++; + padl /= 2; + padr += padl; + break; + case (TBL_CELL_RIGHT): + padl = col->width - term_strlen(tp, str); + break; + default: + padr = col->width - term_strlen(tp, str); + break; + } + + tbl_char(tp, ASCII_NBRSP, padl); + term_word(tp, str); + tbl_char(tp, ASCII_NBRSP, padr); +} + +static void +tbl_number(struct termp *tp, const struct tbl *tbl, + const struct tbl_dat *dp, + const struct roffcol *col) +{ + char *cp; + char buf[2]; + const char *str; + size_t sz, psz, ssz, d, padl; + int i; + + /* + * See calc_data_number(). Left-pad by taking the offset of our + * and the maximum decimal; right-pad by the remaining amount. + */ + + str = dp && dp->string ? dp->string : ""; + + sz = term_strlen(tp, str); + + buf[0] = tbl->decimal; + buf[1] = '\0'; + + psz = term_strlen(tp, buf); + + if (NULL != (cp = strchr(str, tbl->decimal))) { + buf[1] = '\0'; + for (ssz = 0, i = 0; cp != &str[i]; i++) { + buf[0] = str[i]; + ssz += term_strlen(tp, buf); + } + d = ssz + psz; + } 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, str); + tbl_char(tp, ASCII_NBRSP, col->width - sz - padl); +} + diff --git a/commands/mdocml/term.c b/usr.bin/mdocml/dist/term.c similarity index 69% rename from commands/mdocml/term.c rename to usr.bin/mdocml/dist/term.c index 1801ef936..c46b4c413 100644 --- a/commands/mdocml/term.c +++ b/usr.bin/mdocml/dist/term.c @@ -1,6 +1,7 @@ -/* $Id: term.c,v 1.148 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: term.c,v 1.176 2011/01/04 13:14:26 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons + * Copyright (c) 2010 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 @@ -31,13 +32,11 @@ #include "chars.h" #include "out.h" #include "term.h" -#include "man.h" -#include "mdoc.h" #include "main.h" -static void spec(struct termp *, const char *, size_t); +static void spec(struct termp *, enum roffdeco, + const char *, size_t); static void res(struct termp *, const char *, size_t); -static void buffera(struct termp *, const char *, size_t); static void bufferc(struct termp *, char); static void adjbuf(struct termp *p, size_t); static void encode(struct termp *, const char *, size_t); @@ -84,12 +83,10 @@ term_alloc(enum termenc enc) p = calloc(1, sizeof(struct termp)); if (NULL == p) { perror(NULL); - exit(EXIT_FAILURE); + exit((int)MANDOCLEVEL_SYSERR); } - p->tabwidth = 5; p->enc = enc; - p->defrmargin = 78; return(p); } @@ -137,9 +134,11 @@ term_flushln(struct termp *p) size_t vbl; /* number of blanks to prepend to output */ size_t vend; /* end of word visual position on output */ size_t bp; /* visual right border position */ - int j; /* temporary loop index */ - int jhy; /* last hyphen before line overflow */ - size_t maxvis, mmax; + size_t dv; /* temporary for visual pos calculations */ + int j; /* temporary loop index for p->buf */ + int jhy; /* last hyph before overflow w/r/t j */ + size_t maxvis; /* output position of visible boundary */ + size_t mmax; /* used in calculating bp */ /* * First, establish the maximum columns of "visible" content. @@ -147,40 +146,32 @@ term_flushln(struct termp *p) * an indentation, but can be, for tagged lists or columns, a * small set of values. */ - - assert(p->offset < p->rmargin); - - maxvis = (int)(p->rmargin - p->offset) - p->overstep < 0 ? - /* LINTED */ - 0 : p->rmargin - p->offset - p->overstep; - mmax = (int)(p->maxrmargin - p->offset) - p->overstep < 0 ? - /* LINTED */ - 0 : p->maxrmargin - p->offset - p->overstep; + assert (p->rmargin >= p->offset); + dv = p->rmargin - p->offset; + maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; + dv = p->maxrmargin - p->offset; + mmax = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0; bp = TERMP_NOBREAK & p->flags ? mmax : maxvis; /* * Indent the first line of a paragraph. */ - vbl = p->flags & TERMP_NOLPAD ? 0 : p->offset; + vbl = p->flags & TERMP_NOLPAD ? (size_t)0 : p->offset; - /* - * FIXME: if bp is zero, we still output the first word before - * breaking the line. - */ + vis = vend = 0; + i = 0; - vis = vend = i = 0; while (i < (int)p->col) { - /* - * Handle literal tab characters. + * Handle literal tab characters: collapse all + * subsequent tabs into a single huge set of spaces. */ - for (j = i; j < (int)p->col; j++) { - if ('\t' != p->buf[j]) - break; - vend = (vis/p->tabwidth+1)*p->tabwidth; + while (i < (int)p->col && '\t' == p->buf[i]) { + vend = (vis / p->tabwidth + 1) * p->tabwidth; vbl += vend - vis; vis = vend; + i++; } /* @@ -190,17 +181,24 @@ term_flushln(struct termp *p) * space is printed according to regular spacing rules). */ - /* LINTED */ - for (jhy = 0; j < (int)p->col; j++) { + for (j = i, jhy = 0; j < (int)p->col; j++) { if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j]) break; - if (8 != p->buf[j]) { - if (vend > vis && vend < bp && - ASCII_HYPH == p->buf[j]) - jhy = j; - vend++; - } else - vend--; + + /* Back over the the last printed character. */ + if (8 == p->buf[j]) { + assert(j); + vend -= (*p->width)(p, p->buf[j - 1]); + continue; + } + + /* Regular word. */ + /* Break at the hyphen point if we overrun. */ + if (vend > vis && vend < bp && + ASCII_HYPH == p->buf[j]) + jhy = j; + + vend += (*p->width)(p, p->buf[j]); } /* @@ -221,17 +219,10 @@ term_flushln(struct termp *p) /* Remove the p->overstep width. */ - bp += (int)/* LINTED */ - p->overstep; + bp += (size_t)p->overstep; p->overstep = 0; } - /* - * Skip leading tabs, they were handled above. - */ - while (i < (int)p->col && '\t' == p->buf[i]) - i++; - /* Write out the [remaining] word. */ for ( ; i < (int)p->col; i++) { if (vend > bp && jhy > 0 && i > jhy) @@ -239,14 +230,16 @@ term_flushln(struct termp *p) if ('\t' == p->buf[i]) break; if (' ' == p->buf[i]) { - while (' ' == p->buf[i]) { - vbl++; + j = i; + while (' ' == p->buf[i]) i++; - } + dv = (size_t)(i - j) * (*p->width)(p, ' '); + vbl += dv; + vend += dv; break; } if (ASCII_NBRSP == p->buf[i]) { - vbl++; + vbl += (*p->width)(p, ' '); continue; } @@ -261,17 +254,23 @@ term_flushln(struct termp *p) vbl = 0; } - if (ASCII_HYPH == p->buf[i]) + if (ASCII_HYPH == p->buf[i]) { (*p->letter)(p, '-'); - else + p->viscol += (*p->width)(p, '-'); + } else { (*p->letter)(p, p->buf[i]); - - p->viscol += 1; + p->viscol += (*p->width)(p, p->buf[i]); + } } - vend += vbl; vis = vend; } + /* + * If there was trailing white space, it was not printed; + * so reset the cursor position accordingly. + */ + vis -= vbl; + p->col = 0; p->overstep = 0; @@ -283,8 +282,7 @@ term_flushln(struct termp *p) if (TERMP_HANG & p->flags) { /* We need one blank after the tag. */ - p->overstep = /* LINTED */ - vis - maxvis + 1; + p->overstep = (int)(vis - maxvis + (*p->width)(p, ' ')); /* * Behave exactly the same way as groff: @@ -298,8 +296,7 @@ term_flushln(struct termp *p) */ if (p->overstep >= -1) { assert((int)maxvis + p->overstep >= 0); - /* LINTED */ - maxvis += p->overstep; + maxvis += (size_t)p->overstep; } else p->overstep = 0; @@ -307,8 +304,8 @@ term_flushln(struct termp *p) return; /* Right-pad. */ - if (maxvis > vis + /* LINTED */ - ((TERMP_TWOSPACE & p->flags) ? 1 : 0)) { + if (maxvis > vis + + ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) { p->viscol += maxvis - vis; (*p->advance)(p, maxvis - vis); vis += (maxvis - vis); @@ -356,14 +353,16 @@ term_vspace(struct termp *p) static void -spec(struct termp *p, const char *word, size_t len) +spec(struct termp *p, enum roffdeco d, const char *word, size_t len) { const char *rhs; size_t sz; - rhs = chars_a2ascii(p->symtab, word, len, &sz); + rhs = chars_spec2str(p->symtab, word, len, &sz); if (rhs) encode(p, rhs, sz); + else if (DECO_SSPECIAL == d) + encode(p, word, len); } @@ -373,7 +372,7 @@ res(struct termp *p, const char *word, size_t len) const char *rhs; size_t sz; - rhs = chars_a2res(p->symtab, word, len, &sz); + rhs = chars_res2str(p->symtab, word, len, &sz); if (rhs) encode(p, rhs, sz); } @@ -453,7 +452,6 @@ void term_word(struct termp *p, const char *word) { const char *sv, *seq; - int sz; size_t ssz; enum roffdeco deco; @@ -484,34 +482,42 @@ term_word(struct termp *p, const char *word) } if ( ! (TERMP_NOSPACE & p->flags)) { - bufferc(p, ' '); - if (TERMP_SENTENCE & p->flags) + if ( ! (TERMP_KEEP & p->flags)) { + if (TERMP_PREKEEP & p->flags) + p->flags |= TERMP_KEEP; bufferc(p, ' '); + if (TERMP_SENTENCE & p->flags) + bufferc(p, ' '); + } else + bufferc(p, ASCII_NBRSP); } if ( ! (p->flags & TERMP_NONOSPACE)) p->flags &= ~TERMP_NOSPACE; + else + p->flags |= TERMP_NOSPACE; - p->flags &= ~TERMP_SENTENCE; - - /* FIXME: use strcspn. */ + p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM); while (*word) { - if ('\\' != *word) { - encode(p, word, 1); - word++; + if ((ssz = strcspn(word, "\\")) > 0) + encode(p, word, ssz); + + word += ssz; + if ('\\' != *word) continue; - } seq = ++word; - sz = a2roffdeco(&deco, &seq, &ssz); + word += a2roffdeco(&deco, &seq, &ssz); switch (deco) { case (DECO_RESERVED): res(p, seq, ssz); break; case (DECO_SPECIAL): - spec(p, seq, ssz); + /* FALLTHROUGH */ + case (DECO_SSPECIAL): + spec(p, deco, seq, ssz); break; case (DECO_BOLD): term_fontrepl(p, TERMFONT_BOLD); @@ -529,7 +535,6 @@ term_word(struct termp *p, const char *word) break; } - word += sz; if (DECO_NOSPACE == deco && '\0' == *word) p->flags |= TERMP_NOSPACE; } @@ -538,7 +543,7 @@ term_word(struct termp *p, const char *word) * Note that we don't process the pipe: the parser sees it as * punctuation, but we don't in terms of typography. */ - if (sv[0] && 0 == sv[1]) + if (sv[0] && '\0' == sv[1]) switch (sv[0]) { case('('): /* FALLTHROUGH */ @@ -563,23 +568,11 @@ adjbuf(struct termp *p, size_t sz) p->buf = realloc(p->buf, p->maxcols); if (NULL == p->buf) { perror(NULL); - exit(EXIT_FAILURE); + exit((int)MANDOCLEVEL_SYSERR); } } -static void -buffera(struct termp *p, const char *word, size_t sz) -{ - - if (p->col + sz >= p->maxcols) - adjbuf(p, p->col + sz); - - memcpy(&p->buf[(int)p->col], word, sz); - p->col += sz; -} - - static void bufferc(struct termp *p, char c) { @@ -604,29 +597,102 @@ encode(struct termp *p, const char *word, size_t sz) */ if (TERMFONT_NONE == (f = term_fonttop(p))) { - buffera(p, word, sz); + if (p->col + sz >= p->maxcols) + adjbuf(p, p->col + sz); + memcpy(&p->buf[(int)p->col], word, sz); + p->col += sz; return; } + /* Pre-buffer, assuming worst-case. */ + + if (p->col + 1 + (sz * 3) >= p->maxcols) + adjbuf(p, p->col + 1 + (sz * 3)); + for (i = 0; i < (int)sz; i++) { if ( ! isgraph((u_char)word[i])) { - bufferc(p, word[i]); + p->buf[(int)p->col++] = word[i]; continue; } if (TERMFONT_UNDER == f) - bufferc(p, '_'); + p->buf[(int)p->col++] = '_'; else - bufferc(p, word[i]); + p->buf[(int)p->col++] = word[i]; - bufferc(p, 8); - bufferc(p, word[i]); + p->buf[(int)p->col++] = 8; + p->buf[(int)p->col++] = word[i]; } } size_t -term_vspan(const struct roffsu *su) +term_len(const struct termp *p, size_t sz) +{ + + return((*p->width)(p, ' ') * sz); +} + + +size_t +term_strlen(const struct termp *p, const char *cp) +{ + size_t sz, ssz, rsz, i; + enum roffdeco d; + const char *seq, *rhs; + + for (sz = 0; '\0' != *cp; ) + /* + * Account for escaped sequences within string length + * calculations. This follows the logic in term_word() + * as we must calculate the width of produced strings. + */ + if ('\\' == *cp) { + seq = ++cp; + cp += a2roffdeco(&d, &seq, &ssz); + + switch (d) { + case (DECO_RESERVED): + rhs = chars_res2str + (p->symtab, seq, ssz, &rsz); + break; + case (DECO_SPECIAL): + /* FALLTHROUGH */ + case (DECO_SSPECIAL): + rhs = chars_spec2str + (p->symtab, seq, ssz, &rsz); + + /* Allow for one-char escapes. */ + if (DECO_SSPECIAL != d || rhs) + break; + + rhs = seq; + rsz = ssz; + break; + default: + rhs = NULL; + break; + } + + if (rhs) + for (i = 0; i < rsz; i++) + sz += (*p->width)(p, *rhs++); + } else if (ASCII_NBRSP == *cp) { + sz += (*p->width)(p, ' '); + cp++; + } else if (ASCII_HYPH == *cp) { + sz += (*p->width)(p, '-'); + cp++; + } else + sz += (*p->width)(p, *cp++); + + return(sz); +} + + +/* ARGSUSED */ +size_t +term_vspan(const struct termp *p, const struct roffsu *su) { double r; @@ -662,41 +728,13 @@ term_vspan(const struct roffsu *su) size_t -term_hspan(const struct roffsu *su) +term_hspan(const struct termp *p, const struct roffsu *su) { - double r; + double v; - /* XXX: CM, IN, and PT are approximations. */ - - switch (su->unit) { - case (SCALE_CM): - r = 4 * su->scale; - break; - case (SCALE_IN): - /* XXX: this is an approximation. */ - r = 10 * su->scale; - break; - case (SCALE_PC): - r = (10 * su->scale) / 6; - break; - case (SCALE_PT): - r = (10 * su->scale) / 72; - break; - case (SCALE_MM): - r = su->scale / 1000; /* FIXME: double-check. */ - break; - case (SCALE_VS): - r = su->scale * 2 - 1; /* FIXME: double-check. */ - break; - default: - r = su->scale; - break; - } - - if (r < 0.0) - r = 0.0; - return((size_t)/* LINTED */ - r); + v = ((*p->hspan)(p, su)); + if (v < 0.0) + v = 0.0; + return((size_t) /* LINTED */ + v); } - - diff --git a/commands/mdocml/term.h b/usr.bin/mdocml/dist/term.h similarity index 66% rename from commands/mdocml/term.h rename to usr.bin/mdocml/dist/term.h index 12928da61..9edca2212 100644 --- a/commands/mdocml/term.h +++ b/usr.bin/mdocml/dist/term.h @@ -1,6 +1,6 @@ -/* $Id: term.h,v 1.64 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: term.h,v 1.79 2011/01/05 15:37:23 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2008, 2009, 2010 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 @@ -27,13 +27,15 @@ enum termenc { enum termtype { TERMTYPE_CHAR, - TERMTYPE_PS + TERMTYPE_PS, + TERMTYPE_PDF }; enum termfont { TERMFONT_NONE = 0, TERMFONT_BOLD, - TERMFONT_UNDER + TERMFONT_UNDER, + TERMFONT__MAX }; #define TERM_MAXMARGIN 100000 /* FIXME */ @@ -41,22 +43,43 @@ enum termfont { typedef void (*term_margin)(struct termp *, const void *); struct termp_ps { - int psstate; /* state of ps output */ + int flags; #define PS_INLINE (1 << 0) /* we're in a word */ #define PS_MARGINS (1 << 1) /* we're in the margins */ - size_t pscol; /* visible column */ - size_t psrow; /* visible row */ +#define PS_NEWPAGE (1 << 2) /* new page, no words yet */ + size_t pscol; /* visible column (AFM units) */ + size_t psrow; /* visible row (AFM units) */ char *psmarg; /* margin buf */ size_t psmargsz; /* margin buf size */ - size_t psmargcur; /* current pos in margin buf */ - size_t pspage; /* current page */ + size_t psmargcur; /* cur index in margin buf */ char last; /* character buffer */ enum termfont lastf; /* last set font */ + size_t scale; /* font scaling factor */ + size_t pages; /* number of pages shown */ + size_t lineheight; /* line height (AFM units) */ + size_t top; /* body top (AFM units) */ + size_t bottom; /* body bottom (AFM units) */ + size_t height; /* page height (AFM units */ + size_t width; /* page width (AFM units) */ + size_t left; /* body left (AFM units) */ + size_t header; /* header pos (AFM units) */ + size_t footer; /* footer pos (AFM units) */ + size_t pdfbytes; /* current output byte */ + size_t pdflastpg; /* byte of last page mark */ + size_t pdfbody; /* start of body object */ + size_t *pdfobjs; /* table of object offsets */ + size_t pdfobjsz; /* size of pdfobjs */ +}; + +struct termp_tbl { + int width; /* width in fixed chars */ + int decimal; /* decimal point position */ }; struct termp { enum termtype type; - size_t defrmargin; /* Right margin of the device.. */ + struct rofftbl tbl; /* table configuration */ + size_t defrmargin; /* Right margin of the device. */ size_t rmargin; /* Current right margin. */ size_t maxrmargin; /* Max right margin. */ size_t maxcols; /* Max size of buf. */ @@ -78,6 +101,8 @@ struct termp { #define TERMP_NOSPLIT (1 << 11) /* See termp_an_pre/post(). */ #define TERMP_SPLIT (1 << 12) /* See termp_an_pre/post(). */ #define TERMP_ANPREC (1 << 13) /* See termp_an_pre(). */ +#define TERMP_KEEP (1 << 14) /* Keep words together. */ +#define TERMP_PREKEEP (1 << 15) /* ...starting with the next one. */ char *buf; /* Output buffer. */ enum termenc enc; /* Type of encoding. */ void *symtab; /* Encoded-symbol table. */ @@ -91,6 +116,9 @@ struct termp { void (*end)(struct termp *); void (*endline)(struct termp *); void (*advance)(struct termp *, size_t); + size_t (*width)(const struct termp *, char); + double (*hspan)(const struct termp *, + const struct roffsu *); const void *argf; /* arg for headf/footf */ union { struct termp_ps ps; @@ -98,6 +126,7 @@ struct termp { }; struct termp *term_alloc(enum termenc); +void term_tbl(struct termp *, const struct tbl_span *); void term_free(struct termp *); void term_newln(struct termp *); void term_vspace(struct termp *); @@ -107,8 +136,12 @@ void term_begin(struct termp *, term_margin, term_margin, const void *); void term_end(struct termp *); -size_t term_hspan(const struct roffsu *); -size_t term_vspan(const struct roffsu *); +size_t term_hspan(const struct termp *, + const struct roffsu *); +size_t term_vspan(const struct termp *, + const struct roffsu *); +size_t term_strlen(const struct termp *, const char *); +size_t term_len(const struct termp *, size_t); enum termfont term_fonttop(struct termp *); const void *term_fontq(struct termp *); diff --git a/commands/mdocml/term_ascii.c b/usr.bin/mdocml/dist/term_ascii.c similarity index 68% rename from commands/mdocml/term_ascii.c rename to usr.bin/mdocml/dist/term_ascii.c index 84d946486..293f1d27d 100644 --- a/commands/mdocml/term_ascii.c +++ b/usr.bin/mdocml/dist/term_ascii.c @@ -1,6 +1,6 @@ -/* $Id: term_ascii.c,v 1.4 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: term_ascii.c,v 1.11 2011/01/02 12:21:07 kristaps Exp $ */ /* - * Copyright (c) 2008, 2009 Kristaps Dzonsons + * Copyright (c) 2010 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 @@ -26,15 +26,19 @@ #include #include +#include "mandoc.h" #include "out.h" #include "term.h" #include "main.h" +static double ascii_hspan(const struct termp *, + const struct roffsu *); +static size_t ascii_width(const struct termp *, char); +static void ascii_advance(struct termp *, size_t); +static void ascii_begin(struct termp *); +static void ascii_end(struct termp *); static void ascii_endline(struct termp *); static void ascii_letter(struct termp *, char); -static void ascii_begin(struct termp *); -static void ascii_advance(struct termp *, size_t); -static void ascii_end(struct termp *); void * @@ -47,12 +51,17 @@ ascii_alloc(char *outopts) if (NULL == (p = term_alloc(TERMENC_ASCII))) return(NULL); - p->type = TERMTYPE_CHAR; - p->letter = ascii_letter; + p->tabwidth = 5; + p->defrmargin = 78; + + p->advance = ascii_advance; p->begin = ascii_begin; p->end = ascii_end; p->endline = ascii_endline; - p->advance = ascii_advance; + p->hspan = ascii_hspan; + p->letter = ascii_letter; + p->type = TERMTYPE_CHAR; + p->width = ascii_width; toks[0] = "width"; toks[1] = NULL; @@ -74,6 +83,15 @@ ascii_alloc(char *outopts) } +/* ARGSUSED */ +static size_t +ascii_width(const struct termp *p, char c) +{ + + return(1); +} + + void ascii_free(void *arg) { @@ -87,6 +105,7 @@ static void ascii_letter(struct termp *p, char c) { + /* LINTED */ putchar(c); } @@ -126,3 +145,43 @@ ascii_advance(struct termp *p, size_t len) for (i = 0; i < len; i++) putchar(' '); } + + +/* ARGSUSED */ +static double +ascii_hspan(const struct termp *p, const struct roffsu *su) +{ + double r; + + /* + * Approximate based on character width. These are generated + * entirely by eyeballing the screen, but appear to be correct. + */ + + switch (su->unit) { + case (SCALE_CM): + r = 4 * su->scale; + break; + case (SCALE_IN): + r = 10 * su->scale; + break; + case (SCALE_PC): + r = (10 * su->scale) / 6; + break; + case (SCALE_PT): + r = (10 * su->scale) / 72; + break; + case (SCALE_MM): + r = su->scale / 1000; + break; + case (SCALE_VS): + r = su->scale * 2 - 1; + break; + default: + r = su->scale; + break; + } + + return(r); +} + diff --git a/usr.bin/mdocml/dist/term_ps.c b/usr.bin/mdocml/dist/term_ps.c new file mode 100644 index 000000000..c178f90ee --- /dev/null +++ b/usr.bin/mdocml/dist/term_ps.c @@ -0,0 +1,1161 @@ +/* $Vendor-Id: term_ps.c,v 1.45 2010/09/27 23:03:44 schwarze Exp $ */ +/* + * Copyright (c) 2010 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 "mandoc.h" +#include "out.h" +#include "main.h" +#include "term.h" + +/* Convert PostScript point "x" to an AFM unit. */ +#define PNT2AFM(p, x) /* LINTED */ \ + (size_t)((double)(x) * (1000.0 / (double)(p)->engine.ps.scale)) + +/* Convert an AFM unit "x" to a PostScript points */ +#define AFM2PNT(p, x) /* LINTED */ \ + ((double)(x) / (1000.0 / (double)(p)->engine.ps.scale)) + +struct glyph { + unsigned short wx; /* WX in AFM */ +}; + +struct font { + const char *name; /* FontName in AFM */ +#define MAXCHAR 95 /* total characters we can handle */ + struct glyph gly[MAXCHAR]; /* glyph metrics */ +}; + +/* + * We define, for the time being, three fonts: bold, oblique/italic, and + * normal (roman). The following table hard-codes the font metrics for + * ASCII, i.e., 32--127. + */ + +static const struct font fonts[TERMFONT__MAX] = { + { "Times-Roman", { + { 250 }, + { 333 }, + { 408 }, + { 500 }, + { 500 }, + { 833 }, + { 778 }, + { 333 }, + { 333 }, + { 333 }, + { 500 }, + { 564 }, + { 250 }, + { 333 }, + { 250 }, + { 278 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 278 }, + { 278 }, + { 564 }, + { 564 }, + { 564 }, + { 444 }, + { 921 }, + { 722 }, + { 667 }, + { 667 }, + { 722 }, + { 611 }, + { 556 }, + { 722 }, + { 722 }, + { 333 }, + { 389 }, + { 722 }, + { 611 }, + { 889 }, + { 722 }, + { 722 }, + { 556 }, + { 722 }, + { 667 }, + { 556 }, + { 611 }, + { 722 }, + { 722 }, + { 944 }, + { 722 }, + { 722 }, + { 611 }, + { 333 }, + { 278 }, + { 333 }, + { 469 }, + { 500 }, + { 333 }, + { 444 }, + { 500 }, + { 444 }, + { 500}, + { 444}, + { 333}, + { 500}, + { 500}, + { 278}, + { 278}, + { 500}, + { 278}, + { 778}, + { 500}, + { 500}, + { 500}, + { 500}, + { 333}, + { 389}, + { 278}, + { 500}, + { 500}, + { 722}, + { 500}, + { 500}, + { 444}, + { 480}, + { 200}, + { 480}, + { 541}, + } }, + { "Times-Bold", { + { 250 }, + { 333 }, + { 555 }, + { 500 }, + { 500 }, + { 1000 }, + { 833 }, + { 333 }, + { 333 }, + { 333 }, + { 500 }, + { 570 }, + { 250 }, + { 333 }, + { 250 }, + { 278 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 333 }, + { 333 }, + { 570 }, + { 570 }, + { 570 }, + { 500 }, + { 930 }, + { 722 }, + { 667 }, + { 722 }, + { 722 }, + { 667 }, + { 611 }, + { 778 }, + { 778 }, + { 389 }, + { 500 }, + { 778 }, + { 667 }, + { 944 }, + { 722 }, + { 778 }, + { 611 }, + { 778 }, + { 722 }, + { 556 }, + { 667 }, + { 722 }, + { 722 }, + { 1000 }, + { 722 }, + { 722 }, + { 667 }, + { 333 }, + { 278 }, + { 333 }, + { 581 }, + { 500 }, + { 333 }, + { 500 }, + { 556 }, + { 444 }, + { 556 }, + { 444 }, + { 333 }, + { 500 }, + { 556 }, + { 278 }, + { 333 }, + { 556 }, + { 278 }, + { 833 }, + { 556 }, + { 500 }, + { 556 }, + { 556 }, + { 444 }, + { 389 }, + { 333 }, + { 556 }, + { 500 }, + { 722 }, + { 500 }, + { 500 }, + { 444 }, + { 394 }, + { 220 }, + { 394 }, + { 520 }, + } }, + { "Times-Italic", { + { 250 }, + { 333 }, + { 420 }, + { 500 }, + { 500 }, + { 833 }, + { 778 }, + { 333 }, + { 333 }, + { 333 }, + { 500 }, + { 675 }, + { 250 }, + { 333 }, + { 250 }, + { 278 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 333 }, + { 333 }, + { 675 }, + { 675 }, + { 675 }, + { 500 }, + { 920 }, + { 611 }, + { 611 }, + { 667 }, + { 722 }, + { 611 }, + { 611 }, + { 722 }, + { 722 }, + { 333 }, + { 444 }, + { 667 }, + { 556 }, + { 833 }, + { 667 }, + { 722 }, + { 611 }, + { 722 }, + { 611 }, + { 500 }, + { 556 }, + { 722 }, + { 611 }, + { 833 }, + { 611 }, + { 556 }, + { 556 }, + { 389 }, + { 278 }, + { 389 }, + { 422 }, + { 500 }, + { 333 }, + { 500 }, + { 500 }, + { 444 }, + { 500 }, + { 444 }, + { 278 }, + { 500 }, + { 500 }, + { 278 }, + { 278 }, + { 444 }, + { 278 }, + { 722 }, + { 500 }, + { 500 }, + { 500 }, + { 500 }, + { 389 }, + { 389 }, + { 278 }, + { 500 }, + { 444 }, + { 667 }, + { 444 }, + { 444 }, + { 389 }, + { 400 }, + { 275 }, + { 400 }, + { 541 }, + } }, +}; + +/* These work the buffer used by the header and footer. */ +#define PS_BUFSLOP 128 + +static void +ps_growbuf(struct termp *p, size_t sz) +{ + if (p->engine.ps.psmargcur + sz <= p->engine.ps.psmargsz) + return; + + if (sz < PS_BUFSLOP) + sz = PS_BUFSLOP; + + p->engine.ps.psmargsz += sz; + + p->engine.ps.psmarg = realloc + (p->engine.ps.psmarg, + p->engine.ps.psmargsz); + + if (NULL == p->engine.ps.psmarg) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } +} + +static double ps_hspan(const struct termp *, + const struct roffsu *); +static size_t ps_width(const struct termp *, char); +static void ps_advance(struct termp *, size_t); +static void ps_begin(struct termp *); +static void ps_closepage(struct termp *); +static void ps_end(struct termp *); +static void ps_endline(struct termp *); +static void ps_fclose(struct termp *); +static void ps_letter(struct termp *, char); +static void ps_pclose(struct termp *); +static void ps_pletter(struct termp *, int); +static void ps_printf(struct termp *, const char *, ...); +static void ps_putchar(struct termp *, char); +static void ps_setfont(struct termp *, enum termfont); +static struct termp *pspdf_alloc(char *); +static void pdf_obj(struct termp *, size_t); + + +void * +pdf_alloc(char *outopts) +{ + struct termp *p; + + if (NULL != (p = pspdf_alloc(outopts))) + p->type = TERMTYPE_PDF; + + return(p); +} + + +void * +ps_alloc(char *outopts) +{ + struct termp *p; + + if (NULL != (p = pspdf_alloc(outopts))) + p->type = TERMTYPE_PS; + + return(p); +} + + +static struct termp * +pspdf_alloc(char *outopts) +{ + struct termp *p; + size_t pagex, pagey, marginx, marginy, lineheight; + const char *toks[2]; + const char *pp; + char *v; + + if (NULL == (p = term_alloc(TERMENC_ASCII))) + return(NULL); + + p->advance = ps_advance; + p->begin = ps_begin; + p->end = ps_end; + p->endline = ps_endline; + p->hspan = ps_hspan; + p->letter = ps_letter; + p->width = ps_width; + + toks[0] = "paper"; + toks[1] = NULL; + + pp = NULL; + + while (outopts && *outopts) + switch (getsubopt(&outopts, UNCONST(toks), &v)) { + case (0): + pp = v; + break; + default: + break; + } + + /* Default to US letter (millimetres). */ + + pagex = 216; + pagey = 279; + + /* + * The ISO-269 paper sizes can be calculated automatically, but + * it would require bringing in -lm for pow() and I'd rather not + * do that. So just do it the easy way for now. Since this + * only happens once, I'm not terribly concerned. + */ + + if (pp && strcasecmp(pp, "letter")) { + if (0 == strcasecmp(pp, "a3")) { + pagex = 297; + pagey = 420; + } else if (0 == strcasecmp(pp, "a4")) { + pagex = 210; + pagey = 297; + } else if (0 == strcasecmp(pp, "a5")) { + pagex = 148; + pagey = 210; + } else if (0 == strcasecmp(pp, "legal")) { + pagex = 216; + pagey = 356; + } else if (2 != sscanf(pp, "%zux%zu", &pagex, &pagey)) + fprintf(stderr, "%s: Unknown paper\n", pp); + } else if (NULL == pp) + pp = "letter"; + + /* + * This MUST be defined before any PNT2AFM or AFM2PNT + * calculations occur. + */ + + p->engine.ps.scale = 11; + + /* Remember millimetres -> AFM units. */ + + pagex = PNT2AFM(p, ((double)pagex * 2.834)); + pagey = PNT2AFM(p, ((double)pagey * 2.834)); + + /* Margins are 1/9 the page x and y. */ + + marginx = /* LINTED */ + (size_t)((double)pagex / 9.0); + marginy = /* LINTED */ + (size_t)((double)pagey / 9.0); + + /* Line-height is 1.4em. */ + + lineheight = PNT2AFM(p, ((double)p->engine.ps.scale * 1.4)); + + p->engine.ps.width = pagex; + p->engine.ps.height = pagey; + p->engine.ps.header = pagey - (marginy / 2) - (lineheight / 2); + p->engine.ps.top = pagey - marginy; + p->engine.ps.footer = (marginy / 2) - (lineheight / 2); + p->engine.ps.bottom = marginy; + p->engine.ps.left = marginx; + p->engine.ps.lineheight = lineheight; + + p->defrmargin = pagex - (marginx * 2); + return(p); +} + + +void +pspdf_free(void *arg) +{ + struct termp *p; + + p = (struct termp *)arg; + + if (p->engine.ps.psmarg) + free(p->engine.ps.psmarg); + if (p->engine.ps.pdfobjs) + free(p->engine.ps.pdfobjs); + + term_free(p); +} + + +static void +ps_printf(struct termp *p, const char *fmt, ...) +{ + va_list ap; + int pos, len; + + va_start(ap, fmt); + + /* + * If we're running in regular mode, then pipe directly into + * vprintf(). If we're processing margins, then push the data + * into our growable margin buffer. + */ + + if ( ! (PS_MARGINS & p->engine.ps.flags)) { + len = vprintf(fmt, ap); + va_end(ap); + p->engine.ps.pdfbytes += /* LINTED */ + len < 0 ? 0 : (size_t)len; + return; + } + + /* + * XXX: I assume that the in-margin print won't exceed + * PS_BUFSLOP (128 bytes), which is reasonable but still an + * assumption that will cause pukeage if it's not the case. + */ + + ps_growbuf(p, PS_BUFSLOP); + + pos = (int)p->engine.ps.psmargcur; + len = vsnprintf(&p->engine.ps.psmarg[pos], PS_BUFSLOP, fmt, ap); + + va_end(ap); + + p->engine.ps.psmargcur = strlen(p->engine.ps.psmarg); +} + + +static void +ps_putchar(struct termp *p, char c) +{ + int pos; + + /* See ps_printf(). */ + + if ( ! (PS_MARGINS & p->engine.ps.flags)) { + /* LINTED */ + putchar(c); + p->engine.ps.pdfbytes++; + return; + } + + ps_growbuf(p, 2); + + pos = (int)p->engine.ps.psmargcur++; + p->engine.ps.psmarg[pos++] = c; + p->engine.ps.psmarg[pos] = '\0'; +} + + +static void +pdf_obj(struct termp *p, size_t obj) +{ + + assert(obj > 0); + + if ((obj - 1) >= p->engine.ps.pdfobjsz) { + p->engine.ps.pdfobjsz = obj + 128; + p->engine.ps.pdfobjs = realloc + (p->engine.ps.pdfobjs, + p->engine.ps.pdfobjsz * sizeof(size_t)); + if (NULL == p->engine.ps.pdfobjs) { + perror(NULL); + exit((int)MANDOCLEVEL_SYSERR); + } + } + + p->engine.ps.pdfobjs[(int)obj - 1] = p->engine.ps.pdfbytes; + ps_printf(p, "%zu 0 obj\n", obj); +} + + +static void +ps_closepage(struct termp *p) +{ + int i; + size_t len, base; + + /* + * Close out a page that we've already flushed to output. In + * PostScript, we simply note that the page must be showed. In + * PDF, we must now create the Length, Resource, and Page node + * for the page contents. + */ + + assert(p->engine.ps.psmarg && p->engine.ps.psmarg[0]); + ps_printf(p, "%s", p->engine.ps.psmarg); + + if (TERMTYPE_PS != p->type) { + ps_printf(p, "ET\n"); + + len = p->engine.ps.pdfbytes - p->engine.ps.pdflastpg; + base = p->engine.ps.pages * 4 + p->engine.ps.pdfbody; + + ps_printf(p, "endstream\nendobj\n"); + + /* Length of content. */ + pdf_obj(p, base + 1); + ps_printf(p, "%zu\nendobj\n", len); + + /* Resource for content. */ + pdf_obj(p, base + 2); + ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n"); + ps_printf(p, "/Font <<\n"); + for (i = 0; i < (int)TERMFONT__MAX; i++) + ps_printf(p, "/F%d %d 0 R\n", i, 3 + i); + ps_printf(p, ">>\n>>\n"); + + /* Page node. */ + pdf_obj(p, base + 3); + ps_printf(p, "<<\n"); + ps_printf(p, "/Type /Page\n"); + ps_printf(p, "/Parent 2 0 R\n"); + ps_printf(p, "/Resources %zu 0 R\n", base + 2); + ps_printf(p, "/Contents %zu 0 R\n", base); + ps_printf(p, ">>\nendobj\n"); + } else + ps_printf(p, "showpage\n"); + + p->engine.ps.pages++; + p->engine.ps.psrow = p->engine.ps.top; + assert( ! (PS_NEWPAGE & p->engine.ps.flags)); + p->engine.ps.flags |= PS_NEWPAGE; +} + + +/* ARGSUSED */ +static void +ps_end(struct termp *p) +{ + size_t i, xref, base; + + /* + * At the end of the file, do one last showpage. This is the + * same behaviour as groff(1) and works for multiple pages as + * well as just one. + */ + + if ( ! (PS_NEWPAGE & p->engine.ps.flags)) { + assert(0 == p->engine.ps.flags); + assert('\0' == p->engine.ps.last); + ps_closepage(p); + } + + if (TERMTYPE_PS == p->type) { + ps_printf(p, "%%%%Trailer\n"); + ps_printf(p, "%%%%Pages: %zu\n", p->engine.ps.pages); + ps_printf(p, "%%%%EOF\n"); + return; + } + + pdf_obj(p, 2); + ps_printf(p, "<<\n/Type /Pages\n"); + ps_printf(p, "/MediaBox [0 0 %zu %zu]\n", + (size_t)AFM2PNT(p, p->engine.ps.width), + (size_t)AFM2PNT(p, p->engine.ps.height)); + + ps_printf(p, "/Count %zu\n", p->engine.ps.pages); + ps_printf(p, "/Kids ["); + + for (i = 0; i < p->engine.ps.pages; i++) + ps_printf(p, " %zu 0 R", i * 4 + + p->engine.ps.pdfbody + 3); + + base = (p->engine.ps.pages - 1) * 4 + + p->engine.ps.pdfbody + 4; + + ps_printf(p, "]\n>>\nendobj\n"); + pdf_obj(p, base); + ps_printf(p, "<<\n"); + ps_printf(p, "/Type /Catalog\n"); + ps_printf(p, "/Pages 2 0 R\n"); + ps_printf(p, ">>\n"); + xref = p->engine.ps.pdfbytes; + ps_printf(p, "xref\n"); + ps_printf(p, "0 %zu\n", base + 1); + ps_printf(p, "0000000000 65535 f \n"); + + for (i = 0; i < base; i++) + ps_printf(p, "%.10zu 00000 n \n", + p->engine.ps.pdfobjs[(int)i]); + + ps_printf(p, "trailer\n"); + ps_printf(p, "<<\n"); + ps_printf(p, "/Size %zu\n", base + 1); + ps_printf(p, "/Root %zu 0 R\n", base); + ps_printf(p, "/Info 1 0 R\n"); + ps_printf(p, ">>\n"); + ps_printf(p, "startxref\n"); + ps_printf(p, "%zu\n", xref); + ps_printf(p, "%%%%EOF\n"); +} + + +static void +ps_begin(struct termp *p) +{ + time_t t; + int i; + + /* + * Print margins into margin buffer. Nothing gets output to the + * screen yet, so we don't need to initialise the primary state. + */ + + if (p->engine.ps.psmarg) { + assert(p->engine.ps.psmargsz); + p->engine.ps.psmarg[0] = '\0'; + } + + /*p->engine.ps.pdfbytes = 0;*/ + p->engine.ps.psmargcur = 0; + p->engine.ps.flags = PS_MARGINS; + p->engine.ps.pscol = p->engine.ps.left; + p->engine.ps.psrow = p->engine.ps.header; + + ps_setfont(p, TERMFONT_NONE); + + (*p->headf)(p, p->argf); + (*p->endline)(p); + + p->engine.ps.pscol = p->engine.ps.left; + p->engine.ps.psrow = p->engine.ps.footer; + + (*p->footf)(p, p->argf); + (*p->endline)(p); + + p->engine.ps.flags &= ~PS_MARGINS; + + assert(0 == p->engine.ps.flags); + assert(p->engine.ps.psmarg); + assert('\0' != p->engine.ps.psmarg[0]); + + /* + * Print header and initialise page state. Following this, + * stuff gets printed to the screen, so make sure we're sane. + */ + + t = time(NULL); + + if (TERMTYPE_PS == p->type) { + ps_printf(p, "%%!PS-Adobe-3.0\n"); + ps_printf(p, "%%%%Creator: mandoc-%s\n", VERSION); + ps_printf(p, "%%%%CreationDate: %s", ctime(&t)); + ps_printf(p, "%%%%DocumentData: Clean7Bit\n"); + ps_printf(p, "%%%%Orientation: Portrait\n"); + ps_printf(p, "%%%%Pages: (atend)\n"); + ps_printf(p, "%%%%PageOrder: Ascend\n"); + ps_printf(p, "%%%%DocumentMedia: " + "Default %zu %zu 0 () ()\n", + (size_t)AFM2PNT(p, p->engine.ps.width), + (size_t)AFM2PNT(p, p->engine.ps.height)); + ps_printf(p, "%%%%DocumentNeededResources: font"); + + for (i = 0; i < (int)TERMFONT__MAX; i++) + ps_printf(p, " %s", fonts[i].name); + + ps_printf(p, "\n%%%%EndComments\n"); + } else { + ps_printf(p, "%%PDF-1.1\n"); + pdf_obj(p, 1); + ps_printf(p, "<<\n"); + ps_printf(p, "/Creator mandoc-%s\n", VERSION); + ps_printf(p, ">>\n"); + ps_printf(p, "endobj\n"); + + for (i = 0; i < (int)TERMFONT__MAX; i++) { + pdf_obj(p, (size_t)i + 3); + ps_printf(p, "<<\n"); + ps_printf(p, "/Type /Font\n"); + ps_printf(p, "/Subtype /Type1\n"); + ps_printf(p, "/Name /F%zu\n", i); + ps_printf(p, "/BaseFont /%s\n", fonts[i].name); + ps_printf(p, ">>\n"); + } + } + + p->engine.ps.pdfbody = (size_t)TERMFONT__MAX + 3; + p->engine.ps.pscol = p->engine.ps.left; + p->engine.ps.psrow = p->engine.ps.top; + p->engine.ps.flags |= PS_NEWPAGE; + ps_setfont(p, TERMFONT_NONE); +} + + +static void +ps_pletter(struct termp *p, int c) +{ + int f; + + /* + * If we haven't opened a page context, then output that we're + * in a new page and make sure the font is correctly set. + */ + + if (PS_NEWPAGE & p->engine.ps.flags) { + if (TERMTYPE_PS == p->type) { + ps_printf(p, "%%%%Page: %zu %zu\n", + p->engine.ps.pages + 1, + p->engine.ps.pages + 1); + ps_printf(p, "/%s %zu selectfont\n", + fonts[(int)p->engine.ps.lastf].name, + p->engine.ps.scale); + } else { + pdf_obj(p, p->engine.ps.pdfbody + + p->engine.ps.pages * 4); + ps_printf(p, "<<\n"); + ps_printf(p, "/Length %zu 0 R\n", + p->engine.ps.pdfbody + 1 + + p->engine.ps.pages * 4); + ps_printf(p, ">>\nstream\n"); + } + p->engine.ps.pdflastpg = p->engine.ps.pdfbytes; + p->engine.ps.flags &= ~PS_NEWPAGE; + } + + /* + * If we're not in a PostScript "word" context, then open one + * now at the current cursor. + */ + + if ( ! (PS_INLINE & p->engine.ps.flags)) { + if (TERMTYPE_PS != p->type) { + ps_printf(p, "BT\n/F%d %zu Tf\n", + (int)p->engine.ps.lastf, + p->engine.ps.scale); + ps_printf(p, "%.3f %.3f Td\n(", + AFM2PNT(p, p->engine.ps.pscol), + AFM2PNT(p, p->engine.ps.psrow)); + } else + ps_printf(p, "%.3f %.3f moveto\n(", + AFM2PNT(p, p->engine.ps.pscol), + AFM2PNT(p, p->engine.ps.psrow)); + p->engine.ps.flags |= PS_INLINE; + } + + assert( ! (PS_NEWPAGE & p->engine.ps.flags)); + + /* + * We need to escape these characters as per the PostScript + * specification. We would also escape non-graphable characters + * (like tabs), but none of them would get to this point and + * it's superfluous to abort() on them. + */ + + switch (c) { + case ('('): + /* FALLTHROUGH */ + case (')'): + /* FALLTHROUGH */ + case ('\\'): + ps_putchar(p, '\\'); + break; + default: + break; + } + + /* Write the character and adjust where we are on the page. */ + + f = (int)p->engine.ps.lastf; + + if (c <= 32 || (c - 32 >= MAXCHAR)) { + ps_putchar(p, ' '); + p->engine.ps.pscol += (size_t)fonts[f].gly[0].wx; + return; + } + + ps_putchar(p, (char)c); + c -= 32; + p->engine.ps.pscol += (size_t)fonts[f].gly[c].wx; +} + + +static void +ps_pclose(struct termp *p) +{ + + /* + * Spit out that we're exiting a word context (this is a + * "partial close" because we don't check the last-char buffer + * or anything). + */ + + if ( ! (PS_INLINE & p->engine.ps.flags)) + return; + + if (TERMTYPE_PS != p->type) { + ps_printf(p, ") Tj\nET\n"); + } else + ps_printf(p, ") show\n"); + + p->engine.ps.flags &= ~PS_INLINE; +} + + +static void +ps_fclose(struct termp *p) +{ + + /* + * Strong closure: if we have a last-char, spit it out after + * checking that we're in the right font mode. This will of + * course open a new scope, if applicable. + * + * Following this, close out any scope that's open. + */ + + if ('\0' != p->engine.ps.last) { + if (p->engine.ps.lastf != TERMFONT_NONE) { + ps_pclose(p); + ps_setfont(p, TERMFONT_NONE); + } + ps_pletter(p, p->engine.ps.last); + p->engine.ps.last = '\0'; + } + + if ( ! (PS_INLINE & p->engine.ps.flags)) + return; + + ps_pclose(p); +} + + +static void +ps_letter(struct termp *p, char c) +{ + char cc; + + /* + * State machine dictates whether to buffer the last character + * or not. Basically, encoded words are detected by checking if + * we're an "8" and switching on the buffer. Then we put "8" in + * our buffer, and on the next charater, flush both character + * and buffer. Thus, "regular" words are detected by having a + * regular character and a regular buffer character. + */ + + if ('\0' == p->engine.ps.last) { + assert(8 != c); + p->engine.ps.last = c; + return; + } else if (8 == p->engine.ps.last) { + assert(8 != c); + p->engine.ps.last = '\0'; + } else if (8 == c) { + assert(8 != p->engine.ps.last); + if ('_' == p->engine.ps.last) { + if (p->engine.ps.lastf != TERMFONT_UNDER) { + ps_pclose(p); + ps_setfont(p, TERMFONT_UNDER); + } + } else if (p->engine.ps.lastf != TERMFONT_BOLD) { + ps_pclose(p); + ps_setfont(p, TERMFONT_BOLD); + } + p->engine.ps.last = c; + return; + } else { + if (p->engine.ps.lastf != TERMFONT_NONE) { + ps_pclose(p); + ps_setfont(p, TERMFONT_NONE); + } + cc = p->engine.ps.last; + p->engine.ps.last = c; + c = cc; + } + + ps_pletter(p, c); +} + + +static void +ps_advance(struct termp *p, size_t len) +{ + + /* + * Advance some spaces. This can probably be made smarter, + * i.e., to have multiple space-separated words in the same + * scope, but this is easier: just close out the current scope + * and readjust our column settings. + */ + + ps_fclose(p); + p->engine.ps.pscol += len; +} + + +static void +ps_endline(struct termp *p) +{ + + /* Close out any scopes we have open: we're at eoln. */ + + ps_fclose(p); + + /* + * If we're in the margin, don't try to recalculate our current + * row. XXX: if the column tries to be fancy with multiple + * lines, we'll do nasty stuff. + */ + + if (PS_MARGINS & p->engine.ps.flags) + return; + + /* Left-justify. */ + + p->engine.ps.pscol = p->engine.ps.left; + + /* If we haven't printed anything, return. */ + + if (PS_NEWPAGE & p->engine.ps.flags) + return; + + /* + * Put us down a line. If we're at the page bottom, spit out a + * showpage and restart our row. + */ + + if (p->engine.ps.psrow >= p->engine.ps.lineheight + + p->engine.ps.bottom) { + p->engine.ps.psrow -= p->engine.ps.lineheight; + return; + } + + ps_closepage(p); +} + + +static void +ps_setfont(struct termp *p, enum termfont f) +{ + + assert(f < TERMFONT__MAX); + p->engine.ps.lastf = f; + + /* + * If we're still at the top of the page, let the font-setting + * be delayed until we actually have stuff to print. + */ + + if (PS_NEWPAGE & p->engine.ps.flags) + return; + + if (TERMTYPE_PS == p->type) + ps_printf(p, "/%s %zu selectfont\n", + fonts[(int)f].name, + p->engine.ps.scale); + else + ps_printf(p, "/F%d %zu Tf\n", + (int)f, + p->engine.ps.scale); +} + + +/* ARGSUSED */ +static size_t +ps_width(const struct termp *p, char c) +{ + + if (c <= 32 || c - 32 >= MAXCHAR) + return((size_t)fonts[(int)TERMFONT_NONE].gly[0].wx); + + c -= 32; + return((size_t)fonts[(int)TERMFONT_NONE].gly[(int)c].wx); +} + + +static double +ps_hspan(const struct termp *p, const struct roffsu *su) +{ + double r; + + /* + * All of these measurements are derived by converting from the + * native measurement to AFM units. + */ + + switch (su->unit) { + case (SCALE_CM): + r = PNT2AFM(p, su->scale * 28.34); + break; + case (SCALE_IN): + r = PNT2AFM(p, su->scale * 72); + break; + case (SCALE_PC): + r = PNT2AFM(p, su->scale * 12); + break; + case (SCALE_PT): + r = PNT2AFM(p, su->scale * 100); + break; + case (SCALE_EM): + r = su->scale * + fonts[(int)TERMFONT_NONE].gly[109 - 32].wx; + break; + case (SCALE_MM): + r = PNT2AFM(p, su->scale * 2.834); + break; + case (SCALE_EN): + r = su->scale * + fonts[(int)TERMFONT_NONE].gly[110 - 32].wx; + break; + case (SCALE_VS): + r = su->scale * p->engine.ps.lineheight; + break; + default: + r = su->scale; + break; + } + + return(r); +} + diff --git a/commands/mdocml/test-strlcat.c b/usr.bin/mdocml/dist/test-strlcat.c similarity index 100% rename from commands/mdocml/test-strlcat.c rename to usr.bin/mdocml/dist/test-strlcat.c diff --git a/commands/mdocml/test-strlcpy.c b/usr.bin/mdocml/dist/test-strlcpy.c similarity index 100% rename from commands/mdocml/test-strlcpy.c rename to usr.bin/mdocml/dist/test-strlcpy.c diff --git a/commands/mdocml/tree.c b/usr.bin/mdocml/dist/tree.c similarity index 66% rename from commands/mdocml/tree.c rename to usr.bin/mdocml/dist/tree.c index 9125e6a5e..0efbfdbb6 100644 --- a/commands/mdocml/tree.c +++ b/usr.bin/mdocml/dist/tree.c @@ -1,4 +1,4 @@ -/* $Id: tree.c,v 1.21 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: tree.c,v 1.31 2011/01/03 13:59:21 kristaps Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * @@ -30,6 +30,7 @@ static void print_mdoc(const struct mdoc_node *, int); static void print_man(const struct man_node *, int); +static void print_span(const struct tbl_span *, int); /* ARGSUSED */ @@ -74,7 +75,10 @@ print_mdoc(const struct mdoc_node *n, int indent) t = "block-head"; break; case (MDOC_BODY): - t = "block-body"; + if (n->end) + t = "body-end"; + else + t = "block-body"; break; case (MDOC_TAIL): t = "block-tail"; @@ -85,11 +89,16 @@ print_mdoc(const struct mdoc_node *n, int indent) case (MDOC_TEXT): t = "text"; break; + case (MDOC_TBL): + t = "tbl"; + break; default: abort(); /* NOTREACHED */ } + p = NULL; + switch (n->type) { case (MDOC_TEXT): p = n->string; @@ -117,6 +126,8 @@ print_mdoc(const struct mdoc_node *n, int indent) argc = n->args->argc; } break; + case (MDOC_TBL): + break; case (MDOC_ROOT): p = "root"; break; @@ -125,24 +136,32 @@ print_mdoc(const struct mdoc_node *n, int indent) /* NOTREACHED */ } - for (i = 0; i < indent; i++) - (void)printf(" "); - (void)printf("%s (%s)", p, t); + if (n->span) { + assert(NULL == p); + print_span(n->span, indent); + } else { + for (i = 0; i < indent; i++) + putchar('\t'); - for (i = 0; i < (int)argc; i++) { - (void)printf(" -%s", mdoc_argnames[argv[i].arg]); - if (argv[i].sz > 0) - (void)printf(" ["); - for (j = 0; j < (int)argv[i].sz; j++) - (void)printf(" [%s]", argv[i].value[j]); - if (argv[i].sz > 0) - (void)printf(" ]"); + printf("%s (%s)", p, t); + + for (i = 0; i < (int)argc; i++) { + printf(" -%s", mdoc_argnames[argv[i].arg]); + if (argv[i].sz > 0) + printf(" ["); + for (j = 0; j < (int)argv[i].sz; j++) + printf(" [%s]", argv[i].value[j]); + if (argv[i].sz > 0) + printf(" ]"); + } + + for (i = 0; i < (int)sz; i++) + printf(" [%s]", params[i]); + + printf(" %d:%d", n->line, n->pos); } - for (i = 0; i < (int)sz; i++) - (void)printf(" [%s]", params[i]); - - (void)printf(" %d:%d\n", n->line, n->pos); + putchar('\n'); if (n->child) print_mdoc(n->child, indent + 1); @@ -176,11 +195,16 @@ print_man(const struct man_node *n, int indent) case (MAN_BODY): t = "block-body"; break; + case (MAN_TBL): + t = "tbl"; + break; default: abort(); /* NOTREACHED */ } + p = NULL; + switch (n->type) { case (MAN_TEXT): p = n->string; @@ -197,17 +221,69 @@ print_man(const struct man_node *n, int indent) case (MAN_ROOT): p = "root"; break; + case (MAN_TBL): + break; default: abort(); /* NOTREACHED */ } - for (i = 0; i < indent; i++) - (void)printf(" "); - (void)printf("%s (%s) %d:%d\n", p, t, n->line, n->pos); + if (n->span) { + assert(NULL == p); + print_span(n->span, indent); + } else { + for (i = 0; i < indent; i++) + putchar('\t'); + printf("%s (%s) %d:%d", 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_span(const struct tbl_span *sp, int indent) +{ + const struct tbl_dat *dp; + int i; + + for (i = 0; i < indent; i++) + putchar('\t'); + + printf("tbl: "); + + switch (sp->pos) { + case (TBL_SPAN_HORIZ): + putchar('-'); + return; + case (TBL_SPAN_DHORIZ): + putchar('='); + return; + default: + break; + } + + for (dp = sp->first; dp; dp = dp->next) { + switch (dp->pos) { + case (TBL_DATA_HORIZ): + /* FALLTHROUGH */ + case (TBL_DATA_NHORIZ): + putchar('-'); + continue; + case (TBL_DATA_DHORIZ): + /* FALLTHROUGH */ + case (TBL_DATA_NDHORIZ): + putchar('='); + continue; + default: + break; + } + printf("[%s%s]", dp->string, dp->layout ? "" : "*"); + if (dp->next) + putchar(' '); + } +} diff --git a/commands/mdocml/vol.c b/usr.bin/mdocml/dist/vol.c similarity index 94% rename from commands/mdocml/vol.c rename to usr.bin/mdocml/dist/vol.c index 144d363ff..bd5afd533 100644 --- a/commands/mdocml/vol.c +++ b/usr.bin/mdocml/dist/vol.c @@ -1,4 +1,4 @@ -/* $Id: vol.c,v 1.8 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: vol.c,v 1.8 2010/06/19 20:46:28 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/commands/mdocml/vol.in b/usr.bin/mdocml/dist/vol.in similarity index 95% rename from commands/mdocml/vol.in rename to usr.bin/mdocml/dist/vol.in index 7650b57a1..1ccda468b 100644 --- a/commands/mdocml/vol.in +++ b/usr.bin/mdocml/dist/vol.in @@ -1,4 +1,4 @@ -/* $Id: vol.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */ +/* $Vendor-Id: vol.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * diff --git a/usr.bin/mdocml/lib/Makefile b/usr.bin/mdocml/lib/Makefile new file mode 100644 index 000000000..60e75e410 --- /dev/null +++ b/usr.bin/mdocml/lib/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.2 2010/06/01 21:32:39 joerg Exp $ + +SUBDIR= libman libmdoc libroff + +.include diff --git a/usr.bin/mdocml/lib/Makefile.inc b/usr.bin/mdocml/lib/Makefile.inc new file mode 100644 index 000000000..0fe99142a --- /dev/null +++ b/usr.bin/mdocml/lib/Makefile.inc @@ -0,0 +1,3 @@ +# $NetBSD: Makefile.inc,v 1.1 2009/10/21 18:04:52 joerg Exp $ + +.include "${.PARSEDIR}/../Makefile.inc" diff --git a/usr.bin/mdocml/lib/libman/Makefile b/usr.bin/mdocml/lib/libman/Makefile new file mode 100644 index 000000000..326eee4d6 --- /dev/null +++ b/usr.bin/mdocml/lib/libman/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.2 2011/01/12 23:02:22 joerg Exp $ + +LIBISPRIVATE= yes + +LIB= man +SRCS= man_macro.c man.c man_hash.c man_validate.c \ + mandoc.c man_argv.c +MAN= mdoc.3 + +.include diff --git a/usr.bin/mdocml/lib/libmdoc/Makefile b/usr.bin/mdocml/lib/libmdoc/Makefile new file mode 100644 index 000000000..5a77a7f8e --- /dev/null +++ b/usr.bin/mdocml/lib/libmdoc/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.2 2011/01/12 23:02:22 joerg Exp $ + +LIBISPRIVATE= yes + +LIB= mdoc +SRCS= mdoc_macro.c mdoc.c mdoc_hash.c mdoc_strings.c \ + mdoc_argv.c mdoc_validate.c lib.c att.c \ + arch.c vol.c msec.c st.c mandoc.c +MAN= man.3 + +.include diff --git a/usr.bin/mdocml/lib/libroff/Makefile b/usr.bin/mdocml/lib/libroff/Makefile new file mode 100644 index 000000000..efd7c5cea --- /dev/null +++ b/usr.bin/mdocml/lib/libroff/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.2 2011/01/12 23:02:22 joerg Exp $ + +LIBISPRIVATE= yes + +LIB= roff +SRCS= roff.c tbl.c tbl_opts.c tbl_layout.c tbl_data.c +MAN= + +COPTS.tbl_layout.c= -Wno-shadow + +.include diff --git a/usr.bin/mdocml/man/Makefile b/usr.bin/mdocml/man/Makefile new file mode 100644 index 000000000..24195f835 --- /dev/null +++ b/usr.bin/mdocml/man/Makefile @@ -0,0 +1,35 @@ +# $NetBSD: Makefile,v 1.6 2011/01/12 23:02:22 joerg Exp $ + +MAN= mandoc_man.7 mandoc_char.7 mandoc_mdoc.7 mandoc_roff.7 mandoc_tbl.7 + +.include + +mandoc_man.7: ${DISTDIR}/man.7 + ${_MKTARGET_CREATE} + rm -f ${.TARGET} + ${TOOL_CAT} ${DISTDIR}/man.7 > ${.TARGET} + +mandoc_mdoc.7: ${DISTDIR}/mdoc.7 + ${_MKTARGET_CREATE} + rm -f ${.TARGET} + ${TOOL_CAT} ${DISTDIR}/mdoc.7 > ${.TARGET} + +mandoc_roff.7: ${DISTDIR}/roff.7 + ${_MKTARGET_CREATE} + rm -f ${.TARGET} + ${TOOL_CAT} ${DISTDIR}/roff.7 > ${.TARGET} + +mandoc_tbl.7: ${DISTDIR}/tbl.7 + ${_MKTARGET_CREATE} + rm -f ${.TARGET} + ${TOOL_CAT} ${DISTDIR}/tbl.7 > ${.TARGET} + +CLEANFILES+= mandoc_man.7 mandoc_mdoc.7 mandoc_roff.7 mandoc_tbl.7 + +.if (${MKHTML} != "no") && (${MKMAN} != "no") +FILES= ${DISTDIR}/example.style.css +FILESDIR= /usr/man +FILESNAME= style.css +.endif + +.include diff --git a/usr.bin/mdocml/prepare-import.sh b/usr.bin/mdocml/prepare-import.sh new file mode 100644 index 000000000..95f6f565d --- /dev/null +++ b/usr.bin/mdocml/prepare-import.sh @@ -0,0 +1,14 @@ +#/bin/sh + +set -e + +cd dist +rm -rf ChangeLog.xsl style.css index.css *.sgml + +uuencode external.png < external.png > external.png.uu +rm external.png + +for f in [a-z]*; do + sed -e 's/[$]Id:/\$Vendor-Id:/' \ + -e 's/[$]Mdocdate: \([^$]*\) \([0-9][0-9][0-9][0-9]\) [$]/\1, \2/' < $f > $f.new && mv $f.new $f +done