rather than(like .Bd -literal does). - Reported by afresh1@ 12 Apr 2016 14:35:45 -0700 - - .Bf at the beginning of a paragraph inserts a bogus 1ex horizontal space, see for example random(3). Introduced in http://mdocml.bsd.lv/cgi-bin/cvsweb/mdoc_html.c.diff?r1=1.91&r2=1.92 reported by deraadt@ Mon, 28 Sep 2015 20:14:13 -0600 (MDT) loc ** exist ** algo ** size * imp * - jsg on icb, Nov 3, 2014: try to guess Xr in man(7) for hyperlinking + and render them with + https://github.com/Debian/debiman/issues/15 + loc * exist * algo ** size ** imp ** - The tables used to render the three-part page headers actually force the width of the to the max-width given for . Not yet sure how to fix that... Observed by an Anonymous Coward on undeadly.org: http://undeadly.org/cgi?action=article&sid=20140925064244&pid=1 loc * exist * algo ** size * imp *** -- consider whether can be used for Ar Dv Er Ev Fa Va. - from bentley@ Wed, 13 Aug 2014 09:17:55 -0600 - - generatetags in HTML idea from florian@ Tue, 7 Apr 2015 00:26:28 +0000 may be possible to implement with .Lk img://something.png alt_text - check https://github.com/trentm/mdocml ************************************************************************ * formatting issues: gratuitous differences ************************************************************************ - .Fn reopens a new scope after punctuation in mandoc, but closes its scope for good in groff. Do we want to change mandoc or groff? Steffen Nurpmeso Sat, 08 Nov 2014 13:34:59 +0100 loc * exist ** algo ** size * imp ** +- .Lk formatting for long links with line breaks + Franco Fichtner 8 Oct 2013 00:33:42 +0200 + - In .Bl -enum -width 0n, groff continues one the same line after the number, mandoc breaks the line. mail to kristaps@ Mon, 20 Jul 2009 02:21:39 +0200 loc * exist ** algo ** size * imp ** - .Pp between two .It in .Bl -column should produce one, not two blank lines, see e.g. login.conf(5). reported by jmc@ Sun, 17 Apr 2011 14:04:58 +0059 reported again by sthen@ Wed, 18 Jan 2012 02:09:39 +0000 (UTC) loc * exist *** algo ** size * imp ** - If the *first* line after .It is .Pp, break the line right after the tag, do not pad with space characters before breaking. See the description of the a, c, and i commands in sed(1). loc * exist ** algo ** size * imp ** - If the first line after .It is .D1, do not assert a blank line in between, see for example tmux(1). reported by nicm@ 13 Jan 2011 00:18:57 +0000 loc * exist ** algo ** size * imp ** - Trailing punctuation after .It should trigger EOS spacing. reported by Nicolas Joly Sat, 17 Nov 2012 11:49:54 +0100 Probably, this should be fixed somewhere in termp_it_pre(), not sure. loc * exist ** algo ** size * imp ** - .Nx 1.0a should be "NetBSD 1.0A", not "NetBSD 1.0a", see OpenBSD ccdconfig(8). loc * exist * algo * size * imp ** - In .Bl -tag, if a tag exceeds the right margin and must be continued on the next line, it must be indented by -width, not width+1; see "rule block|pass" in OpenBSD ifconfig(8). loc * exist *** algo ** size * imp ** - When the -width string contains macros, the macros must be rendered before measuring the width, for example .Bl -tag -width ".Dv message" in magic(5), located in src/usr.bin/file, is the same as -width 7n, not -width 11n. The same applies to .Bl -column column widths; reported again by Nicolas Joly Thu, 1 Mar 2012 13:41:26 +0100 via wiz@ 5 Mar reported again by Franco Fichtner Fri, 27 Sep 2013 21:02:28 +0200 + reported again by Bruce Evans Fri, 17 Feb 2017 21:22:44 +0100 via bapt@ loc *** exist *** algo *** size ** imp *** An easy partial fix would be to just skip the first word if it starts with a dot, including any following white space, when measuring. loc * exist * algo * size * imp *** - The \& zero-width character counts as output. That is, when it is alone on a line between two .Pp, we want three blank lines, not two as in mandoc. loc ** exist ** algo ** size * imp ** -- Header lines of excessive length: - Port OpenBSD man_term.c rev. 1.25 to mdoc_term.c - and document it in mdoc(7) and man(7) COMPATIBILITY - found while talking to Chris Bennett - loc * exist * algo * size * imp * - - Sequences of multiple man(7) paragraphs (.PP, .IP) interspersed with .ps and .nf/.fi produce execessive blank lines, see libJudy and graphics/dcmtk. The parser reorg may help with this. - trailing whitespace must be ignored even when followed by a font escape, see for example makes \fBdig \fR operate in batch mode in dig(1). loc ** exist ** algo ** size * imp ** ************************************************************************ * warning issues ************************************************************************ - provide a way in mandoc(1) to warn about broken .Xr links; probably cannot be on by default in -Tlint because it needs to access the manpath and mandoc.db(3) after parsing. asked for by jmc@ Fri, 4 Dec 2015 22:39:40 +0000 -- Report errors in -O suboption parsing. - loc * exist * algo * size * imp ** - - warn when .Sh or .Ss contain other macros Steffen Nurpmeso, savannah.gnu.org/bugs/index.php?45034 loc * exist * algo * size * imp ** -- check that MANDOCERR_BADTAB is thrown in the right cases, - i.e. when finding a literal tab character in fill mode, - and possibly change the wording of the warning message - to refer to fill mode, not literal mode - See the mail from Werner LEMBERG on the groff list, - Fri, 14 Feb 2014 18:54:42 +0100 (CET) - loc * exist ** algo ** size * imp ** - - warn about attempts to call non-callable macros Steffen Nurpmeso Tue, 11 Nov 2014 22:55:16 +0100 Note that formatting is inconsistent in groff. .Fn Po prints "Po()", .Ar Sh prints "file ..." and no "Sh". Relatively hard because the relevant code is scattered all over mdoc_macro.c and all subtly different. loc ** exist ** algo ** size ** imp ** -- warn about "new sentence, new line" - loc ** exist ** algo *** size * imp ** - - mandoc_special does not really check the escape sequence, but just the overall format loc ** exist ** algo *** size ** imp ** - integrate mdoclint into mandoc ("end-of-line whitespace" thread) from jmc@ Mon, 13 Jul 2009 17:12:09 +0100 from kristaps@ Mon, 13 Jul 2009 18:34:53 +0200 from jmc@ Mon, 13 Jul 2009 17:45:37 +0059 from kristaps@ Mon, 13 Jul 2009 19:02:03 +0200 (mostly done, check what remains) - -Tlint parser errors and warnings to stdout to tech@mdocml, naddy@ Wed, 28 Sep 2011 11:21:46 +0200 wait! kristaps@ Sun, 02 Oct 2011 17:12:52 +0200 ************************************************************************ * documentation issues ************************************************************************ -- mention hyphenation rules: - breaking at letter-letter in text mode (not macro args) - proper hyphenation is unimplemented - -- talk about spacing around delimiters - to jmc@, kristaps@ Sat, 23 Apr 2011 17:41:27 +0200 - - mark macros as: page structure domain, manual domain, general text domain is this useful? - mention /usr/share/misc/mdoc.template in mdoc(7)? - Is all the content from http://www.std.com/obi/BSD/doc/usd/28.tbl/tbl covered in tbl(7)? ************************************************************************ * performance issues ************************************************************************ - Why are we using MAP_SHARED, not MAP_PRIVATE for mmap(2)? from kristaps@ Sat, 09 Aug 2014 13:51:36 +0200 Several areas can be cleaned up to make mandoc even faster. These are - improve hashing mechanism for macros (quite important: performance) -- improve hashing mechanism for characters (not as important) - - the PDF file is HUGE: this can be reduced by using relative offsets -- instead of re-initialising the roff predefined-strings set before each - parse, create a read-only version the first time and copy it - loc * exist ** algo ** size * imp ** - ************************************************************************ * structural issues ************************************************************************ - POSIX says in the documentation of sysconf(3) that PATH_MAX is allowed to be so large that it is a bad idea to use it for sizing static buffers. So use dynamic buffers throughout. See the file test-PATH_MAX.c for details. Found by Aaron M. Ucko in the GNU Hurd via Bdale Garbee, https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=829624 - We use the input line number at several places to distinguish same-line from different-line input. That plainly doesn't work with user-defined macros, leading to random breakage. - Find better ways to prevent endless loops in roff(7) macro and string expansion. - Finish cleanup of date handling. Decide which formats should be recognized where. Update both mdoc(7) and man(7) documentation. Triggered by Tim van der Molen Tue, 22 Feb 2011 20:30:45 +0100 - struct mparse refactoring Steffen Nurpmeso Thu, 04 Sep 2014 12:50:00 +0200 ************************************************************************ * CGI issues ************************************************************************ - Enable HTTP compression by detecting gzip encoding and filtering output through libz. - Sandbox (see OpenSSH). - Enable caching support via HTTP 304 and If-Modified-Since. - - Allow for cgi.h to be overridden by CGI environment variables. - Otherwise, binary distributions will inherit the compile-time - behaviour, which is not optimal. - Have Mac OSX systems automatically disable -static compilation of the CGI: -static isn't supported. ************************************************************************ * to improve in the groff_mdoc(7) macros ************************************************************************ - use uname(1) to set doc-default-operating-system at install time tobimensch Mon, 1 Dec 2014 00:25:07 +0100 Index: vendor/mdocml/dist/apropos.1 =================================================================== --- vendor/mdocml/dist/apropos.1 (revision 313955) +++ vendor/mdocml/dist/apropos.1 (revision 313956) @@ -1,485 +1,489 @@ -.\" $Id: apropos.1,v 1.39 2015/04/03 08:46:17 schwarze Exp $ +.\" $Id: apropos.1,v 1.40 2017/01/31 19:44:04 schwarze Exp $ .\" .\" Copyright (c) 2011, 2012 Kristaps Dzonsons
.\" Copyright (c) 2011, 2012, 2014 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 3 2015 $ +.Dd $Mdocdate: January 31 2017 $ .Dt APROPOS 1 .Os .Sh NAME .Nm apropos , .Nm whatis .Nd search manual page databases .Sh SYNOPSIS .Nm .Op Fl acfhklw .Op Fl C Ar file .Op Fl M Ar path .Op Fl m Ar path .Op Fl O Ar outkey .Op Fl S Ar arch .Op Fl s Ar section .Ar expression ... .Sh DESCRIPTION The .Nm apropos and .Nm whatis utilities query manual page databases generated by .Xr makewhatis 8 , evaluating .Ar expression for each file in each database. By default, they display the names, section numbers, and description lines of all matching manuals. .Pp By default, .Nm searches for .Xr makewhatis 8 databases in the default paths stipulated by .Xr man 1 and uses case-insensitive substring matching .Pq the Cm = No operator over manual names and descriptions .Pq the Li \&Nm No and Li \&Nd No macro keys . Multiple terms imply pairwise .Fl o . .Pp .Nm whatis is a synonym for .Nm .Fl f . .Pp The options are as follows: .Bl -tag -width Ds .It Fl a Instead of showing only the title lines, show the complete manual pages, just like .Xr man 1 .Fl a would. If the standard output is a terminal device and .Fl c is not specified, use .Xr more 1 to paginate them. In .Fl a mode, the options .Fl IKOTW described in the .Xr mandoc 1 manual are also available. .It Fl C Ar file Specify an alternative configuration .Ar file in .Xr man.conf 5 format. .It Fl c In .Fl a mode, copy the formatted manual pages to the standard output without using .Xr more 1 to paginate them. .It Fl f Search for all words in .Ar expression in manual page names only. The search is case insensitive and matches whole words only. In this mode, macro keys, comparison operators, and logical operators are not available. This overrides any earlier .Fl k and .Fl l options. .It Fl h Instead of showing the title lines, show the SYNOPSIS sections, just like .Xr man 1 .Fl h would. .It Fl k Support the full .Ar expression syntax. This overrides any earlier .Fl f and .Fl l options. It is the default for .Nm . .It Fl l An alias for .Xr mandoc 1 .Fl a . This overrides any earlier .Fl f , .Fl k , and .Fl w options. .It Fl M Ar path Use the colon-separated path instead of the default list of paths searched for .Xr makewhatis 8 databases. Invalid paths, or paths without manual databases, are ignored. .It Fl m Ar path Prepend the colon-separated paths to the list of paths searched for .Xr makewhatis 8 databases. Invalid paths, or paths without manual databases, are ignored. .It Fl O Ar outkey Show the values associated with the key .Ar outkey instead of the manual descriptions. .It Fl S Ar arch Restrict the search to pages for the specified .Xr machine 1 architecture. .Ar arch is case insensitive. 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. See .Xr man 1 for a listing of sections. .It Fl w Instead of showing title lines, show the pathnames of the matching manual pages, just like .Xr man 1 .Fl w would. .El .Pp An .Ar expression consists of search terms joined by logical operators .Fl a .Pq and and .Fl o .Pq or . The .Fl a operator has precedence over .Fl o and both are evaluated left-to-right. .Bl -tag -width Ds .It \&( Ar expr No \&) True if the subexpression .Ar expr is true. .It Ar expr1 Fl a Ar expr2 True if both .Ar expr1 and .Ar expr2 are true (logical .Sq and ) . .It Ar expr1 Oo Fl o Oc Ar expr2 True if .Ar expr1 and/or .Ar expr2 evaluate to true (logical .Sq or ) . .It Ar term True if .Ar term is satisfied. This has syntax .Sm off .Oo .Op Ar key Op , Ar key ... .Pq Cm = | \(ti .Oc .Ar val , .Sm on where .Ar key is an .Xr mdoc 7 macro to query and .Ar val is its value. See .Sx Macro Keys for a list of available keys. Operator .Cm = evaluates a substring, while .Cm \(ti evaluates a regular expression. .It Fl i Ar term If .Ar term is a regular expression, it is evaluated case-insensitively. Has no effect on substring terms. .El .Pp Results are sorted by manual sections and names, with output formatted as .Pp .D1 name[, name...](sec) \- description .Pp Where .Dq name is the manual's name, .Dq sec is the manual section, and .Dq description is the manual's short description. If an architecture is specified for the manual, it is displayed as .Pp .D1 name(sec/arch) \- description .Pp Resulting manuals may be accessed as .Pp .Dl $ man \-s sec name .Pp If an architecture is specified in the output, use .Pp .Dl $ man \-s sec \-S arch name .Ss Macro Keys Queries evaluate over a subset of .Xr mdoc 7 macros indexed by .Xr makewhatis 8 . In addition to the macro keys listed below, the special key .Cm any may be used to match any available macro key. .Pp Names and description: .Bl -column "xLix" description -offset indent -compact .It Li \&Nm Ta manual name .It Li \&Nd Ta one-line manual description .It Li arch Ta machine architecture (case-insensitive) .It Li sec Ta manual section number .El .Pp Sections and cross references: .Bl -column "xLix" description -offset indent -compact .It Li \&Sh Ta section header (excluding standard sections) .It Li \&Ss Ta subsection header .It Li \&Xr Ta cross reference to another manual page .It Li \&Rs Ta bibliographic reference .El .Pp Semantic markup for command line utilities: .Bl -column "xLix" description -offset indent -compact .It Li \&Fl Ta command line options (flags) .It Li \&Cm Ta command modifier .It Li \&Ar Ta command argument .It Li \&Ic Ta internal or interactive command .It Li \&Ev Ta environmental variable .It Li \&Pa Ta file system path .El .Pp Semantic markup for function libraries: .Bl -column "xLix" description -offset indent -compact .It Li \&Lb Ta function library name .It Li \&In Ta include file .It Li \&Ft Ta function return type .It Li \&Fn Ta function name .It Li \&Fa Ta function argument type and name .It Li \&Vt Ta variable type .It Li \&Va Ta variable name .It Li \&Dv Ta defined variable or preprocessor constant .It Li \&Er Ta error constant .It Li \&Ev Ta environmental variable .El .Pp Various semantic markup: .Bl -column "xLix" description -offset indent -compact .It Li \&An Ta author name .It Li \&Lk Ta hyperlink .It Li \&Mt Ta Do mailto Dc hyperlink .It Li \&Cd Ta kernel configuration declaration .It Li \&Ms Ta mathematical symbol .It Li \&Tn Ta tradename .El .Pp Physical markup: .Bl -column "xLix" description -offset indent -compact .It Li \&Em Ta italic font or underline .It Li \&Sy Ta boldface font .It Li \&Li Ta typewriter font .El .Pp Text production: .Bl -column "xLix" description -offset indent -compact .It Li \&St Ta reference to a standards document .It Li \&At Ta At No version reference .It Li \&Bx Ta Bx No version reference .It Li \&Bsx Ta Bsx No version reference .It Li \&Nx Ta Nx No version reference .It Li \&Fx Ta Fx No version reference .It Li \&Ox Ta Ox No version reference .It Li \&Dx Ta Dx No version reference .El .Sh ENVIRONMENT .Bl -tag -width MANPAGER .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER will be used instead of the standard pagination program, .Xr more 1 . .It Ev MANPATH The standard search path used by .Xr man 1 may be changed by specifying a path in the .Ev MANPATH environment variable. Invalid paths, or paths without manual databases, are ignored. Overridden by .Fl M . If .Ev MANPATH begins with a colon, it is appended to the default list; if it ends with a colon, it is prepended to the default list; or if it contains two adjacent colons, the standard search path is inserted between the colons. If none of these conditions are met, it overrides the standard search path. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, .Xr more 1 .Fl s will be used. .El .Sh FILES .Bl -tag -width "/etc/man.conf" -compact .It Pa mandoc.db name of the .Xr makewhatis 8 keyword database .It Pa /etc/man.conf default .Xr man 1 configuration file .El .Sh EXIT STATUS .Ex -std .Sh EXAMPLES Search for .Qq .cf as a substring of manual names and descriptions: .Pp .Dl $ apropos .cf .Pp Include matches for .Qq .cnf and .Qq .conf as well: .Pp .Dl $ apropos .cf .cnf .conf .Pp Search in names and descriptions using a regular expression: .Pp .Dl $ apropos \(aq\(tiset.?[ug]id\(aq .Pp Search for manuals in the library section mentioning both the .Qq optind and the .Qq optarg variables: .Pp .Dl $ apropos \-s 3 Va=optind \-a Va=optarg .Pp Do exactly the same as calling .Xr whatis 1 with the argument .Qq ssh : .Pp .Dl $ apropos \-\- \-i \(aqNm\(ti[[:<:]]ssh[[:>:]]\(aq .Pp The following two invocations are equivalent: .Pp .D1 Li $ apropos -S Ar arch Li -s Ar section expression .Bd -ragged -offset indent .Li $ apropos \e( Ar expression Li \e) .Li -a arch\(ti^( Ns Ar arch Ns Li |any)$ .Li -a sec\(ti^ Ns Ar section Ns Li $ .Ed .Sh SEE ALSO .Xr man 1 , .Xr re_format 7 , .Xr makewhatis 8 .Sh HISTORY Part of the functionality of .Nm whatis was already provided by the former .Nm manwhere utility in .Bx 1 . The .Nm and .Nm whatis utilities first appeared in .Bx 2 . They were rewritten from scratch for .Ox 5.6 . .Pp The .Fl M option and the .Ev MANPATH variable first appeared in .Bx 4.3 ; .Fl m in .Bx 4.3 Reno ; .Fl C in .Bx 4.4 Lite1 ; and .Fl S and .Fl s in .Ox 4.5 for .Nm and in .Ox 5.6 for .Nm whatis . +The options +.Fl acfhIKklOTWw +appeared in +.Ox 5.7 . .Sh AUTHORS .An -nosplit .An Bill Joy wrote .Nm manwhere in 1977 and the original .Bx .Nm and .Nm whatis in February 1979. The current version was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv and .An Ingo Schwarze Aq Mt schwarze@openbsd.org . Index: vendor/mdocml/dist/catman.8 =================================================================== --- vendor/mdocml/dist/catman.8 (nonexistent) +++ vendor/mdocml/dist/catman.8 (revision 313956) @@ -0,0 +1,186 @@ +.\" $Id: catman.8,v 1.7 2017/02/06 19:04:21 schwarze Exp $ +.\" +.\" Copyright (c) 2017 Ingo Schwarze +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: February 6 2017 $ +.Dt CATMAN 8 +.Os +.Sh NAME +.Nm catman +.Nd format all manual pages below a directory +.Sh SYNOPSIS +.Nm catman +.Op Fl I Cm os Ns = Ns Ar name +.Op Fl T Ar output +.Ar srcdir dstdir +.Sh DESCRIPTION +The +.Nm +utility assumes that all files below +.Ar srcdir +are manual pages in +.Xr mdoc 7 +and +.Xr man 7 +format and formats all of them, storing the formatted versions in +the same relative paths below +.Ar dstdir . +Subdirectories of +.Ar dstdir +are created as needed. +Existing files are not explicitly deleted, but possibly overwritten. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl I Cm os Ns = Ns Ar name +Override the default operating system +.Ar name +for the +.Xr mdoc 7 +.Ic Os +and for the +.Xr man 7 +.Ic TH +macro. +.It Fl T Ar output +Output format. +The +.Ar output +argument can be +.Cm ascii , +.Cm utf8 , +or +.Cm html ; +see +.Xr mandoc 1 . +In +.Cm html +output mode, the +.Cm fragment +output option is implied. +Other output options are not supported. +.El +.Sh IMPLEMENTATION NOTES +Since this version avoids +.Xr fork 2 +and +.Xr exec 3 +overhead and uses the much faster +.Sy mandoc +parsers and formatters rather than +.Sy groff , +it may be about one order of magnitude faster than other +.Nm +implementations. +.Sh EXIT STATUS +.Ex -std +.Pp +Possible errors include: +.Bl -bullet +.It +missing, invalid, or excessive command line arguments +.It +failure to change the current working directory to +.Ar srcdir +.It +failure to open +.Ar dstdir +.It +communication failure with +.Xr mandocd 8 +.It +resource exhaustion, for example file descriptor, process table, +or memory exhaustion +.El +.Pp +Except for memory exhaustion and similar system-level failures, +failures while trying to open, read, parse, or format individual +manual pages, to save individual formatted files to the file system, +or even to create directories do not cause +.Nm +to return an error exit status. +In such cases, +.Nm +will simply continue with the next file or subdirectory. +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr mandocd 8 +.Sh HISTORY +A +.Nm +utility first appeared in +.Fx 1.0 . +Other, incompatible implementations appeared in +.Nx 1.0 +and in +.Sy man-db No 2.2 . +.Pp +This version appeared in version 1.14.1 of the +.Sy mandoc +toolkit. +.Sh AUTHORS +.An -nosplit +The first +.Nm +implementation was a short shell script by +.An Christoph Robitschko +in July 1993. +.Pp +The +.Nx +implementations were written by +.An J. T. Conklin Aq Mt jtc@netbsd.org +in 1993, +.An Christian E. Hopps Aq Mt chopps@netbsd.org +in 1994, +and +.An Dante Profeta Aq Mt dante@netbsd.org +in 1999; the +.Sy man-db +implementation by +.An Graeme W. Wilford +in 1994; and the +.Fx +implementations by +.An Wolfram Schneider Aq Mt wosch@freebsd.org +in 1995 and +.An John Rochester Aq Mt john@jrochester.org +in 2002. +.Pp +The concept of the present version was designed and implemented by +.An Michael Stapelberg Aq Mt stapelberg@debian.org +in 2017. +Option and argument handling and directory iteration was added by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +.Sh CAVEATS +All versions of +.Nm +are incompatible with each other because each caters to the needs +of a specific operating system, for example regarding directory +structures and file naming conventions. +.Pp +This version is more flexible than the others in so far as it does +not assume any particular directory structure or naming convention. +That flexibility comes at the price of not being able to change the +names and relative paths of the source files when reusing them to +store the formatted files, of not supporting any configuration file +formats or environment variables, and of being unable to scan for +and remove junk files in +.Ar dstdir . +.Pp +Currently, +.Nm +always reformats each page, even if the formatted version is newer +than the source version. Property changes on: vendor/mdocml/dist/catman.8 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/catman.c =================================================================== --- vendor/mdocml/dist/catman.c (nonexistent) +++ vendor/mdocml/dist/catman.c (revision 313956) @@ -0,0 +1,260 @@ +/* $Id: catman.c,v 1.21 2017/02/18 12:24:24 schwarze Exp $ */ +/* + * Copyright (c) 2017 Michael Stapelberg + * Copyright (c) 2017 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. + */ +#include "config.h" + +#if HAVE_CMSG_XPG42 +#define _XPG4_2 +#endif + +#include +#include +#include + +#if HAVE_ERR +#include +#endif +#include +#include +#if HAVE_FTS +#include +#else +#include "compat_fts.h" +#endif +#include +#include +#include +#include +#include + +int process_manpage(int, int, const char *); +int process_tree(int, int); +void run_mandocd(int, const char *, const char *) + __attribute__((__noreturn__)); +ssize_t sock_fd_write(int, int, int, int); +void usage(void) __attribute__((__noreturn__)); + + +void +run_mandocd(int sockfd, const char *outtype, const char* defos) +{ + char sockfdstr[10]; + + if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1) + err(1, "snprintf"); + if (defos == NULL) + execlp("mandocd", "mandocd", "-T", outtype, + sockfdstr, (char *)NULL); + else + execlp("mandocd", "mandocd", "-T", outtype, + "-I", defos, sockfdstr, (char *)NULL); + err(1, "exec"); +} + +ssize_t +sock_fd_write(int fd, int fd0, int fd1, int fd2) +{ + const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */ + struct msghdr msg; + struct iovec iov; + union { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(3 * sizeof(int))]; + } cmsgu; + struct cmsghdr *cmsg; + int *walk; + ssize_t sz; + unsigned char dummy[1] = {'\0'}; + + iov.iov_base = dummy; + iov.iov_len = sizeof(dummy); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + walk = (int *)CMSG_DATA(cmsg); + *(walk++) = fd0; + *(walk++) = fd1; + *(walk++) = fd2; + + /* + * It appears that on some systems, sendmsg(3) + * may return EAGAIN even in blocking mode. + * Seen for example on Oracle Solaris 11.2. + * The sleeping time was chosen by experimentation, + * to neither cause more than a handful of retries + * in normal operation nor unnecessary delays. + */ + for (;;) { + if ((sz = sendmsg(fd, &msg, 0)) != -1 || + errno != EAGAIN) + break; + nanosleep(&timeout, NULL); + } + return sz; +} + +int +process_manpage(int srv_fd, int dstdir_fd, const char *path) +{ + int in_fd, out_fd; + int irc; + + if ((in_fd = open(path, O_RDONLY)) == -1) { + warn("open(%s)", path); + return 0; + } + + if ((out_fd = openat(dstdir_fd, path, + O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) { + warn("openat(%s)", path); + close(in_fd); + return 0; + } + + irc = sock_fd_write(srv_fd, in_fd, out_fd, STDERR_FILENO); + + close(in_fd); + close(out_fd); + + if (irc < 0) { + warn("sendmsg"); + return -1; + } + return 0; +} + +int +process_tree(int srv_fd, int dstdir_fd) +{ + FTS *ftsp; + FTSENT *entry; + const char *argv[2]; + const char *path; + + argv[0] = "."; + argv[1] = (char *)NULL; + + if ((ftsp = fts_open((char * const *)argv, + FTS_PHYSICAL | FTS_NOCHDIR, NULL)) == NULL) { + warn("fts_open"); + return -1; + } + + while ((entry = fts_read(ftsp)) != NULL) { + path = entry->fts_path + 2; + switch (entry->fts_info) { + case FTS_F: + if (process_manpage(srv_fd, dstdir_fd, path) == -1) { + fts_close(ftsp); + return -1; + } + break; + case FTS_D: + if (*path != '\0' && + mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP | + S_IXGRP | S_IROTH | S_IXOTH) == -1 && + errno != EEXIST) { + warn("mkdirat(%s)", path); + (void)fts_set(ftsp, entry, FTS_SKIP); + } + break; + case FTS_DP: + break; + default: + warnx("%s: not a regular file", path); + break; + } + } + + fts_close(ftsp); + return 0; +} + +int +main(int argc, char **argv) +{ + const char *defos, *outtype; + int srv_fds[2]; + int dstdir_fd; + int opt; + pid_t pid; + + defos = NULL; + outtype = "ascii"; + while ((opt = getopt(argc, argv, "I:T:")) != -1) { + switch (opt) { + case 'I': + defos = optarg; + break; + case 'T': + outtype = optarg; + break; + default: + usage(); + } + } + + if (argc > 0) { + argc -= optind; + argv += optind; + } + if (argc != 2) + usage(); + + if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1) + err(1, "socketpair"); + + pid = fork(); + switch (pid) { + case -1: + err(1, "fork"); + case 0: + close(srv_fds[0]); + run_mandocd(srv_fds[1], outtype, defos); + default: + break; + } + close(srv_fds[1]); + + if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1) + err(1, "open(%s)", argv[1]); + + if (chdir(argv[0]) == -1) + err(1, "chdir(%s)", argv[0]); + + return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0; +} + +void +usage(void) +{ + fprintf(stderr, "usage: %s [-I os=name] [-T output] " + "srcdir dstdir\n", BINM_CATMAN); + exit(1); +} Property changes on: vendor/mdocml/dist/catman.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/cgi.c =================================================================== --- vendor/mdocml/dist/cgi.c (revision 313955) +++ vendor/mdocml/dist/cgi.c (revision 313956) @@ -1,1179 +1,1183 @@ -/* $Id: cgi.c,v 1.144 2017/01/21 01:20:31 schwarze Exp $ */ +/* $Id: cgi.c,v 1.147 2017/02/08 13:34:27 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2014, 2015, 2016, 2017 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. */ #include "config.h" #include #include #include +#if HAVE_ERR #include +#endif #include #include #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "man.h" #include "main.h" #include "manconf.h" #include "mansearch.h" #include "cgi.h" /* * A query as passed to the search function. */ struct query { char *manpath; /* desired manual directory */ char *arch; /* architecture */ char *sec; /* manual section */ char *query; /* unparsed query expression */ int equal; /* match whole names, not substrings */ }; struct req { struct query q; char **p; /* array of available manpaths */ size_t psz; /* number of available manpaths */ int isquery; /* QUERY_STRING used, not PATH_INFO */ }; enum focus { FOCUS_NONE = 0, FOCUS_QUERY }; static void html_print(const char *); static void html_putchar(char); static int http_decode(char *); static void parse_manpath_conf(struct req *); static void parse_path_info(struct req *req, const char *path); static void parse_query_string(struct req *, const char *); static void pg_error_badrequest(const char *); static void pg_error_internal(void); static void pg_index(const struct req *); static void pg_noresult(const struct req *, const char *); static void pg_search(const struct req *); static void pg_searchres(const struct req *, struct manpage *, size_t); static void pg_show(struct req *, const char *); static void resp_begin_html(int, const char *); static void resp_begin_http(int, const char *); static void resp_catman(const struct req *, const char *); static void resp_copy(const char *); static void resp_end_html(void); static void resp_format(const struct req *, const char *); static void resp_searchform(const struct req *, enum focus); static void resp_show(const struct req *, const char *); static void set_query_attr(char **, char **); static int validate_filename(const char *); static int validate_manpath(const struct req *, const char *); static int validate_urifrag(const char *); static const char *scriptname = SCRIPT_NAME; static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static const char *const sec_numbers[] = { "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9" }; static const char *const sec_names[] = { "All Sections", "1 - General Commands", "2 - System Calls", "3 - Library Functions", "3p - Perl Library", "4 - Device Drivers", "5 - File Formats", "6 - Games", "7 - Miscellaneous Information", "8 - System Manager\'s Manual", "9 - Kernel Developer\'s Manual" }; static const int sec_MAX = sizeof(sec_names) / sizeof(char *); static const char *const arch_names[] = { - "amd64", "alpha", "armv7", + "amd64", "alpha", "armv7", "arm64", "hppa", "i386", "landisk", "loongson", "luna88k", "macppc", "mips64", "octeon", "sgi", "socppc", "sparc64", "amiga", "arc", "armish", "arm32", "atari", "aviion", "beagle", "cats", "hppa64", "hp300", "ia64", "mac68k", "mvme68k", "mvme88k", "mvmeppc", "palm", "pc532", "pegasos", "pmax", "powerpc", "solbourne", "sparc", "sun3", "vax", "wgrisc", "x68k", "zaurus" }; static const int arch_MAX = sizeof(arch_names) / sizeof(char *); /* * Print a character, escaping HTML along the way. * This will pass non-ASCII straight to output: be warned! */ static void html_putchar(char c) { switch (c) { case ('"'): printf("""); break; case ('&'): printf("&"); break; case ('>'): printf(">"); break; case ('<'): printf("<"); break; default: putchar((unsigned char)c); break; } } /* * Call through to html_putchar(). * Accepts NULL strings. */ static void html_print(const char *p) { if (NULL == p) return; while ('\0' != *p) html_putchar(*p++); } /* * Transfer the responsibility for the allocated string *val * to the query structure. */ static void set_query_attr(char **attr, char **val) { free(*attr); if (**val == '\0') { *attr = NULL; free(*val); } else *attr = *val; *val = NULL; } /* * Parse the QUERY_STRING for key-value pairs * and store the values into the query structure. */ static void parse_query_string(struct req *req, const char *qs) { char *key, *val; size_t keysz, valsz; req->isquery = 1; req->q.manpath = NULL; req->q.arch = NULL; req->q.sec = NULL; req->q.query = NULL; req->q.equal = 1; key = val = NULL; while (*qs != '\0') { /* Parse one key. */ keysz = strcspn(qs, "=;&"); key = mandoc_strndup(qs, keysz); qs += keysz; if (*qs != '=') goto next; /* Parse one value. */ valsz = strcspn(++qs, ";&"); val = mandoc_strndup(qs, valsz); qs += valsz; /* Decode and catch encoding errors. */ if ( ! (http_decode(key) && http_decode(val))) goto next; /* Handle key-value pairs. */ if ( ! strcmp(key, "query")) set_query_attr(&req->q.query, &val); else if ( ! strcmp(key, "apropos")) req->q.equal = !strcmp(val, "0"); else if ( ! strcmp(key, "manpath")) { #ifdef COMPAT_OLDURI if ( ! strncmp(val, "OpenBSD ", 8)) { val[7] = '-'; if ('C' == val[8]) val[8] = 'c'; } #endif set_query_attr(&req->q.manpath, &val); } else if ( ! (strcmp(key, "sec") #ifdef COMPAT_OLDURI && strcmp(key, "sektion") #endif )) { if ( ! strcmp(val, "0")) *val = '\0'; set_query_attr(&req->q.sec, &val); } else if ( ! strcmp(key, "arch")) { if ( ! strcmp(val, "default")) *val = '\0'; set_query_attr(&req->q.arch, &val); } /* * The key must be freed in any case. * The val may have been handed over to the query * structure, in which case it is now NULL. */ next: free(key); key = NULL; free(val); val = NULL; if (*qs != '\0') qs++; } } /* * HTTP-decode a string. The standard explanation is that this turns * "%4e+foo" into "n foo" in the regular way. This is done in-place * over the allocated string. */ static int http_decode(char *p) { char hex[3]; char *q; int c; hex[2] = '\0'; q = p; for ( ; '\0' != *p; p++, q++) { if ('%' == *p) { if ('\0' == (hex[0] = *(p + 1))) return 0; if ('\0' == (hex[1] = *(p + 2))) return 0; if (1 != sscanf(hex, "%x", &c)) return 0; if ('\0' == c) return 0; *q = (char)c; p += 2; } else *q = '+' == *p ? ' ' : *p; } *q = '\0'; return 1; } static void resp_begin_http(int code, const char *msg) { if (200 != code) printf("Status: %d %s\r\n", code, msg); printf("Content-Type: text/html; charset=utf-8\r\n" "Cache-Control: no-cache\r\n" "Pragma: no-cache\r\n" "\r\n"); fflush(stdout); } static void resp_copy(const char *filename) { char buf[4096]; ssize_t sz; int fd; if ((fd = open(filename, O_RDONLY)) != -1) { fflush(stdout); while ((sz = read(fd, buf, sizeof(buf))) > 0) write(STDOUT_FILENO, buf, sz); close(fd); } } static void resp_begin_html(int code, const char *msg) { resp_begin_http(code, msg); printf("\n" "\n" "\n" " \n" " \n" " %s \n" "\n" "\n", CSS_DIR, CUSTOMIZE_TITLE); resp_copy(MAN_DIR "/header.html"); } static void resp_end_html(void) { resp_copy(MAN_DIR "/footer.html"); puts("\n" ""); } static void resp_searchform(const struct req *req, enum focus focus) { int i; printf(""); } static int validate_urifrag(const char *frag) { while ('\0' != *frag) { if ( ! (isalnum((unsigned char)*frag) || '-' == *frag || '.' == *frag || '/' == *frag || '_' == *frag)) return 0; frag++; } return 1; } static int validate_manpath(const struct req *req, const char* manpath) { size_t i; for (i = 0; i < req->psz; i++) if ( ! strcmp(manpath, req->p[i])) return 1; return 0; } static int validate_filename(const char *file) { if ('.' == file[0] && '/' == file[1]) file += 2; return ! (strstr(file, "../") || strstr(file, "/..") || (strncmp(file, "man", 3) && strncmp(file, "cat", 3))); } static void pg_index(const struct req *req) { resp_begin_html(200, NULL); resp_searchform(req, FOCUS_QUERY); printf("\n" "This web interface is documented in the\n" "man.cgi(8)\n" "manual, and the\n" "apropos(1)\n" "manual explains the query syntax.\n" "
\n", scriptname, *scriptname == '\0' ? "" : "/", scriptname, *scriptname == '\0' ? "" : "/"); resp_end_html(); } static void pg_noresult(const struct req *req, const char *msg) { resp_begin_html(200, NULL); resp_searchform(req, FOCUS_QUERY); puts(""); puts(msg); puts("
"); resp_end_html(); } static void pg_error_badrequest(const char *msg) { resp_begin_html(400, "Bad Request"); puts("Bad Request
\n" "\n"); puts(msg); printf("Try again from the\n" "main page.\n" "
", scriptname); resp_end_html(); } static void pg_error_internal(void) { resp_begin_html(500, "Internal Server Error"); puts("Internal Server Error
"); resp_end_html(); } static void pg_searchres(const struct req *req, struct manpage *r, size_t sz) { char *arch, *archend; const char *sec; size_t i, iuse; int archprio, archpriouse; int prio, priouse; for (i = 0; i < sz; i++) { if (validate_filename(r[i].file)) continue; warnx("invalid filename %s in %s database", r[i].file, req->q.manpath); pg_error_internal(); return; } if (req->isquery && sz == 1) { /* * If we have just one result, then jump there now * without any delay. */ printf("Status: 303 See Other\r\n"); printf("Location: http://%s/%s%s%s/%s", HTTP_HOST, scriptname, *scriptname == '\0' ? "" : "/", req->q.manpath, r[0].file); printf("\r\n" "Content-Type: text/html; charset=utf-8\r\n" "\r\n"); return; } resp_begin_html(200, NULL); resp_searchform(req, req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY); if (sz > 1) { puts(""); for (i = 0; i < sz; i++) { printf("
"); } /* * In man(1) mode, show one of the pages * even if more than one is found. */ if (req->q.equal || sz == 1) { puts("\n" " "); } puts("" "", scriptname, *scriptname == '\0' ? "" : "/", req->q.manpath, r[i].file); html_print(r[i].names); printf(" \n" ""); html_print(r[i].output); puts(" \n" "
"); iuse = 0; priouse = 20; archpriouse = 3; for (i = 0; i < sz; i++) { sec = r[i].file; sec += strcspn(sec, "123456789"); if (sec[0] == '\0') continue; prio = sec_prios[sec[0] - '1']; if (sec[1] != '/') prio += 10; if (req->q.arch == NULL) { archprio = ((arch = strchr(sec + 1, '/')) == NULL) ? 3 : ((archend = strchr(arch + 1, '/')) == NULL) ? 0 : strncmp(arch, "amd64/", archend - arch) ? 2 : 1; if (archprio < archpriouse) { archpriouse = archprio; priouse = prio; iuse = i; continue; } if (archprio > archpriouse) continue; } if (prio >= priouse) continue; priouse = prio; iuse = i; } resp_show(req, r[iuse].file); } resp_end_html(); } static void resp_catman(const struct req *req, const char *file) { FILE *f; char *p; size_t sz; ssize_t len; int i; int italic, bold; if ((f = fopen(file, "r")) == NULL) { puts("You specified an invalid manual file.
"); return; } puts("\n" ""); fclose(f); } static void resp_format(const struct req *req, const char *file) { struct manoutput conf; struct mparse *mp; struct roff_man *man; void *vp; int fd; int usepath; if (-1 == (fd = open(file, O_RDONLY, 0))) { puts(""); p = NULL; sz = 0; while ((len = getline(&p, &sz, f)) != -1) { bold = italic = 0; for (i = 0; i < len - 1; i++) { /* * This means that the catpage is out of state. * Ignore it and keep going (although the * catpage is bogus). */ if ('\b' == p[i] || '\n' == p[i]) continue; /* * Print a regular character. * Close out any bold/italic scopes. * If we're in back-space mode, make sure we'll * have something to enter when we backspace. */ if ('\b' != p[i + 1]) { if (italic) printf(""); if (bold) printf(""); italic = bold = 0; html_putchar(p[i]); continue; } else if (i + 2 >= len) continue; /* Italic mode. */ if ('_' == p[i]) { if (bold) printf(""); if ( ! italic) printf(""); bold = 0; italic = 1; i += 2; html_putchar(p[i]); continue; } /* * Handle funny behaviour troff-isms. * These grok'd from the original man2html.c. */ if (('+' == p[i] && 'o' == p[i + 2]) || ('o' == p[i] && '+' == p[i + 2]) || ('|' == p[i] && '=' == p[i + 2]) || ('=' == p[i] && '|' == p[i + 2]) || ('*' == p[i] && '=' == p[i + 2]) || ('=' == p[i] && '*' == p[i + 2]) || ('*' == p[i] && '|' == p[i + 2]) || ('|' == p[i] && '*' == p[i + 2])) { if (italic) printf(""); if (bold) printf(""); italic = bold = 0; putchar('*'); i += 2; continue; } else if (('|' == p[i] && '-' == p[i + 2]) || ('-' == p[i] && '|' == p[i + 1]) || ('+' == p[i] && '-' == p[i + 1]) || ('-' == p[i] && '+' == p[i + 1]) || ('+' == p[i] && '|' == p[i + 1]) || ('|' == p[i] && '+' == p[i + 1])) { if (italic) printf(""); if (bold) printf(""); italic = bold = 0; putchar('+'); i += 2; continue; } /* Bold mode. */ if (italic) printf(""); if ( ! bold) printf(""); bold = 1; italic = 0; i += 2; html_putchar(p[i]); } /* * Clean up the last character. * We can get to a newline; don't print that. */ if (italic) printf(""); if (bold) printf(""); if (i == len - 1 && p[i] != '\n') html_putchar(p[i]); putchar('\n'); } free(p); puts("\n" "You specified an invalid manual file.
"); return; } mchars_alloc(); mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1, MANDOCLEVEL_BADARG, NULL, req->q.manpath); mparse_readfd(mp, fd, file); close(fd); memset(&conf, 0, sizeof(conf)); conf.fragment = 1; + conf.style = mandoc_strdup(CSS_DIR "/mandoc.css"); usepath = strcmp(req->q.manpath, req->p[0]); mandoc_asprintf(&conf.man, "/%s%s%%N.%%S", usepath ? req->q.manpath : "", usepath ? "/" : ""); mparse_result(mp, &man, NULL); if (man == NULL) { warnx("fatal mandoc error: %s/%s", req->q.manpath, file); pg_error_internal(); mparse_free(mp); mchars_free(); return; } vp = html_alloc(&conf); if (man->macroset == MACROSET_MDOC) { mdoc_validate(man); html_mdoc(vp, man); } else { man_validate(man); html_man(vp, man); } html_free(vp); mparse_free(mp); mchars_free(); free(conf.man); + free(conf.style); } static void resp_show(const struct req *req, const char *file) { if ('.' == file[0] && '/' == file[1]) file += 2; if ('c' == *file) resp_catman(req, file); else resp_format(req, file); } static void pg_show(struct req *req, const char *fullpath) { char *manpath; const char *file; if ((file = strchr(fullpath, '/')) == NULL) { pg_error_badrequest( "You did not specify a page to show."); return; } manpath = mandoc_strndup(fullpath, file - fullpath); file++; if ( ! validate_manpath(req, manpath)) { pg_error_badrequest( "You specified an invalid manpath."); free(manpath); return; } /* * Begin by chdir()ing into the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (chdir(manpath) == -1) { warn("chdir %s", manpath); pg_error_internal(); free(manpath); return; } free(manpath); if ( ! validate_filename(file)) { pg_error_badrequest( "You specified an invalid manual file."); return; } resp_begin_html(200, NULL); resp_searchform(req, FOCUS_NONE); resp_show(req, file); resp_end_html(); } static void pg_search(const struct req *req) { struct mansearch search; struct manpaths paths; struct manpage *res; char **argv; char *query, *rp, *wp; size_t ressz; int argc; /* * Begin by chdir()ing into the root of the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (chdir(req->q.manpath) == -1) { warn("chdir %s", req->q.manpath); pg_error_internal(); return; } search.arch = req->q.arch; search.sec = req->q.sec; search.outkey = "Nd"; search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR; search.firstmatch = 1; paths.sz = 1; paths.paths = mandoc_malloc(sizeof(char *)); paths.paths[0] = mandoc_strdup("."); /* * Break apart at spaces with backslash-escaping. */ argc = 0; argv = NULL; rp = query = mandoc_strdup(req->q.query); for (;;) { while (isspace((unsigned char)*rp)) rp++; if (*rp == '\0') break; argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *)); argv[argc++] = wp = rp; for (;;) { if (isspace((unsigned char)*rp)) { *wp = '\0'; rp++; break; } if (rp[0] == '\\' && rp[1] != '\0') rp++; if (wp != rp) *wp = *rp; if (*rp == '\0') break; wp++; rp++; } } if (0 == mansearch(&search, &paths, argc, argv, &res, &ressz)) pg_noresult(req, "You entered an invalid query."); else if (0 == ressz) pg_noresult(req, "No results found."); else pg_searchres(req, res, ressz); free(query); mansearch_free(res, ressz); free(paths.paths[0]); free(paths.paths); } int main(void) { struct req req; struct itimerval itimer; const char *path; const char *querystring; int i; /* Poor man's ReDoS mitigation. */ itimer.it_value.tv_sec = 2; itimer.it_value.tv_usec = 0; itimer.it_interval.tv_sec = 2; itimer.it_interval.tv_usec = 0; if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) { warn("setitimer"); pg_error_internal(); return EXIT_FAILURE; } /* * First we change directory into the MAN_DIR so that * subsequent scanning for manpath directories is rooted * relative to the same position. */ if (chdir(MAN_DIR) == -1) { warn("MAN_DIR: %s", MAN_DIR); pg_error_internal(); return EXIT_FAILURE; } memset(&req, 0, sizeof(struct req)); req.q.equal = 1; parse_manpath_conf(&req); /* Parse the path info and the query string. */ if ((path = getenv("PATH_INFO")) == NULL) path = ""; else if (*path == '/') path++; if (*path != '\0') { parse_path_info(&req, path); if (req.q.manpath == NULL || access(path, F_OK) == -1) path = ""; } else if ((querystring = getenv("QUERY_STRING")) != NULL) parse_query_string(&req, querystring); /* Validate parsed data and add defaults. */ if (req.q.manpath == NULL) req.q.manpath = mandoc_strdup(req.p[0]); else if ( ! validate_manpath(&req, req.q.manpath)) { pg_error_badrequest( "You specified an invalid manpath."); return EXIT_FAILURE; } if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) { pg_error_badrequest( "You specified an invalid architecture."); return EXIT_FAILURE; } /* Dispatch to the three different pages. */ if ('\0' != *path) pg_show(&req, path); else if (NULL != req.q.query) pg_search(&req); else pg_index(&req); free(req.q.manpath); free(req.q.arch); free(req.q.sec); free(req.q.query); for (i = 0; i < (int)req.psz; i++) free(req.p[i]); free(req.p); return EXIT_SUCCESS; } /* * If PATH_INFO is not a file name, translate it to a query. */ static void parse_path_info(struct req *req, const char *path) { char *dir[4]; int i; req->isquery = 0; req->q.equal = 1; req->q.manpath = mandoc_strdup(path); req->q.arch = NULL; /* Mandatory manual page name. */ if ((req->q.query = strrchr(req->q.manpath, '/')) == NULL) { req->q.query = req->q.manpath; req->q.manpath = NULL; } else *req->q.query++ = '\0'; /* Optional trailing section. */ if ((req->q.sec = strrchr(req->q.query, '.')) != NULL) { if(isdigit((unsigned char)req->q.sec[1])) { *req->q.sec++ = '\0'; req->q.sec = mandoc_strdup(req->q.sec); } else req->q.sec = NULL; } /* Handle the case of name[.section] only. */ if (req->q.manpath == NULL) return; req->q.query = mandoc_strdup(req->q.query); /* Split directory components. */ dir[i = 0] = req->q.manpath; while ((dir[i + 1] = strchr(dir[i], '/')) != NULL) { if (++i == 3) { pg_error_badrequest( "You specified too many directory components."); exit(EXIT_FAILURE); } *dir[i]++ = '\0'; } /* Optional manpath. */ if ((i = validate_manpath(req, req->q.manpath)) == 0) req->q.manpath = NULL; else if (dir[1] == NULL) return; /* Optional section. */ if (strncmp(dir[i], "man", 3) == 0) { free(req->q.sec); req->q.sec = mandoc_strdup(dir[i++] + 3); } if (dir[i] == NULL) { if (req->q.manpath == NULL) free(dir[0]); return; } if (dir[i + 1] != NULL) { pg_error_badrequest( "You specified an invalid directory component."); exit(EXIT_FAILURE); } /* Optional architecture. */ if (i) { req->q.arch = mandoc_strdup(dir[i]); if (req->q.manpath == NULL) free(dir[0]); } else req->q.arch = dir[0]; } /* * Scan for indexable paths. */ static void parse_manpath_conf(struct req *req) { FILE *fp; char *dp; size_t dpsz; ssize_t len; if ((fp = fopen("manpath.conf", "r")) == NULL) { warn("%s/manpath.conf", MAN_DIR); pg_error_internal(); exit(EXIT_FAILURE); } dp = NULL; dpsz = 0; while ((len = getline(&dp, &dpsz, fp)) != -1) { if (dp[len - 1] == '\n') dp[--len] = '\0'; req->p = mandoc_realloc(req->p, (req->psz + 1) * sizeof(char *)); if ( ! validate_urifrag(dp)) { warnx("%s/manpath.conf contains " "unsafe path \"%s\"", MAN_DIR, dp); pg_error_internal(); exit(EXIT_FAILURE); } if (strchr(dp, '/') != NULL) { warnx("%s/manpath.conf contains " "path with slash \"%s\"", MAN_DIR, dp); pg_error_internal(); exit(EXIT_FAILURE); } req->p[req->psz++] = dp; dp = NULL; dpsz = 0; } free(dp); if (req->p == NULL) { warnx("%s/manpath.conf is empty", MAN_DIR); pg_error_internal(); exit(EXIT_FAILURE); } } Index: vendor/mdocml/dist/chars.c =================================================================== --- vendor/mdocml/dist/chars.c (revision 313955) +++ vendor/mdocml/dist/chars.c (revision 313956) @@ -1,494 +1,494 @@ -/* $Id: chars.c,v 1.68 2015/10/13 22:59:54 schwarze Exp $ */ +/* $Id: chars.c,v 1.69 2017/02/17 18:28:06 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons* Copyright (c) 2011, 2014, 2015 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "mandoc_ohash.h" #include "libmandoc.h" struct ln { const char roffcode[16]; const char *ascii; int unicode; }; /* Special break control characters. */ static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' }; static const char ascii_break[2] = { ASCII_BREAK, '\0' }; static struct ln lines[] = { /* Spacing. */ { " ", ascii_nbrsp, 0x00a0 }, { "~", ascii_nbrsp, 0x00a0 }, { "0", " ", 0x2002 }, { "|", "", 0 }, { "^", "", 0 }, { "&", "", 0 }, { "%", "", 0 }, { ":", ascii_break, 0 }, /* XXX The following three do not really belong here. */ { "t", "", 0 }, { "c", "", 0 }, { "}", "", 0 }, /* Lines. */ { "ba", "|", 0x007c }, { "br", "|", 0x2502 }, { "ul", "_", 0x005f }, { "rn", "-", 0x203e }, { "bb", "|", 0x00a6 }, { "sl", "/", 0x002f }, { "rs", "\\", 0x005c }, /* Text markers. */ { "ci", "O", 0x25cb }, { "bu", "+\bo", 0x2022 }, { "dd", "|\b=", 0x2021 }, { "dg", "|\b-", 0x2020 }, { "lz", "<>", 0x25ca }, { "sq", "[]", 0x25a1 }, { "ps", " ", 0x00b6 }, { "sc", " ", 0x00a7 }, { "lh", "<=", 0x261c }, { "rh", "=>", 0x261e }, { "at", "@", 0x0040 }, { "sh", "#", 0x0023 }, { "CR", "_|", 0x21b5 }, { "OK", "\\/", 0x2713 }, /* Legal symbols. */ { "co", "(C)", 0x00a9 }, { "rg", "(R)", 0x00ae }, { "tm", "tm", 0x2122 }, /* Punctuation. */ { "em", "--", 0x2014 }, { "en", "-", 0x2013 }, { "hy", "-", 0x2010 }, { "e", "\\", 0x005c }, { ".", ".", 0x002e }, { "r!", "!", 0x00a1 }, { "r?", "?", 0x00bf }, /* Quotes. */ { "Bq", ",,", 0x201e }, { "bq", ",", 0x201a }, { "lq", "\"", 0x201c }, { "rq", "\"", 0x201d }, - { "Lq", "``", 0x201c }, - { "Rq", "''", 0x201d }, + { "Lq", "\"", 0x201c }, + { "Rq", "\"", 0x201d }, { "oq", "`", 0x2018 }, { "cq", "\'", 0x2019 }, { "aq", "\'", 0x0027 }, { "dq", "\"", 0x0022 }, { "Fo", "<<", 0x00ab }, { "Fc", ">>", 0x00bb }, { "fo", "<", 0x2039 }, { "fc", ">", 0x203a }, /* Brackets. */ { "lB", "[", 0x005b }, { "rB", "]", 0x005d }, { "lC", "{", 0x007b }, { "rC", "}", 0x007d }, { "la", "<", 0x27e8 }, { "ra", ">", 0x27e9 }, { "bv", "|", 0x23aa }, { "braceex", "|", 0x23aa }, { "bracketlefttp", "|", 0x23a1 }, { "bracketleftbt", "|", 0x23a3 }, { "bracketleftex", "|", 0x23a2 }, { "bracketrighttp", "|", 0x23a4 }, { "bracketrightbt", "|", 0x23a6 }, { "bracketrightex", "|", 0x23a5 }, { "lt", ",-", 0x23a7 }, { "bracelefttp", ",-", 0x23a7 }, { "lk", "{", 0x23a8 }, { "braceleftmid", "{", 0x23a8 }, { "lb", "`-", 0x23a9 }, { "braceleftbt", "`-", 0x23a9 }, { "braceleftex", "|", 0x23aa }, { "rt", "-.", 0x23ab }, { "bracerighttp", "-.", 0x23ab }, { "rk", "}", 0x23ac }, { "bracerightmid", "}", 0x23ac }, { "rb", "-\'", 0x23ad }, { "bracerightbt", "-\'", 0x23ad }, { "bracerightex", "|", 0x23aa }, { "parenlefttp", "/", 0x239b }, { "parenleftbt", "\\", 0x239d }, { "parenleftex", "|", 0x239c }, { "parenrighttp", "\\", 0x239e }, { "parenrightbt", "/", 0x23a0 }, { "parenrightex", "|", 0x239f }, /* Arrows and lines. */ { "<-", "<-", 0x2190 }, { "->", "->", 0x2192 }, { "<>", "<->", 0x2194 }, { "da", "|\bv", 0x2193 }, { "ua", "|\b^", 0x2191 }, { "va", "^v", 0x2195 }, { "lA", "<=", 0x21d0 }, { "rA", "=>", 0x21d2 }, { "hA", "<=>", 0x21d4 }, { "uA", "=\b^", 0x21d1 }, { "dA", "=\bv", 0x21d3 }, { "vA", "^=v", 0x21d5 }, /* Logic. */ { "AN", "^", 0x2227 }, { "OR", "v", 0x2228 }, { "no", "~", 0x00ac }, { "tno", "~", 0x00ac }, { "te", "3", 0x2203 }, { "fa", "-\bV", 0x2200 }, { "st", "-)", 0x220b }, { "tf", ".:.", 0x2234 }, { "3d", ".:.", 0x2234 }, { "or", "|", 0x007c }, /* Mathematicals. */ { "pl", "+", 0x002b }, { "mi", "-", 0x2212 }, { "-", "-", 0x002d }, { "-+", "-+", 0x2213 }, { "+-", "+-", 0x00b1 }, { "t+-", "+-", 0x00b1 }, { "pc", ".", 0x00b7 }, { "md", ".", 0x22c5 }, { "mu", "x", 0x00d7 }, { "tmu", "x", 0x00d7 }, { "c*", "O\bx", 0x2297 }, { "c+", "O\b+", 0x2295 }, { "di", "-:-", 0x00f7 }, { "tdi", "-:-", 0x00f7 }, { "f/", "/", 0x2044 }, { "**", "*", 0x2217 }, { "<=", "<=", 0x2264 }, { ">=", ">=", 0x2265 }, { "<<", "<<", 0x226a }, { ">>", ">>", 0x226b }, { "eq", "=", 0x003d }, { "!=", "!=", 0x2260 }, { "==", "==", 0x2261 }, { "ne", "!==", 0x2262 }, { "ap", "~", 0x223c }, { "|=", "-~", 0x2243 }, { "=~", "=~", 0x2245 }, { "~~", "~~", 0x2248 }, { "~=", "~=", 0x2248 }, { "pt", "oc", 0x221d }, { "es", "{}", 0x2205 }, { "mo", "E", 0x2208 }, { "nm", "!E", 0x2209 }, { "sb", "(=", 0x2282 }, { "nb", "(!=", 0x2284 }, { "sp", "=)", 0x2283 }, { "nc", "!=)", 0x2285 }, { "ib", "(=\b_", 0x2286 }, { "ip", "=\b_)", 0x2287 }, { "ca", "(^)", 0x2229 }, { "cu", "U", 0x222a }, { "/_", "_\b/", 0x2220 }, { "pp", "_\b|", 0x22a5 }, { "is", "'\b,\bI", 0x222b }, { "integral", "'\b,\bI", 0x222b }, { "sum", "E", 0x2211 }, { "product", "TT", 0x220f }, { "coproduct", "U", 0x2210 }, { "gr", "V", 0x2207 }, { "sr", "\\/", 0x221a }, { "sqrt", "\\/", 0x221a }, { "lc", "|~", 0x2308 }, { "rc", "~|", 0x2309 }, { "lf", "|_", 0x230a }, { "rf", "_|", 0x230b }, { "if", "oo", 0x221e }, { "Ah", "N", 0x2135 }, { "Im", "I", 0x2111 }, { "Re", "R", 0x211c }, { "pd", "a", 0x2202 }, { "-h", "/h", 0x210f }, { "12", "1/2", 0x00bd }, { "14", "1/4", 0x00bc }, { "34", "3/4", 0x00be }, /* Ligatures. */ { "ff", "ff", 0xfb00 }, { "fi", "fi", 0xfb01 }, { "fl", "fl", 0xfb02 }, { "Fi", "ffi", 0xfb03 }, { "Fl", "ffl", 0xfb04 }, { "AE", "AE", 0x00c6 }, { "ae", "ae", 0x00e6 }, { "OE", "OE", 0x0152 }, { "oe", "oe", 0x0153 }, { "ss", "ss", 0x00df }, { "IJ", "IJ", 0x0132 }, { "ij", "ij", 0x0133 }, /* Accents. */ { "a\"", "\"", 0x02dd }, { "a-", "-", 0x00af }, { "a.", ".", 0x02d9 }, { "a^", "^", 0x005e }, { "aa", "\'", 0x00b4 }, { "\'", "\'", 0x00b4 }, { "ga", "`", 0x0060 }, { "`", "`", 0x0060 }, { "ab", "'\b`", 0x02d8 }, { "ac", ",", 0x00b8 }, { "ad", "\"", 0x00a8 }, { "ah", "v", 0x02c7 }, { "ao", "o", 0x02da }, { "a~", "~", 0x007e }, { "ho", ",", 0x02db }, { "ha", "^", 0x005e }, { "ti", "~", 0x007e }, /* Accented letters. */ { "'A", "'\bA", 0x00c1 }, { "'E", "'\bE", 0x00c9 }, { "'I", "'\bI", 0x00cd }, { "'O", "'\bO", 0x00d3 }, { "'U", "'\bU", 0x00da }, { "'a", "'\ba", 0x00e1 }, { "'e", "'\be", 0x00e9 }, { "'i", "'\bi", 0x00ed }, { "'o", "'\bo", 0x00f3 }, { "'u", "'\bu", 0x00fa }, { "`A", "`\bA", 0x00c0 }, { "`E", "`\bE", 0x00c8 }, { "`I", "`\bI", 0x00cc }, { "`O", "`\bO", 0x00d2 }, { "`U", "`\bU", 0x00d9 }, { "`a", "`\ba", 0x00e0 }, { "`e", "`\be", 0x00e8 }, { "`i", "`\bi", 0x00ec }, { "`o", "`\bo", 0x00f2 }, { "`u", "`\bu", 0x00f9 }, { "~A", "~\bA", 0x00c3 }, { "~N", "~\bN", 0x00d1 }, { "~O", "~\bO", 0x00d5 }, { "~a", "~\ba", 0x00e3 }, { "~n", "~\bn", 0x00f1 }, { "~o", "~\bo", 0x00f5 }, { ":A", "\"\bA", 0x00c4 }, { ":E", "\"\bE", 0x00cb }, { ":I", "\"\bI", 0x00cf }, { ":O", "\"\bO", 0x00d6 }, { ":U", "\"\bU", 0x00dc }, { ":a", "\"\ba", 0x00e4 }, { ":e", "\"\be", 0x00eb }, { ":i", "\"\bi", 0x00ef }, { ":o", "\"\bo", 0x00f6 }, { ":u", "\"\bu", 0x00fc }, { ":y", "\"\by", 0x00ff }, { "^A", "^\bA", 0x00c2 }, { "^E", "^\bE", 0x00ca }, { "^I", "^\bI", 0x00ce }, { "^O", "^\bO", 0x00d4 }, { "^U", "^\bU", 0x00db }, { "^a", "^\ba", 0x00e2 }, { "^e", "^\be", 0x00ea }, { "^i", "^\bi", 0x00ee }, { "^o", "^\bo", 0x00f4 }, { "^u", "^\bu", 0x00fb }, { ",C", ",\bC", 0x00c7 }, { ",c", ",\bc", 0x00e7 }, { "/L", "/\bL", 0x0141 }, { "/l", "/\bl", 0x0142 }, { "/O", "/\bO", 0x00d8 }, { "/o", "/\bo", 0x00f8 }, { "oA", "o\bA", 0x00c5 }, { "oa", "o\ba", 0x00e5 }, /* Special letters. */ { "-D", "-\bD", 0x00d0 }, { "Sd", "d", 0x00f0 }, { "TP", "Th", 0x00de }, { "Tp", "th", 0x00fe }, { ".i", "i", 0x0131 }, { ".j", "j", 0x0237 }, /* Currency. */ { "Do", "$", 0x0024 }, { "ct", "/\bc", 0x00a2 }, { "Eu", "EUR", 0x20ac }, { "eu", "EUR", 0x20ac }, { "Ye", "=\bY", 0x00a5 }, { "Po", "GBP", 0x00a3 }, { "Cs", "o\bx", 0x00a4 }, { "Fn", ",\bf", 0x0192 }, /* Units. */ { "de", " ", 0x00b0 }, { "%0", "%o", 0x2030 }, { "fm", "\'", 0x2032 }, { "sd", "''", 0x2033 }, { "mc", ",\bu", 0x00b5 }, /* Greek characters. */ { "*A", "A", 0x0391 }, { "*B", "B", 0x0392 }, { "*G", "G", 0x0393 }, { "*D", "_\b/_\b\\", 0x0394 }, { "*E", "E", 0x0395 }, { "*Z", "Z", 0x0396 }, { "*Y", "H", 0x0397 }, { "*H", "-\bO", 0x0398 }, { "*I", "I", 0x0399 }, { "*K", "K", 0x039a }, { "*L", "/\\", 0x039b }, { "*M", "M", 0x039c }, { "*N", "N", 0x039d }, { "*C", "_\bH", 0x039e }, { "*O", "O", 0x039f }, { "*P", "TT", 0x03a0 }, { "*R", "P", 0x03a1 }, { "*S", "S", 0x03a3 }, { "*T", "T", 0x03a4 }, { "*U", "Y", 0x03a5 }, { "*F", "I\bO", 0x03a6 }, { "*X", "X", 0x03a7 }, { "*Q", "I\bY", 0x03a8 }, { "*W", "_\bO", 0x03a9 }, { "*a", "a", 0x03b1 }, { "*b", "B", 0x03b2 }, { "*g", "y", 0x03b3 }, { "*d", "d", 0x03b4 }, { "*e", "e", 0x03b5 }, { "*z", ",\bC", 0x03b6 }, { "*y", "n", 0x03b7 }, { "*h", "-\b0", 0x03b8 }, { "*i", "i", 0x03b9 }, { "*k", "k", 0x03ba }, { "*l", ">\b\\", 0x03bb }, { "*m", ",\bu", 0x03bc }, { "*n", "v", 0x03bd }, { "*c", ",\bE", 0x03be }, { "*o", "o", 0x03bf }, { "*p", "-\bn", 0x03c0 }, { "*r", "p", 0x03c1 }, { "*s", "-\bo", 0x03c3 }, { "*t", "~\bt", 0x03c4 }, { "*u", "u", 0x03c5 }, { "*f", "|\bo", 0x03d5 }, { "*x", "x", 0x03c7 }, { "*q", "|\bu", 0x03c8 }, { "*w", "w", 0x03c9 }, { "+h", "-\b0", 0x03d1 }, { "+f", "|\bo", 0x03c6 }, { "+p", "-\bw", 0x03d6 }, { "+e", "e", 0x03f5 }, { "ts", "s", 0x03c2 }, }; static struct ohash mchars; void mchars_free(void) { ohash_delete(&mchars); } void mchars_alloc(void) { size_t i; unsigned int slot; mandoc_ohash_init(&mchars, 9, offsetof(struct ln, roffcode)); for (i = 0; i < sizeof(lines)/sizeof(lines[0]); i++) { slot = ohash_qlookup(&mchars, lines[i].roffcode); assert(ohash_find(&mchars, slot) == NULL); ohash_insert(&mchars, slot, lines + i); } } int mchars_spec2cp(const char *p, size_t sz) { const struct ln *ln; const char *end; end = p + sz; ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end)); return ln != NULL ? ln->unicode : sz == 1 ? (unsigned char)*p : -1; } int mchars_num2char(const char *p, size_t sz) { int i; i = mandoc_strntoi(p, sz, 10); return i >= 0 && i < 256 ? i : -1; } int mchars_num2uc(const char *p, size_t sz) { int i; i = mandoc_strntoi(p, sz, 16); assert(i >= 0 && i <= 0x10FFFF); return i; } const char * mchars_spec2str(const char *p, size_t sz, size_t *rsz) { const struct ln *ln; const char *end; end = p + sz; ln = ohash_find(&mchars, ohash_qlookupi(&mchars, p, &end)); if (ln == NULL) { *rsz = 1; return sz == 1 ? p : NULL; } *rsz = strlen(ln->ascii); return ln->ascii; } const char * mchars_uc2str(int uc) { size_t i; for (i = 0; i < sizeof(lines)/sizeof(lines[0]); i++) if (uc == lines[i].unicode) return lines[i].ascii; return ">"; } Index: vendor/mdocml/dist/compat_fts.c =================================================================== --- vendor/mdocml/dist/compat_fts.c (revision 313955) +++ vendor/mdocml/dist/compat_fts.c (revision 313956) @@ -1,708 +1,701 @@ #include "config.h" #if HAVE_FTS int dummy; #else -/* $Id: compat_fts.c,v 1.12 2016/10/18 23:58:12 schwarze Exp $ */ +/* $Id: compat_fts.c,v 1.14 2017/02/18 12:24:24 schwarze Exp $ */ /* $OpenBSD: fts.c,v 1.56 2016/09/21 04:38:56 guenther Exp $ */ /*- * 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 #include #include #include #include #include #include #include #include #include "compat_fts.h" #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) static FTSENT *fts_alloc(FTS *, const char *, size_t); static FTSENT *fts_build(FTS *); static void fts_lfree(FTSENT *); static void fts_load(FTS *, FTSENT *); static size_t fts_maxarglen(char * const *); static void fts_padjust(FTS *, FTSENT *); static int fts_palloc(FTS *, size_t); static FTSENT *fts_sort(FTS *, FTSENT *, int); static unsigned short fts_stat(FTS *, FTSENT *); #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) -#ifndef O_DIRECTORY -#define O_DIRECTORY 0 -#endif #ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif #define CLR(opt) (sp->fts_options &= ~(opt)) #define ISSET(opt) (sp->fts_options & (opt)) #define SET(opt) (sp->fts_options |= (opt)) FTS * fts_open(char * const *argv, int options, int (*compar)(const FTSENT **, const FTSENT **)) { FTS *sp; FTSENT *p, *root; int nitems; - FTSENT *parent, *tmp; + FTSENT *parent, *prev; /* Options check. */ if (options & ~FTS_OPTIONMASK) { errno = EINVAL; return (NULL); } /* At least one path must be specified. */ if (*argv == NULL) { errno = EINVAL; return (NULL); } /* Allocate/initialize the stream */ if ((sp = calloc(1, sizeof(FTS))) == NULL) return (NULL); sp->fts_compar = compar; sp->fts_options = options; /* * Start out with 1K of path space, and enough, in any case, * to hold the user's paths. */ if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX))) goto mem1; /* Allocate/initialize root's parent. */ if ((parent = fts_alloc(sp, "", 0)) == NULL) goto mem2; parent->fts_level = FTS_ROOTPARENTLEVEL; /* Allocate/initialize root(s). */ - for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + for (root = prev = NULL, nitems = 0; *argv; ++argv, ++nitems) { if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL) goto mem3; p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; p->fts_accpath = p->fts_name; p->fts_info = fts_stat(sp, p); /* Command-line "." and ".." are real directories. */ if (p->fts_info == FTS_DOT) p->fts_info = FTS_D; /* * If comparison routine supplied, traverse in sorted * order; otherwise traverse in the order specified. */ if (compar) { p->fts_link = root; root = p; } else { p->fts_link = NULL; if (root == NULL) - tmp = root = p; - else { - tmp->fts_link = p; - tmp = p; - } + root = p; + else + prev->fts_link = p; + prev = p; } } if (compar && nitems > 1) root = fts_sort(sp, root, nitems); /* * Allocate a dummy pointer and make fts_read think that we've just * finished the node before the root(s); set p->fts_info to FTS_INIT * so that everything about the "current" node is ignored. */ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) goto mem3; sp->fts_cur->fts_link = root; sp->fts_cur->fts_info = FTS_INIT; if (nitems == 0) free(parent); return (sp); mem3: fts_lfree(root); free(parent); mem2: free(sp->fts_path); mem1: free(sp); return (NULL); } static void fts_load(FTS *sp, FTSENT *p) { size_t len; char *cp; /* * Load the stream structure for the next traversal. Since we don't * actually enter the directory until after the preorder visit, set * the fts_accpath field specially so the chdir gets done to the right * place and the user can access the first node. From fts_open it's * known that the path will fit. */ len = p->fts_pathlen = p->fts_namelen; memmove(sp->fts_path, p->fts_name, len + 1); if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { len = strlen(++cp); memmove(p->fts_name, cp, len + 1); p->fts_namelen = len; } p->fts_accpath = p->fts_path = sp->fts_path; sp->fts_dev = p->fts_dev; } int fts_close(FTS *sp) { FTSENT *freep, *p; /* * This still works if we haven't read anything -- the dummy structure * points to the root list, so we step through to the end of the root * list which has a valid parent pointer. */ if (sp->fts_cur) { for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { freep = p; p = p->fts_link ? p->fts_link : p->fts_parent; free(freep); } free(p); } /* Free up child linked list, sort array, path buffer, stream ptr.*/ if (sp->fts_child) fts_lfree(sp->fts_child); free(sp->fts_array); free(sp->fts_path); free(sp); return (0); } /* * Special case of "/" at the end of the path so that slashes aren't * appended which would cause paths to be written as "....//foo". */ #define NAPPEND(p) \ (p->fts_path[p->fts_pathlen - 1] == '/' \ ? p->fts_pathlen - 1 : p->fts_pathlen) FTSENT * fts_read(FTS *sp) { FTSENT *p, *tmp; int instr; char *t; /* If finished or unrecoverable error, return NULL. */ if (sp->fts_cur == NULL || ISSET(FTS_STOP)) return (NULL); /* Set current node pointer. */ p = sp->fts_cur; /* Save and zero out user instructions. */ instr = p->fts_instr; p->fts_instr = FTS_NOINSTR; /* Directory in pre-order. */ if (p->fts_info == FTS_D) { /* If skipped or crossed mount point, do post-order visit. */ if (instr == FTS_SKIP || (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { if (sp->fts_child) { fts_lfree(sp->fts_child); sp->fts_child = NULL; } p->fts_info = FTS_DP; return (p); } /* * If haven't read do so. If the read fails, fts_build sets * FTS_STOP or the fts_info field of the node. */ if (sp->fts_child) { /* nothing */ } else if ((sp->fts_child = fts_build(sp)) == NULL) { if (ISSET(FTS_STOP)) return (NULL); return (p); } p = sp->fts_child; sp->fts_child = NULL; goto name; } /* Move to the next node on this level. */ next: tmp = p; if ((p = p->fts_link)) { free(tmp); /* * If reached the top, return to the original directory (or * the root of the tree), and load the paths for the next root. */ if (p->fts_level == FTS_ROOTLEVEL) { fts_load(sp, p); return (sp->fts_cur = p); } /* * User may have called fts_set on the node. If skipped, * ignore. If followed, get a file descriptor so we can * get back if necessary. */ if (p->fts_instr == FTS_SKIP) goto next; name: t = sp->fts_path + NAPPEND(p->fts_parent); *t++ = '/'; memmove(t, p->fts_name, p->fts_namelen + 1); return (sp->fts_cur = p); } /* Move up to the parent node. */ p = tmp->fts_parent; free(tmp); if (p->fts_level == FTS_ROOTPARENTLEVEL) { /* * Done; free everything up and set errno to 0 so the user * can distinguish between error and EOF. */ free(p); errno = 0; return (sp->fts_cur = NULL); } /* NUL terminate the pathname. */ sp->fts_path[p->fts_pathlen] = '\0'; p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; return (sp->fts_cur = p); } /* * Fts_set takes the stream as an argument although it's not used in this * implementation; it would be necessary if anyone wanted to add global * semantics to fts using fts_set. An error return is allowed for similar * reasons. */ int fts_set(FTS *sp, FTSENT *p, int instr) { if (instr && instr != FTS_NOINSTR && instr != FTS_SKIP) { errno = EINVAL; return (1); } p->fts_instr = instr; return (0); } /* * This is the tricky part -- do not casually change *anything* in here. The * idea is to build the linked list of entries that are used by fts_children * and fts_read. There are lots of special cases. * * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is * set and it's a physical walk (so that symbolic links can't be directories), * we can do things quickly. First, if it's a 4.4BSD file system, the type * of the file is in the directory entry. Otherwise, we assume that the number * of subdirectories in a node is equal to the number of links to the parent. * The former skips all stat calls. The latter skips stat calls in any leaf * directories and for any files after the subdirectories in the directory have * been found, cutting the stat calls by about 2/3. */ static FTSENT * fts_build(FTS *sp) { struct dirent *dp; FTSENT *p, *head; FTSENT *cur, *tail; DIR *dirp; void *oldaddr; size_t dlen, len, maxlen; int nitems, level, doadjust; int saved_errno; char *cp; /* Set current node pointer. */ cur = sp->fts_cur; /* * Open the directory for reading. If this fails, we're done. * If being called from fts_read, set the fts_info field. */ if ((dirp = opendir(cur->fts_accpath)) == NULL) { cur->fts_info = FTS_DNR; cur->fts_errno = errno; return (NULL); } /* * Figure out the max file name length that can be stored in the * current path -- the inner loop allocates more path as necessary. * We really wouldn't have to do the maxlen calculations here, we * could do them in fts_read before returning the path, but it's a * lot easier here since the length is part of the dirent structure. * * If not changing directories set a pointer so that can just append * each new name into the path. */ len = NAPPEND(cur); cp = sp->fts_path + len; *cp++ = '/'; len++; maxlen = sp->fts_pathlen - len; /* * fts_level is signed so we must prevent it from wrapping * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL. */ level = cur->fts_level; if (level < FTS_MAXLEVEL) level++; /* Read the directory, attaching each entry to the `link' pointer. */ doadjust = 0; for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { if (ISDOT(dp->d_name)) continue; #if HAVE_DIRENT_NAMLEN dlen = dp->d_namlen; #else dlen = strlen(dp->d_name); #endif if (!(p = fts_alloc(sp, dp->d_name, dlen))) goto mem1; if (dlen >= maxlen) { /* include space for NUL */ oldaddr = sp->fts_path; if (fts_palloc(sp, dlen + len + 1)) { /* * No more memory for path or structures. Save * errno, free up the current structure and the * structures already allocated. */ mem1: saved_errno = errno; free(p); fts_lfree(head); (void)closedir(dirp); cur->fts_info = FTS_ERR; SET(FTS_STOP); errno = saved_errno; return (NULL); } /* Did realloc() change the pointer? */ if (oldaddr != sp->fts_path) { doadjust = 1; cp = sp->fts_path + len; } maxlen = sp->fts_pathlen - len; } p->fts_level = level; p->fts_parent = sp->fts_cur; p->fts_pathlen = len + dlen; if (p->fts_pathlen < len) { /* * If we wrap, free up the current structure and * the structures already allocated, then error * out with ENAMETOOLONG. */ free(p); fts_lfree(head); (void)closedir(dirp); cur->fts_info = FTS_ERR; SET(FTS_STOP); errno = ENAMETOOLONG; return (NULL); } /* Build a file name for fts_stat to stat. */ p->fts_accpath = p->fts_path; memmove(cp, p->fts_name, p->fts_namelen + 1); /* Stat it. */ p->fts_info = fts_stat(sp, p); /* We walk in directory order so "ls -f" doesn't get upset. */ p->fts_link = NULL; if (head == NULL) head = tail = p; else { tail->fts_link = p; tail = p; } ++nitems; } if (dirp) (void)closedir(dirp); /* * If realloc() changed the address of the path, adjust the * addresses for the rest of the tree and the dir list. */ if (doadjust) fts_padjust(sp, head); /* * If not changing directories, reset the path back to original * state. */ if (len == sp->fts_pathlen || nitems == 0) --cp; *cp = '\0'; /* If didn't find anything, return NULL. */ if (!nitems) { cur->fts_info = FTS_DP; return (NULL); } /* Sort the entries. */ if (sp->fts_compar && nitems > 1) head = fts_sort(sp, head, nitems); return (head); } static unsigned short fts_stat(FTS *sp, FTSENT *p) { FTSENT *t; dev_t dev; ino_t ino; struct stat *sbp; /* If user needs stat info, stat buffer already allocated. */ sbp = p->fts_statp; if (lstat(p->fts_accpath, sbp)) { p->fts_errno = errno; memset(sbp, 0, sizeof(struct stat)); return (FTS_NS); } if (S_ISDIR(sbp->st_mode)) { /* * Set the device/inode. Used to find cycles and check for * crossing mount points. Also remember the link count, used * in fts_build to limit the number of stat calls. It is * understood that these fields are only referenced if fts_info * is set to FTS_D. */ dev = p->fts_dev = sbp->st_dev; ino = p->fts_ino = sbp->st_ino; p->fts_nlink = sbp->st_nlink; if (ISDOT(p->fts_name)) return (FTS_DOT); /* * Cycle detection is done by brute force when the directory * is first encountered. If the tree gets deep enough or the * number of symbolic links to directories is high enough, * something faster might be worthwhile. */ for (t = p->fts_parent; t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) if (ino == t->fts_ino && dev == t->fts_dev) { p->fts_cycle = t; return (FTS_DC); } return (FTS_D); } if (S_ISLNK(sbp->st_mode)) return (FTS_SL); if (S_ISREG(sbp->st_mode)) return (FTS_F); return (FTS_DEFAULT); } static FTSENT * fts_sort(FTS *sp, FTSENT *head, int nitems) { FTSENT **ap, *p; /* * Construct an array of pointers to the structures and call qsort(3). * Reassemble the array in the order returned by qsort. If unable to * sort for memory reasons, return the directory entries in their * current order. Allocate enough space for the current needs plus * 40 so don't realloc one entry at a time. */ if (nitems > sp->fts_nitems) { struct _ftsent **a; sp->fts_nitems = nitems + 40; if ((a = reallocarray(sp->fts_array, sp->fts_nitems, sizeof(FTSENT *))) == NULL) { free(sp->fts_array); sp->fts_array = NULL; sp->fts_nitems = 0; return (head); } sp->fts_array = a; } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; return (head); } static FTSENT * fts_alloc(FTS *sp, const char *name, size_t namelen) { FTSENT *p; size_t len; len = sizeof(FTSENT) + namelen; if ((p = calloc(1, len)) == NULL) return (NULL); p->fts_path = sp->fts_path; p->fts_namelen = namelen; p->fts_instr = FTS_NOINSTR; p->fts_statp = malloc(sizeof(struct stat)); if (p->fts_statp == NULL) { free(p); return (NULL); } memcpy(p->fts_name, name, namelen); return (p); } static void fts_lfree(FTSENT *head) { FTSENT *p; /* Free a linked list of structures. */ while ((p = head)) { head = head->fts_link; free(p); } } /* * Allow essentially unlimited paths; find, rm, ls should all work on any tree. * Most systems will allow creation of paths much longer than PATH_MAX, even * though the kernel won't resolve them. Add the size (not just what's needed) * plus 256 bytes so don't realloc the path 2 bytes at a time. */ static int fts_palloc(FTS *sp, size_t more) { char *p; /* * Check for possible wraparound. */ more += 256; if (sp->fts_pathlen + more < sp->fts_pathlen) { free(sp->fts_path); sp->fts_path = NULL; errno = ENAMETOOLONG; return (1); } sp->fts_pathlen += more; p = realloc(sp->fts_path, sp->fts_pathlen); if (p == NULL) { free(sp->fts_path); sp->fts_path = NULL; return (1); } sp->fts_path = p; return (0); } /* * When the path is realloc'd, have to fix all of the pointers in structures * already returned. */ static void fts_padjust(FTS *sp, FTSENT *head) { FTSENT *p; char *addr = sp->fts_path; #define ADJUST(p) { \ if ((p)->fts_accpath != (p)->fts_name) { \ (p)->fts_accpath = \ (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ } \ (p)->fts_path = addr; \ } /* Adjust the current set of children. */ for (p = sp->fts_child; p; p = p->fts_link) ADJUST(p); /* Adjust the rest of the tree, including the current level. */ for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { ADJUST(p); p = p->fts_link ? p->fts_link : p->fts_parent; } } static size_t fts_maxarglen(char * const *argv) { size_t len, max; for (max = 0; *argv; ++argv) if ((len = strlen(*argv)) > max) max = len; return (max + 1); } #endif Index: vendor/mdocml/dist/configure =================================================================== --- vendor/mdocml/dist/configure (revision 313955) +++ vendor/mdocml/dist/configure (revision 313956) @@ -1,474 +1,525 @@ #!/bin/sh # -# $Id: configure,v 1.55 2017/01/12 15:45:05 schwarze Exp $ +# $Id: configure,v 1.61 2017/02/18 12:24:24 schwarze Exp $ # -# Copyright (c) 2014, 2015, 2016 Ingo Schwarze +# Copyright (c) 2014, 2015, 2016, 2017 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. set -e [ -w config.log ] && mv config.log config.log.old [ -w config.h ] && mv config.h config.h.old # Output file descriptor usage: # 1 (stdout): config.h, Makefile.local # 2 (stderr): original stderr, usually to the console # 3: config.log exec 3> config.log echo "config.log: writing..." # --- default settings ------------------------------------------------- # Initialize all variables here, # such that nothing can leak in from the environment. +SOURCEDIR=`dirname "$0"` + MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man" OSNAME= UTF8_LOCALE= CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | env -i make -sf -` CFLAGS="-g -W -Wall -Wmissing-prototypes -Wstrict-prototypes -Wwrite-strings" CFLAGS="${CFLAGS} -Wno-unused-parameter" LDADD= LDFLAGS= LD_NANOSLEEP= LD_OHASH= +LD_RECVMSG= STATIC="-static" BUILD_CGI=0 +BUILD_CATMAN=0 INSTALL_LIBMANDOC=0 +HAVE_CMSG= +HAVE_CMSG_XPG42=0 HAVE_DIRENT_NAMLEN= HAVE_EFTYPE= HAVE_ENDIAN= HAVE_ERR= HAVE_FTS= HAVE_FTS_COMPARE_CONST= HAVE_GETLINE= HAVE_GETSUBOPT= HAVE_ISBLANK= HAVE_MKDTEMP= HAVE_NANOSLEEP= HAVE_NTOHL= +HAVE_O_DIRECTORY= HAVE_OHASH= HAVE_PATH_MAX= HAVE_PLEDGE= HAVE_PROGNAME= HAVE_REALLOCARRAY= +HAVE_RECVMSG= HAVE_REWB_BSD= HAVE_REWB_SYSV= HAVE_SANDBOX_INIT= HAVE_STRCASESTR= HAVE_STRINGLIST= HAVE_STRLCAT= HAVE_STRLCPY= HAVE_STRPTIME= HAVE_STRSEP= HAVE_STRTONUM= HAVE_SYS_ENDIAN= HAVE_VASPRINTF= HAVE_WCHAR= PREFIX="/usr/local" BINDIR= SBINDIR= INCLUDEDIR= LIBDIR= MANDIR= HOMEBREWDIR= WWWPREFIX="/var/www" HTDOCDIR= CGIBINDIR= BINM_APROPOS="apropos" +BINM_CATMAN="catman" BINM_MAKEWHATIS="makewhatis" BINM_MAN="man" BINM_SOELIM="soelim" BINM_WHATIS="whatis" MANM_MAN="man" MANM_MANCONF="man.conf" MANM_MDOC="mdoc" MANM_ROFF="roff" MANM_EQN="eqn" MANM_TBL="tbl" INSTALL="install" INSTALL_PROGRAM= INSTALL_LIB= INSTALL_MAN= INSTALL_DATA= +LN="ln -f" # --- manual settings from configure.local ----------------------------- if [ -r ./configure.local ]; then echo "configure.local: reading..." 1>&2 echo "configure.local: reading..." 1>&3 cat ./configure.local 1>&3 . ./configure.local else echo "configure.local: no (fully automatic configuration)" 1>&2 echo "configure.local: no (fully automatic configuration)" 1>&3 fi echo 1>&3 # --- tests for config.h ---------------------------------------------- COMP="${CC} ${CFLAGS} -Wno-unused -Werror" # Check whether this HAVE_ setting is manually overridden. # If yes, use the override, if no, do not decide anything yet. # Arguments: lower-case test name, manual value ismanual() { [ -z "${3}" ] && return 1 echo "${1}: manual (HAVE_${2}=${3})" 1>&2 echo "${1}: manual (HAVE_${2}=${3})" 1>&3 echo 1>&3 return 0 } # Run a single autoconfiguration test. # In case of success, enable the feature. # In case of failure, do not decide anything yet. # Arguments: lower-case test name, upper-case test name, additional CFLAGS singletest() { cat 1>&3 << __HEREDOC__ ${1}${3}: testing... ${COMP} -o test-${1} test-${1}.c ${3} __HEREDOC__ - if ${COMP} -o "test-${1}" "test-${1}.c" ${3} 1>&3 2>&3; then + if ${COMP} -o "test-${1}" "${SOURCEDIR}/test-${1}.c" ${3} 1>&3 2>&3 + then echo "${1}${3}: ${CC} succeeded" 1>&3 else echo "${1}${3}: ${CC} failed with $?" 1>&3 echo 1>&3 return 1 fi if ./test-${1} 1>&3 2>&3; then echo "${1}${3}: yes" 1>&2 echo "${1}${3}: yes" 1>&3 echo 1>&3 eval HAVE_${2}=1 rm "test-${1}" return 0 else echo "${1}${3}: execution failed with $?" 1>&3 echo 1>&3 rm "test-${1}" return 1 fi } # Run a complete autoconfiguration test, including the check for # a manual override and disabling the feature on failure. # Arguments: lower case name, upper case name, additional CFLAGS runtest() { eval _manual=\${HAVE_${2}} ismanual "${1}" "${2}" "${_manual}" && return 0 singletest "${1}" "${2}" "${3}" && return 0 echo "${1}${3}: no" 1>&2 eval HAVE_${2}=0 return 1 } # Select a UTF-8 locale. get_locale() { [ -n "${HAVE_WCHAR}" ] && [ "${HAVE_WCHAR}" -eq 0 ] && return 0 ismanual UTF8_LOCALE UTF8_LOCALE "$UTF8_LOCALE" && return 0 echo "UTF8_LOCALE: testing..." 1>&3 UTF8_LOCALE=`locale -a | grep -i '^en_US\.UTF-*8$' | head -n 1` if [ -z "${UTF8_LOCALE}" ]; then UTF8_LOCALE=`locale -a | grep -i '\.UTF-*8' | head -n 1` [ -n "${UTF8_LOCALE}" ] || return 1 fi echo "UTF8_LOCALE=${UTF8_LOCALE}" 1>&2 echo "UTF8_LOCALE=${UTF8_LOCALE}" 1>&3 echo 1>&3 return 0; } # --- library functions --- runtest dirent-namlen DIRENT_NAMLEN || true runtest be32toh ENDIAN || true runtest be32toh SYS_ENDIAN -DSYS_ENDIAN || true runtest EFTYPE EFTYPE || true runtest err ERR || true runtest getline GETLINE || true runtest getsubopt GETSUBOPT || true runtest isblank ISBLANK || true runtest mkdtemp MKDTEMP || true runtest ntohl NTOHL || true +runtest O_DIRECTORY O_DIRECTORY || true runtest PATH_MAX PATH_MAX || true runtest pledge PLEDGE || true runtest sandbox_init SANDBOX_INIT || true runtest progname PROGNAME || true runtest reallocarray REALLOCARRAY || true runtest rewb-bsd REWB_BSD || true runtest rewb-sysv REWB_SYSV || true runtest strcasestr STRCASESTR || true runtest stringlist STRINGLIST || true runtest strlcat STRLCAT || true runtest strlcpy STRLCPY || true runtest strptime STRPTIME || true runtest strsep STRSEP || true runtest strtonum STRTONUM || true runtest vasprintf VASPRINTF || true if [ ${HAVE_ENDIAN} -eq 0 -a \ ${HAVE_SYS_ENDIAN} -eq 0 -a \ ${HAVE_NTOHL} -eq 0 ]; then echo "FATAL: no endian conversion functions found" 1>&2 echo "FATAL: no endian conversion functions found" 1>&3 exit 1 fi if ismanual fts FTS ${HAVE_FTS}; then HAVE_FTS_COMPARE_CONST=0 elif runtest fts FTS_COMPARE_CONST -DFTS_COMPARE_CONST; then HAVE_FTS=1 else runtest fts FTS || true fi # --- wide character and locale support --- if get_locale; then runtest wchar WCHAR -DUTF8_LOCALE=\"${UTF8_LOCALE}\" || true else HAVE_WCHAR=0 echo "wchar: no (no UTF8_LOCALE)" 1>&2 echo "wchar: no (no UTF8_LOCALE)" 1>&3 fi # --- nanosleep --- if [ -n "${LD_NANOSLEEP}" ]; then runtest nanosleep NANOSLEEP "${LD_NANOSLEEP}" || true elif singletest nanosleep NANOSLEEP; then : elif runtest nanosleep NANOSLEEP "-lrt"; then LD_NANOSLEEP="-lrt" fi if [ "${HAVE_NANOSLEEP}" -eq 0 ]; then echo "FATAL: nanosleep: no" 1>&2 echo "FATAL: nanosleep: no" 1>&3 exit 1 fi +if [ ${BUILD_CATMAN} -gt 0 ]; then + # --- recvmsg --- + if [ -n "${LD_RECVMSG}" ]; then + runtest recvmsg RECVMSG "${LD_RECVMSG}" || true + elif singletest recvmsg RECVMSG; then + : + elif runtest recvmsg RECVMSG "-lsocket"; then + LD_RECVMSG="-lsocket" + fi + if [ "${HAVE_RECVMSG}" -eq 0 ]; then + echo "FATAL: recvmsg: no" 1>&2 + echo "FATAL: recvmsg: no" 1>&3 + echo "Without recvmsg(2), you cannot BUILD_CATMAN." 1>&2 + exit 1 + fi + + # --- cmsg --- + if singletest cmsg CMSG; then + : + elif runtest cmsg CMSG "-D_XPG4_2"; then + HAVE_CMSG_XPG42=1 + fi + if [ "${HAVE_CMSG}" -eq 0 ]; then + echo "FATAL: cmsg: no" 1>&2 + echo "FATAL: cmsg: no" 1>&3 + echo "Without CMSG_FIRSTHDR(3), you cannot BUILD_CATMAN." 1>&2 + exit 1 + fi +fi + # --- ohash --- if ismanual ohash OHASH "${HAVE_OHASH}"; then : elif [ -n "${LD_OHASH}" ]; then runtest ohash OHASH "${LD_OHASH}" || true elif singletest ohash OHASH; then : elif runtest ohash OHASH "-lutil"; then LD_OHASH="-lutil" fi if [ "${HAVE_OHASH}" -eq 0 ]; then LD_OHASH= fi # --- LDADD --- -LDADD="${LDADD} ${LD_NANOSLEEP} ${LD_OHASH} -lz" +LDADD="${LDADD} ${LD_NANOSLEEP} ${LD_RECVMSG} ${LD_OHASH} -lz" echo "LDADD=\"${LDADD}\"" 1>&2 echo "LDADD=\"${LDADD}\"" 1>&3 echo 1>&3 # --- write config.h --- exec > config.h cat << __HEREDOC__ #ifdef __cplusplus #error "Do not use C++. See the INSTALL file." #endif #if !defined(__GNUC__) || (__GNUC__ < 4) #define __attribute__(x) #endif #if defined(__linux__) || defined(__MINT__) #define _GNU_SOURCE /* See test-*.c what needs this. */ #endif __HEREDOC__ [ ${HAVE_GETLINE} -eq 0 -o ${HAVE_REALLOCARRAY} -eq 0 -o \ ${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 ] \ && echo "#include " [ ${HAVE_VASPRINTF} -eq 0 ] && echo "#include " [ ${HAVE_GETLINE} -eq 0 ] && echo "#include " echo echo "#define MAN_CONF_FILE \"/etc/${MANM_MANCONF}\"" echo "#define MANPATH_DEFAULT \"${MANPATH_DEFAULT}\"" [ -n "${OSNAME}" ] && echo "#define OSNAME \"${OSNAME}\"" [ -n "${UTF8_LOCALE}" ] && echo "#define UTF8_LOCALE \"${UTF8_LOCALE}\"" [ -n "${HOMEBREWDIR}" ] && echo "#define HOMEBREWDIR \"${HOMEBREWDIR}\"" [ ${HAVE_EFTYPE} -eq 0 ] && echo "#define EFTYPE EINVAL" +[ ${HAVE_O_DIRECTORY} -eq 0 ] && echo "#define O_DIRECTORY 0" [ ${HAVE_PATH_MAX} -eq 0 ] && echo "#define PATH_MAX 4096" if [ ${HAVE_ENDIAN} -eq 0 -a ${HAVE_SYS_ENDIAN} -eq 0 ]; then echo "#define be32toh ntohl" echo "#define htobe32 htonl" fi cat << __HEREDOC__ +#define HAVE_CMSG_XPG42 ${HAVE_CMSG_XPG42} #define HAVE_DIRENT_NAMLEN ${HAVE_DIRENT_NAMLEN} #define HAVE_ENDIAN ${HAVE_ENDIAN} #define HAVE_ERR ${HAVE_ERR} #define HAVE_FTS ${HAVE_FTS} #define HAVE_FTS_COMPARE_CONST ${HAVE_FTS_COMPARE_CONST} #define HAVE_GETLINE ${HAVE_GETLINE} #define HAVE_GETSUBOPT ${HAVE_GETSUBOPT} #define HAVE_ISBLANK ${HAVE_ISBLANK} #define HAVE_MKDTEMP ${HAVE_MKDTEMP} #define HAVE_NTOHL ${HAVE_NTOHL} #define HAVE_PLEDGE ${HAVE_PLEDGE} #define HAVE_PROGNAME ${HAVE_PROGNAME} #define HAVE_REALLOCARRAY ${HAVE_REALLOCARRAY} #define HAVE_REWB_BSD ${HAVE_REWB_BSD} #define HAVE_REWB_SYSV ${HAVE_REWB_SYSV} #define HAVE_SANDBOX_INIT ${HAVE_SANDBOX_INIT} #define HAVE_STRCASESTR ${HAVE_STRCASESTR} #define HAVE_STRINGLIST ${HAVE_STRINGLIST} #define HAVE_STRLCAT ${HAVE_STRLCAT} #define HAVE_STRLCPY ${HAVE_STRLCPY} #define HAVE_STRPTIME ${HAVE_STRPTIME} #define HAVE_STRSEP ${HAVE_STRSEP} #define HAVE_STRTONUM ${HAVE_STRTONUM} #define HAVE_SYS_ENDIAN ${HAVE_SYS_ENDIAN} #define HAVE_VASPRINTF ${HAVE_VASPRINTF} #define HAVE_WCHAR ${HAVE_WCHAR} #define HAVE_OHASH ${HAVE_OHASH} #define BINM_APROPOS "${BINM_APROPOS}" +#define BINM_CATMAN "${BINM_CATMAN}" #define BINM_MAKEWHATIS "${BINM_MAKEWHATIS}" #define BINM_MAN "${BINM_MAN}" #define BINM_SOELIM "${BINM_SOELIM}" #define BINM_WHATIS "${BINM_WHATIS}" __HEREDOC__ if [ ${HAVE_ERR} -eq 0 ]; then echo "extern void err(int, const char *, ...);" echo "extern void errx(int, const char *, ...);" echo "extern void warn(const char *, ...);" echo "extern void warnx(const char *, ...);" fi [ ${HAVE_GETLINE} -eq 0 ] && \ echo "extern ssize_t getline(char **, size_t *, FILE *);" [ ${HAVE_GETSUBOPT} -eq 0 ] && \ echo "extern int getsubopt(char **, char * const *, char **);" [ ${HAVE_ISBLANK} -eq 0 ] && \ echo "extern int isblank(int);" [ ${HAVE_MKDTEMP} -eq 0 ] && \ echo "extern char *mkdtemp(char *);" if [ ${HAVE_PROGNAME} -eq 0 ]; then echo "extern const char *getprogname(void);" echo "extern void setprogname(const char *);" fi [ ${HAVE_REALLOCARRAY} -eq 0 ] && \ echo "extern void *reallocarray(void *, size_t, size_t);" [ ${HAVE_STRCASESTR} -eq 0 ] && \ echo "extern char *strcasestr(const char *, const char *);" [ ${HAVE_STRLCAT} -eq 0 ] && \ echo "extern size_t strlcat(char *, const char *, size_t);" [ ${HAVE_STRLCPY} -eq 0 ] && \ echo "extern size_t strlcpy(char *, const char *, size_t);" [ ${HAVE_STRSEP} -eq 0 ] && \ echo "extern char *strsep(char **, const char *);" [ ${HAVE_STRTONUM} -eq 0 ] && \ echo "extern long long strtonum(const char *, long long, long long, const char **);" [ ${HAVE_VASPRINTF} -eq 0 ] && \ echo "extern int vasprintf(char **, const char *, va_list);" echo "config.h: written" 1>&2 echo "config.h: written" 1>&3 # --- tests for Makefile.local ----------------------------------------- exec > Makefile.local [ -z "${BINDIR}" ] && BINDIR="${PREFIX}/bin" [ -z "${SBINDIR}" ] && SBINDIR="${PREFIX}/sbin" [ -z "${INCLUDEDIR}" ] && INCLUDEDIR="${PREFIX}/include/mandoc" [ -z "${LIBDIR}" ] && LIBDIR="${PREFIX}/lib/mandoc" [ -z "${MANDIR}" ] && MANDIR="${PREFIX}/man" [ -z "${HTDOCDIR}" ] && HTDOCDIR="${WWWPREFIX}/htdocs" [ -z "${CGIBINDIR}" ] && CGIBINDIR="${WWWPREFIX}/cgi-bin" [ -z "${INSTALL_PROGRAM}" ] && INSTALL_PROGRAM="${INSTALL} -m 0555" [ -z "${INSTALL_LIB}" ] && INSTALL_LIB="${INSTALL} -m 0444" [ -z "${INSTALL_MAN}" ] && INSTALL_MAN="${INSTALL} -m 0444" [ -z "${INSTALL_DATA}" ] && INSTALL_DATA="${INSTALL} -m 0444" BUILD_TARGETS= -[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="cgi-build" +[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="man.cgi" +[ ${BUILD_CATMAN} -gt 0 ] && \ + BUILD_TARGETS="${BUILD_TARGETS} mandocd catman" INSTALL_TARGETS= [ ${INSTALL_LIBMANDOC} -gt 0 ] && INSTALL_TARGETS="lib-install" [ ${BUILD_CGI} -gt 0 ] && INSTALL_TARGETS="${INSTALL_TARGETS} cgi-install" +[ ${BUILD_CATMAN} -gt 0 ] && \ + INSTALL_TARGETS="${INSTALL_TARGETS} catman-install" cat << __HEREDOC__ BUILD_TARGETS = ${BUILD_TARGETS} INSTALL_TARGETS = ${INSTALL_TARGETS} CC = ${CC} CFLAGS = ${CFLAGS} LDADD = ${LDADD} LDFLAGS = ${LDFLAGS} STATIC = ${STATIC} PREFIX = ${PREFIX} BINDIR = ${BINDIR} SBINDIR = ${SBINDIR} INCLUDEDIR = ${INCLUDEDIR} LIBDIR = ${LIBDIR} MANDIR = ${MANDIR} WWWPREFIX = ${WWWPREFIX} HTDOCDIR = ${HTDOCDIR} CGIBINDIR = ${CGIBINDIR} BINM_APROPOS = ${BINM_APROPOS} +BINM_CATMAN = ${BINM_CATMAN} BINM_MAKEWHATIS = ${BINM_MAKEWHATIS} BINM_MAN = ${BINM_MAN} BINM_SOELIM = ${BINM_SOELIM} BINM_WHATIS = ${BINM_WHATIS} MANM_MAN = ${MANM_MAN} MANM_MANCONF = ${MANM_MANCONF} MANM_MDOC = ${MANM_MDOC} MANM_ROFF = ${MANM_ROFF} MANM_EQN = ${MANM_EQN} MANM_TBL = ${MANM_TBL} INSTALL = ${INSTALL} INSTALL_PROGRAM = ${INSTALL_PROGRAM} INSTALL_LIB = ${INSTALL_LIB} INSTALL_MAN = ${INSTALL_MAN} INSTALL_DATA = ${INSTALL_DATA} +LN = ${LN} __HEREDOC__ echo "Makefile.local: written" 1>&2 echo "Makefile.local: written" 1>&3 exit 0 Index: vendor/mdocml/dist/configure.local.example =================================================================== --- vendor/mdocml/dist/configure.local.example (revision 313955) +++ vendor/mdocml/dist/configure.local.example (revision 313956) @@ -1,269 +1,299 @@ -# $Id: configure.local.example,v 1.22 2016/11/19 15:24:51 schwarze Exp $ +# $Id: configure.local.example,v 1.29 2017/02/18 12:24:24 schwarze Exp $ # -# Copyright (c) 2014, 2015, 2016 Ingo Schwarze +# Copyright (c) 2014, 2015, 2016, 2017 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. # For all settings documented in this file, there are reasonable # defaults and/or the ./configure script attempts autodetection. # Consequently, you only need to create a file ./configure.local # and put any of these settings into it if ./configure autodetection # fails or if you want to make different choices for other reasons. # If autodetection fails, please tell . # We recommend that you write ./configure.local from scratch and # only put the lines there you need. This file contains examples. # It is not intended as a template to be copied as a whole. # --- user settings relevant for all builds ---------------------------- # For -Tutf8 and -Tlocale operation, mandoc(1) requires # providing setlocale(3) and providing wcwidth(3) and # putwchar(3) with a wchar_t storing UCS-4 values. Theoretically, # the latter should be tested with the __STDC_ISO_10646__ feature # macro. In practice, many headers do not provide that # macro even though they treat wchar_t as UCS-4. So the automatic # test only checks that wchar_t is wide enough, that is, at least # four bytes. # The following line forces multi-byte support. # If your C library does not treat wchar_t as UCS-4, the UTF-8 output # mode will print garbage. HAVE_WCHAR=1 # The following line disables multi-byte support. # The output modes -Tutf8 and -Tlocale will be the same as -Tascii. HAVE_WCHAR=0 # For -Tutf8 mode, mandoc needs to set an arbitrary locale having # a UTF-8 character set. If autodetection of a suitable locale # fails or selects an undesirable locale, you can manually choose # the locale for -Tutf8 mode: UTF8_LOCALE=en_US.UTF-8 # When man(1) or apropos(1) is called without -m and -M options, # MANPATH is not set in the environment, and man.conf(5) is not # available, manuals are searched for in the following directory # trees by default. MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man" # In manual pages written in the mdoc(7) language, the operating system # version is displayed in the page footer line. If an operating system # is specified as an argument to the .Os macro, that is always used. # If the .Os macro has no argument and an operation system is specified # with the mandoc(1) -Ios= command line option, that is used. # Otherwise, the uname(3) library function is called at runtime to find # the name of the operating system. # If you do not want uname(3) to be called but instead want a fixed # string to be used, use the following line: OSNAME="OpenBSD 6.0" # The following installation directories are used. # It is possible to set only one or a few of these variables, # there is no need to copy the whole block. # Even if you set PREFIX to something else, the other variables # pick it up without copying them all over. PREFIX="/usr/local" BINDIR="${PREFIX}/bin" SBINDIR="${PREFIX}/sbin" MANDIR="${PREFIX}/man" # Some distributions may want to avoid naming conflicts # with the configuration files of other man(1) implementations. # This changes the name of the installed section 5 manual page as well. MANM_MANCONF="mandoc.conf" # default is "man.conf" # Some distributions may want to avoid naming conflicts among manuals. # If you want to change the names of installed section 7 manual pages, # the following alternative names are suggested. # The suffix ".7" will automatically be appended. # It is possible to set only one or a few of these variables, # there is no need to copy the whole block. MANM_MAN="mandoc_man" # default is "man" MANM_MDOC="mandoc_mdoc" # default is "mdoc" MANM_ROFF="mandoc_roff" # default is "roff" MANM_EQN="mandoc_eqn" # default is "eqn" MANM_TBL="mandoc_tbl" # default is "tbl" # Some distributions may want to avoid naming conflicts with # other man(1), apropos(1), makewhatis(8), or soelim(1) utilities. # If you want to change the names of binary programs, # the following alternative names are suggested. # Using different names is possible as well. # This changes the names of the installed section 1 and section 8 # manual pages as well. # It is possible to set only one or two of these variables, # there is no need to copy the whole block. BINM_MAN=mman # default is "man" BINM_APROPOS=mapropos # default is "apropos" BINM_WHATIS=mwhatis # default is "whatis" BINM_MAKEWHATIS=mandocdb # default is "makewhatis" BINM_SOELIM=msoelim # default is "soelim" +# Some distributions do not want hardlinks +# between installed binary programs. +# Set the following variable to use symbolic links instead. +# It is also used for links between manual pages. +# It is only used by the install* targets. +# When using this, DESTDIR must be empty or an absolute path. + +LN="ln -sf" # default is "ln -f" + # Before falling back to the bundled version of the ohash(3) hashing # library, autoconfiguration tries the following linker flag to # link against your system version. If you do have ohash(3) on # your system but it needs different linker flags, set the following # variable to specify the required linker flags. LD_OHASH="-lutil" # When library autodetection decides to use -L/usr/local/lib, # -I/usr/local/include is automatically added to CFLAGS. # If you manually set LD_OHASH to something including -L/usr/local/lib, # chances are you will also need the following line: CFLAGS="${CFLAGS} -I/usr/local/include" # Some platforms may need an additional linker flag for nanosleep(2). # If none is needed or it is -lrt, it is autodetected. # Otherwise, set the following variable. LD_NANOSLEEP="-lrt" +# Some platforms may need an additional linker flag for recvmsg(2). +# If none is needed or it is -lsocket, it is autodetected. +# Otherwise, set the following variable. + +LD_RECVMSG="-lsocket" + # Some platforms might need additional linker flags to link against # libmandoc that are not autodetected, though no such cases are # currently known. LDADD="-lm" # Some systems may want to set additional linker flags for all the # binaries, not only for those using libmandoc, for example for # hardening options. LDFLAGS="-Wl,-z,relro" # It is possible to change the utility program used for installation # and the modes files are installed with. The defaults are: INSTALL="install" INSTALL_PROGRAM="${INSTALL} -m 0555" INSTALL_LIB="${INSTALL} -m 0444" INSTALL_MAN="${INSTALL} -m 0444" INSTALL_DATA="${INSTALL} -m 0444" # When using the "homebrew" package manager on Mac OS X, the actual # manuals are located in a so-called "cellar" and only symlinked # into the manual trees. To allow mandoc to follow such symlinks, # you have to specify the physical location of the cellar as returned # by realpath(3), for example: PREFIX="/usr/local" HOMEBREWDIR="${PREFIX}/Cellar" # --- user settings for the mandoc(3) library -------------------------- # By default, libmandoc.a is not installed. It is almost never needed # because there is almost no non-mandoc software out there using this # library. The one notable exception is NetBSD apropos(1). # So, when building for the NetBSD base system - but not for NetBSD # ports nor for pkgsrc! - you may want the following: INSTALL_LIBMANDOC=1 # The following settings are only used when INSTALL_LIBMANDOC is set. INCLUDEDIR="${PREFIX}/include/mandoc" LIBDIR="${PREFIX}/lib/mandoc" # --- user settings related to man.cgi --------------------------------- # By default, building man.cgi(8) is disabled. To enable it, copy # cgi.h.example to cgi.h, edit it, and use the following line. BUILD_CGI=1 # The remaining settings in this section are only relevant if BUILD_CGI # is enabled. Otherwise, they have no effect either way. # By default, man.cgi(8) is linked statically. # Some systems do not support static linking, for example Mac OS X. # In that case, use the following line: STATIC= # Some systems, for example Linux, require -pthread for static linking: STATIC="-static -pthread" # Some directories. # This works just like PREFIX, see above. WWWPREFIX="/var/www" HTDOCDIR="${WWWPREFIX}/htdocs" CGIBINDIR="${WWWPREFIX}/cgi-bin" +# --- user settings related to catman ---------------------------------- + +# By default, building mandocd(8) and catman(8) is disabled. +# To enable it, use the following line. +# It does not work on SunOS 5.10 because there is no mkdirat(2) +# nor on SunOS 5.9 which also lacks CMSG_LEN(3) and CMSG_SPACE(3). + +BUILD_CATMAN=1 + +# Install catman(8) with a different name. +# See BINM_MAN above for details of how this works. + +BINM_CATMAN=mcatman # default is "catman" + # --- settings that rarely need to be touched -------------------------- # Do not set these variables unless you really need to. # You can manually override the compiler to be used. # But that's rarely useful because ./configure asks your make(1) # which compiler to use, and that answer will hardly be wrong. CC=cc # IBM AIX may need: CC=xlc # The default compiler flags are: CFLAGS="-g -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings" # IBM AIX xlc does not support -W; in that case, please use: CFLAGS="-g" # In rare cases, it may be required to skip individual automatic tests. # Each of the following variables can be set to 0 (test will not be run # and will be regarded as failed) or 1 (test will not be run and will # be regarded as successful). HAVE_DIRENT_NAMLEN=0 HAVE_ENDIAN=0 HAVE_EFTYPE=0 HAVE_ERR=0 HAVE_FTS=0 # Setting this implies HAVE_FTS_COMPARE_CONST=0. HAVE_FTS_COMPARE_CONST=0 # Setting this implies HAVE_FTS=1. HAVE_GETLINE=0 HAVE_GETSUBOPT=0 HAVE_ISBLANK=0 HAVE_MKDTEMP=0 HAVE_NTOHL=0 +HAVE_O_DIRECTORY=0 HAVE_OHASH=0 HAVE_PATH_MAX=0 HAVE_PLEDGE=0 HAVE_PROGNAME=0 HAVE_REALLOCARRAY=0 HAVE_REWB_BSD=0 HAVE_REWB_SYSV=0 HAVE_STRCASESTR=0 HAVE_STRINGLIST=0 HAVE_STRLCAT=0 HAVE_STRLCPY=0 HAVE_STRPTIME=0 HAVE_STRSEP=0 HAVE_STRTONUM=0 HAVE_SYS_ENDIAN=0 HAVE_VASPRINTF=0 HAVE_WCHAR=0 Index: vendor/mdocml/dist/dba.c =================================================================== --- vendor/mdocml/dist/dba.c (revision 313955) +++ vendor/mdocml/dist/dba.c (revision 313956) @@ -1,508 +1,508 @@ -/* $Id: dba.c,v 1.9 2017/01/15 15:28:55 schwarze Exp $ */ +/* $Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp $ */ /* * Copyright (c) 2016, 2017 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Allocation-based version of the mandoc database, for read-write access. * The interface is defined in "dba.h". */ #include "config.h" #include #if HAVE_ENDIAN #include #elif HAVE_SYS_ENDIAN #include #elif HAVE_NTOHL #include #endif #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc_ohash.h" #include "mansearch.h" #include "dba_write.h" #include "dba_array.h" #include "dba.h" struct macro_entry { struct dba_array *pages; char value[]; }; static void *prepend(const char *, char); static void dba_pages_write(struct dba_array *); static int compare_names(const void *, const void *); static int compare_strings(const void *, const void *); static struct macro_entry *get_macro_entry(struct ohash *, const char *, int32_t); static void dba_macros_write(struct dba_array *); static void dba_macro_write(struct ohash *); static int compare_entries(const void *, const void *); /*** top-level functions **********************************************/ struct dba * dba_new(int32_t npages) { struct dba *dba; struct ohash *macro; int32_t im; dba = mandoc_malloc(sizeof(*dba)); dba->pages = dba_array_new(npages, DBA_GROW); dba->macros = dba_array_new(MACRO_MAX, 0); for (im = 0; im < MACRO_MAX; im++) { macro = mandoc_malloc(sizeof(*macro)); mandoc_ohash_init(macro, 4, offsetof(struct macro_entry, value)); dba_array_set(dba->macros, im, macro); } return dba; } void dba_free(struct dba *dba) { struct dba_array *page; struct ohash *macro; struct macro_entry *entry; unsigned int slot; dba_array_FOREACH(dba->macros, macro) { for (entry = ohash_first(macro, &slot); entry != NULL; entry = ohash_next(macro, &slot)) { dba_array_free(entry->pages); free(entry); } ohash_delete(macro); free(macro); } dba_array_free(dba->macros); dba_array_undel(dba->pages); dba_array_FOREACH(dba->pages, page) { dba_array_free(dba_array_get(page, DBP_NAME)); dba_array_free(dba_array_get(page, DBP_SECT)); dba_array_free(dba_array_get(page, DBP_ARCH)); free(dba_array_get(page, DBP_DESC)); dba_array_free(dba_array_get(page, DBP_FILE)); dba_array_free(page); } dba_array_free(dba->pages); free(dba); } /* * Write the complete mandoc database to disk; the format is: * - One integer each for magic and version. * - One pointer each to the macros table and to the final magic. * - The pages table. * - The macros table. * - And at the very end, the magic integer again. */ int dba_write(const char *fname, struct dba *dba) { int save_errno; int32_t pos_end, pos_macros, pos_macros_ptr; if (dba_open(fname) == -1) return -1; dba_int_write(MANDOCDB_MAGIC); dba_int_write(MANDOCDB_VERSION); pos_macros_ptr = dba_skip(1, 2); dba_pages_write(dba->pages); pos_macros = dba_tell(); dba_macros_write(dba->macros); pos_end = dba_tell(); dba_int_write(MANDOCDB_MAGIC); dba_seek(pos_macros_ptr); dba_int_write(pos_macros); dba_int_write(pos_end); if (dba_close() == -1) { save_errno = errno; unlink(fname); errno = save_errno; return -1; } return 0; } /*** functions for handling pages *************************************/ /* * Create a new page and append it to the pages table. */ struct dba_array * dba_page_new(struct dba_array *pages, const char *arch, const char *desc, const char *file, enum form form) { struct dba_array *page, *entry; page = dba_array_new(DBP_MAX, 0); entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(page, entry); entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(page, entry); if (arch != NULL && *arch != '\0') { entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(entry, (void *)arch); } else entry = NULL; dba_array_add(page, entry); dba_array_add(page, mandoc_strdup(desc)); entry = dba_array_new(1, DBA_STR | DBA_GROW); dba_array_add(entry, prepend(file, form)); dba_array_add(page, entry); dba_array_add(pages, page); return page; } /* * Add a section, architecture, or file name to an existing page. * Passing the NULL pointer for the architecture makes the page MI. * In that case, any earlier or later architectures are ignored. */ void dba_page_add(struct dba_array *page, int32_t ie, const char *str) { struct dba_array *entries; char *entry; entries = dba_array_get(page, ie); if (ie == DBP_ARCH) { if (entries == NULL) return; if (str == NULL || *str == '\0') { dba_array_free(entries); dba_array_set(page, DBP_ARCH, NULL); return; } } if (*str == '\0') return; dba_array_FOREACH(entries, entry) { if (ie == DBP_FILE && *entry < ' ') entry++; if (strcmp(entry, str) == 0) return; } dba_array_add(entries, (void *)str); } /* * Add an additional name to an existing page. */ void dba_page_alias(struct dba_array *page, const char *name, uint64_t mask) { struct dba_array *entries; char *entry; char maskbyte; if (*name == '\0') return; maskbyte = mask & NAME_MASK; entries = dba_array_get(page, DBP_NAME); dba_array_FOREACH(entries, entry) { if (strcmp(entry + 1, name) == 0) { *entry |= maskbyte; return; } } dba_array_add(entries, prepend(name, maskbyte)); } /* * Return a pointer to a temporary copy of instr with inbyte prepended. */ static void * prepend(const char *instr, char inbyte) { static char *outstr = NULL; static size_t outlen = 0; size_t newlen; newlen = strlen(instr) + 1; if (newlen > outlen) { outstr = mandoc_realloc(outstr, newlen + 1); outlen = newlen; } *outstr = inbyte; memcpy(outstr + 1, instr, newlen); return outstr; } /* * Write the pages table to disk; the format is: * - One integer containing the number of pages. * - For each page, five pointers to the names, sections, * architectures, description, and file names of the page. * MI pages write 0 instead of the architecture pointer. * - One list each for names, sections, architectures, descriptions and * file names. The description for each page ends with a NUL byte. * For all the other lists, each string ends with a NUL byte, * and the last string for a page ends with two NUL bytes. * - To assure alignment of following integers, * the end is padded with NUL bytes up to a multiple of four bytes. */ static void dba_pages_write(struct dba_array *pages) { struct dba_array *page, *entry; int32_t pos_pages, pos_end; pos_pages = dba_array_writelen(pages, 5); dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_NAME, dba_tell()); entry = dba_array_get(page, DBP_NAME); dba_array_sort(entry, compare_names); dba_array_writelst(entry); } dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_SECT, dba_tell()); entry = dba_array_get(page, DBP_SECT); dba_array_sort(entry, compare_strings); dba_array_writelst(entry); } dba_array_FOREACH(pages, page) { if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) { dba_array_setpos(page, DBP_ARCH, dba_tell()); dba_array_sort(entry, compare_strings); dba_array_writelst(entry); } else dba_array_setpos(page, DBP_ARCH, 0); } dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_DESC, dba_tell()); dba_str_write(dba_array_get(page, DBP_DESC)); } dba_array_FOREACH(pages, page) { dba_array_setpos(page, DBP_FILE, dba_tell()); dba_array_writelst(dba_array_get(page, DBP_FILE)); } pos_end = dba_align(); dba_seek(pos_pages); dba_array_FOREACH(pages, page) dba_array_writepos(page); dba_seek(pos_end); } static int compare_names(const void *vp1, const void *vp2) { const char *cp1, *cp2; int diff; - cp1 = *(char **)vp1; - cp2 = *(char **)vp2; + cp1 = *(const char * const *)vp1; + cp2 = *(const char * const *)vp2; return (diff = *cp2 - *cp1) ? diff : strcasecmp(cp1 + 1, cp2 + 1); } static int compare_strings(const void *vp1, const void *vp2) { const char *cp1, *cp2; - cp1 = *(char **)vp1; - cp2 = *(char **)vp2; + cp1 = *(const char * const *)vp1; + cp2 = *(const char * const *)vp2; return strcmp(cp1, cp2); } /*** functions for handling macros ************************************/ /* * In the hash table for a single macro, look up an entry by * the macro value or add an empty one if it doesn't exist yet. */ static struct macro_entry * get_macro_entry(struct ohash *macro, const char *value, int32_t np) { struct macro_entry *entry; size_t len; unsigned int slot; slot = ohash_qlookup(macro, value); if ((entry = ohash_find(macro, slot)) == NULL) { len = strlen(value) + 1; entry = mandoc_malloc(sizeof(*entry) + len); memcpy(&entry->value, value, len); entry->pages = dba_array_new(np, DBA_GROW); ohash_insert(macro, slot, entry); } return entry; } /* * In addition to get_macro_entry(), add multiple page references, * converting them from the on-disk format (byte offsets in the file) * to page pointers in memory. */ void dba_macro_new(struct dba *dba, int32_t im, const char *value, const int32_t *pp) { struct macro_entry *entry; const int32_t *ip; int32_t np; np = 0; for (ip = pp; *ip; ip++) np++; entry = get_macro_entry(dba_array_get(dba->macros, im), value, np); for (ip = pp; *ip; ip++) dba_array_add(entry->pages, dba_array_get(dba->pages, be32toh(*ip) / 5 / sizeof(*ip) - 1)); } /* * In addition to get_macro_entry(), add one page reference, * directly taking the in-memory page pointer as an argument. */ void dba_macro_add(struct dba_array *macros, int32_t im, const char *value, struct dba_array *page) { struct macro_entry *entry; if (*value == '\0') return; entry = get_macro_entry(dba_array_get(macros, im), value, 1); dba_array_add(entry->pages, page); } /* * Write the macros table to disk; the format is: * - The number of macro tables (actually, MACRO_MAX). * - That number of pointers to the individual macro tables. * - The individual macro tables. */ static void dba_macros_write(struct dba_array *macros) { struct ohash *macro; int32_t im, pos_macros, pos_end; pos_macros = dba_array_writelen(macros, 1); im = 0; dba_array_FOREACH(macros, macro) { dba_array_setpos(macros, im++, dba_tell()); dba_macro_write(macro); } pos_end = dba_tell(); dba_seek(pos_macros); dba_array_writepos(macros); dba_seek(pos_end); } /* * Write one individual macro table to disk; the format is: * - The number of entries in the table. * - For each entry, two pointers, the first one to the value * and the second one to the list of pages. * - A list of values, each ending in a NUL byte. * - To assure alignment of following integers, * padding with NUL bytes up to a multiple of four bytes. * - A list of pointers to pages, each list ending in a 0 integer. */ static void dba_macro_write(struct ohash *macro) { struct macro_entry **entries, *entry; struct dba_array *page; int32_t *kpos, *dpos; unsigned int ie, ne, slot; int use; int32_t addr, pos_macro, pos_end; /* Temporary storage for filtering and sorting. */ ne = ohash_entries(macro); entries = mandoc_reallocarray(NULL, ne, sizeof(*entries)); kpos = mandoc_reallocarray(NULL, ne, sizeof(*kpos)); dpos = mandoc_reallocarray(NULL, ne, sizeof(*dpos)); /* Build a list of non-empty entries and sort it. */ ne = 0; for (entry = ohash_first(macro, &slot); entry != NULL; entry = ohash_next(macro, &slot)) { use = 0; dba_array_FOREACH(entry->pages, page) if (dba_array_getpos(page)) use = 1; if (use) entries[ne++] = entry; } qsort(entries, ne, sizeof(*entries), compare_entries); /* Number of entries, and space for the pointer pairs. */ dba_int_write(ne); pos_macro = dba_skip(2, ne); /* String table. */ for (ie = 0; ie < ne; ie++) { kpos[ie] = dba_tell(); dba_str_write(entries[ie]->value); } dba_align(); /* Pages table. */ for (ie = 0; ie < ne; ie++) { dpos[ie] = dba_tell(); dba_array_FOREACH(entries[ie]->pages, page) if ((addr = dba_array_getpos(page))) dba_int_write(addr); dba_int_write(0); } pos_end = dba_tell(); /* Fill in the pointer pairs. */ dba_seek(pos_macro); for (ie = 0; ie < ne; ie++) { dba_int_write(kpos[ie]); dba_int_write(dpos[ie]); } dba_seek(pos_end); free(entries); free(kpos); free(dpos); } static int compare_entries(const void *vp1, const void *vp2) { const struct macro_entry *ep1, *ep2; - ep1 = *(struct macro_entry **)vp1; - ep2 = *(struct macro_entry **)vp2; + ep1 = *(const struct macro_entry * const *)vp1; + ep2 = *(const struct macro_entry * const *)vp2; return strcmp(ep1->value, ep2->value); } Index: vendor/mdocml/dist/dbm_map.c =================================================================== --- vendor/mdocml/dist/dbm_map.c (revision 313955) +++ vendor/mdocml/dist/dbm_map.c (revision 313956) @@ -1,194 +1,194 @@ -/* $Id: dbm_map.c,v 1.7 2016/10/22 10:09:27 schwarze Exp $ */ +/* $Id: dbm_map.c,v 1.8 2017/02/17 14:43:54 schwarze Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze * * 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. * * Low-level routines for the map-based version * of the mandoc database, for read-only access. * The interface is defined in "dbm_map.h". */ #include "config.h" #include #include #include #if HAVE_ENDIAN #include #elif HAVE_SYS_ENDIAN #include #elif HAVE_NTOHL #include #endif #if HAVE_ERR #include #endif #include #include #include #include #include #include #include #include "mansearch.h" #include "dbm_map.h" #include "dbm.h" static struct stat st; static char *dbm_base; static int ifd; static int32_t max_offset; /* * Open a disk-based database for read-only access. * Validate the file format as far as it is not mandoc-specific. * Return 0 on success. Return -1 and set errno on failure. */ int dbm_map(const char *fname) { int save_errno; const int32_t *magic; if ((ifd = open(fname, O_RDONLY)) == -1) return -1; if (fstat(ifd, &st) == -1) goto fail; if (st.st_size < 5) { warnx("dbm_map(%s): File too short", fname); errno = EFTYPE; goto fail; } if (st.st_size > INT32_MAX) { errno = EFBIG; goto fail; } if ((dbm_base = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, ifd, 0)) == MAP_FAILED) goto fail; magic = dbm_getint(0); if (be32toh(*magic) != MANDOCDB_MAGIC) { if (strncmp(dbm_base, "SQLite format 3", 15)) warnx("dbm_map(%s): " "Bad initial magic %x (expected %x)", fname, be32toh(*magic), MANDOCDB_MAGIC); else warnx("dbm_map(%s): " "Obsolete format based on SQLite 3", fname); errno = EFTYPE; goto fail; } magic = dbm_getint(1); if (be32toh(*magic) != MANDOCDB_VERSION) { warnx("dbm_map(%s): Bad version number %d (expected %d)", fname, be32toh(*magic), MANDOCDB_VERSION); errno = EFTYPE; goto fail; } max_offset = be32toh(*dbm_getint(3)) + sizeof(int32_t); if (st.st_size != max_offset) { warnx("dbm_map(%s): Inconsistent file size %lld (expected %d)", fname, (long long)st.st_size, max_offset); errno = EFTYPE; goto fail; } if ((magic = dbm_get(*dbm_getint(3))) == NULL) { errno = EFTYPE; goto fail; } if (be32toh(*magic) != MANDOCDB_MAGIC) { warnx("dbm_map(%s): Bad final magic %x (expected %x)", fname, be32toh(*magic), MANDOCDB_MAGIC); errno = EFTYPE; goto fail; } return 0; fail: save_errno = errno; close(ifd); errno = save_errno; return -1; } void dbm_unmap(void) { if (munmap(dbm_base, st.st_size) == -1) warn("dbm_unmap: munmap"); if (close(ifd) == -1) warn("dbm_unmap: close"); dbm_base = (char *)-1; } /* * Take a raw integer as it was read from the database. * Interpret it as an offset into the database file * and return a pointer to that place in the file. */ void * dbm_get(int32_t offset) { offset = be32toh(offset); if (offset < 0) { warnx("dbm_get: Database corrupt: offset %d", offset); return NULL; } if (offset >= max_offset) { warnx("dbm_get: Database corrupt: offset %d > %d", offset, max_offset); return NULL; } return dbm_base + offset; } /* * Assume the database starts with some integers. * Assume they are numbered starting from 0, increasing. * Get a pointer to one with the number "offset". */ int32_t * dbm_getint(int32_t offset) { return (int32_t *)dbm_base + offset; } /* * The reverse of dbm_get(). * Take pointer into the database file * and convert it to the raw integer * that would be used to refer to that place in the file. */ int32_t dbm_addr(const void *p) { - return htobe32((char *)p - dbm_base); + return htobe32((const char *)p - dbm_base); } int dbm_match(const struct dbm_match *match, const char *str) { switch (match->type) { case DBM_EXACT: return strcmp(str, match->str) == 0; case DBM_SUB: return strcasestr(str, match->str) != NULL; case DBM_REGEX: return regexec(match->re, str, 0, NULL, 0) == 0; default: abort(); } } Index: vendor/mdocml/dist/eqn_term.c =================================================================== --- vendor/mdocml/dist/eqn_term.c (revision 313955) +++ vendor/mdocml/dist/eqn_term.c (revision 313956) @@ -1,127 +1,130 @@ -/* $Id: eqn_term.c,v 1.8 2015/01/01 15:36:08 schwarze Exp $ */ +/* $Id: eqn_term.c,v 1.9 2017/02/12 14:19:01 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons - * Copyright (c) 2014, 2015 Ingo Schwarze + * Copyright (c) 2014, 2015, 2017 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #include "mandoc.h" #include "out.h" #include "term.h" static const enum termfont fontmap[EQNFONT__MAX] = { TERMFONT_NONE, /* EQNFONT_NONE */ TERMFONT_NONE, /* EQNFONT_ROMAN */ TERMFONT_BOLD, /* EQNFONT_BOLD */ TERMFONT_BOLD, /* EQNFONT_FAT */ TERMFONT_UNDER /* EQNFONT_ITALIC */ }; static void eqn_box(struct termp *, const struct eqn_box *); void term_eqn(struct termp *p, const struct eqn *ep) { eqn_box(p, ep->root); p->flags &= ~TERMP_NOSPACE; } static void eqn_box(struct termp *p, const struct eqn_box *bp) { const struct eqn_box *child; if (bp->type == EQN_LIST || (bp->type == EQN_PILE && (bp->prev || bp->next)) || (bp->parent != NULL && bp->parent->pos == EQNPOS_SQRT)) { if (bp->parent->type == EQN_SUBEXPR && bp->prev != NULL) p->flags |= TERMP_NOSPACE; term_word(p, bp->left != NULL ? bp->left : "("); p->flags |= TERMP_NOSPACE; } if (bp->font != EQNFONT_NONE) term_fontpush(p, fontmap[(int)bp->font]); if (bp->text != NULL) term_word(p, bp->text); if (bp->pos == EQNPOS_SQRT) { term_word(p, "sqrt"); - p->flags |= TERMP_NOSPACE; - eqn_box(p, bp->first); + if (bp->first != NULL) { + p->flags |= TERMP_NOSPACE; + eqn_box(p, bp->first); + } } else if (bp->type == EQN_SUBEXPR) { child = bp->first; eqn_box(p, child); p->flags |= TERMP_NOSPACE; term_word(p, bp->pos == EQNPOS_OVER ? "/" : (bp->pos == EQNPOS_SUP || bp->pos == EQNPOS_TO) ? "^" : "_"); p->flags |= TERMP_NOSPACE; child = child->next; if (child != NULL) { eqn_box(p, child); if (bp->pos == EQNPOS_FROMTO || bp->pos == EQNPOS_SUBSUP) { p->flags |= TERMP_NOSPACE; term_word(p, "^"); p->flags |= TERMP_NOSPACE; child = child->next; if (child != NULL) eqn_box(p, child); } } } else { child = bp->first; - if (bp->type == EQN_MATRIX && child->type == EQN_LIST) + if (bp->type == EQN_MATRIX && + child != NULL && child->type == EQN_LIST) child = child->first; while (child != NULL) { eqn_box(p, bp->type == EQN_PILE && child->type == EQN_LIST && child->args == 1 ? child->first : child); child = child->next; } } if (bp->font != EQNFONT_NONE) term_fontpop(p); if (bp->type == EQN_LIST || (bp->type == EQN_PILE && (bp->prev || bp->next)) || (bp->parent != NULL && bp->parent->pos == EQNPOS_SQRT)) { p->flags |= TERMP_NOSPACE; term_word(p, bp->right != NULL ? bp->right : ")"); if (bp->parent->type == EQN_SUBEXPR && bp->next != NULL) p->flags |= TERMP_NOSPACE; } if (bp->top != NULL) { p->flags |= TERMP_NOSPACE; term_word(p, bp->top); } if (bp->bottom != NULL) { p->flags |= TERMP_NOSPACE; term_word(p, "_"); } } Index: vendor/mdocml/dist/gmdiff =================================================================== --- vendor/mdocml/dist/gmdiff (revision 313955) +++ vendor/mdocml/dist/gmdiff (revision 313956) @@ -1,51 +1,51 @@ #!/bin/sh # Copyright (c) 2013, 2014 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. if [ `id -u` -eq 0 ]; then echo "$0: do not run me as root" exit 1 fi if [ $# -eq 0 ]; then - echo "usage: $0 -h manual_source_file ..." + echo "usage: $0 [-h] manual_source_file ..." exit 1 fi if [ "X$1" = "X-h" ]; then shift export PATH="/usr/local/heirloom-doctools/bin:$PATH" EQN="neqn" ROFF="nroff" MOPT="-Omdoc $MOPT" else EQN="eqn -Tascii" ROFF="groff -ww -Tascii -P -c" fi -MOPT="-Werror $MOPT" +MOPT="-Werror -Tascii $MOPT" while [ -n "$1" ]; do file=$1 shift echo " ========== $file ========== " tbl $file | $EQN | $ROFF -mandoc 2> /tmp/roff.err > /tmp/roff.out ${MANDOC:=mandoc} -Ios='OpenBSD ports' $MOPT $file \ 2> /tmp/mandoc.err > /tmp/mandoc.out for i in roff mandoc; do [[ -s /tmp/$i.err ]] && echo "$i errors:" && cat /tmp/$i.err done diff -au /tmp/roff.out /tmp/mandoc.out 2>&1 done exit 0 Index: vendor/mdocml/dist/html.c =================================================================== --- vendor/mdocml/dist/html.c (revision 313955) +++ vendor/mdocml/dist/html.c (revision 313956) @@ -1,898 +1,920 @@ -/* $Id: html.c,v 1.200 2017/01/21 02:29:57 schwarze Exp $ */ +/* $Id: html.c,v 1.207 2017/02/05 20:22:04 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * Copyright (c) 2011-2015, 2017 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. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "out.h" #include "html.h" #include "manconf.h" #include "main.h" struct htmldata { const char *name; int flags; #define HTML_NOSTACK (1 << 0) #define HTML_AUTOCLOSE (1 << 1) #define HTML_NLBEFORE (1 << 2) #define HTML_NLBEGIN (1 << 3) #define HTML_NLEND (1 << 4) #define HTML_NLAFTER (1 << 5) #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) #define HTML_INDENT (1 << 6) #define HTML_NOINDENT (1 << 7) }; static const struct htmldata htmltags[TAG_MAX] = { {"html", HTML_NLALL}, {"head", HTML_NLALL | HTML_INDENT}, {"body", HTML_NLALL}, {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"title", HTML_NLAROUND}, {"div", HTML_NLAROUND}, {"h1", HTML_NLAROUND}, {"h2", HTML_NLAROUND}, {"span", 0}, {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"a", 0}, {"table", HTML_NLALL | HTML_INDENT}, - {"tbody", HTML_NLALL | HTML_INDENT}, + {"colgroup", HTML_NLALL | HTML_INDENT}, {"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"tr", HTML_NLALL | HTML_INDENT}, {"td", HTML_NLAROUND}, {"li", HTML_NLAROUND | HTML_INDENT}, {"ul", HTML_NLALL | HTML_INDENT}, {"ol", HTML_NLALL | HTML_INDENT}, {"dl", HTML_NLALL | HTML_INDENT}, {"dt", HTML_NLAROUND}, {"dd", HTML_NLAROUND | HTML_INDENT}, {"pre", HTML_NLALL | HTML_NOINDENT}, + {"var", 0}, + {"cite", 0}, {"b", 0}, {"i", 0}, {"code", 0}, {"small", 0}, {"style", HTML_NLALL | HTML_INDENT}, {"math", HTML_NLALL | HTML_INDENT}, {"mrow", 0}, {"mi", 0}, {"mo", 0}, {"msup", 0}, {"msub", 0}, {"msubsup", 0}, {"mfrac", 0}, {"msqrt", 0}, {"mfenced", 0}, {"mtable", 0}, {"mtr", 0}, {"mtd", 0}, {"munderover", 0}, {"munder", 0}, {"mover", 0}, }; static const char *const roffscales[SCALE_MAX] = { "cm", /* SCALE_CM */ "in", /* SCALE_IN */ "pc", /* SCALE_PC */ "pt", /* SCALE_PT */ "em", /* SCALE_EM */ "em", /* SCALE_MM */ "ex", /* SCALE_EN */ "ex", /* SCALE_BU */ "em", /* SCALE_VS */ "ex", /* SCALE_FS */ }; static void a2width(const char *, struct roffsu *); static void print_byte(struct html *, char); -static void print_endline(struct html *); static void print_endword(struct html *); static void print_indent(struct html *); static void print_word(struct html *, const char *); static void print_ctag(struct html *, struct tag *); static int print_escape(struct html *, char); static int print_encode(struct html *, const char *, const char *, int); static void print_href(struct html *, const char *, const char *, int); static void print_metaf(struct html *, enum mandoc_esc); void * html_alloc(const struct manoutput *outopts) { struct html *h; h = mandoc_calloc(1, sizeof(struct html)); - h->tags.head = NULL; + h->tag = NULL; h->style = outopts->style; h->base_man = outopts->man; h->base_includes = outopts->includes; if (outopts->fragment) h->oflags |= HTML_FRAGMENT; return h; } void html_free(void *p) { struct tag *tag; struct html *h; h = (struct html *)p; - while ((tag = h->tags.head) != NULL) { - h->tags.head = tag->next; + while ((tag = h->tag) != NULL) { + h->tag = tag->next; free(tag); } free(h); } void print_gen_head(struct html *h) { struct tag *t; print_otag(h, TAG_META, "?", "charset", "utf-8"); /* * Print a default style-sheet. */ t = print_otag(h, TAG_STYLE, ""); print_text(h, "table.head, table.foot { width: 100%; }"); print_endline(h); print_text(h, "td.head-rtitle, td.foot-os { text-align: right; }"); print_endline(h); print_text(h, "td.head-vol { text-align: center; }"); print_endline(h); print_text(h, "div.Pp { margin: 1ex 0ex; }"); print_tagq(h, t); if (h->style) print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", h->style, "type", "text/css", "media", "all"); } static void print_metaf(struct html *h, enum mandoc_esc deco) { enum htmlfont font; switch (deco) { case ESCAPE_FONTPREV: font = h->metal; break; case ESCAPE_FONTITALIC: font = HTMLFONT_ITALIC; break; case ESCAPE_FONTBOLD: font = HTMLFONT_BOLD; break; case ESCAPE_FONTBI: font = HTMLFONT_BI; break; case ESCAPE_FONT: case ESCAPE_FONTROMAN: font = HTMLFONT_NONE; break; default: abort(); } if (h->metaf) { print_tagq(h, h->metaf); h->metaf = NULL; } h->metal = h->metac; h->metac = font; switch (font) { case HTMLFONT_ITALIC: h->metaf = print_otag(h, TAG_I, ""); break; case HTMLFONT_BOLD: h->metaf = print_otag(h, TAG_B, ""); break; case HTMLFONT_BI: h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; default: break; } } int html_strlen(const char *cp) { size_t rsz; int skip, sz; /* * Account for escaped sequences within string length * calculations. This follows the logic in term_strlen() as we * must calculate the width of produced strings. * Assume that characters are always width of "1". This is * hacky, but it gets the job done for approximation of widths. */ sz = 0; skip = 0; while (1) { rsz = strcspn(cp, "\\"); if (rsz) { cp += rsz; if (skip) { skip = 0; rsz--; } sz += rsz; } if ('\0' == *cp) break; cp++; switch (mandoc_escape(&cp, NULL, NULL)) { case ESCAPE_ERROR: return sz; case ESCAPE_UNICODE: case ESCAPE_NUMBERED: case ESCAPE_SPECIAL: case ESCAPE_OVERSTRIKE: if (skip) skip = 0; else sz++; break; case ESCAPE_SKIPCHAR: skip = 1; break; default: break; } } return sz; } static int print_escape(struct html *h, char c) { switch (c) { case '<': print_word(h, "<"); break; case '>': print_word(h, ">"); break; case '&': print_word(h, "&"); break; case '"': print_word(h, """); break; case ASCII_NBRSP: print_word(h, " "); break; case ASCII_HYPH: print_byte(h, '-'); break; case ASCII_BREAK: break; default: return 0; } return 1; } static int print_encode(struct html *h, const char *p, const char *pend, int norecurse) { char numbuf[16]; size_t sz; int c, len, nospace; const char *seq; enum mandoc_esc esc; static const char rejs[9] = { '\\', '<', '>', '&', '"', ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; if (pend == NULL) pend = strchr(p, '\0'); nospace = 0; while (p < pend) { if (HTML_SKIPCHAR & h->flags && '\\' != *p) { h->flags &= ~HTML_SKIPCHAR; p++; continue; } for (sz = strcspn(p, rejs); sz-- && p < pend; p++) if (*p == ' ') print_endword(h); else print_byte(h, *p); if (p >= pend) break; if (print_escape(h, *p++)) continue; esc = mandoc_escape(&p, &seq, &len); if (ESCAPE_ERROR == esc) break; switch (esc) { case ESCAPE_FONT: case ESCAPE_FONTPREV: case ESCAPE_FONTBOLD: case ESCAPE_FONTITALIC: case ESCAPE_FONTBI: case ESCAPE_FONTROMAN: if (0 == norecurse) print_metaf(h, esc); continue; case ESCAPE_SKIPCHAR: h->flags |= HTML_SKIPCHAR; continue; default: break; } if (h->flags & HTML_SKIPCHAR) { h->flags &= ~HTML_SKIPCHAR; continue; } switch (esc) { case ESCAPE_UNICODE: /* Skip past "u" header. */ c = mchars_num2uc(seq + 1, len - 1); break; case ESCAPE_NUMBERED: c = mchars_num2char(seq, len); if (c < 0) continue; break; case ESCAPE_SPECIAL: c = mchars_spec2cp(seq, len); if (c <= 0) continue; break; case ESCAPE_NOSPACE: if ('\0' == *p) nospace = 1; continue; case ESCAPE_OVERSTRIKE: if (len == 0) continue; c = seq[len - 1]; break; default: continue; } if ((c < 0x20 && c != 0x09) || (c > 0x7E && c < 0xA0)) c = 0xFFFD; if (c > 0x7E) { (void)snprintf(numbuf, sizeof(numbuf), "%d;", c); print_word(h, numbuf); } else if (print_escape(h, c) == 0) print_byte(h, c); } return nospace; } static void print_href(struct html *h, const char *name, const char *sec, int man) { const char *p, *pp; pp = man ? h->base_man : h->base_includes; while ((p = strchr(pp, '%')) != NULL) { print_encode(h, pp, p, 1); if (man && p[1] == 'S') { if (sec == NULL) print_byte(h, '1'); else print_encode(h, sec, NULL, 1); } else if ((man && p[1] == 'N') || (man == 0 && p[1] == 'I')) print_encode(h, name, NULL, 1); else print_encode(h, p, p + 2, 1); pp = p + 2; } if (*pp != '\0') print_encode(h, pp, NULL, 1); } struct tag * print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) { va_list ap; struct roffsu mysu, *su; char numbuf[16]; struct tag *t; const char *attr; - char *s; + char *arg1, *arg2; double v; int i, have_style, tflags; tflags = htmltags[tag].flags; - /* Push this tags onto the stack of open scopes. */ + /* Push this tag onto the stack of open scopes. */ if ((tflags & HTML_NOSTACK) == 0) { t = mandoc_malloc(sizeof(struct tag)); t->tag = tag; - t->next = h->tags.head; - h->tags.head = t; + t->next = h->tag; + h->tag = t; } else t = NULL; if (tflags & HTML_NLBEFORE) print_endline(h); if (h->col == 0) print_indent(h); else if ((h->flags & HTML_NOSPACE) == 0) { if (h->flags & HTML_KEEP) print_word(h, " "); else { if (h->flags & HTML_PREKEEP) h->flags |= HTML_KEEP; print_endword(h); } } if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; else h->flags |= HTML_NOSPACE; /* Print out the tag name and attributes. */ print_byte(h, '<'); print_word(h, htmltags[tag].name); va_start(ap, fmt); have_style = 0; while (*fmt != '\0') { if (*fmt == 's') { - print_word(h, " style=\""); have_style = 1; fmt++; break; } - s = va_arg(ap, char *); + + /* Parse a non-style attribute and its arguments. */ + + arg1 = va_arg(ap, char *); switch (*fmt++) { case 'c': attr = "class"; break; case 'h': attr = "href"; break; case 'i': attr = "id"; break; case '?': - attr = s; - s = va_arg(ap, char *); + attr = arg1; + arg1 = va_arg(ap, char *); break; default: abort(); } + arg2 = NULL; + if (*fmt == 'M') + arg2 = va_arg(ap, char *); + if (arg1 == NULL) + continue; + + /* Print the non-style attributes. */ + print_byte(h, ' '); print_word(h, attr); print_byte(h, '='); print_byte(h, '"'); switch (*fmt) { case 'M': - print_href(h, s, va_arg(ap, char *), 1); + print_href(h, arg1, arg2, 1); fmt++; break; case 'I': - print_href(h, s, NULL, 0); + print_href(h, arg1, NULL, 0); fmt++; break; case 'R': print_byte(h, '#'); fmt++; /* FALLTHROUGH */ default: - print_encode(h, s, NULL, 1); + print_encode(h, arg1, NULL, 1); break; } print_byte(h, '"'); } /* Print out styles. */ - s = NULL; - su = &mysu; while (*fmt != '\0') { + arg1 = NULL; + su = NULL; /* First letter: input argument type. */ switch (*fmt++) { case 'h': i = va_arg(ap, int); + su = &mysu; SCALE_HS_INIT(su, i); break; case 's': - s = va_arg(ap, char *); + arg1 = va_arg(ap, char *); break; case 'u': su = va_arg(ap, struct roffsu *); break; case 'v': i = va_arg(ap, int); + su = &mysu; SCALE_VS_INIT(su, i); break; case 'w': - s = va_arg(ap, char *); - a2width(s, su); + case 'W': + if ((arg2 = va_arg(ap, char *)) == NULL) + break; + su = &mysu; + a2width(arg2, su); + if (fmt[-1] == 'W') + su->scale *= -1.0; break; default: abort(); } /* Second letter: style name. */ switch (*fmt++) { case 'b': attr = "margin-bottom"; break; case 'h': attr = "height"; break; case 'i': attr = "text-indent"; break; case 'l': attr = "margin-left"; break; case 't': attr = "margin-top"; break; case 'w': attr = "width"; break; case 'W': attr = "min-width"; break; case '?': - print_word(h, s); - print_byte(h, ':'); - print_byte(h, ' '); - print_word(h, va_arg(ap, char *)); - print_byte(h, ';'); - if (*fmt != '\0') - print_byte(h, ' '); - continue; + attr = arg1; + arg1 = va_arg(ap, char *); + break; default: abort(); } - v = su->scale; - if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) - v = 1.0; - else if (su->unit == SCALE_BU) - v /= 24.0; + if (su == NULL && arg1 == NULL) + continue; + + if (have_style == 1) + print_word(h, " style=\""); + else + print_byte(h, ' '); print_word(h, attr); print_byte(h, ':'); print_byte(h, ' '); - (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); - print_word(h, numbuf); - print_word(h, roffscales[su->unit]); + if (su != NULL) { + v = su->scale; + if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) + v = 1.0; + else if (su->unit == SCALE_BU) + v /= 24.0; + (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); + print_word(h, numbuf); + print_word(h, roffscales[su->unit]); + } else + print_word(h, arg1); print_byte(h, ';'); - if (*fmt != '\0') - print_byte(h, ' '); + have_style = 2; } - if (have_style) + if (have_style == 2) print_byte(h, '"'); va_end(ap); /* Accommodate for "well-formed" singleton escaping. */ if (HTML_AUTOCLOSE & htmltags[tag].flags) print_byte(h, '/'); print_byte(h, '>'); if (tflags & HTML_NLBEGIN) print_endline(h); else h->flags |= HTML_NOSPACE; if (tflags & HTML_INDENT) h->indent++; if (tflags & HTML_NOINDENT) h->noindent++; return t; } static void print_ctag(struct html *h, struct tag *tag) { int tflags; /* * Remember to close out and nullify the current * meta-font and table, if applicable. */ if (tag == h->metaf) h->metaf = NULL; if (tag == h->tblt) h->tblt = NULL; tflags = htmltags[tag->tag].flags; if (tflags & HTML_INDENT) h->indent--; if (tflags & HTML_NOINDENT) h->noindent--; if (tflags & HTML_NLEND) print_endline(h); print_indent(h); print_byte(h, '<'); print_byte(h, '/'); print_word(h, htmltags[tag->tag].name); print_byte(h, '>'); if (tflags & HTML_NLAFTER) print_endline(h); - h->tags.head = tag->next; + h->tag = tag->next; free(tag); } void print_gen_decls(struct html *h) { print_word(h, ""); print_endline(h); } void print_text(struct html *h, const char *word) { if (h->col && (h->flags & HTML_NOSPACE) == 0) { if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) h->flags |= HTML_KEEP; print_endword(h); } else print_word(h, " "); } assert(NULL == h->metaf); switch (h->metac) { case HTMLFONT_ITALIC: h->metaf = print_otag(h, TAG_I, ""); break; case HTMLFONT_BOLD: h->metaf = print_otag(h, TAG_B, ""); break; case HTMLFONT_BI: h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; default: print_indent(h); break; } assert(word); if ( ! print_encode(h, word, NULL, 0)) { if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; h->flags &= ~HTML_NONEWLINE; } else h->flags |= HTML_NOSPACE | HTML_NONEWLINE; if (h->metaf) { print_tagq(h, h->metaf); h->metaf = NULL; } h->flags &= ~HTML_IGNDELIM; } void print_tagq(struct html *h, const struct tag *until) { struct tag *tag; - while ((tag = h->tags.head) != NULL) { + while ((tag = h->tag) != NULL) { print_ctag(h, tag); if (until && tag == until) return; } } void print_stagq(struct html *h, const struct tag *suntil) { struct tag *tag; - while ((tag = h->tags.head) != NULL) { + while ((tag = h->tag) != NULL) { if (suntil && tag == suntil) return; print_ctag(h, tag); } } void print_paragraph(struct html *h) { struct tag *t; t = print_otag(h, TAG_DIV, "c", "Pp"); print_tagq(h, t); } /*********************************************************************** * Low level output functions. * They implement line breaking using a short static buffer. ***********************************************************************/ /* * Buffer one HTML output byte. * If the buffer is full, flush and deactivate it and start a new line. * If the buffer is inactive, print directly. */ static void print_byte(struct html *h, char c) { if ((h->flags & HTML_BUFFER) == 0) { putchar(c); h->col++; return; } if (h->col + h->bufcol < sizeof(h->buf)) { h->buf[h->bufcol++] = c; return; } putchar('\n'); h->col = 0; print_indent(h); putchar(' '); putchar(' '); fwrite(h->buf, h->bufcol, 1, stdout); putchar(c); h->col = (h->indent + 1) * 2 + h->bufcol + 1; h->bufcol = 0; h->flags &= ~HTML_BUFFER; } /* * If something was printed on the current output line, end it. * Not to be called right after print_indent(). */ -static void +void print_endline(struct html *h) { if (h->col == 0) return; if (h->bufcol) { putchar(' '); fwrite(h->buf, h->bufcol, 1, stdout); h->bufcol = 0; } putchar('\n'); h->col = 0; h->flags |= HTML_NOSPACE; h->flags &= ~HTML_BUFFER; } /* * Flush the HTML output buffer. * If it is inactive, activate it. */ static void print_endword(struct html *h) { if (h->noindent) { print_byte(h, ' '); return; } if ((h->flags & HTML_BUFFER) == 0) { h->col++; h->flags |= HTML_BUFFER; } else if (h->bufcol) { putchar(' '); fwrite(h->buf, h->bufcol, 1, stdout); h->col += h->bufcol + 1; } h->bufcol = 0; } /* * If at the beginning of a new output line, * perform indentation and mark the line as containing output. * Make sure to really produce some output right afterwards, * but do not use print_otag() for producing it. */ static void print_indent(struct html *h) { size_t i; if (h->col) return; if (h->noindent == 0) { h->col = h->indent * 2; for (i = 0; i < h->col; i++) putchar(' '); } h->flags &= ~HTML_NOSPACE; } /* * Print or buffer some characters * depending on the current HTML output buffer state. */ static void print_word(struct html *h, const char *cp) { while (*cp != '\0') print_byte(h, *cp++); } /* * Calculate the scaling unit passed in a `-width' argument. This uses * either a native scaling unit (e.g., 1i, 2m) or the string length of * the value. */ static void a2width(const char *p, struct roffsu *su) { if (a2roffsu(p, su, SCALE_MAX) < 2) { su->unit = SCALE_EN; su->scale = html_strlen(p); } else if (su->scale < 0.0) su->scale = 0.0; } Index: vendor/mdocml/dist/html.h =================================================================== --- vendor/mdocml/dist/html.h (revision 313955) +++ vendor/mdocml/dist/html.h (revision 313956) @@ -1,131 +1,130 @@ -/* $Id: html.h,v 1.78 2017/01/19 16:59:30 schwarze Exp $ */ +/* $Id: html.h,v 1.83 2017/02/05 20:22:04 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * Copyright (c) 2017 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. */ enum htmltag { TAG_HTML, TAG_HEAD, TAG_BODY, TAG_META, TAG_TITLE, TAG_DIV, TAG_H1, TAG_H2, TAG_SPAN, TAG_LINK, TAG_BR, TAG_A, TAG_TABLE, - TAG_TBODY, + TAG_COLGROUP, TAG_COL, TAG_TR, TAG_TD, TAG_LI, TAG_UL, TAG_OL, TAG_DL, TAG_DT, TAG_DD, TAG_PRE, + TAG_VAR, + TAG_CITE, TAG_B, TAG_I, TAG_CODE, TAG_SMALL, TAG_STYLE, TAG_MATH, TAG_MROW, TAG_MI, TAG_MO, TAG_MSUP, TAG_MSUB, TAG_MSUBSUP, TAG_MFRAC, TAG_MSQRT, TAG_MFENCED, TAG_MTABLE, TAG_MTR, TAG_MTD, TAG_MUNDEROVER, TAG_MUNDER, TAG_MOVER, TAG_MAX }; enum htmlfont { HTMLFONT_NONE = 0, HTMLFONT_BOLD, HTMLFONT_ITALIC, HTMLFONT_BI, HTMLFONT_MAX }; struct tag { struct tag *next; enum htmltag tag; }; -struct tagq { - struct tag *head; -}; - struct html { int flags; #define HTML_NOSPACE (1 << 0) /* suppress next space */ #define HTML_IGNDELIM (1 << 1) #define HTML_KEEP (1 << 2) #define HTML_PREKEEP (1 << 3) #define HTML_NONOSPACE (1 << 4) /* never add spaces */ #define HTML_LITERAL (1 << 5) /* literal (e.g., ) context */ #define HTML_SKIPCHAR (1 << 6) /* skip the next character */ #define HTML_NOSPLIT (1 << 7) /* do not break line before .An */ #define HTML_SPLIT (1 << 8) /* break line before .An */ #define HTML_NONEWLINE (1 << 9) /* No line break in nofill mode. */ #define HTML_BUFFER (1 << 10) /* Collect a word to see if it fits. */ size_t indent; /* current output indentation level */ int noindent; /* indent disabled by*/ size_t col; /* current output byte position */ size_t bufcol; /* current buf byte position */ char buf[80]; /* output buffer */ - struct tagq tags; /* stack of open tags */ + struct tag *tag; /* last open tag */ struct rofftbl tbl; /* current table */ struct tag *tblt; /* current open table scope */ char *base_man; /* base for manpage href */ char *base_includes; /* base for include href */ char *style; /* style-sheet URI */ struct tag *metaf; /* current open font scope */ enum htmlfont metal; /* last used font */ enum htmlfont metac; /* current font mode */ int oflags; /* output options */ #define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */ }; struct tbl_span; struct eqn; void print_gen_decls(struct html *); void print_gen_head(struct html *); struct tag *print_otag(struct html *, enum htmltag, const char *, ...); 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_tblclose(struct html *); void print_tbl(struct html *, const struct tbl_span *); void print_eqn(struct html *, const struct eqn *); void print_paragraph(struct html *); +void print_endline(struct html *); int html_strlen(const char *); Index: vendor/mdocml/dist/libmandoc.h =================================================================== --- vendor/mdocml/dist/libmandoc.h (revision 313955) +++ vendor/mdocml/dist/libmandoc.h (revision 313956) @@ -1,82 +1,82 @@ -/* $Id: libmandoc.h,v 1.64 2016/07/19 13:36:13 schwarze Exp $ */ +/* $Id: libmandoc.h,v 1.66 2017/02/18 13:43:52 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons* Copyright (c) 2013, 2014, 2015 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. */ 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_EQN /* an equation was successfully parsed */ }; struct buf { char *buf; size_t sz; }; struct mparse; struct tbl_span; struct eqn; struct roff; struct roff_man; void mandoc_msg(enum mandocerr, struct mparse *, int, int, const char *); void mandoc_vmsg(enum mandocerr, struct mparse *, int, int, const char *, ...) - __attribute__((__format__ (printf, 5, 6))); + __attribute__((__format__ (__printf__, 5, 6))); char *mandoc_getarg(struct mparse *, char **, int, int *); char *mandoc_normdate(struct mparse *, char *, int, int); int mandoc_eos(const char *, size_t); int mandoc_strntoi(const char *, size_t, int); const char *mandoc_a2msec(const char*); void mdoc_hash_init(void); int mdoc_parseln(struct roff_man *, int, char *, int); void mdoc_endparse(struct roff_man *); void man_hash_init(void); int man_parseln(struct roff_man *, int, char *, int); void man_endparse(struct roff_man *); int preconv_cue(const struct buf *, size_t); -int preconv_encode(struct buf *, size_t *, +int preconv_encode(const struct buf *, size_t *, struct buf *, size_t *, int *); void roff_free(struct roff *); struct roff *roff_alloc(struct mparse *, int); void roff_reset(struct roff *); void roff_man_free(struct roff_man *); struct roff_man *roff_man_alloc(struct roff *, struct mparse *, const char *, int); void roff_man_reset(struct roff_man *); enum rofferr roff_parseln(struct roff *, int, struct buf *, int *); void roff_endparse(struct roff *); void roff_setreg(struct roff *, const char *, int, char sign); int roff_getreg(const struct roff *, const char *); char *roff_strdup(const struct roff *, const char *); int roff_getcontrol(const struct roff *, const char *, int *); int roff_getformat(const struct roff *); const struct tbl_span *roff_span(const struct roff *); const struct eqn *roff_eqn(const struct roff *); Index: vendor/mdocml/dist/libmdoc.h =================================================================== --- vendor/mdocml/dist/libmdoc.h (revision 313955) +++ vendor/mdocml/dist/libmdoc.h (revision 313956) @@ -1,88 +1,88 @@ -/* $Id: libmdoc.h,v 1.108 2015/11/07 14:01:16 schwarze Exp $ */ +/* $Id: libmdoc.h,v 1.109 2017/02/16 03:00:23 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2013, 2014, 2015 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. */ #define MACRO_PROT_ARGS struct roff_man *mdoc, \ int tok, \ int line, \ int ppos, \ int *pos, \ char *buf struct mdoc_macro { void (*fp)(MACRO_PROT_ARGS); int flags; #define MDOC_CALLABLE (1 << 0) #define MDOC_PARSED (1 << 1) #define MDOC_EXPLICIT (1 << 2) #define MDOC_PROLOGUE (1 << 3) #define MDOC_IGNDELIM (1 << 4) #define MDOC_JOIN (1 << 5) }; enum margserr { ARGS_ERROR, ARGS_EOLN, /* end-of-line */ ARGS_WORD, /* normal word */ ARGS_PUNCT, /* series of punctuation */ ARGS_QWORD, /* quoted word */ ARGS_PHRASE /* Bl -column phrase */ }; /* * A punctuation delimiter is opening, closing, or "middle mark" * punctuation. These govern spacing. * Opening punctuation (e.g., the opening parenthesis) suppresses the * following space; closing punctuation (e.g., the closing parenthesis) * suppresses the leading space; middle punctuation (e.g., the vertical * bar) can do either. The middle punctuation delimiter bends the rules * depending on usage. */ enum mdelim { DELIM_NONE = 0, DELIM_OPEN, DELIM_MIDDLE, DELIM_CLOSE, DELIM_MAX }; extern const struct mdoc_macro *const mdoc_macros; void mdoc_macro(MACRO_PROT_ARGS); void mdoc_elem_alloc(struct roff_man *, int, int, int, struct mdoc_arg *); struct roff_node *mdoc_block_alloc(struct roff_man *, int, int, int, struct mdoc_arg *); void mdoc_tail_alloc(struct roff_man *, int, int, int); struct roff_node *mdoc_endbody_alloc(struct roff_man *, int, int, int, - struct roff_node *, enum mdoc_endbody); + struct roff_node *); void mdoc_node_relink(struct roff_man *, struct roff_node *); void mdoc_node_validate(struct roff_man *); void mdoc_state(struct roff_man *, struct roff_node *); void mdoc_state_reset(struct roff_man *); int mdoc_hash_find(const char *); const char *mdoc_a2arch(const char *); const char *mdoc_a2att(const char *); const char *mdoc_a2lib(const char *); enum roff_sec mdoc_a2sec(const char *); const char *mdoc_a2st(const char *); void mdoc_argv(struct roff_man *, int, int, struct mdoc_arg **, int *, char *); enum margserr mdoc_args(struct roff_man *, int, int *, char *, int, char **); enum mdelim mdoc_isdelim(const char *); Index: vendor/mdocml/dist/main.c =================================================================== --- vendor/mdocml/dist/main.c (revision 313955) +++ vendor/mdocml/dist/main.c (revision 313956) @@ -1,1102 +1,1115 @@ -/* $Id: main.c,v 1.279 2017/01/09 17:49:57 schwarze Exp $ */ +/* $Id: main.c,v 1.283 2017/02/17 14:31:52 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons * Copyright (c) 2010-2012, 2014-2017 Ingo Schwarze * Copyright (c) 2010 Joerg Sonnenberger * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include /* MACHINE */ #include #include #include #if HAVE_ERR #include #endif #include #include #include #if HAVE_SANDBOX_INIT #include #endif #include #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "man.h" #include "tag.h" #include "main.h" #include "manconf.h" #include "mansearch.h" enum outmode { OUTMODE_DEF = 0, OUTMODE_FLN, OUTMODE_LST, OUTMODE_ALL, OUTMODE_INT, OUTMODE_ONE }; enum outt { OUTT_ASCII = 0, /* -Tascii */ OUTT_LOCALE, /* -Tlocale */ OUTT_UTF8, /* -Tutf8 */ OUTT_TREE, /* -Ttree */ OUTT_MAN, /* -Tman */ OUTT_HTML, /* -Thtml */ OUTT_LINT, /* -Tlint */ OUTT_PS, /* -Tps */ OUTT_PDF /* -Tpdf */ }; struct curparse { struct mparse *mp; enum mandoclevel wlevel; /* ignore messages below this */ int wstop; /* stop after a file with a warning */ enum outt outtype; /* which output to use */ void *outdata; /* data for output */ struct manoutput *outopts; /* output options */ }; int mandocdb(int, char *[]); static int fs_lookup(const struct manpaths *, size_t ipath, const char *, const char *, const char *, struct manpage **, size_t *); static void fs_search(const struct mansearch *, const struct manpaths *, int, char**, struct manpage **, size_t *); static int koptions(int *, char *); static int moptions(int *, char *); static void mmsg(enum mandocerr, enum mandoclevel, const char *, int, int, const char *); static void outdata_alloc(struct curparse *); static void parse(struct curparse *, int, const char *); static void passthrough(const char *, int, int); static pid_t spawn_pager(struct tag_files *); static int toptions(struct curparse *, char *); -static void usage(enum argmode) __attribute__((noreturn)); +static void usage(enum argmode) __attribute__((__noreturn__)); static int woptions(struct curparse *, char *); static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static char help_arg[] = "help"; static char *help_argv[] = {help_arg, NULL}; static enum mandoclevel rc; int main(int argc, char *argv[]) { struct manconf conf; - struct curparse curp; struct mansearch search; + struct curparse curp; struct tag_files *tag_files; - const char *progname; - char *auxpaths; - char *defos; - unsigned char *uc; struct manpage *res, *resp; - char *conf_file, *defpaths; - const char *sec; + const char *progname, *sec, *thisarg; + char *conf_file, *defpaths, *auxpaths; + char *defos, *oarg; + unsigned char *uc; size_t i, sz; int prio, best_prio; enum outmode outmode; int fd; int show_usage; int options; int use_pager; int status, signum; int c; pid_t pager_pid, tc_pgid, man_pgid, pid; #if HAVE_PROGNAME progname = getprogname(); #else if (argc < 1) progname = mandoc_strdup("mandoc"); else if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; setprogname(progname); #endif if (strncmp(progname, "mandocdb", 8) == 0 || strcmp(progname, BINM_MAKEWHATIS) == 0) return mandocdb(argc, argv); #if HAVE_PLEDGE if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); #endif #if HAVE_SANDBOX_INIT if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); #endif /* Search options. */ memset(&conf, 0, sizeof(conf)); conf_file = defpaths = NULL; auxpaths = NULL; memset(&search, 0, sizeof(struct mansearch)); search.outkey = "Nd"; + oarg = NULL; if (strcmp(progname, BINM_MAN) == 0) search.argmode = ARG_NAME; else if (strcmp(progname, BINM_APROPOS) == 0) search.argmode = ARG_EXPR; else if (strcmp(progname, BINM_WHATIS) == 0) search.argmode = ARG_WORD; else if (strncmp(progname, "help", 4) == 0) search.argmode = ARG_NAME; else search.argmode = ARG_FILE; /* Parser and formatter options. */ memset(&curp, 0, sizeof(struct curparse)); curp.outtype = OUTT_LOCALE; curp.wlevel = MANDOCLEVEL_BADARG; curp.outopts = &conf.output; options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; defos = NULL; use_pager = 1; tag_files = NULL; show_usage = 0; outmode = OUTMODE_DEF; while (-1 != (c = getopt(argc, argv, "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) { switch (c) { case 'a': outmode = OUTMODE_ALL; break; case 'C': conf_file = optarg; break; case 'c': use_pager = 0; break; case 'f': search.argmode = ARG_WORD; break; case 'h': conf.output.synopsisonly = 1; use_pager = 0; outmode = OUTMODE_ALL; break; case 'I': if (strncmp(optarg, "os=", 3)) { warnx("-I %s: Bad argument", optarg); return (int)MANDOCLEVEL_BADARG; } if (defos) { warnx("-I %s: Duplicate argument", optarg); return (int)MANDOCLEVEL_BADARG; } defos = mandoc_strdup(optarg + 3); break; case 'i': outmode = OUTMODE_INT; break; case 'K': if ( ! koptions(&options, optarg)) return (int)MANDOCLEVEL_BADARG; break; case 'k': search.argmode = ARG_EXPR; break; case 'l': search.argmode = ARG_FILE; outmode = OUTMODE_ALL; break; case 'M': defpaths = optarg; break; case 'm': auxpaths = optarg; break; case 'O': - search.outkey = optarg; - while (optarg != NULL) - manconf_output(&conf.output, - strsep(&optarg, ",")); + oarg = optarg; break; case 'S': search.arch = optarg; break; case 's': search.sec = optarg; break; case 'T': if ( ! toptions(&curp, optarg)) return (int)MANDOCLEVEL_BADARG; break; case 'W': if ( ! woptions(&curp, optarg)) return (int)MANDOCLEVEL_BADARG; break; case 'w': outmode = OUTMODE_FLN; break; default: show_usage = 1; break; } } if (show_usage) usage(search.argmode); /* Postprocess options. */ if (outmode == OUTMODE_DEF) { switch (search.argmode) { case ARG_FILE: outmode = OUTMODE_ALL; use_pager = 0; break; case ARG_NAME: outmode = OUTMODE_ONE; break; default: outmode = OUTMODE_LST; break; } } + if (oarg != NULL) { + if (outmode == OUTMODE_LST) + search.outkey = oarg; + else { + while (oarg != NULL) { + thisarg = oarg; + if (manconf_output(&conf.output, + strsep(&oarg, ","), 0) == 0) + continue; + warnx("-O %s: Bad argument", thisarg); + return (int)MANDOCLEVEL_BADARG; + } + } + } + if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST || !isatty(STDOUT_FILENO)) use_pager = 0; #if HAVE_PLEDGE if (!use_pager) if (pledge("stdio rpath flock", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); #endif /* Parse arguments. */ if (argc > 0) { argc -= optind; argv += optind; } resp = NULL; /* * Quirks for help(1) * and for a man(1) section argument without -s. */ if (search.argmode == ARG_NAME) { if (*progname == 'h') { if (argc == 0) { argv = help_argv; argc = 1; } } else if (argc > 1 && ((uc = (unsigned char *)argv[0]) != NULL) && ((isdigit(uc[0]) && (uc[1] == '\0' || (isalpha(uc[1]) && uc[2] == '\0'))) || (uc[0] == 'n' && uc[1] == '\0'))) { search.sec = (char *)uc; argv++; argc--; } if (search.arch == NULL) search.arch = getenv("MACHINE"); #ifdef MACHINE if (search.arch == NULL) search.arch = MACHINE; #endif } rc = MANDOCLEVEL_OK; /* man(1), whatis(1), apropos(1) */ if (search.argmode != ARG_FILE) { if (search.argmode == ARG_NAME && outmode == OUTMODE_ONE) search.firstmatch = 1; /* Access the mandoc database. */ manconf_parse(&conf, conf_file, defpaths, auxpaths); if ( ! mansearch(&search, &conf.manpath, argc, argv, &res, &sz)) usage(search.argmode); if (sz == 0) { if (search.argmode == ARG_NAME) fs_search(&search, &conf.manpath, argc, argv, &res, &sz); else warnx("nothing appropriate"); } if (sz == 0) { rc = MANDOCLEVEL_BADARG; goto out; } /* * For standard man(1) and -a output mode, * prepare for copying filename pointers * into the program parameter array. */ if (outmode == OUTMODE_ONE) { argc = 1; best_prio = 20; } else if (outmode == OUTMODE_ALL) argc = (int)sz; /* Iterate all matching manuals. */ resp = res; for (i = 0; i < sz; i++) { if (outmode == OUTMODE_FLN) puts(res[i].file); else if (outmode == OUTMODE_LST) printf("%s - %s\n", res[i].names, res[i].output == NULL ? "" : res[i].output); else if (outmode == OUTMODE_ONE) { /* Search for the best section. */ sec = res[i].file; sec += strcspn(sec, "123456789"); if (sec[0] == '\0') continue; prio = sec_prios[sec[0] - '1']; if (sec[1] != '/') prio += 10; if (prio >= best_prio) continue; best_prio = prio; resp = res + i; } } /* * For man(1), -a and -i output mode, fall through * to the main mandoc(1) code iterating files * and running the parsers on each of them. */ if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) goto out; } /* mandoc(1) */ #if HAVE_PLEDGE if (use_pager) { if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); } else { if (pledge("stdio rpath", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); } #endif if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths)) return (int)MANDOCLEVEL_BADARG; mchars_alloc(); curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos); /* * Conditionally start up the lookaside buffer before parsing. */ if (OUTT_MAN == curp.outtype) mparse_keep(curp.mp); if (argc < 1) { if (use_pager) tag_files = tag_init(); parse(&curp, STDIN_FILENO, " "); } while (argc > 0) { fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); if (fd != -1) { if (use_pager) { tag_files = tag_init(); use_pager = 0; } if (resp == NULL) parse(&curp, fd, *argv); else if (resp->form == FORM_SRC) { /* For .so only; ignore failure. */ chdir(conf.manpath.paths[resp->ipath]); parse(&curp, fd, resp->file); } else passthrough(resp->file, fd, conf.output.synopsisonly); if (argc > 1 && curp.outtype <= OUTT_UTF8) { if (curp.outdata == NULL) outdata_alloc(&curp); terminal_sepline(curp.outdata); } } else if (rc < MANDOCLEVEL_ERROR) rc = MANDOCLEVEL_ERROR; if (MANDOCLEVEL_OK != rc && curp.wstop) break; if (resp != NULL) resp++; else argv++; if (--argc) mparse_reset(curp.mp); } if (curp.outdata != NULL) { switch (curp.outtype) { case OUTT_HTML: html_free(curp.outdata); break; case OUTT_UTF8: case OUTT_LOCALE: case OUTT_ASCII: ascii_free(curp.outdata); break; case OUTT_PDF: case OUTT_PS: pspdf_free(curp.outdata); break; default: break; } } mparse_free(curp.mp); mchars_free(); out: if (search.argmode != ARG_FILE) { manconf_free(&conf); mansearch_free(res, sz); } free(defos); /* * When using a pager, finish writing both temporary files, * fork it, wait for the user to close it, and clean up. */ if (tag_files != NULL) { fclose(stdout); tag_write(); man_pgid = getpgid(0); tag_files->tcpgid = man_pgid == getpid() ? getpgid(getppid()) : man_pgid; pager_pid = 0; signum = SIGSTOP; for (;;) { /* Stop here until moved to the foreground. */ tc_pgid = tcgetpgrp(tag_files->ofd); if (tc_pgid != man_pgid) { if (tc_pgid == pager_pid) { (void)tcsetpgrp(tag_files->ofd, man_pgid); if (signum == SIGTTIN) continue; } else tag_files->tcpgid = tc_pgid; kill(0, signum); continue; } /* Once in the foreground, activate the pager. */ if (pager_pid) { (void)tcsetpgrp(tag_files->ofd, pager_pid); kill(pager_pid, SIGCONT); } else pager_pid = spawn_pager(tag_files); /* Wait for the pager to stop or exit. */ while ((pid = waitpid(pager_pid, &status, WUNTRACED)) == -1 && errno == EINTR) continue; if (pid == -1) { warn("wait"); rc = MANDOCLEVEL_SYSERR; break; } if (!WIFSTOPPED(status)) break; signum = WSTOPSIG(status); } tag_unlink(); } return (int)rc; } static void usage(enum argmode argmode) { switch (argmode) { case ARG_FILE: fputs("usage: mandoc [-acfhkl] [-I os=name] " "[-K encoding] [-mformat] [-O option]\n" "\t [-T output] [-W level] [file ...]\n", stderr); break; case ARG_NAME: fputs("usage: man [-acfhklw] [-C file] [-I os=name] " "[-K encoding] [-M path] [-m path]\n" "\t [-O option=value] [-S subsection] [-s section] " "[-T output] [-W level]\n" "\t [section] name ...\n", stderr); break; case ARG_WORD: fputs("usage: whatis [-acfhklw] [-C file] " "[-M path] [-m path] [-O outkey] [-S arch]\n" "\t [-s section] name ...\n", stderr); break; case ARG_EXPR: fputs("usage: apropos [-acfhklw] [-C file] " "[-M path] [-m path] [-O outkey] [-S arch]\n" "\t [-s section] expression ...\n", stderr); break; } exit((int)MANDOCLEVEL_BADARG); } static int fs_lookup(const struct manpaths *paths, size_t ipath, const char *sec, const char *arch, const char *name, struct manpage **res, size_t *ressz) { glob_t globinfo; struct manpage *page; char *file; int globres; enum form form; form = FORM_SRC; mandoc_asprintf(&file, "%s/man%s/%s.%s", paths->paths[ipath], sec, name, sec); if (access(file, R_OK) != -1) goto found; free(file); mandoc_asprintf(&file, "%s/cat%s/%s.0", paths->paths[ipath], sec, name); if (access(file, R_OK) != -1) { form = FORM_CAT; goto found; } free(file); if (arch != NULL) { mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", paths->paths[ipath], sec, arch, name, sec); if (access(file, R_OK) != -1) goto found; free(file); } mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", paths->paths[ipath], sec, name); globres = glob(file, 0, NULL, &globinfo); if (globres != 0 && globres != GLOB_NOMATCH) warn("%s: glob", file); free(file); if (globres == 0) file = mandoc_strdup(*globinfo.gl_pathv); globfree(&globinfo); if (globres != 0) return 0; found: warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); page = *res + (*ressz - 1); page->file = file; page->names = NULL; page->output = NULL; page->ipath = ipath; page->bits = NAME_FILE & NAME_MASK; page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; page->form = form; return 1; } static void fs_search(const struct mansearch *cfg, const struct manpaths *paths, int argc, char **argv, struct manpage **res, size_t *ressz) { const char *const sections[] = {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; const size_t nsec = sizeof(sections)/sizeof(sections[0]); size_t ipath, isec, lastsz; assert(cfg->argmode == ARG_NAME); *res = NULL; *ressz = lastsz = 0; while (argc) { for (ipath = 0; ipath < paths->sz; ipath++) { if (cfg->sec != NULL) { if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, *argv, res, ressz) && cfg->firstmatch) return; } else for (isec = 0; isec < nsec; isec++) if (fs_lookup(paths, ipath, sections[isec], cfg->arch, *argv, res, ressz) && cfg->firstmatch) return; } if (*ressz == lastsz) warnx("No entry for %s in the manual.", *argv); lastsz = *ressz; argv++; argc--; } } static void parse(struct curparse *curp, int fd, const char *file) { enum mandoclevel rctmp; struct roff_man *man; /* Begin by parsing the file itself. */ assert(file); assert(fd >= 0); rctmp = mparse_readfd(curp->mp, fd, file); if (fd != STDIN_FILENO) close(fd); if (rc < rctmp) rc = rctmp; /* * With -Wstop and warnings or errors of at least the requested * level, do not produce output. */ if (rctmp != MANDOCLEVEL_OK && curp->wstop) return; if (curp->outdata == NULL) outdata_alloc(curp); mparse_result(curp->mp, &man, NULL); /* Execute the out device, if it exists. */ if (man == NULL) return; if (man->macroset == MACROSET_MDOC) { - mdoc_validate(man); + if (curp->outtype != OUTT_TREE || !curp->outopts->noval) + mdoc_validate(man); switch (curp->outtype) { case OUTT_HTML: html_mdoc(curp->outdata, man); break; case OUTT_TREE: tree_mdoc(curp->outdata, man); break; case OUTT_MAN: man_mdoc(curp->outdata, man); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: terminal_mdoc(curp->outdata, man); break; default: break; } } if (man->macroset == MACROSET_MAN) { - man_validate(man); + if (curp->outtype != OUTT_TREE || !curp->outopts->noval) + man_validate(man); switch (curp->outtype) { case OUTT_HTML: html_man(curp->outdata, man); break; case OUTT_TREE: tree_man(curp->outdata, man); break; case OUTT_MAN: man_man(curp->outdata, man); break; case OUTT_PDF: case OUTT_ASCII: case OUTT_UTF8: case OUTT_LOCALE: case OUTT_PS: terminal_man(curp->outdata, man); break; default: break; } } mparse_updaterc(curp->mp, &rc); } static void outdata_alloc(struct curparse *curp) { switch (curp->outtype) { case OUTT_HTML: curp->outdata = html_alloc(curp->outopts); break; case OUTT_UTF8: curp->outdata = utf8_alloc(curp->outopts); break; case OUTT_LOCALE: curp->outdata = locale_alloc(curp->outopts); break; case OUTT_ASCII: curp->outdata = ascii_alloc(curp->outopts); break; case OUTT_PDF: curp->outdata = pdf_alloc(curp->outopts); break; case OUTT_PS: curp->outdata = ps_alloc(curp->outopts); break; default: break; } } static void passthrough(const char *file, int fd, int synopsis_only) { const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; const char synr[] = "SYNOPSIS"; FILE *stream; const char *syscall; char *line, *cp; size_t linesz; ssize_t len, written; int print; line = NULL; linesz = 0; if (fflush(stdout) == EOF) { syscall = "fflush"; goto fail; } if ((stream = fdopen(fd, "r")) == NULL) { close(fd); syscall = "fdopen"; goto fail; } print = 0; while ((len = getline(&line, &linesz, stream)) != -1) { cp = line; if (synopsis_only) { if (print) { if ( ! isspace((unsigned char)*cp)) goto done; while (isspace((unsigned char)*cp)) { cp++; len--; } } else { if (strcmp(cp, synb) == 0 || strcmp(cp, synr) == 0) print = 1; continue; } } for (; len > 0; len -= written) { if ((written = write(STDOUT_FILENO, cp, len)) != -1) continue; fclose(stream); syscall = "write"; goto fail; } } if (ferror(stream)) { fclose(stream); syscall = "getline"; goto fail; } done: free(line); fclose(stream); return; fail: free(line); warn("%s: SYSERR: %s", file, syscall); if (rc < MANDOCLEVEL_SYSERR) rc = MANDOCLEVEL_SYSERR; } static int koptions(int *options, char *arg) { if ( ! strcmp(arg, "utf-8")) { *options |= MPARSE_UTF8; *options &= ~MPARSE_LATIN1; } else if ( ! strcmp(arg, "iso-8859-1")) { *options |= MPARSE_LATIN1; *options &= ~MPARSE_UTF8; } else if ( ! strcmp(arg, "us-ascii")) { *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); } else { warnx("-K %s: Bad argument", arg); return 0; } return 1; } static int moptions(int *options, char *arg) { if (arg == NULL) /* nothing to do */; else if (0 == strcmp(arg, "doc")) *options |= MPARSE_MDOC; else if (0 == strcmp(arg, "andoc")) /* nothing to do */; else if (0 == strcmp(arg, "an")) *options |= MPARSE_MAN; else { warnx("-m %s: Bad argument", 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, "man")) curp->outtype = OUTT_MAN; else if (0 == strcmp(arg, "html")) curp->outtype = OUTT_HTML; else if (0 == strcmp(arg, "utf8")) curp->outtype = OUTT_UTF8; else if (0 == strcmp(arg, "locale")) curp->outtype = OUTT_LOCALE; else if (0 == strcmp(arg, "xhtml")) curp->outtype = OUTT_HTML; else if (0 == strcmp(arg, "ps")) curp->outtype = OUTT_PS; else if (0 == strcmp(arg, "pdf")) curp->outtype = OUTT_PDF; else { warnx("-T %s: Bad argument", arg); return 0; } return 1; } static int woptions(struct curparse *curp, char *arg) { char *v, *o; const char *toks[7]; toks[0] = "stop"; toks[1] = "all"; toks[2] = "warning"; toks[3] = "error"; toks[4] = "unsupp"; toks[5] = "fatal"; toks[6] = NULL; while (*arg) { o = arg; switch (getsubopt(&arg, (char * const *)toks, &v)) { case 0: curp->wstop = 1; break; case 1: case 2: curp->wlevel = MANDOCLEVEL_WARNING; break; case 3: curp->wlevel = MANDOCLEVEL_ERROR; break; case 4: curp->wlevel = MANDOCLEVEL_UNSUPP; break; case 5: curp->wlevel = MANDOCLEVEL_BADARG; break; default: warnx("-W %s: Bad argument", o); return 0; } } return 1; } static void mmsg(enum mandocerr t, enum mandoclevel lvl, const char *file, int line, int col, const char *msg) { const char *mparse_msg; fprintf(stderr, "%s: %s:", getprogname(), file == NULL ? " " : file); if (line) fprintf(stderr, "%d:%d:", line, col + 1); fprintf(stderr, " %s", mparse_strlevel(lvl)); if (NULL != (mparse_msg = mparse_strerror(t))) fprintf(stderr, ": %s", mparse_msg); if (msg) fprintf(stderr, ": %s", msg); fputc('\n', stderr); } static pid_t spawn_pager(struct tag_files *tag_files) { const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ #define MAX_PAGER_ARGS 16 char *argv[MAX_PAGER_ARGS]; const char *pager; char *cp; size_t cmdlen; int argc; pid_t pager_pid; pager = getenv("MANPAGER"); if (pager == NULL || *pager == '\0') pager = getenv("PAGER"); if (pager == NULL || *pager == '\0') pager = "more -s"; cp = mandoc_strdup(pager); /* * Parse the pager command into words. * Intentionally do not do anything fancy here. */ argc = 0; while (argc + 4 < MAX_PAGER_ARGS) { argv[argc++] = cp; cp = strchr(cp, ' '); if (cp == NULL) break; *cp++ = '\0'; while (*cp == ' ') cp++; if (*cp == '\0') break; } /* For less(1), use the tag file. */ if ((cmdlen = strlen(argv[0])) >= 4) { cp = argv[0] + cmdlen - 4; if (strcmp(cp, "less") == 0) { argv[argc++] = mandoc_strdup("-T"); argv[argc++] = tag_files->tfn; } } argv[argc++] = tag_files->ofn; argv[argc] = NULL; switch (pager_pid = fork()) { case -1: err((int)MANDOCLEVEL_SYSERR, "fork"); case 0: break; default: (void)setpgid(pager_pid, 0); (void)tcsetpgrp(tag_files->ofd, pager_pid); #if HAVE_PLEDGE if (pledge("stdio rpath tmppath tty proc", NULL) == -1) err((int)MANDOCLEVEL_SYSERR, "pledge"); #endif tag_files->pager_pid = pager_pid; return pager_pid; } /* The child process becomes the pager. */ if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) err((int)MANDOCLEVEL_SYSERR, "pager stdout"); close(tag_files->ofd); close(tag_files->tfd); /* Do not start the pager before controlling the terminal. */ while (tcgetpgrp(STDOUT_FILENO) != getpid()) nanosleep(&timeout, NULL); execvp(argv[0], argv); err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); } Index: vendor/mdocml/dist/man.1 =================================================================== --- vendor/mdocml/dist/man.1 (revision 313955) +++ vendor/mdocml/dist/man.1 (revision 313956) @@ -1,435 +1,449 @@ -.\" $Id: man.1,v 1.20 2017/01/06 01:34:57 schwarze Exp $ +.\" $Id: man.1,v 1.21 2017/01/31 19:44:04 schwarze Exp $ .\" .\" Copyright (c) 1989, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" Copyright (c) 2003, 2007, 2008, 2014 Jason McIntyre .\" Copyright (c) 2010, 2011, 2014, 2015 Ingo Schwarze .\" .\" 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. .\" .\" @(#)man.1 8.2 (Berkeley) 1/2/94 .\" -.Dd $Mdocdate: January 6 2017 $ +.Dd $Mdocdate: January 31 2017 $ .Dt MAN 1 .Os .Sh NAME .Nm man .Nd display manual pages .Sh SYNOPSIS .Nm man .Op Fl acfhklw .Op Fl C Ar file .Op Fl I Cm os Ns = Ns Ar name .Op Fl K Ar encoding .Op Fl M Ar path .Op Fl m Ar path .Op Fl O Ar option Ns = Ns Ar value .Op Fl S Ar subsection .Op Fl s Ar section .Op Fl T Ar output .Op Fl W Ar level .Op Ar section .Ar name ... .Sh DESCRIPTION The .Nm utility displays the manual pages entitled .Ar name . Pages may be selected according to a specific category .Pq Ar section or machine architecture .Pq Ar subsection . .Pp The options are as follows: .Bl -tag -width Ds .It Fl a Display all matching manual pages. Normally, only the first page found is displayed. .It Fl C Ar file Use the specified .Ar file instead of the default configuration file. This permits users to configure their own manual environment. See .Xr man.conf 5 for a description of the contents of this file. .It Fl c Copy the manual page to the standard output instead of using .Xr more 1 to paginate it. This is done by default if the standard output is not a terminal device. .It Fl f A synonym for .Xr whatis 1 . It searches for .Ar name in manual page names and displays the header lines from all matching pages. The search is case insensitive and matches whole words only. This overrides any earlier .Fl k and .Fl l options. .It Fl h Display only the SYNOPSIS lines of the requested manual pages. Implies .Fl a and .Fl c . .It Fl I Cm os Ns = Ns Ar name Override the default operating system .Ar name for the .Xr mdoc 7 .Ic \&Os and for the .Xr man 7 .Ic \&TH macro. .It Fl K Ar encoding Specify the input encoding. The supported .Ar encoding arguments are .Cm us-ascii , .Cm iso-8859-1 , and .Cm utf-8 . By default, the encoding is automatically detected as described in the .Xr mandoc 1 manual. .It Fl k A synonym for .Xr apropos 1 . Instead of .Ar name , an expression can be provided using the syntax described in the .Xr apropos 1 manual. By default, it displays the header lines of all matching pages. This overrides any earlier .Fl f and .Fl l options. .It Fl l A synonym for .Xr mandoc 1 .Fl a . The .Ar name arguments are interpreted as filenames. No search is done and .Ar file , .Ar path , .Ar section , and .Ar subsection are ignored. This overrides any earlier .Fl f , .Fl k , and .Fl w options. .It Fl M Ar path Override the list of standard directories which .Nm searches for manual pages. The supplied .Ar path must be a colon .Pq Ql \&: separated list of directories. This search path may also be set using the environment variable .Ev MANPATH . .It Fl m Ar path Augment the list of standard directories which .Nm searches for manual pages. The supplied .Ar path must be a colon .Pq Ql \&: separated list of directories. These directories will be searched before the standard directories or the directories specified using the .Fl M option or the .Ev MANPATH environment variable. .It Fl O Ar option Ns = Ns Ar value Comma-separated output options. For each output format, the available options are described in the .Xr mandoc 1 manual. .It Fl S Ar subsection Restricts the directories that .Nm will search to those of a specific .Xr machine 1 architecture. .Ar subsection is case insensitive. .Pp By default manual pages for all architectures are installed. Therefore this option can be used to view pages for one architecture whilst using another. .Pp This option overrides the .Ev MACHINE environment variable. .It Oo Fl s Oc Ar section Only select manuals from the specified .Ar section . The currently available sections are: .Pp .Bl -tag -width "localXXX" -offset indent -compact .It 1 General commands .Pq tools and utilities . .It 2 System calls and error numbers. .It 3 Library functions. .It 3p .Xr perl 1 programmer's reference guide. .It 4 Device drivers. .It 5 File formats. .It 6 Games. .It 7 Miscellaneous information. .It 8 System maintenance and operation commands. .It 9 Kernel internals. .El .It Fl T Ar output Select the output format. The default is .Cm locale . The other output modes .Cm ascii , .Cm html , .Cm lint , .Cm man , .Cm pdf , .Cm ps , .Cm tree , and .Cm utf8 are described in the .Xr mandoc 1 manual. .It Fl W Ar level Specify the minimum message .Ar level to be reported on the standard error output and to affect the exit status. The .Ar level can be .Cm warning , .Cm error , or .Cm unsupp ; .Cm all is an alias for .Cm warning . By default, .Nm is silent. See the .Xr mandoc 1 manual for details. .It Fl w List the pathnames of the manual pages which .Nm would display for the specified .Ar section and .Ar name combination. .El .Pp Guidelines for writing man pages can be found in .Xr mdoc 7 . .Pp If both a formatted and an unformatted version of the same manual page, for example .Pa cat1/foo.0 and .Pa man1/foo.1 , exist in the same directory, and at least one of them is selected, only the newer one is used. However, if both the .Fl a and the .Fl w options are specified, both file names are printed. .Sh ENVIRONMENT .Bl -tag -width MANPATHX .It Ev MACHINE As some manual pages are intended only for specific architectures, .Nm searches any subdirectories, with the same name as the current architecture, in every directory which it searches. Machine specific areas are checked before general areas. The current machine type may be overridden by setting the environment variable .Ev MACHINE to the name of a specific architecture, or with the .Fl S option. .Ev MACHINE is case insensitive. .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER will be used instead of the standard pagination program, .Xr more 1 . If .Xr less 1 is used, the interactive .Ic :t command can be used to go to the definitions of various terms, for example command line options, command modifiers, internal commands, environment variables, function names, preprocessor macros, .Xr errno 2 values, and some other emphasized words. Some terms may have defining text at more than one place. In that case, the .Xr less 1 interactive commands .Ic t and .Ic T can be used to move to the next and to the previous place providing information about the term last searched for with .Ic :t . .It Ev MANPATH The standard search path used by .Nm may be overridden by specifying a path in the .Ev MANPATH environment variable. The format of the path is a colon .Pq Ql \&: separated list of directories. .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, .Xr more 1 .Fl s will be used. .El .Sh FILES .Bl -tag -width /etc/man.conf -compact .It Pa /etc/man.conf default man configuration file .El .Sh EXIT STATUS .Ex -std man .Sh SEE ALSO .Xr apropos 1 , .Xr intro 1 , .Xr whatis 1 , .Xr whereis 1 , .Xr intro 2 , .Xr intro 3 , .Xr intro 4 , .Xr intro 5 , .Xr man.conf 5 , .Xr intro 6 , .Xr intro 7 , .Xr mdoc 7 , .Xr intro 8 , .Xr intro 9 .Sh STANDARDS The .Nm utility is compliant with the .St -p1003.1-2008 specification. .Pp The flags .Op Fl aCcfhIKlMmOSsTWw , as well as the environment variables .Ev MACHINE , .Ev MANPAGER , and .Ev MANPATH , are extensions to that specification. .Sh HISTORY A .Nm command first appeared in .At v3 . .Pp The .Fl w option first appeared in .At v7 ; .Fl f and .Fl k in .Bx 4 ; .Fl M in .Bx 4.3 ; .Fl a in .Bx 4.3 Tahoe ; .Fl c and .Fl m in .Bx 4.3 Reno ; .Fl h in .Bx 4.3 Net/2 ; .Fl C in .Nx 1.0 ; -and .Fl s and .Fl S in -.Ox 2.3 . +.Ox 2.3 ; +and +.Fl I , +.Fl K , +.Fl l , +.Fl O , +and +.Fl W +in +.Ox 5.7 . +The +.Fl T +option first appeared in +.At III +and was also added in +.Ox 5.7 . Index: vendor/mdocml/dist/man.options.1 =================================================================== --- vendor/mdocml/dist/man.options.1 (nonexistent) +++ vendor/mdocml/dist/man.options.1 (revision 313956) @@ -0,0 +1,1326 @@ +.\" $Id: man.options.1,v 1.6 2017/02/02 20:10:51 schwarze Exp $ +.\" +.\" Copyright (c) 2017 Ingo Schwarze +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: February 2 2017 $ +.Dt MAN.OPTIONS 1 +.Os +.Sh NAME +.Nm man.options +.Nd assignment of option letters in manual page utilities +.\" +.\" Sources that occur repeatedly. +.\" Only use if the precise implementation time is unknown. +.\" +.de PWB +.No PWB/UNIX 1.0 Pq July 1, 1977 \\$1 +.. +.de At7 +.At v7 Pq January 1979 \\$1 +.. +.de At3 +.At III Pq June 1980 \\$1 +.. +.de Bx4 +.Bx 4 Pq November 16, 1980 \\$1 +.. +.de At5 +.At V Pq January 1983 \\$1 +.. +.de Bx43 +.Bx 4.3 Pq June 1986 \\$1 +.. +.\" option was present in groff-1.01 as contained in 4.3BSD-Net/2 +.\" and no mention of it could be found in the ChangeLog, +.\" so it's probably older than groff-0.4, where the log started +.de g04 +.No probably before groff-0.4 Pq before July 14, 1990 \\$1 +.. +.de Eaton +.No Eaton Pq before July 7, 1993; 1990/91? \\$1 +.. +.\" man-1.5e was released on July 11, 1998. +.de man15e +.No man-1.5e Pq not before 1993, not after 1998 \\$1 +.. +.\" man-1.5g was released on April 7, 1999. +.de man15g +.No man-1.5g Pq not before 1993, not after 1999 \\$1 +.. +.\" code first seen in the initial import of man-db into CVS , +.\" which was more or less debian man-db-2.3.17 +.\" Colin Watson's first release was 2.3.18 on May 14, 2001 +.\" no clue about it found in ChangeLog-2013, +.\" so it was probably already present before man-db-2.2a4 +.de dbI +.No man-db probably before 2.2a4 Pq before Nov 8, 1994 \\$1 +.. +.\" +.\" -------------------------------------------------------------------- +.\" +.Sh DESCRIPTION +This manual page lists option letters used in many different versions +of the +.Nm man , +.Nm apropos , +.Nm whatis , +.Nm mandoc , +.Nm makewhatis , +.Nm mandb , +.Nm makemandb , +.Nm catman , +and +.Nm manpath +utilities. +Option letters used by +.Nm groff , +.Nm nroff , +.Nm troff , +and +.Nm roff +are also included because beginning with +.At v7 , +many versions of +.Xr man 1 +pass on unrecognized options to these programs. +.Pp +For each option letter, information is first grouped into paragraphs, +each paragraph describing similar functionality and starting with +one line briefly summarizing that functionality. +.Pp +For each program using the letter for that functionality, one line +is provided, giving the name of the program, a colon, the system +where this letter first appeared for this functionality in this +program, optionally a comma and a list of other system versions +introducing the same, a semicolon, and a list of current systems +supporting it. +If a system appears before the semicolon, it is not repeated +afterwards. +.Pp +Entries are sorted by historical precedence, except that obsolete +options are moved to the end. +Dates are commit dates where known, and release dates otherwise. +.Bl -tag -width 3n +.It Fl a +display all matching manual pages +.br +.Nm man : +.Bx 4.3 Tahoe Pq June 1988 , +.Eaton ; +.Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +only display items that match all keywords +.br +.Nm apropos : +.No man-db Pq Aug 29, 2007 +.Pp +use all directories and files for +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.Pp +.Bq superseded by Fl T Cm ascii +ASCII output mode +.br +.Nm troff : +.At7 +.br +.Nm groff : +.g04 +.It Fl B +use specified browser +.br +.Nm man : +.No man-1.6 Pq June 24, 2005 +.It Fl b +print a backtrace with each warning or error message +.br +.Nm groff : +.g04 +.Pp +.Bq obsolete hardware +report whether the phototypesetter is busy +.br +.Nm troff : +.At7 +.It Fl C +alternate configuration file +.br +.Nm apropos , whatis : +.Bx 4.4 Lite1 Pq April 22, 1994 , +.No man-db Pq Feb 22, 2003 ; +.Ox , Nx +.br +.Nm man : +.Nx 1.0 Pq Oct 26, 1994 , +.man15e ; +.Ox +.br +.Nm mandb , catman , manpath : +.No man-db Pq Feb 22, 2003 +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +.Bq obsolete +enable compatibility mode +.br +.Nm groff : +.No before groff-0.5 Pq before August 3, 1990 +.It Fl c +do not use a pager +.br +.Nm man : +.Bx 4.3 Reno Pq June 1990 ; +.Ox , Nx +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +process given catpath +.br +.Nm makewhatis : +.Pq not before 1992, not after 1995 +.Pp +recreate databases from scratch +.br +.Nm mandb : +.dbI +.Pp +produce a catpath as opposed to a manpath +.br +.Nm manpath : +.dbI +.Pp +internal option for use by +.Xr catman 1 +.br +.Nm man : +.dbI +.Pp +reformat source page even if cat page exists +.br +.Nm man : +.man15e +.Pp +disable terminal color output in +.Xr grotty 1 +.br +.Nm groff : +.No groff-1.18.0 Pq Oct 4, 2001 +.Pp +recreate nroff versions from SGML sources +.br +.Nm catman : +.No Solaris 9-11 +.Pp +.Bq obsolete +postprocess with +.Xr col 1 +.br +.Nm man : +.At3 , +.At5 +.It Fl D +reset whatever was set with +.Ev MANOPT +.br +.Nm man : +.dbI +.Pp +print debugging info in addition to manual page +.br +.Nm man : +.man15e +.Pp +set default input encoding for +.Xr preconv 1 +.br +.Nm groff : +.No groff-1.20 Pq August 20, 2008 +.Pp +display all files added to +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.It Fl d +define a user-defined string +.br +.Nm groff : +.g04 +.Pp +print debugging information +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm apropos , whatis : +.dbI ; +.Fx +.br +.Nm mandb , catman : +.dbI +.Pp +remove and re-add a file to +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 2.7 Pq Feb 3, 2000 +.Pp +.Bq superseded by Fl l +interpret arguments as file names +.br +.Nm man : +.At3 , +.At5 +.It Fl E +inhibit all error messages +.br +.Nm groff : +.g04 +.Pp +select output encoding +.br +.Nm man : +.No man-db Pq Dec 23, 2001 +.It Fl e +preprocess with +.Xr eqn 7 +.br +.Nm man : +.At7 +.br +.Nm groff : +.g04 +.Pp +adjust text to left and right margins +.br +.Nm nroff : +.At7 +.Pp +use exact matching +.br +.Nm apropos , whatis : +.dbI +.Pp +restrict search by section extension +.br +.Nm man : +.No man-db-2.3.5 Pq April 21, 1995 +.It Fl F +use alternate font directory +.br +.Nm troff : +.Bx 4.2 Pq September 1983 +.br +.Nm groff : +.g04 +.Pp +preformat only, do not display +.br +.Nm man : +.No man-1.5g Pq April 7, 1999 +.Pp +force searching dirs, do not use index (default) +.br +.Nm man : +.No illumos , Solaris 9-11 +.It Fl f +.Xr whatis 1 +mode +.br +.Nm man : +.Bx4 , +.Eaton ; +.Ox , Fx , No man-db , man-1.6 +.br +.Nm apropos , whatis : +.No man-db Pq Dec 2, 2010 , +.Ox 5.7 Pq August 27, 2014 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +set the default font family +.br +.Nm groff : +.g04 +.Pp +force formatting even if cat page is newer +.br +.Nm catman : +.Fx Pq March 15, 1995 +.Pp +update only the entries for the given file +.br +.Nm mandb : +.No man-db Pq Feb 21, 2003 +.Pp +force rebuilding the database from scratch +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.Pp +locate manual page related to given file name +.br +.Nm man : +.No illumos , Solaris 9-11 +.Pp +.Bq obsolete hardware +do not feed out paper nor stop phototypesetter +.br +.Nm troff : +.At7 +.It Fl G +preprocess with +.Xr grap 1 +.br +.Nm groff : +.No groff-1.16 Pq May 1, 2000 +.It Fl g +produce a global manpath +.br +.Nm manpath : +.No man-db-2.2a7 Pq Nov 16, 1994 +.Pp +preprocess with +.Xr grn 1 +.br +.Nm groff : +.No groff-1.16 Pq Feb 20, 2000 +.Pp +.Bq obsolete hardware +output to a GCOS phototypesetter +.br +.Nm troff : +.At7 +.Pp +.Bq obsolete hardware +output to a DASI 300 terminal in 12-pitch mode +.br +.Nm man : +.PWB +.It Fl H +read hyphenation patterns from the given file +.br +.Nm groff : +.g04 +.Pp +produce HTML output +.br +.Nm man : +.No man-db-1.3.12 to 1.3.17 Pq not before 1996, not after 2001 +.Pp +use program to render HTML files as text +.br +.Nm man : +.No man-1.6 Pq June 24, 2005 +.It Fl h +print a help message and exit +.br +.Nm groff : +.g04 +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.br +.Nm manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm apropos , whatis , mandb , catman : +.dbI +.Pp +display the SYNOPSIS lines only +.br +.Nm man : +.Bx 4.3 Net/2 Pq August 20, 1991 ; +.Ox , Nx +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq Sep 3, 2014 +.Pp +turn on HTML formatting +.br +.Nm apropos : +.Nx Pq Apr 2, 2013 +.Pp +.Bq obsolete +replace spaces by tabs in the output +.br +.Nm roff , nroff : +.At7 +.It Fl I +input file search path for +.Xr soelim 1 +.br +.Nm groff : +.No groff-1.12 Pq Sep 11, 1999 +.Pp +respect case when matching manual page names +.br +.Nm man , catman : +.No man-db Pq Apr 21, 2002 +.Pp +input options, in particular default operating system name +.br +.Nm mandoc : +.Ox 5.2 Pq May 24, 2012 +.br +.Nm man , apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.It Fl i +read standard input after the input files are exhausted +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +ignore case when matching manual page names +.br +.Nm man , catman : +.No man-db Pq Apr 21, 2002 +.Pp +turn on terminal escape code formatting +.br +.Nm apropos : +.Nx Pq March 29, 2013 +.It Fl J +preprocess with +.Xr gideal 1 +.br +.Nm groff : +.No groff-1.22.3 Pq June 17, 2014 +.It Fl j +preprocess with +.Xr chem 1 +.br +.Nm groff : +.No groff-1.22 Pq Jan 22, 2011 +.It Fl K +source code full text search +.br +.Nm man : +.man15e , +.No man-db Pq June 28, 2009 ; +.No Solaris 11 +.Pp +input encoding +.br +.Nm groff : +.No groff-1.20 Pq Dec 31, 2005 +.br +.Nm man , apropos , whatis , mandoc : +.Ox 5.7 Pq Oct 30, 2014 +.It Fl k +.Xr apropos 1 +mode +.br +.Nm man : +.Bx4 , +.Eaton ; +.No POSIX , Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +ignore formatting errors +.br +.Nm catman : +.Nx Pq April 26, 1994 +.Pp +preprocess with +.Xr preconv 1 +.br +.Nm groff : +.No groff-1.20 Pq Dec 31, 2005 +.Pp +.Bq obsolete hardware +display on a Tektronix 4014 terminal +.br +.Nm man : +.At7 +.It Fl L +pass argument to the spooler +.br +.Nm groff : +.No groff-0.6 Pq Sep 14, 1990 +.Pp +use alternate +.Xr locale 1 +.br +.Nm man , apropos , whatis : +.No before man-db-2.2a13 Pq before Dec 15, 1994 +.Pp +print list of locales +.br +.Nm manpath : +.Fx Pq Nov 23, 1999 +.Pp +use +.Xr locale 1 +specified in the environment +.br +.Nm catman : +.Fx Pq May 18, 2002 +.It Fl l +spool the output +.br +.Nm groff : +.g04 +.Pp +interpret arguments as file names +.br +.Nm man : +.No before man-2.2a7 Pq before Nov 16, 1994 , +.Ox 5.7 Pq Aug 30, 2014 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq Aug 30, 2014 +.Pp +do not trim output to the terminal width +.br +.Nm apropos , whatis : +.No man-db Pq Aug 19, 2007 +.Pp +only parse NAME sections +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.Pp +legacy mode: search Nm,Nd, no context or formatting +.br +.Nm apropos : +.Nx Pq March 29, 2013 +.Pp +list all manual pages matching name within the search path +.br +.Nm man : +.No illumos , Solaris 9-11 +.It Fl M +override manual page search path +.br +.Nm man : +.Bx43 , +.Eaton ; +.Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm apropos , whatis : +.Bx43 , +.No before man-db-2.2a14 Pq before Dec 16, 1994 ; +.Ox , No illumos +.br +.Nm catman : +.dbI ; +.Nx Pq July 27, 1993 , +.No Solaris 9-11 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +prepend to macro file search path +.br +.Nm groff : +.g04 +.Pp +do not show the context of the match +.br +.Nm apropos : +.Nx Pq May 22, 2016 +.It Fl m +specify input macro language +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.br +.Nm mandoc : +.Ox 4.8 Pq April 6, 2009 +.Pp +augment manual page search path +.br +.Nm man , apropos , whatis : +.Bx 4.3 Reno Pq June 1990 ; +.Ox , Nx +.br +.Nm catman : +.Nx Pq Apr 4, 1999 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +override operating system +.br +.Nm man : +.Eaton ; +.No man-db , man-1.6 +.br +.Nm apropos , whatis , manpath : +.dbI +.Pp +override architecture +.br +.Nm man : +.Fx Pq Jan 11, 2002 +.Pp +show the context of the match +.br +.Nm apropos : +.Nx Pq May 22, 2016 +.It Fl N +do not allow newlines between +.Xr eqn 7 +delimiters +.br +.Nm groff : +.No groff-1.01 Pq Feb 21, 1991 +.It Fl n +specify a page number for the first page +.br +.Nm troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +.Xr nroff 1 +output mode +.br +.Nm man : +.At7 +.Pp +do not create the +.Xr whatis 1 +database +.br +.Nm catman : +.Nx Pq July 27, 1993 +.Pp +print commands instead of executing them +.br +.Nm catman : +.Fx Pq May 18, 2002 , +.No Solaris 9-11 +.Pp +limit the number of results +.br +.Nm apropos : +.Nx Pq Feb 7, 2012 +.Pp +dry run simulating +.Xr mandoc.db 5 +creation +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.It Fl O +output options +.br +.Nm mandoc : +.Ox 4.8 Pq Oct 27, 2009 +.br +.Nm man , apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.It Fl o +select pages by numbers +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +force use of non-localized manual pages +.br +.Nm man : +.Fx Pq June 7, 1999 +.Pp +optimize index for speed and disk space +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.It Fl P +pass argument to postprocessor +.br +.Nm groff : +.No groff-0.6 Pq Sep 14, 1990 +.Pp +use specified pager +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.Pp +turn on pager formatting +.br +.Nm apropos : +.Nx Pq Apr 2, 2013 +.It Fl p +preprocess with +.Xr pic 1 +.br +.Nm groff : +.g04 +.Pp +use the given list of preprocessors +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.Pp +dry run, display commands instead of executing them +.br +.Nm catman : +.Nx Pq July 27, 1993 , +.Fx Pq March 15, 1995 to May 18, 2002 , +.No Solaris 9-11 +.Pp +print warnings when building +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 2.7 Pq April 23, 2000 +.Pp +do not look for deleted manual pages +.br +.Nm mandb : +.No man-db Pq June 28, 2001 +.Pp +print the search path for manual pages +.br +.Nm man : +.Nx Pq June 14 , 2011 +.Pp +turn on pager formatting and pipe through pager +.br +.Nm apropos : +.Nx Pq Feb 7, 2012 +.Pp +.Bq obsolete hardware +set phototypesetter point size +.br +.Nm troff : +.At7 +.It Fl Q +print only fatal error messages +.br +.Nm makemandb : +.Nx Pq Aug 29, 2012 +.Pp +quick mode of +.Xr mandoc.db 5 +creation +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.It Fl q +invoke the simultaneous input-output mode of the .rd request +.br +.Nm nroff , troff : +.At7 +.Pp +issue no warnings +.br +.Nm manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm mandb : +.dbI +.Pp +print only warnings and errors, no status updates +.br +.Nm makemandb : +.Nx Pq Aug 29, 2012 +.It Fl R +postprocess with +.Xr refer 1 +.br +.Nm groff : +.No groff-1.02 Pq June 2, 1991 +.Pp +recode to the specified encoding +.br +.Nm man : +.No man-db Pq Dec 31, 2007 +.It Fl r +set number register +.br +.Nm nroff , troff : +.At7 +.br +.Nm groff : +.g04 +.Pp +scan for and remove junk files +.br +.Nm catman : +.Fx Pq March 31, 1995 +.Pp +set +.Xr less 1 +prompt +.br +.Nm man : +.No man-db-2.3.5 Pq April 21, 1995 +.Pp +use regular expression matching +.br +.Nm apropos , whatis : +.No man-db-2.3.5 Pq April 21, 1995 +.Pp +turn off formatting +.br +.Nm apropos : +.Nx Pq Feb 10, 2013 +.Pp +check for formatting errors, do not display +.br +.Nm man : +.No illumos , Solaris 9-11 +.It Fl S +manual section search list +.br +.Nm man : +.Eaton ; +.Fx , No man-db , man-1.6 +.Pp +safer mode +.br +.Nm groff : +.No groff-1.10 Pq May 17, 1994 +.Pp +restrict architecture +.br +.Nm man : +.Ox 2.3 Pq March 9, 1998 , +.Nx Pq May 27, 2000 +.br +.Nm apropos : +.Ox 4.5 Pq Dec 24, 2008 , +.Nx Pq May 8, 2009 +.br +.Nm whatis : +.Ox 5.6 Pq April 18, 2014 +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.It Fl s +preprocess with +.Xr soelim 1 +.br +.Nm groff : +.g04 +.Pp +silent mode, do not echo commands +.br +.Nm catman : +.Nx Pq April 26, 1994 +.Pp +restrict section +.br +.Nm makewhatis : +.man15g +.br +.Nm man : +.Ox 2.3 Pq March 9, 1998 , +.Nx Pq June 12, 2000 ; +.No illumos , Solaris 9-11 +.br +.Nm apropos : +.No man-db Pq Nov 16, 2003 , +.Ox 4.5 Pq Dec 24, 2008 , +.Nx Pq May 8, 2009 ; +.No illumos +.br +.Nm whatis : +.No man-db Pq Nov 16, 2003 , +.Ox 5.6 Pq April 18, 2014 ; +.No illumos +.br +.Nm mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +do not look for stray cats +.br +.Nm mandb : +.dbI +.Pp +.Bq SysV compat, recommends Fl S +manual section search list +.br +.Nm man : +.No man-db Pq Jan 1, 2008 +.Pp +.Bq superseded by Fl h +display the SYNOPSIS lines only +.br +.Nm man : +.PWB +.Pp +.Bq obsolete hardware +pause before each page for paper manipulation +.br +.Nm roff : +.At7 +.Pp +.Bq obsolete hardware +.Xr troff 1 +output mode, small format +.br +.Nm man : +.At3 , +.At5 +.It Fl T +select terminal output format +.br +.Nm nroff : +.At7 +.br +.Nm man : +.At3 , +.At5 , +.dbI , +.Ox 5.7 Pq August 27, 2014 +.br +.Nm groff : +.g04 +.br +.Nm mandoc : +.Ox 4.8 Pq April 6, 2009 +.br +.Nm apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.Pp +use UTF-8 for +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 5.6 Pq April 18, 2014 +.Pp +.Bq superseded by Fl m +use other macro package +.br +.Nm man , catman : +.No Solaris 9-11 +.It Fl t +.Xr troff 1 +output mode +.br +.Nm man : +.PWB , +.At7 , +.Bx 2 Pq May 10, 1979 , +.At3 , +.At5 , +.Eaton ; +.Fx , No man-db , man-1.6 , illumos , Solaris 9-11 +.br +.Nm catman : +.No Solaris 9-11 +.Pp +preprocess with +.Xr tbl 7 +.br +.Nm groff : +.g04 +.Pp +check manual pages in the hierarchy +.br +.Nm mandb : +.No man-db-1.3.12 to 1.3.17 Pq not before 1996, not after 2001 +.Pp +check files for problems related to +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 2.7 Pq April 23, 2000 +.It Fl U +unsafe mode +.br +.Nm groff : +.No groff-1.12 Pq Dec 13, 1999 +.It Fl u +update database +.br +.Nm makewhatis : +.Pq not before 1992, not after 1995 +.Pp +create user databases only +.br +.Nm mandb : +.dbI +.Pp +update database cache (requires suid) +.br +.Nm man : +.No before man-db-2.2a10 Pq before Dec 6, 1994 +.Pp +remove files from +.Xr mandoc.db 5 +.br +.Nm makewhatis : +.Ox 3.4 Pq July 9, 2003 +.It Fl V +print the pipeline on stdout instead of executing it +.br +.Nm groff : +.No groff-0.6 Pq Sep 2, 1990 +.Pp +print version information +.br +.Nm man , apropos , whatis , mandb , catman , manpath : +.dbI +.It Fl v +print version number +.br +.Nm groff : +.g04 +.Pp +verbose mode +.br +.Nm catman : +.Fx Pq March 15, 1995 +.br +.Nm makewhatis : +.man15g +.br +.Nm apropos , whatis : +.No man-db Pq Dec 29, 2002 +.Pp +print the name of every parsed file +.br +.Nm makemandb : +.Nx Pq Feb 7, 2012 +.Pp +.Bq obsolete hardware +produce output on the Versatec printer +.br +.Nm man : +.PWB +.It Fl W +disable the named warning +.br +.Nm groff : +.No groff-0.5 Pq August 14, 1990 +.Pp +list pathnames without additional information +.br +.Nm man : +.man15e +.Pp +list pathnames of cat files +.br +.Nm man : +.No man-db Pq Aug 13, 2002 +.Pp +minimum message level to display +.br +.Nm mandoc : +.Ox 4.8 Pq April 6, 2009 +.br +.Nm man , apropos , whatis : +.Ox 5.7 Pq August 27, 2014 +.It Fl w +list pathnames +.br +.Nm man : +.At7 , +.At3 , +.At5 , +.Eaton ; +.Ox , Fx , Nx , No man-db , man-1.6 +.br +.Nm apropos , whatis , mandoc : +.Ox 5.7 Pq August 27, 2014 +.Pp +enable the named warning +.br +.Nm groff : +.No groff-0.5 Pq August 14, 1990 +.Pp +only create the +.Xr whatis 1 +database +.br +.Nm catman : +.Nx Pq July 27, 1993 , +.No Solaris 9-11 +.Pp +use wildcard matching +.br +.Nm apropos , whatis : +.No man-db-2.3.5 Pq April 21, 1995 +.Pp +use manpath obtained from man --path +.br +.Nm makewhatis : +.man15g +.Pp +update the +.Xr whatis 1 +database +.br +.Nm man : +.No illumos +.Pp +.Bq obsolete hardware +wait until the phototypesetter is available +.br +.Nm troff : +.At7 +.It Fl X +display with +.Xr gxditview 1 +.br +.Nm groff : +.No groff-1.06 Pq Sep 1, 1992 +.br +.Nm man : +.dbI +.It Fl y +use the non-compacted version of the macros +.br +.Nm man : +.At3 , +.At5 +.It Fl Z +do not run preprocessors +.br +.Nm groff : +.g04 +.br +.Nm man : +.No man-db-2.2a5 Pq Nov 10, 1994 +.It Fl z +suppress formatted output from +.Xr troff 1 , +print only error messages +.br +.Nm groff : +.g04 +.It Fl 7 +ASCII output mode +.br +.Nm man : +.No man-db-2.3.5 Pq April 21, 1995 +.It Fl \&? +print a help message and exit +.br +.Nm groff : +.g04 +.br +.Nm man , manpath : +.Eaton ; +.Fx , No man-db +.br +.Nm apropos , whatis , mandb , catman : +.dbI +.El +.Pp +Multi-letter options: +.Bl -tag -width Ds +.It Fl hp +.Bq obsolete hardware +output to a Hewlett Packard terminal +.br +.Nm man : +.PWB +.It Fl 12 +.Bq obsolete hardware +use 12-pitch for certain terminals +.br +.Nm man : +.At3 , +.At5 +.It Fl 450 +.Bq obsolete hardware +output to a DASI 450 terminal +.br +.Nm man : +.PWB +.El +.Pp +In +.At v3 , +.Xr man 1 +had no options. +.br +The syntax was: +.Sy man Ar name Op Ar section +.Pp +In +.At v4 , +.br +the syntax changed to: +.Sy man Oo Ar section Oc Op Ar name ... +.Sh AUTHORS +This information was assembled by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org +using +.Bl -bullet -compact +.It +the Unix Archive of the Unix Heritage Society +.It +the CSRG Archive CD-ROMs +.It +the FreeBSD SVN repository +.It +the OpenBSD CVS repository +.It +the NetBSD CVS repository +.It +the GNU roff (groff) git repository +.It +the 4.3BSD-Net/2 groff CHANGES file (Oct 1990 to March 1991) +.It +the 4.3BSD-Net/2 groff ChangeLog file (July 1990 to March 1991) +.It +the man-db CVS and git repositories (since April 2001) +.It +the man-db NEWS file (April 1995 to Dec 2016) +.It +the man-db ChangeLog-2013 file (Nov 1994 to Dec 2013) +.It +release tarballs man-1.5g (July 1998) to man-1.5p (Jan 2005), +man-1.6 (June 2005), and man-1.6a to man-1.6g (Dec 2010) +.It +a makewhatis release tarball without version number from 1995 +.It +the illumos manual pages on the WWW +.It +and Solaris 11, SunOS 5.10, and SunOS 5.9 machines at opencsw.org. +.El Property changes on: vendor/mdocml/dist/man.options.1 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/man_html.c =================================================================== --- vendor/mdocml/dist/man_html.c (revision 313955) +++ vendor/mdocml/dist/man_html.c (revision 313956) @@ -1,616 +1,669 @@ -/* $Id: man_html.c,v 1.129 2017/01/21 01:20:32 schwarze Exp $ */ +/* $Id: man_html.c,v 1.133 2017/02/05 18:15:39 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons * Copyright (c) 2013, 2014, 2015, 2017 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. */ #include "config.h" #include #include #include #include #include #include #include "mandoc_aux.h" #include "roff.h" #include "man.h" #include "out.h" #include "html.h" #include "main.h" -/* TODO: preserve ident widths. */ /* FIXME: have PD set the default vspace width. */ #define INDENT 5 #define MAN_ARGS const struct roff_meta *man, \ const struct roff_node *n, \ - struct mhtml *mh, \ struct html *h -struct mhtml { - int fl; -#define MANH_LITERAL (1 << 0) /* literal context */ -}; - struct htmlman { int (*pre)(MAN_ARGS); int (*post)(MAN_ARGS); }; static void print_bvspace(struct html *, const struct roff_node *); static void print_man_head(MAN_ARGS); static void print_man_nodelist(MAN_ARGS); static void print_man_node(MAN_ARGS); +static int fillmode(struct html *, int); static int a2width(const struct roff_node *, struct roffsu *); static int man_B_pre(MAN_ARGS); static int man_HP_pre(MAN_ARGS); static int man_IP_pre(MAN_ARGS); static int man_I_pre(MAN_ARGS); static int man_OP_pre(MAN_ARGS); static int man_PP_pre(MAN_ARGS); static int man_RS_pre(MAN_ARGS); static int man_SH_pre(MAN_ARGS); static int man_SM_pre(MAN_ARGS); static int man_SS_pre(MAN_ARGS); static int man_UR_pre(MAN_ARGS); static int man_alt_pre(MAN_ARGS); static int man_br_pre(MAN_ARGS); static int man_ign_pre(MAN_ARGS); static int man_in_pre(MAN_ARGS); -static int man_literal_pre(MAN_ARGS); static void man_root_post(MAN_ARGS); static void man_root_pre(MAN_ARGS); static const struct htmlman mans[MAN_MAX] = { { man_br_pre, NULL }, /* br */ { NULL, NULL }, /* TH */ { man_SH_pre, NULL }, /* SH */ { man_SS_pre, NULL }, /* SS */ { man_IP_pre, NULL }, /* TP */ { man_PP_pre, NULL }, /* LP */ { man_PP_pre, NULL }, /* PP */ { man_PP_pre, NULL }, /* P */ { man_IP_pre, NULL }, /* IP */ { man_HP_pre, NULL }, /* HP */ { man_SM_pre, NULL }, /* SM */ { man_SM_pre, NULL }, /* SB */ { man_alt_pre, NULL }, /* BI */ { man_alt_pre, NULL }, /* IB */ { man_alt_pre, NULL }, /* BR */ { man_alt_pre, NULL }, /* RB */ { NULL, NULL }, /* R */ { man_B_pre, NULL }, /* B */ { man_I_pre, NULL }, /* I */ { man_alt_pre, NULL }, /* IR */ { man_alt_pre, NULL }, /* RI */ { man_br_pre, NULL }, /* sp */ - { man_literal_pre, NULL }, /* nf */ - { man_literal_pre, NULL }, /* fi */ + { NULL, NULL }, /* nf */ + { NULL, NULL }, /* fi */ { NULL, NULL }, /* RE */ { man_RS_pre, NULL }, /* RS */ { man_ign_pre, NULL }, /* DT */ { man_ign_pre, NULL }, /* UC */ { man_ign_pre, NULL }, /* PD */ { man_ign_pre, NULL }, /* AT */ { man_in_pre, NULL }, /* in */ { man_ign_pre, NULL }, /* ft */ { man_OP_pre, NULL }, /* OP */ - { man_literal_pre, NULL }, /* EX */ - { man_literal_pre, NULL }, /* EE */ + { NULL, NULL }, /* EX */ + { NULL, NULL }, /* EE */ { man_UR_pre, NULL }, /* UR */ { NULL, NULL }, /* UE */ { man_ign_pre, NULL }, /* ll */ }; /* * Printing leading vertical space before a block. * This is used for the paragraph macros. * The rules are pretty simple, since there's very little nesting going * on here. Basically, if we're the first within another block (SS/SH), * then don't emit vertical space. If we are (RS), then do. If not the * first, print it. */ static void print_bvspace(struct html *h, const struct roff_node *n) { if (n->body && n->body->child) if (n->body->child->type == ROFFT_TBL) return; if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) if (NULL == n->prev) return; print_paragraph(h); } void html_man(void *arg, const struct roff_man *man) { - struct mhtml mh; struct html *h; struct tag *t; - memset(&mh, 0, sizeof(mh)); h = (struct html *)arg; if ((h->oflags & HTML_FRAGMENT) == 0) { print_gen_decls(h); print_otag(h, TAG_HTML, ""); t = print_otag(h, TAG_HEAD, ""); - print_man_head(&man->meta, man->first, &mh, h); + print_man_head(&man->meta, man->first, h); print_tagq(h, t); print_otag(h, TAG_BODY, ""); } - man_root_pre(&man->meta, man->first, &mh, h); + man_root_pre(&man->meta, man->first, h); t = print_otag(h, TAG_DIV, "c", "manual-text"); - print_man_nodelist(&man->meta, man->first->child, &mh, h); + print_man_nodelist(&man->meta, man->first->child, h); print_tagq(h, t); - man_root_post(&man->meta, man->first, &mh, h); + man_root_post(&man->meta, man->first, h); print_tagq(h, NULL); } static void print_man_head(MAN_ARGS) { char *cp; print_gen_head(h); mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec); print_otag(h, TAG_TITLE, ""); print_text(h, cp); free(cp); } static void print_man_nodelist(MAN_ARGS) { while (n != NULL) { - print_man_node(man, n, mh, h); + print_man_node(man, n, h); n = n->next; } } static void print_man_node(MAN_ARGS) { - int child; + static int want_fillmode = MAN_fi; + static int save_fillmode; + struct tag *t; + int child; - child = 1; - t = h->tags.head; + /* + * Handle fill mode switch requests up front, + * they would just cause trouble in the subsequent code. + */ + switch (n->tok) { + case MAN_nf: + case MAN_EX: + want_fillmode = MAN_nf; + return; + case MAN_fi: + case MAN_EE: + want_fillmode = MAN_fi; + if (fillmode(h, 0) == MAN_fi) + print_otag(h, TAG_BR, ""); + return; + default: + break; + } + + /* Set up fill mode for the upcoming node. */ + switch (n->type) { - case ROFFT_TEXT: - if ('\0' == *n->string) { - print_paragraph(h); - return; + case ROFFT_BLOCK: + save_fillmode = 0; + /* Some block macros suspend or cancel .nf. */ + switch (n->tok) { + case MAN_TP: /* Tagged paragraphs */ + case MAN_IP: /* temporarily disable .nf */ + case MAN_HP: /* for the head. */ + save_fillmode = want_fillmode; + /* FALLTHROUGH */ + case MAN_SH: /* Section headers */ + case MAN_SS: /* permanently cancel .nf. */ + want_fillmode = MAN_fi; + /* FALLTHROUGH */ + case MAN_PP: /* These have no head. */ + case MAN_LP: /* They will simply */ + case MAN_P: /* reopen .nf in the body. */ + case MAN_RS: + case MAN_UR: + fillmode(h, MAN_fi); + break; + default: + break; } - if (n->flags & NODE_LINE && (*n->string == ' ' || - (n->prev != NULL && mh->fl & MANH_LITERAL && - ! (h->flags & HTML_NONEWLINE)))) + break; + case ROFFT_TBL: + fillmode(h, MAN_fi); + break; + case ROFFT_ELEM: + /* + * Some in-line macros produce tags and/or text + * in the handler, so they require fill mode to be + * configured up front just like for text nodes. + * For the others, keep the traditional approach + * of doing the same, for now. + */ + fillmode(h, want_fillmode); + break; + case ROFFT_TEXT: + if (fillmode(h, want_fillmode) == MAN_fi && + want_fillmode == MAN_fi && + n->flags & NODE_LINE && *n->string == ' ') print_otag(h, TAG_BR, ""); - print_text(h, n->string); + if (*n->string != '\0') + break; + print_paragraph(h); return; + default: + break; + } + + /* Produce output for this node. */ + + child = 1; + switch (n->type) { + case ROFFT_TEXT: + t = h->tag; + print_text(h, n->string); + break; case ROFFT_EQN: + t = h->tag; print_eqn(h, n->eqn); break; case ROFFT_TBL: /* * This will take care of initialising all of the table * state data for the first table, then tearing it down * for the last one. */ print_tbl(h, n->span); return; default: /* * Close out scope of font prior to opening a macro * scope. */ if (HTMLFONT_NONE != h->metac) { h->metal = h->metac; h->metac = HTMLFONT_NONE; } /* * Close out the current table, if it's open, and unset * the "meta" table state. This will be reopened on the * next table element. */ - if (h->tblt) { + if (h->tblt) print_tblclose(h); - t = h->tags.head; - } + + t = h->tag; if (mans[n->tok].pre) - child = (*mans[n->tok].pre)(man, n, mh, h); + child = (*mans[n->tok].pre)(man, n, h); + + /* Some block macros resume .nf in the body. */ + if (save_fillmode && n->type == ROFFT_BODY) + want_fillmode = save_fillmode; + break; } if (child && n->child) - print_man_nodelist(man, n->child, mh, h); + print_man_nodelist(man, n->child, h); /* This will automatically close out any font scope. */ print_stagq(h, t); - switch (n->type) { - case ROFFT_EQN: - break; - default: - if (mans[n->tok].post) - (*mans[n->tok].post)(man, n, mh, h); - break; + if (fillmode(h, 0) == MAN_nf && + n->next != NULL && n->next->flags & NODE_LINE) + print_endline(h); +} + +/* + * MAN_nf switches to no-fill mode, MAN_fi to fill mode. + * Other arguments do not switch. + * The old mode is returned. + */ +static int +fillmode(struct html *h, int want) +{ + struct tag *pre; + int had; + + for (pre = h->tag; pre != NULL; pre = pre->next) + if (pre->tag == TAG_PRE) + break; + + had = pre == NULL ? MAN_fi : MAN_nf; + + if (want && want != had) { + if (want == MAN_nf) + print_otag(h, TAG_PRE, ""); + else + print_tagq(h, pre); } + return had; } static int a2width(const struct roff_node *n, struct roffsu *su) { if (n->type != ROFFT_TEXT) return 0; if (a2roffsu(n->string, su, SCALE_EN)) return 1; return 0; } static void man_root_pre(MAN_ARGS) { struct tag *t, *tt; char *title; assert(man->title); assert(man->msec); mandoc_asprintf(&title, "%s(%s)", man->title, man->msec); t = print_otag(h, TAG_TABLE, "c", "head"); - print_otag(h, TAG_TBODY, ""); tt = print_otag(h, TAG_TR, ""); print_otag(h, TAG_TD, "c", "head-ltitle"); print_text(h, title); print_stagq(h, tt); print_otag(h, TAG_TD, "c", "head-vol"); if (NULL != man->vol) print_text(h, man->vol); print_stagq(h, tt); print_otag(h, TAG_TD, "c", "head-rtitle"); print_text(h, title); print_tagq(h, t); free(title); } static void man_root_post(MAN_ARGS) { struct tag *t, *tt; t = print_otag(h, TAG_TABLE, "c", "foot"); tt = print_otag(h, TAG_TR, ""); print_otag(h, TAG_TD, "c", "foot-date"); print_text(h, man->date); print_stagq(h, tt); print_otag(h, TAG_TD, "c", "foot-os"); if (man->os) print_text(h, man->os); print_tagq(h, t); } static int man_br_pre(MAN_ARGS) { struct roffsu su; SCALE_VS_INIT(&su, 1); if (MAN_sp == n->tok) { if (NULL != (n = n->child)) if ( ! a2roffsu(n->string, &su, SCALE_VS)) su.scale = 1.0; } else su.scale = 0.0; print_otag(h, TAG_DIV, "suh", &su); /* So the div isn't empty: */ print_text(h, "\\~"); return 0; } static int man_SH_pre(MAN_ARGS) { - if (n->type == ROFFT_BLOCK) { - mh->fl &= ~MANH_LITERAL; - return 1; - } else if (n->type == ROFFT_BODY) - return 1; - - print_otag(h, TAG_H1, "c", "Sh"); + if (n->type == ROFFT_HEAD) + print_otag(h, TAG_H1, "c", "Sh"); return 1; } static int man_alt_pre(MAN_ARGS) { const struct roff_node *nn; - int i, savelit; + int i; enum htmltag fp; struct tag *t; - if ((savelit = mh->fl & MANH_LITERAL)) - print_otag(h, TAG_BR, ""); - - mh->fl &= ~MANH_LITERAL; - for (i = 0, nn = n->child; nn; nn = nn->next, i++) { - t = NULL; switch (n->tok) { case MAN_BI: fp = i % 2 ? TAG_I : TAG_B; break; case MAN_IB: fp = i % 2 ? TAG_B : TAG_I; break; case MAN_RI: fp = i % 2 ? TAG_I : TAG_MAX; break; case MAN_IR: fp = i % 2 ? TAG_MAX : TAG_I; break; case MAN_BR: fp = i % 2 ? TAG_MAX : TAG_B; break; case MAN_RB: fp = i % 2 ? TAG_B : TAG_MAX; break; default: abort(); } if (i) h->flags |= HTML_NOSPACE; - if (TAG_MAX != fp) + if (fp != TAG_MAX) t = print_otag(h, fp, ""); - print_man_node(man, nn, mh, h); + print_text(h, nn->string); - if (t) + if (fp != TAG_MAX) print_tagq(h, t); } - - if (savelit) - mh->fl |= MANH_LITERAL; - return 0; } static int man_SM_pre(MAN_ARGS) { print_otag(h, TAG_SMALL, ""); if (MAN_SB == n->tok) print_otag(h, TAG_B, ""); return 1; } static int man_SS_pre(MAN_ARGS) { - if (n->type == ROFFT_BLOCK) { - mh->fl &= ~MANH_LITERAL; - return 1; - } else if (n->type == ROFFT_BODY) - return 1; - - print_otag(h, TAG_H2, "c", "Ss"); + if (n->type == ROFFT_HEAD) + print_otag(h, TAG_H2, "c", "Ss"); return 1; } static int man_PP_pre(MAN_ARGS) { if (n->type == ROFFT_HEAD) return 0; else if (n->type == ROFFT_BLOCK) print_bvspace(h, n); return 1; } static int man_IP_pre(MAN_ARGS) { const struct roff_node *nn; if (n->type == ROFFT_BODY) { print_otag(h, TAG_DD, "c", "It-tag"); return 1; } else if (n->type != ROFFT_HEAD) { print_otag(h, TAG_DL, "c", "Bl-tag"); return 1; } /* FIXME: width specification. */ print_otag(h, TAG_DT, "c", "It-tag"); /* For IP, only print the first header element. */ if (MAN_IP == n->tok && n->child) - print_man_node(man, n->child, mh, h); + print_man_node(man, n->child, h); /* For TP, only print next-line header elements. */ if (MAN_TP == n->tok) { nn = n->child; while (NULL != nn && 0 == (NODE_LINE & nn->flags)) nn = nn->next; while (NULL != nn) { - print_man_node(man, nn, mh, h); + print_man_node(man, nn, h); nn = nn->next; } } return 0; } static int man_HP_pre(MAN_ARGS) { struct roffsu sum, sui; const struct roff_node *np; if (n->type == ROFFT_HEAD) return 0; else if (n->type != ROFFT_BLOCK) return 1; np = n->head->child; if (np == NULL || !a2width(np, &sum)) SCALE_HS_INIT(&sum, INDENT); sui.unit = sum.unit; sui.scale = -sum.scale; print_bvspace(h, n); print_otag(h, TAG_DIV, "csului", "Pp", &sum, &sui); return 1; } static int man_OP_pre(MAN_ARGS) { struct tag *tt; print_text(h, "["); h->flags |= HTML_NOSPACE; tt = print_otag(h, TAG_SPAN, "c", "Op"); if (NULL != (n = n->child)) { print_otag(h, TAG_B, ""); print_text(h, n->string); } print_stagq(h, tt); if (NULL != n && NULL != n->next) { print_otag(h, TAG_I, ""); print_text(h, n->next->string); } print_stagq(h, tt); h->flags |= HTML_NOSPACE; print_text(h, "]"); return 0; } static int man_B_pre(MAN_ARGS) { print_otag(h, TAG_B, ""); return 1; } static int man_I_pre(MAN_ARGS) { print_otag(h, TAG_I, ""); return 1; } static int -man_literal_pre(MAN_ARGS) -{ - - if (MAN_fi == n->tok || MAN_EE == n->tok) { - print_otag(h, TAG_BR, ""); - mh->fl &= ~MANH_LITERAL; - } else - mh->fl |= MANH_LITERAL; - - return 0; -} - -static int man_in_pre(MAN_ARGS) { print_otag(h, TAG_BR, ""); return 0; } static int man_ign_pre(MAN_ARGS) { return 0; } static int man_RS_pre(MAN_ARGS) { struct roffsu su; if (n->type == ROFFT_HEAD) return 0; else if (n->type == ROFFT_BODY) return 1; SCALE_HS_INIT(&su, INDENT); if (n->head->child) a2width(n->head->child, &su); print_otag(h, TAG_DIV, "sul", &su); return 1; } static int man_UR_pre(MAN_ARGS) { n = n->child; assert(n->type == ROFFT_HEAD); if (n->child != NULL) { assert(n->child->type == ROFFT_TEXT); print_otag(h, TAG_A, "ch", "Lk", n->child->string); } assert(n->next->type == ROFFT_BODY); if (n->next->child != NULL) n = n->next; - print_man_nodelist(man, n->child, mh, h); + print_man_nodelist(man, n->child, h); return 0; } Index: vendor/mdocml/dist/man_term.c =================================================================== --- vendor/mdocml/dist/man_term.c (revision 313955) +++ vendor/mdocml/dist/man_term.c (revision 313956) @@ -1,1177 +1,1181 @@ -/* $Id: man_term.c,v 1.188 2017/01/10 13:47:00 schwarze Exp $ */ +/* $Id: man_term.c,v 1.191 2017/02/15 14:10:08 schwarze Exp $ */ /* * Copyright (c) 2008-2012 Kristaps Dzonsons - * Copyright (c) 2010-2015 Ingo Schwarze + * Copyright (c) 2010-2015, 2017 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. */ #include "config.h" #include #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc.h" #include "roff.h" #include "man.h" #include "out.h" #include "term.h" #include "main.h" #define MAXMARGINS 64 /* maximum number of indented scopes */ struct mtermp { int fl; #define MANT_LITERAL (1 << 0) int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */ int lmargincur; /* index of current margin */ int lmarginsz; /* actual number of nested margins */ size_t offset; /* default offset to visible page */ int pardist; /* vert. space before par., unit: [v] */ }; #define DECL_ARGS struct termp *p, \ struct mtermp *mt, \ struct roff_node *n, \ const struct roff_meta *meta struct termact { int (*pre)(DECL_ARGS); void (*post)(DECL_ARGS); int flags; #define MAN_NOTEXT (1 << 0) /* Never has text children. */ }; static void print_man_nodelist(DECL_ARGS); static void print_man_node(DECL_ARGS); static void print_man_head(struct termp *, const struct roff_meta *); static void print_man_foot(struct termp *, const struct roff_meta *); static void print_bvspace(struct termp *, const struct roff_node *, int); static int pre_B(DECL_ARGS); static int pre_HP(DECL_ARGS); static int pre_I(DECL_ARGS); static int pre_IP(DECL_ARGS); static int pre_OP(DECL_ARGS); static int pre_PD(DECL_ARGS); static int pre_PP(DECL_ARGS); static int pre_RS(DECL_ARGS); static int pre_SH(DECL_ARGS); static int pre_SS(DECL_ARGS); static int pre_TP(DECL_ARGS); static int pre_UR(DECL_ARGS); static int pre_alternate(DECL_ARGS); static int pre_ft(DECL_ARGS); static int pre_ign(DECL_ARGS); static int pre_in(DECL_ARGS); static int pre_literal(DECL_ARGS); static int pre_ll(DECL_ARGS); static int pre_sp(DECL_ARGS); static void post_IP(DECL_ARGS); static void post_HP(DECL_ARGS); static void post_RS(DECL_ARGS); static void post_SH(DECL_ARGS); static void post_SS(DECL_ARGS); static void post_TP(DECL_ARGS); static void post_UR(DECL_ARGS); static const struct termact termacts[MAN_MAX] = { { pre_sp, NULL, MAN_NOTEXT }, /* br */ { NULL, NULL, 0 }, /* TH */ { pre_SH, post_SH, 0 }, /* SH */ { pre_SS, post_SS, 0 }, /* SS */ { pre_TP, post_TP, 0 }, /* TP */ { pre_PP, NULL, 0 }, /* LP */ { pre_PP, NULL, 0 }, /* PP */ { pre_PP, NULL, 0 }, /* P */ { pre_IP, post_IP, 0 }, /* IP */ { pre_HP, post_HP, 0 }, /* HP */ { NULL, NULL, 0 }, /* SM */ { pre_B, NULL, 0 }, /* SB */ { 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_alternate, NULL, 0 }, /* IR */ { pre_alternate, NULL, 0 }, /* RI */ { pre_sp, NULL, MAN_NOTEXT }, /* sp */ { pre_literal, NULL, 0 }, /* nf */ { pre_literal, NULL, 0 }, /* fi */ { NULL, NULL, 0 }, /* RE */ { pre_RS, post_RS, 0 }, /* RS */ { pre_ign, NULL, 0 }, /* DT */ { pre_ign, NULL, MAN_NOTEXT }, /* UC */ { pre_PD, NULL, MAN_NOTEXT }, /* PD */ { pre_ign, NULL, 0 }, /* AT */ { pre_in, NULL, MAN_NOTEXT }, /* in */ { pre_ft, NULL, MAN_NOTEXT }, /* ft */ { pre_OP, NULL, 0 }, /* OP */ { pre_literal, NULL, 0 }, /* EX */ { pre_literal, NULL, 0 }, /* EE */ { pre_UR, post_UR, 0 }, /* UR */ { NULL, NULL, 0 }, /* UE */ { pre_ll, NULL, MAN_NOTEXT }, /* ll */ }; void terminal_man(void *arg, const struct roff_man *man) { struct termp *p; struct roff_node *n; struct mtermp mt; + size_t save_defindent; p = (struct termp *)arg; p->overstep = 0; p->rmargin = p->maxrmargin = p->defrmargin; p->tabwidth = term_len(p, 5); memset(&mt, 0, sizeof(struct mtermp)); mt.lmargin[mt.lmargincur] = term_len(p, p->defindent); mt.offset = term_len(p, p->defindent); mt.pardist = 1; n = man->first->child; if (p->synopsisonly) { while (n != NULL) { if (n->tok == MAN_SH && n->child->child->type == ROFFT_TEXT && !strcmp(n->child->child->string, "SYNOPSIS")) { if (n->child->next->child != NULL) print_man_nodelist(p, &mt, n->child->next->child, &man->meta); term_newln(p); break; } n = n->next; } } else { + save_defindent = p->defindent; if (p->defindent == 0) p->defindent = 7; term_begin(p, print_man_head, print_man_foot, &man->meta); p->flags |= TERMP_NOSPACE; if (n != NULL) print_man_nodelist(p, &mt, n, &man->meta); term_end(p); + p->defindent = save_defindent; } } /* * Printing leading vertical space before a block. * This is used for the paragraph macros. * The rules are pretty simple, since there's very little nesting going * on here. Basically, if we're the first within another block (SS/SH), * then don't emit vertical space. If we are (RS), then do. If not the * first, print it. */ static void print_bvspace(struct termp *p, const struct roff_node *n, int pardist) { int i; term_newln(p); if (n->body && n->body->child) if (n->body->child->type == ROFFT_TBL) return; if (n->parent->type == ROFFT_ROOT || n->parent->tok != MAN_RS) if (NULL == n->prev) return; for (i = 0; i < pardist; i++) term_vspace(p); } static int pre_ign(DECL_ARGS) { return 0; } static int pre_ll(DECL_ARGS) { term_setwidth(p, n->child != NULL ? n->child->string : NULL); return 0; } static int pre_I(DECL_ARGS) { term_fontrepl(p, TERMFONT_UNDER); return 1; } static int pre_literal(DECL_ARGS) { term_newln(p); if (MAN_nf == n->tok || MAN_EX == n->tok) mt->fl |= MANT_LITERAL; else mt->fl &= ~MANT_LITERAL; /* * Unlike .IP and .TP, .HP does not have a HEAD. * So in case a second call to term_flushln() is needed, * indentation has to be set up explicitly. */ if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) { p->offset = p->rmargin; p->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); p->flags |= TERMP_NOSPACE; } return 0; } static int pre_PD(DECL_ARGS) { struct roffsu su; n = n->child; if (n == NULL) { mt->pardist = 1; return 0; } assert(n->type == ROFFT_TEXT); if (a2roffsu(n->string, &su, SCALE_VS)) mt->pardist = term_vspan(p, &su); return 0; } static int pre_alternate(DECL_ARGS) { enum termfont font[2]; struct roff_node *nn; int savelit, i; 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; assert(nn->type == ROFFT_TEXT); term_word(p, nn->string); if (nn->flags & NODE_EOS) p->flags |= TERMP_SENTENCE; if (nn->next) p->flags |= TERMP_NOSPACE; } return 0; } static int pre_B(DECL_ARGS) { term_fontrepl(p, TERMFONT_BOLD); return 1; } static int pre_OP(DECL_ARGS) { term_word(p, "["); p->flags |= TERMP_NOSPACE; if (NULL != (n = n->child)) { term_fontrepl(p, TERMFONT_BOLD); term_word(p, n->string); } if (NULL != n && NULL != n->next) { term_fontrepl(p, TERMFONT_UNDER); term_word(p, n->next->string); } term_fontrepl(p, TERMFONT_NONE); p->flags |= TERMP_NOSPACE; term_word(p, "]"); return 0; } static int pre_ft(DECL_ARGS) { const char *cp; if (NULL == n->child) { term_fontlast(p); return 0; } cp = n->child->string; switch (*cp) { case '4': case '3': case 'B': term_fontrepl(p, TERMFONT_BOLD); break; case '2': case 'I': term_fontrepl(p, TERMFONT_UNDER); break; case 'P': term_fontlast(p); break; case '1': case 'C': case 'R': term_fontrepl(p, TERMFONT_NONE); break; default: break; } return 0; } static int pre_in(DECL_ARGS) { struct roffsu su; const char *cp; size_t v; int less; term_newln(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 ( ! a2roffsu(++cp, &su, SCALE_EN)) return 0; v = (term_hspan(p, &su) + 11) / 24; if (less < 0) p->offset -= p->offset > v ? v : p->offset; else if (less > 0) p->offset += v; else p->offset = v; if (p->offset > SHRT_MAX) p->offset = term_len(p, p->defindent); return 0; } static int pre_sp(DECL_ARGS) { struct roffsu su; int i, len; if ((NULL == n->prev && n->parent)) { switch (n->parent->tok) { case MAN_SH: case MAN_SS: case MAN_PP: case MAN_LP: case MAN_P: return 0; default: break; } } if (n->tok == MAN_br) len = 0; else if (n->child == NULL) len = 1; else { if ( ! a2roffsu(n->child->string, &su, SCALE_VS)) su.scale = 1.0; len = term_vspan(p, &su); } if (len == 0) term_newln(p); else if (len < 0) p->skipvsp -= len; else for (i = 0; i < len; i++) term_vspace(p); /* * Handle an explicit break request in the same way * as an overflowing line. */ if (p->flags & TERMP_BRIND) { p->offset = p->rmargin; p->rmargin = p->maxrmargin; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); } return 0; } static int pre_HP(DECL_ARGS) { struct roffsu su; const struct roff_node *nn; int len; switch (n->type) { case ROFFT_BLOCK: print_bvspace(p, n, mt->pardist); return 1; case ROFFT_BODY: break; default: return 0; } if ( ! (MANT_LITERAL & mt->fl)) { p->flags |= TERMP_NOBREAK | TERMP_BRIND; p->trailspace = 2; } /* Calculate offset. */ if ((nn = n->parent->head->child) != NULL && a2roffsu(nn->string, &su, SCALE_EN)) { len = term_hspan(p, &su) / 24; if (len < 0 && (size_t)(-len) > mt->offset) len = -mt->offset; else if (len > SHRT_MAX) len = term_len(p, p->defindent); mt->lmargin[mt->lmargincur] = len; } else len = mt->lmargin[mt->lmargincur]; p->offset = mt->offset; p->rmargin = mt->offset + len; return 1; } static void post_HP(DECL_ARGS) { switch (n->type) { case ROFFT_BODY: term_newln(p); /* * Compatibility with a groff bug. * The .HP macro uses the undocumented .tag request * which causes a line break and cancels no-space * mode even if there isn't any output. */ if (n->child == NULL) term_vspace(p); p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); p->trailspace = 0; p->offset = mt->offset; p->rmargin = p->maxrmargin; break; default: break; } } static int pre_PP(DECL_ARGS) { switch (n->type) { case ROFFT_BLOCK: mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); print_bvspace(p, n, mt->pardist); break; default: p->offset = mt->offset; break; } return n->type != ROFFT_HEAD; } static int pre_IP(DECL_ARGS) { struct roffsu su; const struct roff_node *nn; int len, savelit; switch (n->type) { case ROFFT_BODY: p->flags |= TERMP_NOSPACE; break; case ROFFT_HEAD: p->flags |= TERMP_NOBREAK; p->trailspace = 1; break; case ROFFT_BLOCK: print_bvspace(p, n, mt->pardist); /* FALLTHROUGH */ default: return 1; } /* Calculate the offset from the optional second argument. */ if ((nn = n->parent->head->child) != NULL && (nn = nn->next) != NULL && a2roffsu(nn->string, &su, SCALE_EN)) { len = term_hspan(p, &su) / 24; if (len < 0 && (size_t)(-len) > mt->offset) len = -mt->offset; else if (len > SHRT_MAX) len = term_len(p, p->defindent); mt->lmargin[mt->lmargincur] = len; } else len = mt->lmargin[mt->lmargincur]; switch (n->type) { case ROFFT_HEAD: p->offset = mt->offset; p->rmargin = mt->offset + len; savelit = MANT_LITERAL & mt->fl; mt->fl &= ~MANT_LITERAL; if (n->child) print_man_node(p, mt, n->child, meta); if (savelit) mt->fl |= MANT_LITERAL; return 0; case ROFFT_BODY: p->offset = mt->offset + len; p->rmargin = p->maxrmargin; break; default: break; } return 1; } static void post_IP(DECL_ARGS) { switch (n->type) { case ROFFT_HEAD: term_flushln(p); p->flags &= ~TERMP_NOBREAK; p->trailspace = 0; p->rmargin = p->maxrmargin; break; case ROFFT_BODY: term_newln(p); p->offset = mt->offset; break; default: break; } } static int pre_TP(DECL_ARGS) { struct roffsu su; struct roff_node *nn; int len, savelit; switch (n->type) { case ROFFT_HEAD: p->flags |= TERMP_NOBREAK | TERMP_BRTRSP; p->trailspace = 1; break; case ROFFT_BODY: p->flags |= TERMP_NOSPACE; break; case ROFFT_BLOCK: print_bvspace(p, n, mt->pardist); /* FALLTHROUGH */ default: return 1; } /* Calculate offset. */ if ((nn = n->parent->head->child) != NULL && nn->string != NULL && ! (NODE_LINE & nn->flags) && a2roffsu(nn->string, &su, SCALE_EN)) { len = term_hspan(p, &su) / 24; if (len < 0 && (size_t)(-len) > mt->offset) len = -mt->offset; else if (len > SHRT_MAX) len = term_len(p, p->defindent); mt->lmargin[mt->lmargincur] = len; } else len = mt->lmargin[mt->lmargincur]; switch (n->type) { case ROFFT_HEAD: p->offset = mt->offset; p->rmargin = mt->offset + len; savelit = MANT_LITERAL & mt->fl; mt->fl &= ~MANT_LITERAL; /* Don't print same-line elements. */ nn = n->child; while (NULL != nn && 0 == (NODE_LINE & nn->flags)) nn = nn->next; while (NULL != nn) { print_man_node(p, mt, nn, meta); nn = nn->next; } if (savelit) mt->fl |= MANT_LITERAL; return 0; case ROFFT_BODY: p->offset = mt->offset + len; p->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP); break; default: break; } return 1; } static void post_TP(DECL_ARGS) { switch (n->type) { case ROFFT_HEAD: term_flushln(p); break; case ROFFT_BODY: term_newln(p); p->offset = mt->offset; break; default: break; } } static int pre_SS(DECL_ARGS) { int i; switch (n->type) { case ROFFT_BLOCK: mt->fl &= ~MANT_LITERAL; mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); mt->offset = term_len(p, p->defindent); /* * No vertical space before the first subsection * and after an empty subsection. */ do { n = n->prev; } while (n != NULL && n->tok != TOKEN_NONE && termacts[n->tok].flags & MAN_NOTEXT); if (n == NULL || (n->tok == MAN_SS && n->body->child == NULL)) break; for (i = 0; i < mt->pardist; i++) term_vspace(p); break; case ROFFT_HEAD: term_fontrepl(p, TERMFONT_BOLD); p->offset = term_len(p, 3); p->rmargin = mt->offset; p->trailspace = mt->offset; p->flags |= TERMP_NOBREAK | TERMP_BRIND; break; case ROFFT_BODY: p->offset = mt->offset; p->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); break; default: break; } return 1; } static void post_SS(DECL_ARGS) { switch (n->type) { case ROFFT_HEAD: term_newln(p); break; case ROFFT_BODY: term_newln(p); break; default: break; } } static int pre_SH(DECL_ARGS) { int i; switch (n->type) { case ROFFT_BLOCK: mt->fl &= ~MANT_LITERAL; mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); mt->offset = term_len(p, p->defindent); /* * No vertical space before the first section * and after an empty section. */ do { n = n->prev; - } while (n != NULL && termacts[n->tok].flags & MAN_NOTEXT); + } while (n != NULL && n->tok != TOKEN_NONE && + termacts[n->tok].flags & MAN_NOTEXT); if (n == NULL || (n->tok == MAN_SH && n->body->child == NULL)) break; for (i = 0; i < mt->pardist; i++) term_vspace(p); break; case ROFFT_HEAD: term_fontrepl(p, TERMFONT_BOLD); p->offset = 0; p->rmargin = mt->offset; p->trailspace = mt->offset; p->flags |= TERMP_NOBREAK | TERMP_BRIND; break; case ROFFT_BODY: p->offset = mt->offset; p->rmargin = p->maxrmargin; p->trailspace = 0; p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND); break; default: break; } return 1; } static void post_SH(DECL_ARGS) { switch (n->type) { case ROFFT_HEAD: term_newln(p); break; case ROFFT_BODY: term_newln(p); break; default: break; } } static int pre_RS(DECL_ARGS) { struct roffsu su; switch (n->type) { case ROFFT_BLOCK: term_newln(p); return 1; case ROFFT_HEAD: return 0; default: break; } n = n->parent->head; n->aux = SHRT_MAX + 1; if (n->child == NULL) n->aux = mt->lmargin[mt->lmargincur]; else if (a2roffsu(n->child->string, &su, SCALE_EN)) n->aux = term_hspan(p, &su) / 24; if (n->aux < 0 && (size_t)(-n->aux) > mt->offset) n->aux = -mt->offset; else if (n->aux > SHRT_MAX) n->aux = term_len(p, p->defindent); mt->offset += n->aux; p->offset = mt->offset; p->rmargin = p->maxrmargin; if (++mt->lmarginsz < MAXMARGINS) mt->lmargincur = mt->lmarginsz; mt->lmargin[mt->lmargincur] = term_len(p, p->defindent); return 1; } static void post_RS(DECL_ARGS) { switch (n->type) { case ROFFT_BLOCK: return; case ROFFT_HEAD: return; default: term_newln(p); break; } mt->offset -= n->parent->head->aux; p->offset = mt->offset; if (--mt->lmarginsz < MAXMARGINS) mt->lmargincur = mt->lmarginsz; } static int pre_UR(DECL_ARGS) { return n->type != ROFFT_HEAD; } static void post_UR(DECL_ARGS) { if (n->type != ROFFT_BLOCK) return; term_word(p, "<"); p->flags |= TERMP_NOSPACE; if (NULL != n->child->child) print_man_node(p, mt, n->child->child, meta); p->flags |= TERMP_NOSPACE; term_word(p, ">"); } static void print_man_node(DECL_ARGS) { size_t rm, rmax; int c; switch (n->type) { case ROFFT_TEXT: /* * If we have a blank line, output a vertical space. * If we have a space as the first character, break * before printing the line's data. */ if ('\0' == *n->string) { term_vspace(p); return; } else if (' ' == *n->string && NODE_LINE & n->flags) term_newln(p); term_word(p, n->string); goto out; case ROFFT_EQN: if ( ! (n->flags & NODE_LINE)) p->flags |= TERMP_NOSPACE; term_eqn(p, n->eqn); if (n->next != NULL && ! (n->next->flags & NODE_LINE)) p->flags |= TERMP_NOSPACE; return; case ROFFT_TBL: if (p->tbl.cols == NULL) term_vspace(p); term_tbl(p, n->span); return; default: break; } if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) term_fontrepl(p, TERMFONT_NONE); c = 1; if (termacts[n->tok].pre) c = (*termacts[n->tok].pre)(p, mt, n, meta); if (c && n->child) print_man_nodelist(p, mt, n->child, meta); if (termacts[n->tok].post) (*termacts[n->tok].post)(p, mt, n, meta); if ( ! (MAN_NOTEXT & termacts[n->tok].flags)) term_fontrepl(p, TERMFONT_NONE); out: /* * If we're in a literal context, make sure that words * together on the same line stay together. This is a * POST-printing call, so we check the NEXT word. Since * -man doesn't have nested macros, we don't need to be * more specific than this. */ if (mt->fl & MANT_LITERAL && ! (p->flags & (TERMP_NOBREAK | TERMP_NONEWLINE)) && (n->next == NULL || n->next->flags & NODE_LINE)) { rm = p->rmargin; rmax = p->maxrmargin; p->rmargin = p->maxrmargin = TERM_MAXMARGIN; p->flags |= TERMP_NOSPACE; if (n->string != NULL && *n->string != '\0') term_flushln(p); else term_newln(p); if (rm < rmax && n->parent->tok == MAN_HP) { p->offset = rm; p->rmargin = rmax; } else p->rmargin = rm; p->maxrmargin = rmax; } if (NODE_EOS & n->flags) p->flags |= TERMP_SENTENCE; } static void print_man_nodelist(DECL_ARGS) { while (n != NULL) { print_man_node(p, mt, n, meta); n = n->next; } } static void print_man_foot(struct termp *p, const struct roff_meta *meta) { char *title; size_t datelen, titlen; assert(meta->title); assert(meta->msec); assert(meta->date); term_fontrepl(p, TERMFONT_NONE); if (meta->hasbody) term_vspace(p); /* * Temporary, undocumented option to imitate mdoc(7) output. * In the bottom right corner, use the operating system * instead of the title. */ if ( ! p->mdocstyle) { if (meta->hasbody) { term_vspace(p); term_vspace(p); } mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); } else if (meta->os) { title = mandoc_strdup(meta->os); } else { title = mandoc_strdup(""); } datelen = term_strlen(p, meta->date); /* Bottom left corner: operating system. */ p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; p->trailspace = 1; p->offset = 0; p->rmargin = p->maxrmargin > datelen ? (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0; if (meta->os) term_word(p, meta->os); term_flushln(p); /* At the bottom in the middle: manual date. */ p->offset = p->rmargin; titlen = term_strlen(p, title); p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0; p->flags |= TERMP_NOSPACE; term_word(p, meta->date); term_flushln(p); /* Bottom right corner: manual title and section. */ p->flags &= ~TERMP_NOBREAK; p->flags |= TERMP_NOSPACE; p->trailspace = 0; p->offset = p->rmargin; p->rmargin = p->maxrmargin; term_word(p, title); term_flushln(p); free(title); } static void print_man_head(struct termp *p, const struct roff_meta *meta) { const char *volume; char *title; size_t vollen, titlen; assert(meta->title); assert(meta->msec); volume = NULL == meta->vol ? "" : meta->vol; vollen = term_strlen(p, volume); /* Top left corner: manual title and section. */ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec); titlen = term_strlen(p, title); p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; p->trailspace = 1; p->offset = 0; p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? (p->maxrmargin - vollen + term_len(p, 1)) / 2 : vollen < p->maxrmargin ? p->maxrmargin - vollen : 0; term_word(p, title); term_flushln(p); /* At the top in the middle: manual volume. */ p->flags |= TERMP_NOSPACE; p->offset = p->rmargin; p->rmargin = p->offset + vollen + titlen < p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin; term_word(p, volume); term_flushln(p); /* Top right corner: title and section, again. */ p->flags &= ~TERMP_NOBREAK; p->trailspace = 0; if (p->rmargin + titlen <= p->maxrmargin) { p->flags |= TERMP_NOSPACE; p->offset = p->rmargin; p->rmargin = p->maxrmargin; term_word(p, title); term_flushln(p); } p->flags &= ~TERMP_NOSPACE; p->offset = 0; p->rmargin = p->maxrmargin; /* * Groff prints three blank lines before the content. * Do the same, except in the temporary, undocumented * mode imitating mdoc(7) output. */ term_vspace(p); if ( ! p->mdocstyle) { term_vspace(p); term_vspace(p); } free(title); } Index: vendor/mdocml/dist/manconf.h =================================================================== --- vendor/mdocml/dist/manconf.h (revision 313955) +++ vendor/mdocml/dist/manconf.h (revision 313956) @@ -1,48 +1,49 @@ /* $OpenBSD$ */ /* * Copyright (c) 2011, 2015 Ingo Schwarze * Copyright (c) 2011 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE 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. */ /* List of unique, absolute paths to manual trees. */ struct manpaths { char **paths; size_t sz; }; /* Data from -O options and man.conf(5) output directives. */ struct manoutput { char *includes; char *man; char *paper; char *style; size_t indent; size_t width; int fragment; int mdoc; int synopsisonly; + int noval; }; struct manconf { struct manoutput output; struct manpaths manpath; }; void manconf_parse(struct manconf *, const char *, char *, char *); -void manconf_output(struct manoutput *, const char *); +int manconf_output(struct manoutput *, const char *, int); void manconf_free(struct manconf *); Index: vendor/mdocml/dist/mandoc.1 =================================================================== --- vendor/mdocml/dist/mandoc.1 (revision 313955) +++ vendor/mdocml/dist/mandoc.1 (revision 313956) @@ -1,1842 +1,1872 @@ -.\" $Id: mandoc.1,v 1.171 2017/01/21 02:32:39 schwarze Exp $ +.\" $Id: mandoc.1,v 1.174 2017/02/10 15:45:28 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons .\" Copyright (c) 2012, 2014-2017 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 21 2017 $ +.Dd $Mdocdate: February 10 2017 $ .Dt MANDOC 1 .Os .Sh NAME .Nm mandoc .Nd format and display UNIX manuals .Sh SYNOPSIS .Nm mandoc .Op Fl acfhkl .Op Fl I Cm os Ns = Ns Ar name .Op Fl K Ar encoding .Op Fl m Ns Ar format .Op Fl O Ar option .Op Fl T Ar output .Op Fl W Ar level .Op Ar .Sh DESCRIPTION The .Nm utility formats .Ux manual pages for display. .Pp By default, .Nm reads .Xr mdoc 7 or .Xr man 7 text from stdin, implying .Fl m Ns Cm andoc , and produces .Fl T Cm locale output. .Pp The options are as follows: .Bl -tag -width Ds .It Fl a If the standard output is a terminal device and .Fl c is not specified, use .Xr more 1 to paginate the output, just like .Xr man 1 would. .It Fl c Copy the formatted manual pages to the standard output without using .Xr more 1 to paginate them. This is the default. It can be specified to override .Fl a . .It Fl f A synonym for .Xr whatis 1 . This overrides any earlier .Fl k and .Fl l options. .It Fl h Display only the SYNOPSIS lines. Implies .Fl c . .It Fl I Cm os Ns = Ns Ar name Override the default operating system .Ar name for the .Xr mdoc 7 .Sq \&Os and for the .Xr man 7 .Sq \&TH macro. .It Fl K Ar encoding Specify the input encoding. The supported .Ar encoding arguments are .Cm us-ascii , .Cm iso-8859-1 , and .Cm utf-8 . If not specified, autodetection uses the first match: .Bl -tag -width iso-8859-1 .It Cm utf-8 if the first three bytes of the input file are the UTF-8 byte order mark (BOM, 0xefbbbf) .It Ar encoding if the first or second line of the input file matches the .Sy emacs mode line format .Pp .D1 .\e" -*- Oo ...; Oc coding: Ar encoding ; No -*- .It Cm utf-8 if the first non-ASCII byte in the file introduces a valid UTF-8 sequence .It Cm iso-8859-1 otherwise .El .It Fl k A synonym for .Xr apropos 1 . This overrides any earlier .Fl f and .Fl l options. .It Fl l A synonym for .Fl a . Also reverts any earlier .Fl f and .Fl k options. .It Fl m Ns Ar format Input format. See .Sx Input Formats for available formats. Defaults to .Fl m Ns Cm andoc . .It Fl O Ar option Comma-separated output options. .It Fl T Ar output Output format. See .Sx Output Formats for available formats. Defaults to .Fl T Cm locale . .It Fl W Ar level Specify the minimum message .Ar level to be reported on the standard error output and to affect the exit status. The .Ar level can be .Cm warning , .Cm error , or .Cm unsupp ; .Cm all is an alias for .Cm warning . By default, .Nm is silent. See .Sx EXIT STATUS and .Sx DIAGNOSTICS for details. .Pp The special option .Fl W Cm stop tells .Nm to exit after parsing a file that causes warnings or errors of at least the requested level. No formatted output will be produced from that file. If both a .Ar level and .Cm stop are requested, they can be joined with a comma, for example .Fl W Cm error , Ns Cm stop . .It Ar file Read input from zero or more files. If unspecified, reads from stdin. If multiple files are specified, .Nm will halt with the first failed parse. .El .Pp In .Fl f and .Fl k mode, .Nm also supports the options .Fl CMmOSsw described in the .Xr apropos 1 manual. .Ss Input Formats The .Nm utility accepts .Xr mdoc 7 and .Xr man 7 input with .Fl m Ns Cm doc and .Fl m Ns Cm an , respectively. The .Xr mdoc 7 format is .Em strongly recommended; .Xr man 7 should only be used for legacy manuals. .Pp A third option, .Fl m Ns Cm andoc , which is also the default, determines encoding on-the-fly: if the first non-comment macro is .Sq \&Dd or .Sq \&Dt , the .Xr mdoc 7 parser is used; otherwise, the .Xr man 7 parser is used. .Pp If multiple files are specified with .Fl m Ns Cm andoc , each has its file-type determined this way. If multiple files are specified and .Fl m Ns Cm doc or .Fl m Ns Cm an is specified, then this format is used exclusively. .Ss Output Formats The .Nm utility accepts the following .Fl T arguments, which correspond to output modes: .Bl -tag -width "-T locale" .It Fl T Cm ascii Produce 7-bit ASCII output. See .Sx ASCII Output . .It Fl T Cm html Produce HTML5, CSS1, and MathML output. See .Sx HTML Output . .It Fl T Cm lint Parse only: produce no output. Implies .Fl W Cm warning . .It Fl T Cm locale Encode output using the current locale. This is the default. See .Sx Locale Output . .It Fl T Cm man Produce .Xr man 7 format output. See .Sx Man Output . .It Fl T Cm pdf Produce PDF output. See .Sx PDF Output . .It Fl T Cm ps Produce PostScript output. See .Sx PostScript Output . .It Fl T Cm tree Produce an indented parse tree. See .Sx Syntax tree output . .It Fl T Cm utf8 Encode output in the UTF\-8 multi-byte format. See .Sx UTF\-8 Output . .It Fl T Cm xhtml This is a synonym for .Fl T Cm html . .El .Pp If multiple input files are specified, these will be processed by the corresponding filter in-order. .Ss ASCII Output Output produced by .Fl T Cm ascii is rendered in standard 7-bit ASCII documented in .Xr ascii 7 . .Pp Font styles are applied by using back-spaced encoding such that an underlined character .Sq c is rendered as .Sq _ Ns \e[bs] Ns c , where .Sq \e[bs] is the back-space character number 8. Emboldened characters are rendered as .Sq c Ns \e[bs] Ns c . .Pp The special characters documented in .Xr mandoc_char 7 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 indent Ns = Ns Ar indent The left margin for normal text is set to .Ar indent blank characters instead of the default of five for .Xr mdoc 7 and seven for .Xr man 7 . Increasing this is not recommended; it may result in degraded formatting, for example overfull lines or ugly line breaks. .It Cm width Ns = Ns Ar width The output width is set to .Ar width , which will normalise to \(>=58. .El .Ss HTML Output Output produced by .Fl T Cm html conforms to HTML5 using optional self-closing tags. Default styles use only CSS1. Equations rendered from .Xr eqn 7 blocks use MathML. .Pp The .Pa mandoc.css file documents style-sheet classes available for customising output. If a style-sheet is not specified with .Fl O Cm style , .Fl T Cm html defaults to simple output (via an embedded style-sheet) 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 fragment Omit the declaration and the , , and elements and only emit the subtree below the element. The .Cm style argument will be ignored. This is useful when embedding manual content within existing documents. .It Cm includes Ns = Ns Ar fmt The string .Ar fmt , for example, .Ar ../src/%I.html , is used as a template for linked header files (usually via the .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 Locale Output Locale-depending output encoding is triggered with .Fl T Cm locale . This is the default. .Pp This option is not available on all systems: systems without locale support, or those whose internal representation is not natively UCS-4, will fall back to .Fl T Cm ascii . See .Sx ASCII Output for font style specification and available command-line arguments. .Ss Man Output Translate input format into .Xr man 7 output format. This is useful for distributing manual sources to legacy systems lacking .Xr mdoc 7 formatters. .Pp If .Xr mdoc 7 is passed as input, it is translated into .Xr man 7 . If the input format is .Xr man 7 , the input is copied to the output, expanding any .Xr roff 7 .Sq so requests. The parser is also run, and as usual, the .Fl W level controls which .Sx DIAGNOSTICS are displayed before copying the input to the output. .Ss PDF Output PDF-1.1 output may be generated by .Fl T Cm pdf . See .Sx PostScript Output for .Fl O arguments and defaults. .Ss PostScript Output PostScript .Qq Adobe-3.0 Level-2 pages may be generated by .Fl T Cm ps . Output pages default to letter sized and are rendered in the Times font family, 11-point. Margins are calculated as 1/9 the page length and width. Line-height is 1.4m. .Pp Special characters are rendered as in .Sx ASCII Output . .Pp The following .Fl O arguments are accepted: .Bl -tag -width Ds .It Cm paper Ns = Ns Ar name The paper size .Ar name may be one of .Ar a3 , .Ar a4 , .Ar a5 , .Ar legal , or .Ar letter . You may also manually specify dimensions as .Ar NNxNN , width by height in millimetres. If an unknown value is encountered, .Ar letter is used. .El .Ss UTF\-8 Output Use .Fl T Cm utf8 to force a UTF\-8 locale. See .Sx Locale Output for details and options. .Ss Syntax tree output Use .Fl T Cm tree to show a human readable representation of the syntax tree. It is useful for debugging the source code of manual pages. The exact format is subject to change, so don't write parsers for it. .Pp The first paragraph shows meta data found in the .Xr mdoc 7 prologue, on the .Xr man 7 .Ic \&TH line, or the fallbacks used. .Pp In the tree dump, each output line shows one syntax tree node. Child nodes are indented with respect to their parent node. The columns are: .Pp .Bl -enum -compact .It For macro nodes, the macro name; for text and .Xr tbl 7 nodes, the content. There is a special format for .Xr eqn 7 nodes. .It Node type (text, elem, block, head, body, body-end, tail, tbl, eqn). .It Flags: .Bl -dash -compact .It An opening parenthesis if the node is an opening delimiter. .It An asterisk if the node starts a new input line. .It The input line number (starting at one). .It A colon. .It The input column number (starting at one). .It A closing parenthesis if the node is a closing delimiter. .It A full stop if the node ends a sentence. .It +BROKEN if the node is a block broken by another block. +.It NOSRC if the node is not in the input file, but automatically generated from macros. .It NOPRT if the node is not supposed to generate output for any output format. .El .El +.Pp +The following +.Fl O +argument is accepted: +.Bl -tag -width Ds +.It Cm noval +Skip validation and show the unvalidated syntax tree. +This can help to find out whether a given behaviour is caused by +the parser or by the validator. +Meta data is not available in this case. +.El .Sh ENVIRONMENT .Bl -tag -width MANPAGER .It Ev MANPAGER Any non-empty value of the environment variable .Ev MANPAGER will be used instead of the standard pagination program, .Xr more 1 . .It Ev PAGER Specifies the pagination program to use when .Ev MANPAGER is not defined. If neither PAGER nor MANPAGER is defined, .Xr more 1 .Fl s will be used. .El .Sh EXIT STATUS The .Nm utility exits with one of the following values, controlled by the message .Ar level associated with the .Fl W option: .Pp .Bl -tag -width Ds -compact .It 0 No 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 Cm warning was specified. .It 3 At least one parsing error occurred, but no unsupported feature was encountered, and .Fl W Cm error or .Fl W Cm warning was specified. .It 4 At least one unsupported feature was encountered, and .Fl W Cm unsupp , .Fl W Cm error or .Fl W Cm warning was specified. .It 5 Invalid command line arguments were specified. No input files have been read. .It 6 An operating system error occurred, for example exhaustion of memory, file descriptors, or process table entries. Such errors cause .Nm to exit at once, possibly in the middle of parsing or formatting a file. .El .Pp Note that selecting .Fl T Cm lint output mode implies .Fl W Cm warning . .Sh EXAMPLES To page manuals to the terminal: .Pp .Dl $ mandoc \-W all,stop mandoc.1 2\*(Gt&1 | less .Dl $ mandoc mandoc.1 mdoc.3 mdoc.7 | less .Pp To produce HTML manuals with .Pa mandoc.css as the style-sheet: .Pp .Dl $ mandoc \-T html -O style=mandoc.css mdoc.7 \*(Gt mdoc.7.html .Pp To check over a large set of manuals: .Pp .Dl $ mandoc \-T lint \(gafind /usr/src -name \e*\e.[1-9]\(ga .Pp To produce a series of PostScript manuals for A4 paper: .Pp .Dl $ mandoc \-T ps \-O paper=a4 mdoc.7 man.7 \*(Gt manuals.ps .Pp Convert a modern .Xr mdoc 7 manual to the older .Xr man 7 format, for use on systems lacking an .Xr mdoc 7 parser: .Pp .Dl $ mandoc \-T man foo.mdoc \*(Gt foo.man .Sh DIAGNOSTICS Messages displayed by .Nm follow this format: .Pp .D1 Nm Ns : Ar file : Ns Ar line : Ns Ar column : level : message : macro args .Pp Line and column numbers start at 1. Both are omitted for messages referring to an input file as a whole. Macro names and arguments are omitted where meaningless. Fatal messages about invalid command line arguments or operating system errors, for example when memory is exhausted, may also omit the .Ar file and .Ar level fields. .Pp Message levels have the following meanings: .Bl -tag -width "warning" .It Cm unsupp An input file uses unsupported low-level .Xr roff 7 features. The output may be incomplete and/or misformatted, so using GNU troff instead of .Nm to process the file may be preferable. .It Cm error An input file contains invalid syntax that cannot be safely interpreted. By discarding part of the input or inserting missing tokens, the parser is able to continue, and the error does not prevent generation of formatted output, but typically, preparing that output involves information loss, broken document structure or unintended formatting, no matter whether .Nm or GNU troff is used. In many cases, the output of .Nm and GNU troff is identical, but in some, .Nm is more resilient than GNU troff with respect to malformed input. .Pp Non-existent or unreadable input files are also reported on the .Cm error level. In that case, the parser cannot even be started and no output is produced from those input files. .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 , .Cm error , and .Cm unsupp levels except those about non-existent or unreadable input files are hidden unless their level, or a lower level, is requested using a .Fl W option or .Fl T Cm lint output mode. .Ss Warnings related to the document prologue .Bl -ohang .It Sy "missing manual title, using UNTITLED" .Pq mdoc A .Ic \&Dt macro has no arguments, or there is no .Ic \&Dt macro before the first non-prologue macro. .It Sy "missing manual title, using \(dq\(dq" .Pq man There is no .Ic \&TH macro, or it has no arguments. .It Sy "lower case character in document title" .Pq mdoc , man The title is still used as given in the .Ic \&Dt or .Ic \&TH macro. .It Sy "missing manual section, using \(dq\(dq" .Pq mdoc , man A .Ic \&Dt or .Ic \&TH macro lacks the mandatory section argument. .It Sy "unknown manual section" .Pq mdoc The section number in a .Ic \&Dt line is invalid, but still used. .It Sy "missing date, using today's date" .Pq mdoc, man The document was parsed as .Xr mdoc 7 and it has no .Ic \&Dd macro, or the .Ic \&Dd macro has no arguments or only empty arguments; or the document was parsed as .Xr man 7 and it has no .Ic \&TH macro, or the .Ic \&TH macro has less than three arguments or its third argument is empty. .It Sy "cannot parse date, using it verbatim" .Pq mdoc , man The date given in a .Ic \&Dd or .Ic \&TH macro does not follow the conventional format. .It Sy "missing Os macro, using \(dq\(dq" .Pq mdoc The default or current system is not shown in this case. .It Sy "duplicate prologue macro" .Pq mdoc One of the prologue macros occurs more than once. The last instance overrides all previous ones. .It Sy "late prologue macro" .Pq mdoc A .Ic \&Dd or .Ic \&Os macro occurs after some non-prologue macro, but still takes effect. .It Sy "skipping late title macro" .Pq mdoc The .Ic \&Dt macro appears after the first non-prologue macro. Traditional formatters cannot handle this because they write the page header before parsing the document body. Even though this technical restriction does not apply to .Nm , traditional semantics is preserved. The late macro is discarded including its arguments. .It Sy "prologue macros out of order" .Pq mdoc The prologue macros are not given in the conventional order .Ic \&Dd , .Ic \&Dt , .Ic \&Os . All three macros are used even when given in another order. .El .Ss Warnings regarding document structure .Bl -ohang .It Sy ".so is fragile, better use ln(1)" .Pq roff Including files only works when the parser program runs with the correct current working directory. .It Sy "no document body" .Pq mdoc , man The document body contains neither text nor macros. An empty document is shown, consisting only of a header and a footer line. .It Sy "content before first section header" .Pq mdoc , man Some macros or text precede the first .Ic \&Sh or .Ic \&SH section header. The offending macros and text are parsed and added to the top level of the syntax tree, outside any section block. .It Sy "first section is not NAME" .Pq mdoc The argument of the first .Ic \&Sh macro is not .Sq NAME . This may confuse .Xr makewhatis 8 and .Xr apropos 1 . .It Sy "NAME section without Nm before Nd" .Pq mdoc The NAME section does not contain any .Ic \&Nm child macro before the first .Ic \&Nd macro. .It Sy "NAME section without description" .Pq mdoc The NAME section lacks the mandatory .Ic \&Nd child macro. .It Sy "description not at the end of NAME" .Pq mdoc The NAME section does contain an .Ic \&Nd child macro, but other content follows it. .It Sy "bad NAME section content" .Pq mdoc The NAME section contains plain text or macros other than .Ic \&Nm and .Ic \&Nd . .It Sy "missing comma before name" .Pq mdoc The NAME section contains an .Ic \&Nm macro that is neither the first one nor preceded by a comma. .It Sy "missing description line, using \(dq\(dq" .Pq mdoc The .Ic \&Nd macro lacks the required argument. The title line of the manual will end after the dash. .It Sy "sections out of conventional order" .Pq mdoc A standard section occurs after another section it usually precedes. All section titles are used as given, and the order of sections is not changed. .It Sy "duplicate section title" .Pq mdoc The same standard section title occurs more than once. .It Sy "unexpected section" .Pq mdoc A standard section header occurs in a section of the manual where it normally isn't useful. .It Sy "unusual Xr order" .Pq mdoc In the SEE ALSO section, an .Ic \&Xr macro with a lower section number follows one with a higher number, or two .Ic \&Xr macros referring to the same section are out of alphabetical order. .It Sy "unusual Xr punctuation" .Pq mdoc In the SEE ALSO section, punctuation between two .Ic \&Xr macros differs from a single comma, or there is trailing punctuation after the last .Ic \&Xr macro. .It Sy "AUTHORS section without An macro" .Pq mdoc An AUTHORS sections contains no .Ic \&An macros, or only empty ones. Probably, there are author names lacking markup. .El .Ss "Warnings related to macros and nesting" .Bl -ohang .It Sy "obsolete macro" .Pq mdoc See the .Xr mdoc 7 manual for replacements. .It Sy "macro neither callable nor escaped" .Pq mdoc The name of a macro that is not callable appears on a macro line. It is printed verbatim. If the intention is to call it, move it to its own input line; otherwise, escape it by prepending .Sq \e& . .It Sy "skipping paragraph macro" In .Xr mdoc 7 documents, this happens .Bl -dash -compact .It at the beginning and end of sections and subsections .It right before non-compact lists and displays .It at the end of items in non-column, non-compact lists .It and for multiple consecutive paragraph macros. .El In .Xr man 7 documents, it happens .Bl -dash -compact .It for empty .Ic \&P , .Ic \&PP , and .Ic \&LP macros .It for .Ic \&IP macros having neither head nor body arguments .It for .Ic \&br or .Ic \&sp right after .Ic \&SH or .Ic \&SS .El .It Sy "moving paragraph macro out of list" .Pq mdoc A list item in a .Ic \&Bl list contains a trailing paragraph macro. The paragraph macro is moved after the end of the list. .It Sy "skipping no-space macro" .Pq mdoc An input line begins with an .Ic \&Ns macro. The macro is ignored. .It Sy "blocks badly nested" .Pq mdoc If two blocks intersect, one should completely contain the other. Otherwise, rendered output is likely to look strange in any output format, and rendering in SGML-based output formats is likely to be outright wrong because such languages do not support badly nested blocks at all. Typical examples of badly nested blocks are .Qq Ic \&Ao \&Bo \&Ac \&Bc and .Qq Ic \&Ao \&Bq \&Ac . In these examples, .Ic \&Ac breaks .Ic \&Bo and .Ic \&Bq , respectively. .It Sy "nested displays are not portable" .Pq mdoc A .Ic \&Bd , .Ic \&D1 , or .Ic \&Dl display occurs nested inside another .Ic \&Bd display. This works with .Nm , but fails with most other implementations. .It Sy "moving content out of list" .Pq mdoc A .Ic \&Bl list block contains text or macros before the first .Ic \&It macro. The offending children are moved before the beginning of the list. .It Sy "fill mode already enabled, skipping" .Pq man A .Ic \&fi request occurs even though the document is still in fill mode, or already switched back to fill mode. It has no effect. .It Sy "fill mode already disabled, skipping" .Pq man An .Ic \&nf request occurs even though the document already switched to no-fill mode and did not switch back to fill mode yet. It has no effect. .It Sy "line scope broken" .Pq man While parsing the next-line scope of the previous macro, another macro is found that prematurely terminates the previous one. The previous, interrupted macro is deleted from the parse tree. .El .Ss "Warnings related to missing arguments" .Bl -ohang .It Sy "skipping empty request" .Pq roff , eqn The macro name is missing from a macro definition request, or an .Xr eqn 7 control statement or operation keyword lacks its required argument. .It Sy "conditional request controls empty scope" .Pq roff A conditional request is only useful if any of the following follows it on the same logical input line: .Bl -dash -compact .It The .Sq \e{ keyword to open a multi-line scope. .It A request or macro or some text, resulting in a single-line scope. .It The immediate end of the logical line without any intervening whitespace, resulting in next-line scope. .El Here, a conditional request is followed by trailing whitespace only, and there is no other content on its logical input line. Note that it doesn't matter whether the logical input line is split across multiple physical input lines using .Sq \e line continuation characters. This is one of the rare cases where trailing whitespace is syntactically significant. The conditional request controls a scope containing whitespace only, so it is unlikely to have a significant effect, except that it may control a following .Ic \&el clause. .It Sy "skipping empty macro" .Pq mdoc The indicated macro has no arguments and hence no effect. .It Sy "empty block" .Pq mdoc , man A .Ic \&Bd , .Ic \&Bk , .Ic \&Bl , .Ic \&D1 , .Ic \&Dl , .Ic \&RS , or .Ic \&UR block contains nothing in its body and will produce no output. .It Sy "empty argument, using 0n" .Pq mdoc The required width is missing after .Ic \&Bd or .Ic \&Bl .Fl offset or .Fl width. .It Sy "missing display type, using -ragged" .Pq mdoc The .Ic \&Bd macro is invoked without the required display type. .It Sy "list type is not the first argument" .Pq mdoc In a .Ic \&Bl macro, at least one other argument precedes the type argument. The .Nm utility copes with any argument order, but some other .Xr mdoc 7 implementations do not. .It Sy "missing -width in -tag list, using 8n" .Pq mdoc Every .Ic \&Bl macro having the .Fl tag argument requires .Fl width , too. .It Sy "missing utility name, using \(dq\(dq" .Pq mdoc The .Ic \&Ex Fl std macro is called without an argument before .Ic \&Nm has first been called with an argument. .It Sy "missing function name, using \(dq\(dq" .Pq mdoc The .Ic \&Fo macro is called without an argument. No function name is printed. .It Sy "empty head in list item" .Pq mdoc In a .Ic \&Bl .Fl diag , .Fl hang , .Fl inset , .Fl ohang , or .Fl tag list, an .Ic \&It macro lacks the required argument. The item head is left empty. .It Sy "empty list item" .Pq mdoc In a .Ic \&Bl .Fl bullet , .Fl dash , .Fl enum , or .Fl hyphen list, an .Ic \&It block is empty. An empty list item is shown. .It Sy "missing font type, using \efR" .Pq mdoc A .Ic \&Bf macro has no argument. It switches to the default font. .It Sy "unknown font type, using \efR" .Pq mdoc The .Ic \&Bf argument is invalid. The default font is used instead. .It Sy "nothing follows prefix" .Pq mdoc A .Ic \&Pf macro has no argument, or only one argument and no macro follows on the same input line. This defeats its purpose; in particular, spacing is not suppressed before the text or macros following on the next input line. .It Sy "empty reference block" .Pq mdoc An .Ic \&Rs macro is immediately followed by an .Ic \&Re macro on the next input line. Such an empty block does not produce any output. .It Sy "missing section argument" .Pq mdoc An .Ic \&Xr macro lacks its second, section number argument. The first argument, i.e. the name, is printed, but without subsequent parentheses. .It Sy "missing -std argument, adding it" .Pq mdoc An .Ic \&Ex or .Ic \&Rv macro lacks the required .Fl std argument. The .Nm utility assumes .Fl std even when it is not specified, but other implementations may not. .It Sy "missing option string, using \(dq\(dq" .Pq man The .Ic \&OP macro is invoked without any argument. An empty pair of square brackets is shown. .It Sy "missing resource identifier, using \(dq\(dq" .Pq man The .Ic \&UR macro is invoked without any argument. An empty pair of angle brackets is shown. .It Sy "missing eqn box, using \(dq\(dq" .Pq eqn A diacritic mark or a binary operator is found, but there is nothing to the left of it. An empty box is inserted. .El .Ss "Warnings related to bad macro arguments" .Bl -ohang .It Sy "unterminated quoted argument" .Pq roff Macro arguments can be enclosed in double quote characters such that space characters and macro names contained in the quoted argument need not be escaped. The closing quote of the last argument of a macro can be omitted. However, omitting it is not recommended because it makes the code harder to read. .It Sy "duplicate argument" .Pq mdoc A .Ic \&Bd or .Ic \&Bl macro has more than one .Fl compact , more than one .Fl offset , or more than one .Fl width argument. All but the last instances of these arguments are ignored. .It Sy "skipping duplicate argument" .Pq mdoc An .Ic \&An macro has more than one .Fl split or .Fl nosplit argument. All but the first of these arguments are ignored. .It Sy "skipping duplicate display type" .Pq mdoc A .Ic \&Bd macro has more than one type argument; the first one is used. .It Sy "skipping duplicate list type" .Pq mdoc A .Ic \&Bl macro has more than one type argument; the first one is used. .It Sy "skipping -width argument" .Pq mdoc A .Ic \&Bl .Fl column , .Fl diag , .Fl ohang , .Fl inset , or .Fl item list has a .Fl width argument. That has no effect. .It Sy "wrong number of cells" In a line of a .Ic \&Bl Fl column list, the number of tabs or .Ic \&Ta macros is less than the number expected from the list header line or exceeds the expected number by more than one. Missing cells remain empty, and all cells exceeding the number of columns are joined into one single cell. .It Sy "unknown AT&T UNIX version" .Pq mdoc An .Ic \&At macro has an invalid argument. It is used verbatim, with .Qq "AT&T UNIX " prefixed to it. .It Sy "comma in function argument" .Pq mdoc An argument of an .Ic \&Fa or .Ic \&Fn macro contains a comma; it should probably be split into two arguments. .It Sy "parenthesis in function name" .Pq mdoc The first argument of an .Ic \&Fc or .Ic \&Fn macro contains an opening or closing parenthesis; that's probably wrong, parentheses are added automatically. .It Sy "invalid content in Rs block" .Pq mdoc An .Ic \&Rs block contains plain text or non-% macros. The bogus content is left in the syntax tree. Formatting may be poor. .It Sy "invalid Boolean argument" .Pq mdoc An .Ic \&Sm macro has an argument other than .Cm on or .Cm off . The invalid argument is moved out of the macro, which leaves the macro empty, causing it to toggle the spacing mode. .It Sy "unknown font, skipping request" .Pq man , tbl A .Xr roff 7 .Ic \&ft request or a .Xr tbl 7 .Ic \&f layout modifier has an unknown .Ar font argument. .It Sy "odd number of characters in request" .Pq roff A .Ic \&tr request contains an odd number of characters. The last character is mapped to the blank character. .El .Ss "Warnings related to plain text" .Bl -ohang .It Sy "blank line in fill mode, using .sp" .Pq mdoc The meaning of blank input lines is only well-defined in non-fill mode: In fill mode, line breaks of text input lines are not supposed to be significant. However, for compatibility with groff, blank lines in fill mode are replaced with .Ic \&sp requests. .It Sy "tab in filled text" .Pq mdoc , man The meaning of tab characters is only well-defined in non-fill mode: In fill mode, whitespace is not supposed to be significant on text input lines. As an implementation dependent choice, tab characters on text lines are passed through to the formatters in any case. Given that the text before the tab character will be filled, it is hard to predict which tab stop position the tab will advance to. .It Sy "whitespace at end of input line" .Pq mdoc , man , roff Whitespace at the end of input lines is almost never semantically significant \(em but in the odd case where it might be, it is extremely confusing when reviewing and maintaining documents. +.It Sy "new sentence, new line" +.Pq mdoc +A new sentence starts in the middle of a text line. +Start it on a new input line to help formatters produce correct spacing. .It Sy "bad comment style" .Pq roff Comment lines start with a dot, a backslash, and a double-quote character. The .Nm utility treats the line as a comment line even without the backslash, but leaving out the backslash might not be portable. .It Sy "invalid escape sequence" .Pq roff An escape sequence has an invalid opening argument delimiter, lacks the closing argument delimiter, or the argument has too few characters. If the argument is incomplete, .Ic \e* and .Ic \en expand to an empty string, .Ic \eB to the digit .Sq 0 , and .Ic \ew to the length of the incomplete argument. All other invalid escape sequences are ignored. .It Sy "undefined string, using \(dq\(dq" .Pq roff If a string is used without being defined before, its value is implicitly set to the empty string. However, defining strings explicitly before use keeps the code more readable. .El .Ss "Warnings related to tables" .Bl -ohang .It Sy "tbl line starts with span" .Pq tbl The first cell in a table layout line is a horizontal span .Pq Sq Cm s . Data provided for this cell is ignored, and nothing is printed in the cell. .It Sy "tbl column starts with span" .Pq tbl The first line of a table layout specification requests a vertical span .Pq Sq Cm ^ . Data provided for this cell is ignored, and nothing is printed in the cell. .It Sy "skipping vertical bar in tbl layout" .Pq tbl A table layout specification contains more than two consecutive vertical bars. A double bar is printed, all additional bars are discarded. .El .Ss "Errors related to tables" .Bl -ohang .It Sy "non-alphabetic character in tbl options" .Pq tbl The table options line contains a character other than a letter, blank, or comma where the beginning of an option name is expected. The character is ignored. .It Sy "skipping unknown tbl option" .Pq tbl The table options line contains a string of letters that does not match any known option name. The word is ignored. .It Sy "missing tbl option argument" .Pq tbl A table option that requires an argument is not followed by an opening parenthesis, or the opening parenthesis is immediately followed by a closing parenthesis. The option is ignored. .It Sy "wrong tbl option argument size" .Pq tbl A table option argument contains an invalid number of characters. Both the option and the argument are ignored. .It Sy "empty tbl layout" .Pq tbl A table layout specification is completely empty, specifying zero lines and zero columns. As a fallback, a single left-justified column is used. .It Sy "invalid character in tbl layout" .Pq tbl A table layout specification contains a character that can neither be interpreted as a layout key character nor as a layout modifier, or a modifier precedes the first key. The invalid character is discarded. .It Sy "unmatched parenthesis in tbl layout" .Pq tbl A table layout specification contains an opening parenthesis, but no matching closing parenthesis. The rest of the input line, starting from the parenthesis, has no effect. .It Sy "tbl without any data cells" .Pq tbl A table does not contain any data cells. It will probably produce no output. .It Sy "ignoring data in spanned tbl cell" .Pq tbl A table cell is marked as a horizontal span .Pq Sq Cm s or vertical span .Pq Sq Cm ^ in the table layout, but it contains data. The data is ignored. .It Sy "ignoring extra tbl data cells" .Pq tbl A data line contains more cells than the corresponding layout line. The data in the extra cells is ignored. .It Sy "data block open at end of tbl" .Pq tbl A data block is opened with .Cm T{ , but never closed with a matching .Cm T} . The remaining data lines of the table are all put into one cell, and any remaining cells stay empty. .El .Ss "Errors related to roff, mdoc, and man code" .Bl -ohang .It Sy "input stack limit exceeded, infinite loop?" .Pq roff Explicit recursion limits are implemented for the following features, in order to prevent infinite loops: .Bl -dash -compact .It expansion of nested escape sequences including expansion of strings and number registers, .It expansion of nested user-defined macros, .It and .Ic \&so file inclusion. .El When a limit is hit, the output is incorrect, typically losing some content, but the parser can continue. .It Sy "skipping bad character" .Pq mdoc , man , roff The input file contains a byte that is not a printable .Xr ascii 7 character. The message mentions the character number. The offending byte is replaced with a question mark .Pq Sq \&? . Consider editing the input file to replace the byte with an ASCII transliteration of the intended character. .It Sy "skipping unknown macro" .Pq mdoc , man , roff The first identifier on a request or macro line is neither recognized as a .Xr roff 7 request, nor as a user-defined macro, nor, respectively, as an .Xr mdoc 7 or .Xr man 7 macro. It may be mistyped or unsupported. The request or macro is discarded including its arguments. .It Sy "skipping insecure request" .Pq roff An input file attempted to run a shell command or to read or write an external file. Such attempts are denied for security reasons. .It Sy "skipping item outside list" .Pq mdoc , eqn An .Ic \&It macro occurs outside any .Ic \&Bl list, or an .Xr eqn 7 .Ic above delimiter occurs outside any pile. It is discarded including its arguments. .It Sy "skipping column outside column list" .Pq mdoc A .Ic \&Ta macro occurs outside any .Ic \&Bl Fl column block. It is discarded including its arguments. .It Sy "skipping end of block that is not open" .Pq mdoc , man , eqn , tbl , roff Various syntax elements can only be used to explicitly close blocks that have previously been opened. An .Xr mdoc 7 block closing macro, a .Xr man 7 .Ic \&RE or .Ic \&UE macro, an .Xr eqn 7 right delimiter or closing brace, or the end of an equation, table, or .Xr roff 7 conditional request is encountered but no matching block is open. The offending request or macro is discarded. .It Sy "fewer RS blocks open, skipping" .Pq man The .Ic \&RE macro is invoked with an argument, but less than the specified number of .Ic \&RS blocks is open. The .Ic \&RE macro is discarded. .It Sy "inserting missing end of block" .Pq mdoc , tbl Various .Xr mdoc 7 macros as well as tables require explicit closing by dedicated macros. A block that doesn't support bad nesting ends before all of its children are properly closed. The open child nodes are closed implicitly. .It Sy "appending missing end of block" .Pq mdoc , man , eqn , tbl , roff At the end of the document, an explicit .Xr mdoc 7 block, a .Xr man 7 next-line scope or .Ic \&RS or .Ic \&UR block, an equation, table, or .Xr roff 7 conditional or ignore block is still open. The open block is closed implicitly. .It Sy "escaped character not allowed in a name" .Pq roff Macro, string and register identifiers consist of printable, non-whitespace ASCII characters. Escape sequences and characters and strings expressed in terms of them cannot form part of a name. The first argument of an .Ic \&am , .Ic \&as , .Ic \&de , .Ic \&ds , .Ic \&nr , or .Ic \&rr request, or any argument of an .Ic \&rm request, or the name of a request or user defined macro being called, is terminated by an escape sequence. In the cases of .Ic \&as , .Ic \&ds , and .Ic \&nr , the request has no effect at all. In the cases of .Ic \&am , .Ic \&de , .Ic \&rr , and .Ic \&rm , what was parsed up to this point is used as the arguments to the request, and the rest of the input line is discarded including the escape sequence. When parsing for a request or a user-defined macro name to be called, only the escape sequence is discarded. The characters preceding it are used as the request or macro name, the characters following it are used as the arguments to the request or macro. .It Sy "NOT IMPLEMENTED: Bd -file" .Pq mdoc For security reasons, the .Ic \&Bd macro does not support the .Fl file argument. By requesting the inclusion of a sensitive file, a malicious document might otherwise trick a privileged user into inadvertently displaying the file on the screen, revealing the file content to bystanders. The argument is ignored including the file name following it. .It Sy "skipping display without arguments" .Pq mdoc A .Ic \&Bd block macro does not have any arguments. The block is discarded, and the block content is displayed in whatever mode was active before the block. .It Sy "missing list type, using -item" .Pq mdoc A .Ic \&Bl macro fails to specify the list type. .It Sy "missing manual name, using \(dq\(dq" .Pq mdoc The first call to .Ic \&Nm , or any call in the NAME section, lacks the required argument. .It Sy "uname(3) system call failed, using UNKNOWN" .Pq mdoc The .Ic \&Os macro is called without arguments, and the .Xr uname 3 system call failed. As a workaround, .Nm can be compiled with .Sm off .Fl D Cm OSNAME=\(dq\e\(dq Ar string Cm \e\(dq\(dq . .Sm on .It Sy "unknown standard specifier" .Pq mdoc An .Ic \&St macro has an unknown argument and is discarded. .It Sy "skipping request without numeric argument" .Pq roff , eqn An .Ic \&it request or an .Xr eqn 7 .Ic \&size or .Ic \&gsize statement has a non-numeric or negative argument or no argument at all. The invalid request or statement is ignored. .It Sy "NOT IMPLEMENTED: .so with absolute path or \(dq..\(dq" .Pq roff For security reasons, .Nm allows .Ic \&so file inclusion requests only with relative paths and only without ascending to any parent directory. By requesting the inclusion of a sensitive file, a malicious document might otherwise trick a privileged user into inadvertently displaying the file on the screen, revealing the file content to bystanders. .Nm only shows the path as it appears behind .Ic \&so . .It Sy ".so request failed" .Pq roff Servicing a .Ic \&so request requires reading an external file, but the file could not be opened. .Nm only shows the path as it appears behind .Ic \&so . .It Sy "skipping all arguments" .Pq mdoc , man , eqn , roff An .Xr mdoc 7 .Ic \&Bt , .Ic \&Ed , .Ic \&Ef , .Ic \&Ek , .Ic \&El , .Ic \&Lp , .Ic \&Pp , .Ic \&Re , .Ic \&Rs , or .Ic \&Ud macro, an .Ic \&It macro in a list that don't support item heads, a .Xr man 7 .Ic \&LP , .Ic \&P , or .Ic \&PP macro, an .Xr eqn 7 .Ic \&EQ or .Ic \&EN macro, or a .Xr roff 7 .Ic \&br , .Ic \&fi , or .Ic \&nf request or .Sq \&.. block closing request is invoked with at least one argument. All arguments are ignored. .It Sy "skipping excess arguments" .Pq mdoc , man , roff A macro or request is invoked with too many arguments: .Bl -dash -offset 2n -width 2n -compact .It .Ic \&Fo , .Ic \&PD , .Ic \&RS , .Ic \&UR , .Ic \&ft , or .Ic \&sp with more than one argument .It .Ic \&An with another argument after .Fl split or .Fl nosplit .It .Ic \&RE with more than one argument or with a non-integer argument .It .Ic \&OP or a request of the .Ic \&de family with more than two arguments .It .Ic \&Dt with more than three arguments .It .Ic \&TH with more than five arguments .It .Ic \&Bd , .Ic \&Bk , or .Ic \&Bl with invalid arguments .El The excess arguments are ignored. .El .Ss Unsupported features .Bl -ohang .It Sy "input too large" .Pq mdoc , man Currently, .Nm cannot handle input files larger than its arbitrary size limit of 2^31 bytes (2 Gigabytes). Since useful manuals are always small, this is not a problem in practice. Parsing is aborted as soon as the condition is detected. .It Sy "unsupported control character" .Pq roff An ASCII control character supported by other .Xr roff 7 implementations but not by .Nm was found in an input file. It is replaced by a question mark. .It Sy "unsupported roff request" .Pq roff An input file contains a .Xr roff 7 request supported by GNU troff or Heirloom troff but not by .Nm , and it is likely that this will cause information loss or considerable misformatting. .It Sy "eqn delim option in tbl" .Pq eqn , tbl The options line of a table defines equation delimiters. Any equation source code contained in the table will be printed unformatted. .It Sy "unsupported table layout modifier" .Pq tbl A table layout specification contains an .Sq Cm m modifier. The modifier is discarded. .It Sy "ignoring macro in table" .Pq tbl , mdoc , man A table contains an invocation of an .Xr mdoc 7 or .Xr man 7 macro or of an undefined macro. The macro is ignored, and its arguments are handled as if they were a text line. .El .Sh SEE ALSO .Xr apropos 1 , .Xr man 1 , .Xr eqn 7 , .Xr man 7 , .Xr mandoc_char 7 , .Xr mdoc 7 , .Xr roff 7 , .Xr tbl 7 +.Sh HISTORY +The +.Nm +utility first appeared in +.Ox 4.8 . +The option +.Fl I +appeared in +.Ox 5.2 , +and +.Fl aCcfhKklMSsw +in +.Ox 5.7 . .Sh AUTHORS .An -nosplit The .Nm utility was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv and is maintained by .An Ingo Schwarze Aq Mt schwarze@openbsd.org . Index: vendor/mdocml/dist/mandoc.css =================================================================== --- vendor/mdocml/dist/mandoc.css (revision 313955) +++ vendor/mdocml/dist/mandoc.css (revision 313956) @@ -1,173 +1,189 @@ -/* $Id: mandoc.css,v 1.13 2017/01/21 02:29:57 schwarze Exp $ */ +/* $Id: mandoc.css,v 1.17 2017/02/05 21:00:43 schwarze Exp $ */ /* * Standard style sheet for mandoc(1) -Thtml and man.cgi(8). */ /* Global defaults. */ html { max-width: 100ex; } body { font-family: Helvetica,Arial,sans-serif; } table { margin-top: 0em; margin-bottom: 0em; } td { vertical-align: top; } ul, ol, dl { margin-top: 0em; margin-bottom: 0em; } li, dt { margin-top: 1em; } /* Search form and search results. */ fieldset { border: thin solid silver; border-radius: 1em; text-align: center; } input[name=expr] { width: 25%; } table.results { margin-top: 1em; margin-left: 2em; font-size: smaller; } /* Header and footer lines. */ table.head { width: 100%; border-bottom: 1px dotted #808080; margin-bottom: 1em; font-size: smaller; } td.head-vol { text-align: center; } td.head-rtitle { text-align: right; } span.Nd { } table.foot { width: 100%; border-top: 1px dotted #808080; margin-top: 1em; font-size: smaller; } td.foot-os { text-align: right; } /* Sections and paragraphs. */ div.manual-text { margin-left: 5ex; } h1.Sh { margin-top: 2ex; margin-bottom: 1ex; margin-left: -4ex; font-size: 110%; } h2.Ss { margin-top: 2ex; margin-bottom: 1ex; margin-left: -2ex; font-size: 105%; } div.Pp { margin: 1ex 0ex; } a.Sx { } a.Xr { } /* Displays and lists. */ div.Bd { } div.D1 { margin-left: 5ex; } ul.Bl-bullet { list-style-type: disc; padding-left: 1em; } li.It-bullet { } ul.Bl-dash { list-style-type: none; padding-left: 0em; } li.It-dash:before { content: "\2014 "; } ul.Bl-item { list-style-type: none; padding-left: 0em; } li.It-item { } ol.Bl-enum { padding-left: 2em; } li.It-enum { } dl.Bl-diag { } dt.It-diag { } dd.It-diag { } b.It-diag { font-style: normal; } dl.Bl-hang { } dt.It-hang { } dd.It-hang { } dl.Bl-inset { } dt.It-inset { } dd.It-inset { } dl.Bl-ohang { } dt.It-ohang { } dd.It-ohang { margin-left: 0ex; } -dl.Bl-tag { } -dt.It-tag { } -dd.It-tag { } +dl.Bl-tag { margin-left: 8ex; } +dt.It-tag { float: left; + clear: both; + margin-top: 0ex; + margin-left: -8ex; + padding-right: 2ex; + vertical-align: top; } +dd.It-tag { width: 100%; + margin-top: 0ex; + margin-left: 0ex; + vertical-align: top; + overflow: auto; } table.Bl-column { } tr.It-column { } td.It-column { margin-top: 1em; } -span.Rs { } +cite.Rs { font-style: normal; + font-weight: normal; } span.RsA { } i.RsB { font-weight: normal; } span.RsC { } span.RsD { } i.RsI { font-weight: normal; } i.RsJ { font-weight: normal; } span.RsN { } span.RsO { } span.RsP { } span.RsQ { } span.RsR { } span.RsT { text-decoration: underline; } a.RsU { } span.RsV { } span.eqn { } table.tbl { } /* Semantic markup for command line utilities. */ table.Nm { } b.Nm { font-style: normal; } b.Fl { font-style: normal; } b.Cm { font-style: normal; } -i.Ar { font-weight: normal; } +var.Ar { font-style: italic; + font-weight: normal; } span.Op { } b.Ic { font-style: normal; } code.Ev { font-style: normal; font-weight: normal; font-family: monospace; } i.Pa { font-weight: normal; } /* Semantic markup for function libraries. */ span.Lb { } b.In { font-style: normal; } a.In { } b.Fd { font-style: normal; } -i.Ft { font-weight: normal; } +var.Ft { font-style: italic; + font-weight: normal; } b.Fn { font-style: normal; } -i.Fa { font-weight: normal; } -i.Vt { font-weight: normal; } -i.Va { font-weight: normal; } +var.Fa { font-style: italic; + font-weight: normal; } +var.Vt { font-style: italic; + font-weight: normal; } +var.Va { font-style: italic; + font-weight: normal; } code.Dv { font-style: normal; font-weight: normal; font-family: monospace; } code.Er { font-style: normal; font-weight: normal; font-family: monospace; } /* Various semantic markup. */ span.An { } a.Lk { } a.Mt { } b.Cd { font-style: normal; } i.Ad { font-weight: normal; } b.Ms { font-style: normal; } +span.St { } a.Ux { } /* Physical markup. */ .No { font-style: normal; font-weight: normal; } .Em { font-style: italic; font-weight: normal; } .Sy { font-style: normal; font-weight: bold; } .Li { font-style: normal; font-weight: normal; font-family: monospace; } Index: vendor/mdocml/dist/mandoc.h =================================================================== --- vendor/mdocml/dist/mandoc.h (revision 313955) +++ vendor/mdocml/dist/mandoc.h (revision 313956) @@ -1,438 +1,439 @@ -/* $Id: mandoc.h,v 1.213 2017/01/09 01:37:03 schwarze Exp $ */ +/* $Id: mandoc.h,v 1.214 2017/01/28 23:30:08 schwarze Exp $ */ /* * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons * Copyright (c) 2010-2017 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. */ #define ASCII_NBRSP 31 /* non-breaking space */ #define ASCII_HYPH 30 /* breakable hyphen */ #define ASCII_BREAK 29 /* breakable zero-width space */ /* * 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_UNSUPP, /* input needs unimplemented features */ 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_DT_NOTITLE, /* missing manual title, using UNTITLED: line */ MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */ MANDOCERR_TITLE_CASE, /* lower case character in document title */ MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */ MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */ MANDOCERR_DATE_MISSING, /* missing date, using today's date */ MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */ MANDOCERR_OS_MISSING, /* missing Os macro, using "" */ MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */ MANDOCERR_PROLOG_LATE, /* late prologue macro: macro */ MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */ MANDOCERR_PROLOG_ORDER, /* prologue macros out of order: macros */ /* related to document structure */ MANDOCERR_SO, /* .so is fragile, better use ln(1): so path */ MANDOCERR_DOC_EMPTY, /* no document body */ MANDOCERR_SEC_BEFORE, /* content before first section header: macro */ MANDOCERR_NAMESEC_FIRST, /* first section is not NAME: Sh title */ MANDOCERR_NAMESEC_NONM, /* NAME section without Nm before Nd */ MANDOCERR_NAMESEC_NOND, /* NAME section without description */ MANDOCERR_NAMESEC_ND, /* description not at the end of NAME */ MANDOCERR_NAMESEC_BAD, /* bad NAME section content: macro */ MANDOCERR_NAMESEC_PUNCT, /* missing comma before name: Nm name */ MANDOCERR_ND_EMPTY, /* missing description line, using "" */ MANDOCERR_SEC_ORDER, /* sections out of conventional order: Sh title */ MANDOCERR_SEC_REP, /* duplicate section title: Sh title */ MANDOCERR_SEC_MSEC, /* unexpected section: Sh title for ... only */ MANDOCERR_XR_ORDER, /* unusual Xr order: ... after ... */ MANDOCERR_XR_PUNCT, /* unusual Xr punctuation: ... after ... */ MANDOCERR_AN_MISSING, /* AUTHORS section without An macro */ /* related to macros and nesting */ MANDOCERR_MACRO_OBS, /* obsolete macro: macro */ MANDOCERR_MACRO_CALL, /* macro neither callable nor escaped: macro */ MANDOCERR_PAR_SKIP, /* skipping paragraph macro: macro ... */ MANDOCERR_PAR_MOVE, /* moving paragraph macro out of list: macro */ MANDOCERR_NS_SKIP, /* skipping no-space macro */ MANDOCERR_BLK_NEST, /* blocks badly nested: macro ... */ MANDOCERR_BD_NEST, /* nested displays are not portable: macro ... */ MANDOCERR_BL_MOVE, /* moving content out of list: macro */ MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */ MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */ MANDOCERR_BLK_LINE, /* line scope broken: macro breaks macro */ /* related to missing arguments */ MANDOCERR_REQ_EMPTY, /* skipping empty request: request */ MANDOCERR_COND_EMPTY, /* conditional request controls empty scope */ MANDOCERR_MACRO_EMPTY, /* skipping empty macro: macro */ MANDOCERR_BLK_EMPTY, /* empty block: macro */ MANDOCERR_ARG_EMPTY, /* empty argument, using 0n: macro arg */ MANDOCERR_BD_NOTYPE, /* missing display type, using -ragged: Bd */ MANDOCERR_BL_LATETYPE, /* list type is not the first argument: Bl arg */ MANDOCERR_BL_NOWIDTH, /* missing -width in -tag list, using 6n */ MANDOCERR_EX_NONAME, /* missing utility name, using "": Ex */ MANDOCERR_FO_NOHEAD, /* missing function name, using "": Fo */ MANDOCERR_IT_NOHEAD, /* empty head in list item: Bl -type It */ MANDOCERR_IT_NOBODY, /* empty list item: Bl -type It */ MANDOCERR_BF_NOFONT, /* missing font type, using \fR: Bf */ MANDOCERR_BF_BADFONT, /* unknown font type, using \fR: Bf font */ MANDOCERR_PF_SKIP, /* nothing follows prefix: Pf arg */ MANDOCERR_RS_EMPTY, /* empty reference block: Rs */ MANDOCERR_XR_NOSEC, /* missing section argument: Xr arg */ MANDOCERR_ARG_STD, /* missing -std argument, adding it: macro */ MANDOCERR_OP_EMPTY, /* missing option string, using "": OP */ MANDOCERR_UR_NOHEAD, /* missing resource identifier, using "": UR */ MANDOCERR_EQN_NOBOX, /* missing eqn box, using "": op */ /* related to bad arguments */ MANDOCERR_ARG_QUOTE, /* unterminated quoted argument */ MANDOCERR_ARG_REP, /* duplicate argument: macro arg */ MANDOCERR_AN_REP, /* skipping duplicate argument: An -arg */ MANDOCERR_BD_REP, /* skipping duplicate display type: Bd -type */ MANDOCERR_BL_REP, /* skipping duplicate list type: Bl -type */ MANDOCERR_BL_SKIPW, /* skipping -width argument: Bl -type */ MANDOCERR_BL_COL, /* wrong number of cells */ MANDOCERR_AT_BAD, /* unknown AT&T UNIX version: At version */ MANDOCERR_FA_COMMA, /* comma in function argument: arg */ MANDOCERR_FN_PAREN, /* parenthesis in function name: arg */ MANDOCERR_RS_BAD, /* invalid content in Rs block: macro */ MANDOCERR_SM_BAD, /* invalid Boolean argument: macro arg */ MANDOCERR_FT_BAD, /* unknown font, skipping request: ft font */ MANDOCERR_TR_ODD, /* odd number of characters in request: tr char */ /* related to plain text */ MANDOCERR_FI_BLANK, /* blank line in fill mode, using .sp */ MANDOCERR_FI_TAB, /* tab in filled text */ MANDOCERR_SPACE_EOL, /* whitespace at end of input line */ + MANDOCERR_EOS, /* new sentence, new line */ MANDOCERR_COMMENT_BAD, /* bad comment style */ MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */ MANDOCERR_STR_UNDEF, /* undefined string, using "": name */ /* related to tables */ MANDOCERR_TBLLAYOUT_SPAN, /* tbl line starts with span */ MANDOCERR_TBLLAYOUT_DOWN, /* tbl column starts with span */ MANDOCERR_TBLLAYOUT_VERT, /* skipping vertical bar in tbl layout */ MANDOCERR_ERROR, /* ===== start of errors ===== */ /* related to tables */ MANDOCERR_TBLOPT_ALPHA, /* non-alphabetic character in tbl options */ MANDOCERR_TBLOPT_BAD, /* skipping unknown tbl option: option */ MANDOCERR_TBLOPT_NOARG, /* missing tbl option argument: option */ MANDOCERR_TBLOPT_ARGSZ, /* wrong tbl option argument size: option */ MANDOCERR_TBLLAYOUT_NONE, /* empty tbl layout */ MANDOCERR_TBLLAYOUT_CHAR, /* invalid character in tbl layout: char */ MANDOCERR_TBLLAYOUT_PAR, /* unmatched parenthesis in tbl layout */ MANDOCERR_TBLDATA_NONE, /* tbl without any data cells */ MANDOCERR_TBLDATA_SPAN, /* ignoring data in spanned tbl cell: data */ MANDOCERR_TBLDATA_EXTRA, /* ignoring extra tbl data cells: data */ MANDOCERR_TBLDATA_BLK, /* data block open at end of tbl: macro */ /* related to document structure and macros */ MANDOCERR_FILE, /* cannot open file */ MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ MANDOCERR_CHAR_BAD, /* skipping bad character: number */ MANDOCERR_MACRO, /* skipping unknown macro: macro */ MANDOCERR_REQ_INSEC, /* skipping insecure request: request */ MANDOCERR_IT_STRAY, /* skipping item outside list: It ... */ MANDOCERR_TA_STRAY, /* skipping column outside column list: Ta */ MANDOCERR_BLK_NOTOPEN, /* skipping end of block that is not open */ MANDOCERR_RE_NOTOPEN, /* fewer RS blocks open, skipping: RE arg */ MANDOCERR_BLK_BROKEN, /* inserting missing end of block: macro ... */ MANDOCERR_BLK_NOEND, /* appending missing end of block: macro */ /* related to request and macro arguments */ MANDOCERR_NAMESC, /* escaped character not allowed in a name: name */ MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */ MANDOCERR_BD_NOARG, /* skipping display without arguments: Bd */ MANDOCERR_BL_NOTYPE, /* missing list type, using -item: Bl */ MANDOCERR_NM_NONAME, /* missing manual name, using "": Nm */ MANDOCERR_OS_UNAME, /* uname(3) system call failed, using UNKNOWN */ MANDOCERR_ST_BAD, /* unknown standard specifier: St standard */ MANDOCERR_IT_NONUM, /* skipping request without numeric argument */ MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */ MANDOCERR_SO_FAIL, /* .so request failed */ MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */ MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */ MANDOCERR_DIVZERO, /* divide by zero */ MANDOCERR_UNSUPP, /* ===== start of unsupported features ===== */ MANDOCERR_TOOLARGE, /* input too large */ MANDOCERR_CHAR_UNSUPP, /* unsupported control character: number */ MANDOCERR_REQ_UNSUPP, /* unsupported roff request: request */ MANDOCERR_TBLOPT_EQN, /* eqn delim option in tbl: arg */ MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */ MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */ MANDOCERR_MAX }; struct tbl_opts { char tab; /* cell-separator */ char decimal; /* decimal point */ 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) #define TBL_OPT_NOWARN (1 << 7) int cols; /* number of columns */ int lvert; /* width of left vertical line */ int rvert; /* width of right vertical line */ }; 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_MAX }; /* * A cell in a layout row. */ struct tbl_cell { struct tbl_cell *next; int vert; /* width of subsequent vertical line */ enum tbl_cellt pos; size_t spacing; int col; /* column number, starting from 0 */ int flags; #define TBL_CELL_TALIGN (1 << 0) /* t, T */ #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 */ #define TBL_CELL_WMAX (1 << 7) /* x, X */ }; /* * A layout row. */ struct tbl_row { struct tbl_row *next; struct tbl_cell *first; struct tbl_cell *last; int vert; /* width of left vertical line */ }; enum tbl_datt { TBL_DATA_NONE, /* has no data */ TBL_DATA_DATA, /* consists of data/string */ TBL_DATA_HORIZ, /* horizontal line */ TBL_DATA_DHORIZ, /* double-horizontal line */ TBL_DATA_NHORIZ, /* squeezed horizontal line */ TBL_DATA_NDHORIZ /* squeezed double-horizontal line */ }; /* * 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 */ int spans; /* how many spans follow */ struct tbl_dat *next; char *string; /* data (NULL if not TBL_DATA_DATA) */ 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_opts *opts; struct tbl_row *layout; /* layout row */ struct tbl_dat *first; struct tbl_dat *last; struct tbl_span *prev; struct tbl_span *next; int line; /* parse line */ enum tbl_spant pos; }; enum eqn_boxt { EQN_ROOT, /* root of parse tree */ EQN_TEXT, /* text (number, variable, whatever) */ EQN_SUBEXPR, /* nested `eqn' subexpression */ EQN_LIST, /* list (braces, etc.) */ EQN_LISTONE, /* singleton list */ EQN_PILE, /* vertical pile */ EQN_MATRIX /* pile of piles */ }; enum eqn_fontt { EQNFONT_NONE = 0, EQNFONT_ROMAN, EQNFONT_BOLD, EQNFONT_FAT, EQNFONT_ITALIC, EQNFONT__MAX }; enum eqn_post { EQNPOS_NONE = 0, EQNPOS_SUP, EQNPOS_SUBSUP, EQNPOS_SUB, EQNPOS_TO, EQNPOS_FROM, EQNPOS_FROMTO, EQNPOS_OVER, EQNPOS_SQRT, EQNPOS__MAX }; enum eqn_pilet { EQNPILE_NONE = 0, EQNPILE_PILE, EQNPILE_CPILE, EQNPILE_RPILE, EQNPILE_LPILE, EQNPILE_COL, EQNPILE_CCOL, EQNPILE_RCOL, EQNPILE_LCOL, EQNPILE__MAX }; /* * A "box" is a parsed mathematical expression as defined by the eqn.7 * grammar. */ struct eqn_box { int size; /* font size of expression */ #define EQN_DEFSIZE INT_MIN enum eqn_boxt type; /* type of node */ struct eqn_box *first; /* first child node */ struct eqn_box *last; /* last child node */ struct eqn_box *next; /* node sibling */ struct eqn_box *prev; /* node sibling */ struct eqn_box *parent; /* node sibling */ char *text; /* text (or NULL) */ char *left; /* fence left-hand */ char *right; /* fence right-hand */ char *top; /* expression over-symbol */ char *bottom; /* expression under-symbol */ size_t args; /* arguments in parent */ size_t expectargs; /* max arguments in parent */ enum eqn_post pos; /* position of next box */ enum eqn_fontt font; /* font of box */ enum eqn_pilet pile; /* equation piling */ }; /* * An equation consists of a tree of expressions starting at a given * line and position. */ struct eqn { char *name; /* identifier (or NULL) */ struct eqn_box *root; /* root mathematical expression */ int ln; /* invocation line */ int pos; /* invocation position */ }; /* * Parse options. */ #define MPARSE_MDOC 1 /* assume -mdoc */ #define MPARSE_MAN 2 /* assume -man */ #define MPARSE_SO 4 /* honour .so requests */ #define MPARSE_QUICK 8 /* abort the parse early */ #define MPARSE_UTF8 16 /* accept UTF-8 input */ #define MPARSE_LATIN1 32 /* accept ISO-LATIN-1 input */ enum mandoc_esc { ESCAPE_ERROR = 0, /* bail! unparsable escape */ ESCAPE_IGNORE, /* escape to be ignored */ ESCAPE_SPECIAL, /* a regular special character */ ESCAPE_FONT, /* a generic font mode */ ESCAPE_FONTBOLD, /* bold font mode */ ESCAPE_FONTITALIC, /* italic font mode */ ESCAPE_FONTBI, /* bold italic font mode */ ESCAPE_FONTROMAN, /* roman font mode */ ESCAPE_FONTPREV, /* previous font mode */ ESCAPE_NUMBERED, /* a numbered glyph */ ESCAPE_UNICODE, /* a unicode codepoint */ ESCAPE_NOSPACE, /* suppress space if the last on a line */ ESCAPE_SKIPCHAR, /* skip the next character */ ESCAPE_OVERSTRIKE /* overstrike all chars in the argument */ }; typedef void (*mandocmsg)(enum mandocerr, enum mandoclevel, const char *, int, int, const char *); struct mparse; struct roff_man; enum mandoc_esc mandoc_escape(const char **, const char **, int *); void mchars_alloc(void); void mchars_free(void); int mchars_num2char(const char *, size_t); const char *mchars_uc2str(int); int mchars_num2uc(const char *, size_t); int mchars_spec2cp(const char *, size_t); const char *mchars_spec2str(const char *, size_t, size_t *); struct mparse *mparse_alloc(int, enum mandoclevel, mandocmsg, const char *); void mparse_free(struct mparse *); void mparse_keep(struct mparse *); int mparse_open(struct mparse *, const char *); enum mandoclevel mparse_readfd(struct mparse *, int, const char *); enum mandoclevel mparse_readmem(struct mparse *, void *, size_t, const char *); void mparse_reset(struct mparse *); void mparse_result(struct mparse *, struct roff_man **, char **); const char *mparse_getkeep(const struct mparse *); const char *mparse_strerror(enum mandocerr); const char *mparse_strlevel(enum mandoclevel); void mparse_updaterc(struct mparse *, enum mandoclevel *); Index: vendor/mdocml/dist/mandoc_aux.h =================================================================== --- vendor/mdocml/dist/mandoc_aux.h (revision 313955) +++ vendor/mdocml/dist/mandoc_aux.h (revision 313956) @@ -1,26 +1,26 @@ -/* $Id: mandoc_aux.h,v 1.5 2016/07/19 13:36:13 schwarze Exp $ */ +/* $Id: mandoc_aux.h,v 1.6 2017/02/17 14:31:52 schwarze Exp $ */ /* * Copyright (c) 2009, 2011 Kristaps Dzonsons * Copyright (c) 2014 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. */ int mandoc_asprintf(char **, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); + __attribute__((__format__ (__printf__, 2, 3))); void *mandoc_calloc(size_t, size_t); void *mandoc_malloc(size_t); void *mandoc_realloc(void *, size_t); void *mandoc_reallocarray(void *, size_t, size_t); char *mandoc_strdup(const char *); char *mandoc_strndup(const char *, size_t); Index: vendor/mdocml/dist/mandoc_char.7 =================================================================== --- vendor/mdocml/dist/mandoc_char.7 (revision 313955) +++ vendor/mdocml/dist/mandoc_char.7 (revision 313956) @@ -1,765 +1,789 @@ -.\" $Id: mandoc_char.7,v 1.63 2015/09/02 15:38:35 schwarze Exp $ +.\" $Id: mandoc_char.7,v 1.64 2017/02/05 21:41:21 schwarze Exp $ .\" .\" Copyright (c) 2003 Jason McIntyre .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons .\" Copyright (c) 2011, 2013, 2015 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 2 2015 $ +.Dd $Mdocdate: February 5 2017 $ .Dt MANDOC_CHAR 7 .Os .Sh NAME .Nm mandoc_char .Nd mandoc special characters .Sh DESCRIPTION This page documents the .Xr roff 7 escape sequences accepted by .Xr mandoc 1 to represent special characters in .Xr mdoc 7 and .Xr man 7 documents. .Pp The rendering depends on the .Xr mandoc 1 output mode; in ASCII output, most characters are completely unintelligible. For that reason, using any of the special characters documented here, except those discussed in the .Sx DESCRIPTION , is strongly discouraged; they are supported merely for backwards compatibility with existing documents. .Pp In particular, in English manual pages, do not use special-character escape sequences to represent national language characters in author names; instead, provide ASCII transcriptions of the names. .Ss Dashes and Hyphens In typography there are different types of dashes of various width: the hyphen (-), the minus sign (\(mi), the en-dash (\(en), and the em-dash (\(em). .Pp Hyphens are used for adjectives; to separate the two parts of a compound word; or to separate a word across two successive lines of text. The hyphen does not need to be escaped: .Bd -unfilled -offset indent blue-eyed lorry-driver .Ed +.Pp +If a word on a text input line contains a hyphen, a formatter may decide +to insert an output line break after the hyphen if that helps filling +the current output line, but the whole word would overflow the line. +If it is important that the word is not broken across lines in this +way, a zero-width space +.Pq Sq \e& +can be inserted before or after the hyphen. +While +.Xr mandoc 1 +never breaks the output line after hyphens adjacent to a zero-width +space, after any of the other dash- or hyphen-like characters +represented by escape sequences, or after hyphens inside words in +macro arguments, other software may not respect these rules and may +break the line even in such cases. +.Pp +Some +.Xr roff 7 +implementations contains dictionaries allowing to break the line +at syllable boundaries even inside words that contain no hyphens. +Such automatic hyphenation is not supported by +.Xr mandoc 1 , +which only breaks the line at whitespace, and inside words only +after existing hyphens. .Pp The mathematical minus sign is used for negative numbers or subtraction. It should be written as .Sq \e(mi : .Bd -unfilled -offset indent a = 3 \e(mi 1; b = \e(mi2; .Ed .Pp The en-dash is used to separate the two elements of a range, or can be used the same way as an em-dash. It should be written as .Sq \e(en : .Bd -unfilled -offset indent pp. 95\e(en97. Go away \e(en or else! .Ed .Pp The em-dash can be used to show an interruption or can be used the same way as colons, semi-colons, or parentheses. It should be written as .Sq \e(em : .Bd -unfilled -offset indent Three things \e(em apples, oranges, and bananas. This is not that \e(em rather, this is that. .Ed .Pp Note: hyphens, minus signs, and en-dashes look identical under normal ASCII output. Other formats, such as PostScript, render them correctly, with differing widths. .Ss Spaces To separate words in normal text, for indenting and alignment in literal context, and when none of the following special cases apply, just use the normal space character .Pq Sq \ . .Pp When filling text, output lines may be broken between words, i.e. at space characters. To prevent a line break between two particular words, use the unpaddable non-breaking space escape sequence .Pq Sq \e\ \& instead of the normal space character. For example, the input string .Dq number\e\ 1 will be kept together as .Dq number\ 1 on the same output line. .Pp On request and macro lines, the normal space character serves as an argument delimiter. To include whitespace into arguments, quoting is usually the best choice; see the MACRO SYNTAX section in .Xr roff 7 . In some cases, using the non-breaking space escape sequence .Pq Sq \e\ \& may be preferable. .Pp To escape macro names and to protect whitespace at the end of input lines, the zero-width space .Pq Sq \e& is often useful. For example, in .Xr mdoc 7 , a normal space character can be displayed in single quotes in either of the following ways: .Pp .Dl .Sq \(dq \(dq .Dl .Sq \e \e& .Ss Quotes On request and macro lines, the double-quote character .Pq Sq \(dq is handled specially to allow quoting. One way to prevent this special handling is by using the .Sq \e(dq escape sequence. .Pp Note that on text lines, literal double-quote characters can be used verbatim. All other quote-like characters can be used verbatim as well, even on request and macro lines. .Ss Accents In output modes supporting such special output characters, for example .Fl T Cm pdf , some .Xr roff 7 formatters convert the following ASCII input characters to the following Unicode special output characters: .Bl -column x(ga U+2018 -offset indent .It \(ga Ta U+2018 Ta left single quotation mark .It \(aq Ta U+2019 Ta right single quotation mark .It \(ti Ta U+02DC Ta small tilde .El .Pp In prose, this automatic substitution is often desirable; but when these characters have to be displayed as plain ASCII characters, for example in source code samples, they require escaping to render as follows: .Bl -column x(ga U+2018 -offset indent .It \e(ga Ta U+0060 Ta grave accent .It \e(aq Ta U+0027 Ta apostrophe .It \e(ti Ta U+007E Ta tilde .El .Ss Periods The period .Pq Sq \&. is handled specially at the beginning of an input line, where it introduces a .Xr roff 7 request or a macro, and when appearing alone as a macro argument in .Xr mdoc 7 . In such situations, prepend a zero-width space .Pq Sq \e&. to make it behave like normal text. .Pp Do not use the .Sq \e. escape sequence. It does not prevent special handling of the period. .Ss Backslashes To include a literal backslash .Pq Sq \e into the output, use the .Pq Sq \ee escape sequence. .Pp Note that doubling it .Pq Sq \e\e is not the right way to output a backslash. Because .Xr mandoc 1 does not implement full .Xr roff 7 functionality, it may work with .Xr mandoc 1 , but it may have weird effects on complete .Xr roff 7 implementations. .Sh SPECIAL CHARACTERS Special characters are encoded as .Sq \eX .Pq for a one-character escape , .Sq \e(XX .Pq two-character , and .Sq \e[N] .Pq N-character . For details, see the .Em Special Characters subsection of the .Xr roff 7 manual. .Pp Spacing: .Bl -column "Input" "Description" -offset indent -compact .It Em Input Ta Em Description .It Sq \e\ \& Ta unpaddable non-breaking space .It \e\(ti Ta paddable non-breaking space .It \e0 Ta unpaddable, breaking digit-width space .It \e| Ta one-sixth \e(em narrow space, zero width in nroff mode .It \e^ Ta one-twelfth \e(em half-narrow space, zero width in nroff .It \e& Ta zero-width space .It \e% Ta zero-width space allowing hyphenation .El .Pp Lines: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(ba Ta \(ba Ta bar .It \e(br Ta \(br Ta box rule .It \e(ul Ta \(ul Ta underscore .It \e(rn Ta \(rn Ta overline .It \e(bb Ta \(bb Ta broken bar .It \e(sl Ta \(sl Ta forward slash .It \e(rs Ta \(rs Ta backward slash .El .Pp Text markers: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(ci Ta \(ci Ta circle .It \e(bu Ta \(bu Ta bullet .It \e(dd Ta \(dd Ta double dagger .It \e(dg Ta \(dg Ta dagger .It \e(lz Ta \(lz Ta lozenge .It \e(sq Ta \(sq Ta white square .It \e(ps Ta \(ps Ta paragraph .It \e(sc Ta \(sc Ta section .It \e(lh Ta \(lh Ta left hand .It \e(rh Ta \(rh Ta right hand .It \e(at Ta \(at Ta at .It \e(sh Ta \(sh Ta hash (pound) .It \e(CR Ta \(CR Ta carriage return .It \e(OK Ta \(OK Ta check mark .El .Pp Legal symbols: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(co Ta \(co Ta copyright .It \e(rg Ta \(rg Ta registered .It \e(tm Ta \(tm Ta trademarked .El .Pp Punctuation: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(em Ta \(em Ta em-dash .It \e(en Ta \(en Ta en-dash .It \e(hy Ta \(hy Ta hyphen .It \ee Ta \e Ta back-slash .It \e. Ta \. Ta period .It \e(r! Ta \(r! Ta upside-down exclamation .It \e(r? Ta \(r? Ta upside-down question .El .Pp Quotes: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(Bq Ta \(Bq Ta right low double-quote .It \e(bq Ta \(bq Ta right low single-quote .It \e(lq Ta \(lq Ta left double-quote .It \e(rq Ta \(rq Ta right double-quote .It \e(oq Ta \(oq Ta left single-quote .It \e(cq Ta \(cq Ta right single-quote .It \e(aq Ta \(aq Ta apostrophe quote (text) .It \e(dq Ta \(dq Ta double quote (text) .It \e(Fo Ta \(Fo Ta left guillemet .It \e(Fc Ta \(Fc Ta right guillemet .It \e(fo Ta \(fo Ta left single guillemet .It \e(fc Ta \(fc Ta right single guillemet .El .Pp Brackets: .Bl -column "xxbracketrightbtx" Rendered Description -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(lB Ta \(lB Ta left bracket .It \e(rB Ta \(rB Ta right bracket .It \e(lC Ta \(lC Ta left brace .It \e(rC Ta \(rC Ta right brace .It \e(la Ta \(la Ta left angle .It \e(ra Ta \(ra Ta right angle .It \e(bv Ta \(bv Ta brace extension .It \e[braceex] Ta \[braceex] Ta brace extension .It \e[bracketlefttp] Ta \[bracketlefttp] Ta top-left hooked bracket .It \e[bracketleftbt] Ta \[bracketleftbt] Ta bottom-left hooked bracket .It \e[bracketleftex] Ta \[bracketleftex] Ta left hooked bracket extension .It \e[bracketrighttp] Ta \[bracketrighttp] Ta top-right hooked bracket .It \e[bracketrightbt] Ta \[bracketrightbt] Ta bottom-right hooked bracket .It \e[bracketrightex] Ta \[bracketrightex] Ta right hooked bracket extension .It \e(lt Ta \(lt Ta top-left hooked brace .It \e[bracelefttp] Ta \[bracelefttp] Ta top-left hooked brace .It \e(lk Ta \(lk Ta mid-left hooked brace .It \e[braceleftmid] Ta \[braceleftmid] Ta mid-left hooked brace .It \e(lb Ta \(lb Ta bottom-left hooked brace .It \e[braceleftbt] Ta \[braceleftbt] Ta bottom-left hooked brace .It \e[braceleftex] Ta \[braceleftex] Ta left hooked brace extension .It \e(rt Ta \(rt Ta top-left hooked brace .It \e[bracerighttp] Ta \[bracerighttp] Ta top-right hooked brace .It \e(rk Ta \(rk Ta mid-right hooked brace .It \e[bracerightmid] Ta \[bracerightmid] Ta mid-right hooked brace .It \e(rb Ta \(rb Ta bottom-right hooked brace .It \e[bracerightbt] Ta \[bracerightbt] Ta bottom-right hooked brace .It \e[bracerightex] Ta \[bracerightex] Ta right hooked brace extension .It \e[parenlefttp] Ta \[parenlefttp] Ta top-left hooked parenthesis .It \e[parenleftbt] Ta \[parenleftbt] Ta bottom-left hooked parenthesis .It \e[parenleftex] Ta \[parenleftex] Ta left hooked parenthesis extension .It \e[parenrighttp] Ta \[parenrighttp] Ta top-right hooked parenthesis .It \e[parenrightbt] Ta \[parenrightbt] Ta bottom-right hooked parenthesis .It \e[parenrightex] Ta \[parenrightex] Ta right hooked parenthesis extension .El .Pp Arrows: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(<- Ta \(<- Ta left arrow .It \e(-> Ta \(-> Ta right arrow .It \e(<> Ta \(<> Ta left-right arrow .It \e(da Ta \(da Ta down arrow .It \e(ua Ta \(ua Ta up arrow .It \e(va Ta \(va Ta up-down arrow .It \e(lA Ta \(lA Ta left double-arrow .It \e(rA Ta \(rA Ta right double-arrow .It \e(hA Ta \(hA Ta left-right double-arrow .It \e(uA Ta \(uA Ta up double-arrow .It \e(dA Ta \(dA Ta down double-arrow .It \e(vA Ta \(vA Ta up-down double-arrow .El .Pp Logical: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(AN Ta \(AN Ta logical and .It \e(OR Ta \(OR Ta logical or .It \e(no Ta \(no Ta logical not .It \e[tno] Ta \[tno] Ta logical not (text) .It \e(te Ta \(te Ta existential quantifier .It \e(fa Ta \(fa Ta universal quantifier .It \e(st Ta \(st Ta such that .It \e(tf Ta \(tf Ta therefore .It \e(3d Ta \(3d Ta therefore .It \e(or Ta \(or Ta bitwise or .El .Pp Mathematical: .Bl -column "xxcoproductxx" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(pl Ta \(pl Ta plus .It \e(mi Ta \(mi Ta minus .It \e- Ta \- Ta minus (text) .It \e(-+ Ta \(-+ Ta minus-plus .It \e(+- Ta \(+- Ta plus-minus .It \e[t+-] Ta \[t+-] Ta plus-minus (text) .It \e(pc Ta \(pc Ta center-dot .It \e(mu Ta \(mu Ta multiply .It \e[tmu] Ta \[tmu] Ta multiply (text) .It \e(c* Ta \(c* Ta circle-multiply .It \e(c+ Ta \(c+ Ta circle-plus .It \e(di Ta \(di Ta divide .It \e[tdi] Ta \[tdi] Ta divide (text) .It \e(f/ Ta \(f/ Ta fraction .It \e(** Ta \(** Ta asterisk .It \e(<= Ta \(<= Ta less-than-equal .It \e(>= Ta \(>= Ta greater-than-equal .It \e(<< Ta \(<< Ta much less .It \e(>> Ta \(>> Ta much greater .It \e(eq Ta \(eq Ta equal .It \e(!= Ta \(!= Ta not equal .It \e(== Ta \(== Ta equivalent .It \e(ne Ta \(ne Ta not equivalent .It \e(ap Ta \(ap Ta tilde operator .It \e(|= Ta \(|= Ta asymptotically equal .It \e(=\(ti Ta \(=~ Ta approximately equal .It \e(\(ti\(ti Ta \(~~ Ta almost equal .It \e(\(ti= Ta \(~= Ta almost equal .It \e(pt Ta \(pt Ta proportionate .It \e(es Ta \(es Ta empty set .It \e(mo Ta \(mo Ta element .It \e(nm Ta \(nm Ta not element .It \e(sb Ta \(sb Ta proper subset .It \e(nb Ta \(nb Ta not subset .It \e(sp Ta \(sp Ta proper superset .It \e(nc Ta \(nc Ta not superset .It \e(ib Ta \(ib Ta reflexive subset .It \e(ip Ta \(ip Ta reflexive superset .It \e(ca Ta \(ca Ta intersection .It \e(cu Ta \(cu Ta union .It \e(/_ Ta \(/_ Ta angle .It \e(pp Ta \(pp Ta perpendicular .It \e(is Ta \(is Ta integral .It \e[integral] Ta \[integral] Ta integral .It \e[sum] Ta \[sum] Ta summation .It \e[product] Ta \[product] Ta product .It \e[coproduct] Ta \[coproduct] Ta coproduct .It \e(gr Ta \(gr Ta gradient .It \e(sr Ta \(sr Ta square root .It \e[sqrt] Ta \[sqrt] Ta square root .It \e(lc Ta \(lc Ta left-ceiling .It \e(rc Ta \(rc Ta right-ceiling .It \e(lf Ta \(lf Ta left-floor .It \e(rf Ta \(rf Ta right-floor .It \e(if Ta \(if Ta infinity .It \e(Ah Ta \(Ah Ta aleph .It \e(Im Ta \(Im Ta imaginary .It \e(Re Ta \(Re Ta real .It \e(pd Ta \(pd Ta partial differential .It \e(-h Ta \(-h Ta Planck constant over 2\(*p .It \e[12] Ta \[12] Ta one-half .It \e[14] Ta \[14] Ta one-fourth .It \e[34] Ta \[34] Ta three-fourths .El .Pp Ligatures: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(ff Ta \(ff Ta ff ligature .It \e(fi Ta \(fi Ta fi ligature .It \e(fl Ta \(fl Ta fl ligature .It \e(Fi Ta \(Fi Ta ffi ligature .It \e(Fl Ta \(Fl Ta ffl ligature .It \e(AE Ta \(AE Ta AE .It \e(ae Ta \(ae Ta ae .It \e(OE Ta \(OE Ta OE .It \e(oe Ta \(oe Ta oe .It \e(ss Ta \(ss Ta German eszett .It \e(IJ Ta \(IJ Ta IJ ligature .It \e(ij Ta \(ij Ta ij ligature .El .Pp Accents: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(a" Ta \(a" Ta Hungarian umlaut .It \e(a- Ta \(a- Ta macron .It \e(a. Ta \(a. Ta dotted .It \e(a^ Ta \(a^ Ta circumflex .It \e(aa Ta \(aa Ta acute .It \e\(aq Ta \' Ta acute .It \e(ga Ta \(ga Ta grave .It \e\(ga Ta \` Ta grave .It \e(ab Ta \(ab Ta breve .It \e(ac Ta \(ac Ta cedilla .It \e(ad Ta \(ad Ta dieresis .It \e(ah Ta \(ah Ta caron .It \e(ao Ta \(ao Ta ring .It \e(a\(ti Ta \(a~ Ta tilde .It \e(ho Ta \(ho Ta ogonek .It \e(ha Ta \(ha Ta hat (text) .It \e(ti Ta \(ti Ta tilde (text) .El .Pp Accented letters: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(\(aqA Ta \('A Ta acute A .It \e(\(aqE Ta \('E Ta acute E .It \e(\(aqI Ta \('I Ta acute I .It \e(\(aqO Ta \('O Ta acute O .It \e(\(aqU Ta \('U Ta acute U .It \e(\(aqa Ta \('a Ta acute a .It \e(\(aqe Ta \('e Ta acute e .It \e(\(aqi Ta \('i Ta acute i .It \e(\(aqo Ta \('o Ta acute o .It \e(\(aqu Ta \('u Ta acute u .It \e(\(gaA Ta \(`A Ta grave A .It \e(\(gaE Ta \(`E Ta grave E .It \e(\(gaI Ta \(`I Ta grave I .It \e(\(gaO Ta \(`O Ta grave O .It \e(\(gaU Ta \(`U Ta grave U .It \e(\(gaa Ta \(`a Ta grave a .It \e(\(gae Ta \(`e Ta grave e .It \e(\(gai Ta \(`i Ta grave i .It \e(\(gao Ta \(`i Ta grave o .It \e(\(gau Ta \(`u Ta grave u .It \e(\(tiA Ta \(~A Ta tilde A .It \e(\(tiN Ta \(~N Ta tilde N .It \e(\(tiO Ta \(~O Ta tilde O .It \e(\(tia Ta \(~a Ta tilde a .It \e(\(tin Ta \(~n Ta tilde n .It \e(\(tio Ta \(~o Ta tilde o .It \e(:A Ta \(:A Ta dieresis A .It \e(:E Ta \(:E Ta dieresis E .It \e(:I Ta \(:I Ta dieresis I .It \e(:O Ta \(:O Ta dieresis O .It \e(:U Ta \(:U Ta dieresis U .It \e(:a Ta \(:a Ta dieresis a .It \e(:e Ta \(:e Ta dieresis e .It \e(:i Ta \(:i Ta dieresis i .It \e(:o Ta \(:o Ta dieresis o .It \e(:u Ta \(:u Ta dieresis u .It \e(:y Ta \(:y Ta dieresis y .It \e(^A Ta \(^A Ta circumflex A .It \e(^E Ta \(^E Ta circumflex E .It \e(^I Ta \(^I Ta circumflex I .It \e(^O Ta \(^O Ta circumflex O .It \e(^U Ta \(^U Ta circumflex U .It \e(^a Ta \(^a Ta circumflex a .It \e(^e Ta \(^e Ta circumflex e .It \e(^i Ta \(^i Ta circumflex i .It \e(^o Ta \(^o Ta circumflex o .It \e(^u Ta \(^u Ta circumflex u .It \e(,C Ta \(,C Ta cedilla C .It \e(,c Ta \(,c Ta cedilla c .It \e(/L Ta \(/L Ta stroke L .It \e(/l Ta \(/l Ta stroke l .It \e(/O Ta \(/O Ta stroke O .It \e(/o Ta \(/o Ta stroke o .It \e(oA Ta \(oA Ta ring A .It \e(oa Ta \(oa Ta ring a .El .Pp Special letters: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(-D Ta \(-D Ta Eth .It \e(Sd Ta \(Sd Ta eth .It \e(TP Ta \(TP Ta Thorn .It \e(Tp Ta \(Tp Ta thorn .It \e(.i Ta \(.i Ta dotless i .It \e(.j Ta \(.j Ta dotless j .El .Pp Currency: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(Do Ta \(Do Ta dollar .It \e(ct Ta \(ct Ta cent .It \e(Eu Ta \(Eu Ta Euro symbol .It \e(eu Ta \(eu Ta Euro symbol .It \e(Ye Ta \(Ye Ta yen .It \e(Po Ta \(Po Ta pound .It \e(Cs Ta \(Cs Ta Scandinavian .It \e(Fn Ta \(Fn Ta florin .El .Pp Units: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(de Ta \(de Ta degree .It \e(%0 Ta \(%0 Ta per-thousand .It \e(fm Ta \(fm Ta minute .It \e(sd Ta \(sd Ta second .It \e(mc Ta \(mc Ta micro .El .Pp Greek letters: .Bl -column "Input" "Rendered" "Description" -offset indent -compact .It Em Input Ta Em Rendered Ta Em Description .It \e(*A Ta \(*A Ta Alpha .It \e(*B Ta \(*B Ta Beta .It \e(*G Ta \(*G Ta Gamma .It \e(*D Ta \(*D Ta Delta .It \e(*E Ta \(*E Ta Epsilon .It \e(*Z Ta \(*Z Ta Zeta .It \e(*Y Ta \(*Y Ta Eta .It \e(*H Ta \(*H Ta Theta .It \e(*I Ta \(*I Ta Iota .It \e(*K Ta \(*K Ta Kappa .It \e(*L Ta \(*L Ta Lambda .It \e(*M Ta \(*M Ta Mu .It \e(*N Ta \(*N Ta Nu .It \e(*C Ta \(*C Ta Xi .It \e(*O Ta \(*O Ta Omicron .It \e(*P Ta \(*P Ta Pi .It \e(*R Ta \(*R Ta Rho .It \e(*S Ta \(*S Ta Sigma .It \e(*T Ta \(*T Ta Tau .It \e(*U Ta \(*U Ta Upsilon .It \e(*F Ta \(*F Ta Phi .It \e(*X Ta \(*X Ta Chi .It \e(*Q Ta \(*Q Ta Psi .It \e(*W Ta \(*W Ta Omega .It \e(*a Ta \(*a Ta alpha .It \e(*b Ta \(*b Ta beta .It \e(*g Ta \(*g Ta gamma .It \e(*d Ta \(*d Ta delta .It \e(*e Ta \(*e Ta epsilon .It \e(*z Ta \(*z Ta zeta .It \e(*y Ta \(*y Ta eta .It \e(*h Ta \(*h Ta theta .It \e(*i Ta \(*i Ta iota .It \e(*k Ta \(*k Ta kappa .It \e(*l Ta \(*l Ta lambda .It \e(*m Ta \(*m Ta mu .It \e(*n Ta \(*n Ta nu .It \e(*c Ta \(*c Ta xi .It \e(*o Ta \(*o Ta omicron .It \e(*p Ta \(*p Ta pi .It \e(*r Ta \(*r Ta rho .It \e(*s Ta \(*s Ta sigma .It \e(*t Ta \(*t Ta tau .It \e(*u Ta \(*u Ta upsilon .It \e(*f Ta \(*f Ta phi .It \e(*x Ta \(*x Ta chi .It \e(*q Ta \(*q Ta psi .It \e(*w Ta \(*w Ta omega .It \e(+h Ta \(+h Ta theta variant .It \e(+f Ta \(+f Ta phi variant .It \e(+p Ta \(+p Ta pi variant .It \e(+e Ta \(+e Ta epsilon variant .It \e(ts Ta \(ts Ta sigma terminal .El .Sh PREDEFINED STRINGS Predefined strings are inherited from the macro packages of historical troff implementations. They are .Em not recommended for use, as they differ across implementations. Manuals using these predefined strings are almost certainly not portable. .Pp Their syntax is similar to special characters, using .Sq \e*X .Pq for a one-character escape , .Sq \e*(XX .Pq two-character , and .Sq \e*[N] .Pq N-character . For details, see the .Em Predefined Strings subsection of the .Xr roff 7 manual. .Bl -column "Input" "Rendered" "Description" -offset indent .It Em Input Ta Em Rendered Ta Em Description .It \e*(Ba Ta \*(Ba Ta vertical bar .It \e*(Ne Ta \*(Ne Ta not equal .It \e*(Ge Ta \*(Ge Ta greater-than-equal .It \e*(Le Ta \*(Le Ta less-than-equal .It \e*(Gt Ta \*(Gt Ta greater-than .It \e*(Lt Ta \*(Lt Ta less-than .It \e*(Pm Ta \*(Pm Ta plus-minus .It \e*(If Ta \*(If Ta infinity .It \e*(Pi Ta \*(Pi Ta pi .It \e*(Na Ta \*(Na Ta NaN .It \e*(Am Ta \*(Am Ta ampersand .It \e*R Ta \*R Ta restricted mark .It \e*(Tm Ta \*(Tm Ta trade mark .It \e*q Ta \*q Ta double-quote .It \e*(Rq Ta \*(Rq Ta right-double-quote .It \e*(Lq Ta \*(Lq Ta left-double-quote .It \e*(lp Ta \*(lp Ta right-parenthesis .It \e*(rp Ta \*(rp Ta left-parenthesis .It \e*(lq Ta \*(lq Ta left double-quote .It \e*(rq Ta \*(rq Ta right double-quote .It \e*(ua Ta \*(ua Ta up arrow .It \e*(va Ta \*(va Ta up-down arrow .It \e*(<= Ta \*(<= Ta less-than-equal .It \e*(>= Ta \*(>= Ta greater-than-equal .It \e*(aa Ta \*(aa Ta acute .It \e*(ga Ta \*(ga Ta grave .It \e*(Px Ta \*(Px Ta POSIX standard name .It \e*(Ai Ta \*(Ai Ta ANSI standard name .El .Sh UNICODE CHARACTERS The escape sequences .Pp .Dl \e[uXXXX] and \eC\(aquXXXX\(aq .Pp are interpreted as Unicode codepoints. The codepoint must be in the range above U+0080 and less than U+10FFFF. For compatibility, the hexadecimal digits .Sq A to .Sq F must be given as uppercase characters, and points must be zero-padded to four characters; if greater than four characters, no zero padding is allowed. Unicode surrogates are not allowed. .Sh NUMBERED CHARACTERS For backward compatibility with existing manuals, .Xr mandoc 1 also supports the .Pp .Dl \eN\(aq Ns Ar number Ns \(aq .Pp escape sequence, inserting the character .Ar number from the current character set into the output. Of course, this is inherently non-portable and is already marked as deprecated in the Heirloom roff manual. For example, do not use \eN\(aq34\(aq, use \e(dq, or even the plain .Sq \(dq character where possible. .Sh COMPATIBILITY This section documents compatibility between mandoc and other troff implementations, at this time limited to GNU troff .Pq Qq groff . .Pp .Bl -dash -compact .It The \eN\(aq\(aq escape sequence is limited to printable characters; in groff, it accepts arbitrary character numbers. .It In .Fl T Ns Cm ascii , the \e(ss, \e(nm, \e(nb, \e(nc, \e(ib, \e(ip, \e(pp, \e[sum], \e[product], \e[coproduct], \e(gr, \e(-h, and \e(a. special characters render differently between mandoc and groff. .It In .Fl T Ns Cm html and .Fl T Ns Cm xhtml , the \e(\(ti=, \e(nb, and \e(nc special characters render differently between mandoc and groff. .It The .Fl T Ns Cm ps and .Fl T Ns Cm pdf modes format like .Fl T Ns Cm ascii instead of rendering glyphs as in groff. .It The \e[radicalex], \e[sqrtex], and \e(ru special characters have been omitted from mandoc either because they are poorly documented or they have no known representation. .El .Sh SEE ALSO .Xr mandoc 1 , .Xr man 7 , .Xr mdoc 7 , .Xr roff 7 .Sh AUTHORS The .Nm manual page was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . .Sh CAVEATS The predefined string .Sq \e*(Ba mimics the behaviour of the .Sq \&| character in .Xr mdoc 7 ; thus, if you wish to render a vertical bar with no side effects, use the .Sq \e(ba escape. Index: vendor/mdocml/dist/mandoc_html.3 =================================================================== --- vendor/mdocml/dist/mandoc_html.3 (revision 313955) +++ vendor/mdocml/dist/mandoc_html.3 (revision 313956) @@ -1,340 +1,355 @@ -.\" $Id: mandoc_html.3,v 1.3 2017/01/17 15:32:44 schwarze Exp $ +.\" $Id: mandoc_html.3,v 1.5 2017/01/28 22:36:38 schwarze Exp $ .\" .\" Copyright (c) 2014, 2017 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: January 17 2017 $ +.Dd $Mdocdate: January 28 2017 $ .Dt MANDOC_HTML 3 .Os .Sh NAME .Nm mandoc_html .Nd internals of the mandoc HTML formatter .Sh SYNOPSIS .In "html.h" .Ft void .Fn print_gen_decls "struct html *h" .Ft void .Fn print_gen_head "struct html *h" .Ft struct tag * .Fo print_otag .Fa "struct html *h" .Fa "enum htmltag tag" .Fa "const char *fmt" .Fa ... .Fc .Ft void .Fo print_tagq .Fa "struct html *h" .Fa "const struct tag *until" .Fc .Ft void .Fo print_stagq .Fa "struct html *h" .Fa "const struct tag *suntil" .Fc .Ft void .Fo print_text .Fa "struct html *h" .Fa "const char *word" .Fc .Sh DESCRIPTION The mandoc HTML formatter is not a formal library. However, as it is compiled into more than one program, in particular .Xr mandoc 1 and .Xr man.cgi 8 , and because it may be security-critical in some contexts, some documentation is useful to help to use it correctly and to prevent XSS vulnerabilities. .Pp The formatter produces HTML output on the standard output. Since proper escaping is usually required and best taken care of at one central place, the language-specific formatters .Po .Pa *_html.c , see .Sx FILES .Pc are not supposed to print directly to .Dv stdout using functions like .Xr printf 3 , .Xr putc 3 , .Xr puts 3 , or .Xr write 2 . Instead, they are expected to use the output functions declared in .Pa html.h and implemented as part of the main HTML formatting engine in .Pa html.c . .Ss Data structures These structures are declared in .Pa html.h . .Bl -tag -width Ds .It Vt struct html Internal state of the HTML formatter. .It Vt struct tag One entry for the LIFO stack of HTML elements. Members are .Fa "enum htmltag tag" and .Fa "struct tag *next" . .El .Ss Private interface functions The function .Fn print_gen_decls prints the opening .Ao Pf \&? Ic xml ? Ac and .Aq Pf \&! Ic DOCTYPE declarations required for the current document type. .Pp The function .Fn print_gen_head prints the opening .Aq Ic META and .Aq Ic LINK elements for the document .Aq Ic HEAD , using the .Fa style member of .Fa h unless that is .Dv NULL . It uses .Fn print_otag which takes care of properly encoding attributes, which is relevant for the .Fa style link in particular. .Pp The function .Fn print_otag prints the start tag of an HTML element with the name .Fa tag , optionally including the attributes specified by .Fa fmt . If .Fa fmt is the empty string, no attributes are written. Each letter of .Fa fmt specifies one attribute to write. Most attributes require one .Va char * argument which becomes the value of the attribute. The arguments have to be given in the same order as the attribute letters. +If an argument is +.Dv NULL , +the respective attribute is not written. .Bl -tag -width 1n -offset indent .It Cm c Print a .Cm class attribute. .It Cm h Print a .Cm href attribute. This attribute letter can optionally be followed by a modifier letter. If followed by .Cm R , it formats the link as a local one by prefixing a .Sq # character. If followed by .Cm I , it interpretes the argument as a header file name and generates a link using the .Xr mandoc 1 .Fl O Cm includes option. If followed by .Cm M , it takes two arguments instead of one, a manual page name and section, and formats them as a link to a manual page using the .Xr mandoc 1 .Fl O Cm man option. .It Cm i Print an .Cm id attribute. .It Cm \&? Print an arbitrary attribute. This format letter requires two .Vt char * arguments, the attribute name and the value. +The name must not be +.Dv NULL . .It Cm s Print a .Cm style attribute. If present, it must be the last format letter. In contrast to the other format letters, this one does not yet -print the value and does not require an argument. +print the value and does not take an argument. Instead, the rest of the format string consists of pairs of argument type letters and style name letters. .El .Pp Argument type letters each require on argument as follows: .Bl -tag -width 1n -offset indent .It Cm h Requires one .Vt int argument, interpreted as a horizontal length in units of .Dv SCALE_EN . .It Cm s Requires one .Vt char * argument, used as a style value. .It Cm u Requires one .Vt struct roffsu * argument, used as a length. .It Cm v Requires one .Vt int argument, interpreted as a vertical length in units of .Dv SCALE_VS . .It Cm w Requires one .Vt char * argument, interpreted as an .Xr mdoc 7 Ns -style width specifier. +If the argument is +.Dv NULL , +nothing is printed for this pair. +.It Cm W +Similar to +.Cm w , +but makes the width negative by multiplying it with \(mi1. .El .Pp Style name letters decide what to do with the preceding argument: .Bl -tag -width 1n -offset indent .It Cm b Set .Cm margin-bottom to the given length. .It Cm h Set .Cm height to the given length. .It Cm i Set .Cm text-indent to the given length. .It Cm l Set .Cm margin-left to the given length. .It Cm t Set .Cm margin-top to the given length. .It Cm w Set .Cm width to the given length. .It Cm W Set .Cm min-width to the given length. .It Cm \&? The special pair .Cm s? requires two .Vt char * arguments. The first is the style name, the second its value. +The style name must not be +.Dv NULL . .El .Pp .Fn print_otag uses the private function .Fn print_encode to take care of HTML encoding. If required by the element type, it remembers in .Fa h that the element is open. The function .Fn print_tagq is used to close out all open elements up to and including .Fa until ; .Fn print_stagq is a variant to close out all open elements up to but excluding .Fa suntil . .Pp The function .Fn print_text prints HTML element content. It uses the private function .Fn print_encode to take care of HTML encoding. If the document has requested a non-standard font, for example using a .Xr roff 7 .Ic \ef font escape sequence, .Fn print_text wraps .Fa word in an HTML font selection element using the .Fn print_otag and .Fn print_tagq functions. .Pp The functions .Fn html_strlen , .Fn print_eqn , .Fn print_tbl , and .Fn print_tblclose are not yet documented. .Sh FILES .Bl -tag -width mandoc_aux.c -compact .It Pa main.h declarations of public functions for use by the main program, not yet documented .It Pa html.h declarations of data types and private functions for use by language-specific HTML formatters .It Pa html.c main HTML formatting engine and utility functions .It Pa mdoc_html.c .Xr mdoc 7 HTML formatter .It Pa man_html.c .Xr man 7 HTML formatter .It Pa tbl_html.c .Xr tbl 7 HTML formatter .It Pa eqn_html.c .Xr eqn 7 HTML formatter .It Pa out.h declarations of data types and private functions for shared use by all mandoc formatters, not yet documented .It Pa out.c private functions for shared use by all mandoc formatters .It Pa mandoc_aux.h declarations of common mandoc utility functions, see .Xr mandoc 3 .It Pa mandoc_aux.c implementation of common mandoc utility functions .El .Sh SEE ALSO .Xr mandoc 1 , .Xr mandoc 3 , .Xr man.cgi 8 .Sh AUTHORS .An -nosplit The mandoc HTML formatter was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . -This manual was written by -.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +It is maintained by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org , +who also wrote this manual. Index: vendor/mdocml/dist/mandocd.8 =================================================================== --- vendor/mdocml/dist/mandocd.8 (nonexistent) +++ vendor/mdocml/dist/mandocd.8 (revision 313956) @@ -0,0 +1,198 @@ +.\" $Id: mandocd.8,v 1.1 2017/02/06 19:04:21 schwarze Exp $ +.\" +.\" Copyright (c) 2017 Ingo Schwarze +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: February 6 2017 $ +.Dt MANDOCD 8 +.Os +.Sh NAME +.Nm mandocd +.Nd server process to format manual pages in batch mode +.Sh SYNOPSIS +.Nm mandocd +.Op Fl I Cm os Ns = Ns Ar name +.Op Fl T Ar output +.Ar socket_fd +.Sh DESCRIPTION +The +.Nm +utility formats many manual pages without requiring +.Xr fork 2 +and +.Xr exec 3 +overhead in between. +It does not require listing all the manuals to be formatted on the +command line, and it supports writing each formatted manual to its +own file descriptor. +.Pp +This server requires that a connected UNIX domain +.Xr socket 2 +is already present at +.Xr exec 3 +time. +Consequently, it cannot be started from the +.Xr sh 1 +command line because the shell cannot supply such a socket. +Typically, the socket is created by the parent process using +.Xr socketpair 2 +before calling +.Xr fork 2 +and +.Xr exec 3 +on +.Nm . +The parent process will pass the file descriptor number as an argument to +.Xr exec 3 , +formatted as a decimal ASCII-encoded integer. +See +.Xr catman 8 +for a typical implementation of a parent process. +.Pp +.Nm +loops reading one-byte messages with +.Xr recvmsg 2 +from the file descriptor number +.Ar socket_fd . +It ignores the byte read and only uses the out-of-band auxiliary +.Vt struct cmsghdr +control data, typically supplied by the calling process using +.Xr CMSG_FIRSTHDR 3 . +The parent process is expected to pass three file descriptors +with each dummy byte. +The first one is used for +.Xr mdoc 7 +or +.Xr man 7 +input, the second one for formatted output, and the third one +for error output. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl I Cm os Ns = Ns Ar name +Override the default operating system +.Ar name +for the +.Xr mdoc 7 +.Ic Os +and for the +.Xr man 7 +.Ic TH +macro. +.It Fl T Ar output +Output format. +The +.Ar output +argument can be +.Cm ascii , +.Cm utf8 , +or +.Cm html ; +see +.Xr mandoc 1 . +In +.Cm html +output mode, the +.Cm fragment +output option is implied. +Other output options are not supported. +.El +.Pp +After exhausting one input file descriptor, all three file descriptors +are closed before reading the next dummy byte and control message. +.Pp +When a zero-byte message is read, when the +.Ar socket_fd +is closed by the parent process, +or when an error occurs, +.Nm +exits. +.Sh EXIT STATUS +.Ex -std +.Pp +A zero-byte message or a closed +.Ar socket_fd +is considered success. +Possible errors include: +.Bl -bullet +.It +missing, invalid, or excessive +.Xr exec 3 +arguments +.It +.Xr recvmsg 2 +failure, for example due to +.Er EMSGSIZE +.It +missing or unexpected control data, in particular a +.Fa cmsg_level +in the +.Vt struct cmsghdr +that differs from +.Dv SOL_SOCKET , +a +.Fa cmsg_type +that differs from +.Dv SCM_RIGHTS , +or a +.Fa cmsg_len +that is not three times the size of an +.Vt int +.It +invalid file descriptors passed in the +.Xr CMSG_DATA 3 +.It +resource exhaustion, in particular +.Xr dup 2 +or +.Xr malloc 3 +failure +.El +.Pp +Except for memory exhaustion and similar system-level failures, +parsing and formatting errors do not cause +.Nm +to return an error exit status. +Even after severe parsing errors, +.Nm +will simply accept and process the next input file descriptor. +.Sh SEE ALSO +.Xr mandoc 1 , +.Xr mandoc 3 , +.Xr catman 8 +.Sh HISTORY +The +.Nm +utility appeared in version 1.14.1 or the +.Sy mandoc +toolkit. +.Sh AUTHORS +.An -nosplit +The concept was designed and implemented by +.An Michael Stapelberg Aq Mt stapelberg@debian.org . +The +.Xr mandoc 3 +glue needed to make it a stand-alone process was added by +.An Ingo Schwarze Aq Mt schwarze@openbsd.org . +.Sh CAVEATS +If the parsed manual pages contain +.Xr roff 7 +.Pf . Ic so +requests, +.Nm +needs to be started with the current working directory set to the +root of the manual page tree. +Avoid starting it in directories that contain secret files in any +subdirectories, in particular in the user starting it has read +access to these secret files. Property changes on: vendor/mdocml/dist/mandocd.8 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/mandocd.c =================================================================== --- vendor/mdocml/dist/mandocd.c (nonexistent) +++ vendor/mdocml/dist/mandocd.c (revision 313956) @@ -0,0 +1,285 @@ +/* $Id: mandocd.c,v 1.5 2017/02/17 14:31:52 schwarze Exp $ */ +/* + * Copyright (c) 2017 Michael Stapelberg + * Copyright (c) 2017 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. + */ +#include "config.h" + +#if HAVE_CMSG_XPG42 +#define _XPG4_2 +#endif + +#include +#include + +#if HAVE_ERR +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "mandoc.h" +#include "roff.h" +#include "mdoc.h" +#include "man.h" +#include "main.h" +#include "manconf.h" + +enum outt { + OUTT_ASCII = 0, + OUTT_UTF8, + OUTT_HTML +}; + +static void process(struct mparse *, enum outt, void *); +static int read_fds(int, int *); +static void usage(void) __attribute__((__noreturn__)); + + +#define NUM_FDS 3 +static int +read_fds(int clientfd, int *fds) +{ + struct msghdr msg; + struct iovec iov[1]; + unsigned char dummy[1]; + struct cmsghdr *cmsg; + int *walk; + int cnt; + + /* Union used for alignment. */ + union { + uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))]; + struct cmsghdr align; + } u; + + memset(&msg, '\0', sizeof(msg)); + msg.msg_control = u.controlbuf; + msg.msg_controllen = sizeof(u.controlbuf); + + /* + * Read a dummy byte - sendmsg cannot send an empty message, + * even if we are only interested in the OOB data. + */ + + iov[0].iov_base = dummy; + iov[0].iov_len = sizeof(dummy); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + switch (recvmsg(clientfd, &msg, 0)) { + case -1: + warn("recvmsg"); + return -1; + case 0: + return 0; + default: + break; + } + + if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) { + warnx("CMSG_FIRSTHDR: missing control message"); + return -1; + } + + if (cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS || + cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) { + warnx("CMSG_FIRSTHDR: invalid control message"); + return -1; + } + + walk = (int *)CMSG_DATA(cmsg); + for (cnt = 0; cnt < NUM_FDS; cnt++) + fds[cnt] = *walk++; + + return 1; +} + +int +main(int argc, char *argv[]) +{ + struct manoutput options; + struct mparse *parser; + void *formatter; + const char *defos; + const char *errstr; + int clientfd; + int old_stdin; + int old_stdout; + int old_stderr; + int fds[3]; + int state, opt; + enum outt outtype; + + defos = NULL; + outtype = OUTT_ASCII; + while ((opt = getopt(argc, argv, "I:T:")) != -1) { + switch (opt) { + case 'I': + if (strncmp(optarg, "os=", 3) == 0) + defos = optarg + 3; + else { + warnx("-I %s: Bad argument", optarg); + usage(); + } + break; + case 'T': + if (strcmp(optarg, "ascii") == 0) + outtype = OUTT_ASCII; + else if (strcmp(optarg, "utf8") == 0) + outtype = OUTT_UTF8; + else if (strcmp(optarg, "html") == 0) + outtype = OUTT_HTML; + else { + warnx("-T %s: Bad argument", optarg); + usage(); + } + break; + default: + usage(); + } + } + + if (argc > 0) { + argc -= optind; + argv += optind; + } + if (argc != 1) + usage(); + + errstr = NULL; + clientfd = strtonum(argv[0], 3, INT_MAX, &errstr); + if (errstr) + errx(1, "file descriptor %s %s", argv[1], errstr); + + mchars_alloc(); + parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1, + MANDOCLEVEL_BADARG, NULL, defos); + + memset(&options, 0, sizeof(options)); + switch (outtype) { + case OUTT_ASCII: + formatter = ascii_alloc(&options); + break; + case OUTT_UTF8: + formatter = utf8_alloc(&options); + break; + case OUTT_HTML: + options.fragment = 1; + formatter = html_alloc(&options); + break; + } + + state = 1; /* work to do */ + fflush(stdout); + fflush(stderr); + if ((old_stdin = dup(STDIN_FILENO)) == -1 || + (old_stdout = dup(STDOUT_FILENO)) == -1 || + (old_stderr = dup(STDERR_FILENO)) == -1) { + warn("dup"); + state = -1; /* error */ + } + + while (state == 1 && (state = read_fds(clientfd, fds)) == 1) { + if (dup2(fds[0], STDIN_FILENO) == -1 || + dup2(fds[1], STDOUT_FILENO) == -1 || + dup2(fds[2], STDERR_FILENO) == -1) { + warn("dup2"); + state = -1; + break; + } + + close(fds[0]); + close(fds[1]); + close(fds[2]); + + process(parser, outtype, formatter); + mparse_reset(parser); + + fflush(stdout); + fflush(stderr); + /* Close file descriptors by restoring the old ones. */ + if (dup2(old_stderr, STDERR_FILENO) == -1 || + dup2(old_stdout, STDOUT_FILENO) == -1 || + dup2(old_stdin, STDIN_FILENO) == -1) { + warn("dup2"); + state = -1; + break; + } + } + + close(clientfd); + switch (outtype) { + case OUTT_ASCII: + case OUTT_UTF8: + ascii_free(formatter); + break; + case OUTT_HTML: + html_free(formatter); + break; + } + mparse_free(parser); + mchars_free(); + return state == -1 ? 1 : 0; +} + +static void +process(struct mparse *parser, enum outt outtype, void *formatter) +{ + struct roff_man *man; + + mparse_readfd(parser, STDIN_FILENO, " "); + mparse_result(parser, &man, NULL); + + if (man == NULL) + return; + + if (man->macroset == MACROSET_MDOC) { + mdoc_validate(man); + switch (outtype) { + case OUTT_ASCII: + case OUTT_UTF8: + terminal_mdoc(formatter, man); + break; + case OUTT_HTML: + html_mdoc(formatter, man); + break; + } + } + if (man->macroset == MACROSET_MAN) { + man_validate(man); + switch (outtype) { + case OUTT_ASCII: + case OUTT_UTF8: + terminal_man(formatter, man); + break; + case OUTT_HTML: + html_man(formatter, man); + break; + } + } +} + +void +usage(void) +{ + fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n"); + exit(1); +} Property changes on: vendor/mdocml/dist/mandocd.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/mandocdb.c =================================================================== --- vendor/mdocml/dist/mandocdb.c (revision 313955) +++ vendor/mdocml/dist/mandocdb.c (revision 313956) @@ -1,2274 +1,2316 @@ -/* $Id: mandocdb.c,v 1.237 2017/01/11 17:39:53 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.244 2017/02/17 14:45:55 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011-2017 Ingo Schwarze * Copyright (c) 2016 Ed Maste * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #if HAVE_ERR #include #endif #include #include #if HAVE_FTS #include #else #include "compat_fts.h" #endif #include #if HAVE_SANDBOX_INIT #include #endif #include #include #include #include #include #include #include #include "mandoc_aux.h" #include "mandoc_ohash.h" #include "mandoc.h" #include "roff.h" #include "mdoc.h" #include "man.h" #include "manconf.h" #include "mansearch.h" #include "dba_array.h" #include "dba.h" extern const char *const mansearch_keynames[]; enum op { OP_DEFAULT = 0, /* new dbs from dir list or default config */ OP_CONFFILE, /* new databases from custom config file */ OP_UPDATE, /* delete/add entries in existing database */ OP_DELETE, /* delete entries from existing database */ OP_TEST /* change no databases, report potential problems */ }; struct str { const struct mpage *mpage; /* if set, the owning parse */ uint64_t mask; /* bitmask in sequence */ char key[]; /* rendered text */ }; struct inodev { ino_t st_ino; dev_t st_dev; }; struct mpage { struct inodev inodev; /* used for hashing routine */ struct dba_array *dba; char *sec; /* section from file content */ char *arch; /* architecture from file content */ char *title; /* title from file content */ char *desc; /* description from file content */ struct mpage *next; /* singly linked list */ struct mlink *mlinks; /* singly linked list */ int name_head_done; enum form form; /* format from file content */ }; struct mlink { char file[PATH_MAX]; /* filename rel. to manpath */ char *dsec; /* section from directory */ char *arch; /* architecture from directory */ char *name; /* name from file name (not empty) */ char *fsec; /* section from file name suffix */ struct mlink *next; /* singly linked list */ struct mpage *mpage; /* parent */ int gzip; /* filename has a .gz suffix */ enum form dform; /* format from directory */ enum form fform; /* format from file name suffix */ }; typedef int (*mdoc_fp)(struct mpage *, const struct roff_meta *, const struct roff_node *); struct mdoc_handler { mdoc_fp fp; /* optional handler */ uint64_t mask; /* set unless handler returns 0 */ int taboo; /* node flags that must not be set */ }; int mandocdb(int, char *[]); static void dbadd(struct dba *, struct mpage *); static void dbadd_mlink(const struct mlink *mlink); static void dbprune(struct dba *); static void dbwrite(struct dba *); static void filescan(const char *); #if HAVE_FTS_COMPARE_CONST static int fts_compare(const FTSENT *const *, const FTSENT *const *); #else static int fts_compare(const FTSENT **, const FTSENT **); #endif static void mlink_add(struct mlink *, const struct stat *); static void mlink_check(struct mpage *, struct mlink *); static void mlink_free(struct mlink *); static void mlinks_undupe(struct mpage *); static void mpages_free(void); static void mpages_merge(struct dba *, struct mparse *); static void parse_cat(struct mpage *, int); static void parse_man(struct mpage *, const struct roff_meta *, const struct roff_node *); static void parse_mdoc(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_head(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Fd(struct mpage *, const struct roff_meta *, const struct roff_node *); static void parse_mdoc_fname(struct mpage *, const struct roff_node *); static int parse_mdoc_Fn(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Fo(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Nd(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Nm(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Sh(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Va(struct mpage *, const struct roff_meta *, const struct roff_node *); static int parse_mdoc_Xr(struct mpage *, const struct roff_meta *, const struct roff_node *); static void putkey(const struct mpage *, char *, uint64_t); static void putkeys(const struct mpage *, char *, size_t, uint64_t); static void putmdockey(const struct mpage *, const struct roff_node *, uint64_t, int); static int render_string(char **, size_t *); static void say(const char *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); + __attribute__((__format__ (__printf__, 2, 3))); static int set_basedir(const char *, int); static int treescan(void); static size_t utf8(unsigned int, char [7]); static int nodb; /* no database changes */ static int mparse_options; /* abort the parse early */ static int use_all; /* use all found files */ static int debug; /* print what we're doing */ static int warnings; /* warn about crap */ static int write_utf8; /* write UTF-8 output; else ASCII */ static int exitcode; /* to be returned by main */ static enum op op; /* operational mode */ static char basedir[PATH_MAX]; /* current base directory */ static struct mpage *mpage_head; /* list of distinct manual pages */ static struct ohash mpages; /* table of distinct manual pages */ static struct ohash mlinks; /* table of directory entries */ static struct ohash names; /* table of all names */ static struct ohash strings; /* table of all strings */ static uint64_t name_mask; static const struct mdoc_handler mdocs[MDOC_MAX] = { { NULL, 0, 0 }, /* Ap */ { NULL, 0, NODE_NOPRT }, /* Dd */ { NULL, 0, NODE_NOPRT }, /* Dt */ { NULL, 0, NODE_NOPRT }, /* Os */ { parse_mdoc_Sh, TYPE_Sh, 0 }, /* Sh */ { parse_mdoc_head, TYPE_Ss, 0 }, /* Ss */ { NULL, 0, 0 }, /* Pp */ { NULL, 0, 0 }, /* D1 */ { NULL, 0, 0 }, /* Dl */ { NULL, 0, 0 }, /* Bd */ { NULL, 0, 0 }, /* Ed */ { NULL, 0, 0 }, /* Bl */ { NULL, 0, 0 }, /* El */ { NULL, 0, 0 }, /* It */ { NULL, 0, 0 }, /* Ad */ { NULL, TYPE_An, 0 }, /* An */ { NULL, TYPE_Ar, 0 }, /* Ar */ { NULL, TYPE_Cd, 0 }, /* Cd */ { NULL, TYPE_Cm, 0 }, /* Cm */ { NULL, TYPE_Dv, 0 }, /* Dv */ { NULL, TYPE_Er, 0 }, /* Er */ { NULL, TYPE_Ev, 0 }, /* Ev */ { NULL, 0, 0 }, /* Ex */ { NULL, TYPE_Fa, 0 }, /* Fa */ { parse_mdoc_Fd, 0, 0 }, /* Fd */ { NULL, TYPE_Fl, 0 }, /* Fl */ { parse_mdoc_Fn, 0, 0 }, /* Fn */ { NULL, TYPE_Ft, 0 }, /* Ft */ { NULL, TYPE_Ic, 0 }, /* Ic */ { NULL, TYPE_In, 0 }, /* In */ { NULL, TYPE_Li, 0 }, /* Li */ { parse_mdoc_Nd, 0, 0 }, /* Nd */ { parse_mdoc_Nm, 0, 0 }, /* Nm */ { NULL, 0, 0 }, /* Op */ { NULL, 0, 0 }, /* Ot */ { NULL, TYPE_Pa, NODE_NOSRC }, /* Pa */ { NULL, 0, 0 }, /* Rv */ { NULL, TYPE_St, 0 }, /* St */ { parse_mdoc_Va, TYPE_Va, 0 }, /* Va */ { parse_mdoc_Va, TYPE_Vt, 0 }, /* Vt */ { parse_mdoc_Xr, 0, 0 }, /* Xr */ { NULL, 0, 0 }, /* %A */ { NULL, 0, 0 }, /* %B */ { NULL, 0, 0 }, /* %D */ { NULL, 0, 0 }, /* %I */ { NULL, 0, 0 }, /* %J */ { NULL, 0, 0 }, /* %N */ { NULL, 0, 0 }, /* %O */ { NULL, 0, 0 }, /* %P */ { NULL, 0, 0 }, /* %R */ { NULL, 0, 0 }, /* %T */ { NULL, 0, 0 }, /* %V */ { NULL, 0, 0 }, /* Ac */ { NULL, 0, 0 }, /* Ao */ { NULL, 0, 0 }, /* Aq */ { NULL, TYPE_At, 0 }, /* At */ { NULL, 0, 0 }, /* Bc */ { NULL, 0, 0 }, /* Bf */ { NULL, 0, 0 }, /* Bo */ { NULL, 0, 0 }, /* Bq */ { NULL, TYPE_Bsx, NODE_NOSRC }, /* Bsx */ { NULL, TYPE_Bx, NODE_NOSRC }, /* Bx */ { NULL, 0, 0 }, /* Db */ { NULL, 0, 0 }, /* Dc */ { NULL, 0, 0 }, /* Do */ { NULL, 0, 0 }, /* Dq */ { NULL, 0, 0 }, /* Ec */ { NULL, 0, 0 }, /* Ef */ { NULL, TYPE_Em, 0 }, /* Em */ { NULL, 0, 0 }, /* Eo */ { NULL, TYPE_Fx, NODE_NOSRC }, /* Fx */ { NULL, TYPE_Ms, 0 }, /* Ms */ { NULL, 0, 0 }, /* No */ { NULL, 0, 0 }, /* Ns */ { NULL, TYPE_Nx, NODE_NOSRC }, /* Nx */ { NULL, TYPE_Ox, NODE_NOSRC }, /* Ox */ { NULL, 0, 0 }, /* Pc */ { NULL, 0, 0 }, /* Pf */ { NULL, 0, 0 }, /* Po */ { NULL, 0, 0 }, /* Pq */ { NULL, 0, 0 }, /* Qc */ { NULL, 0, 0 }, /* Ql */ { NULL, 0, 0 }, /* Qo */ { NULL, 0, 0 }, /* Qq */ { NULL, 0, 0 }, /* Re */ { NULL, 0, 0 }, /* Rs */ { NULL, 0, 0 }, /* Sc */ { NULL, 0, 0 }, /* So */ { NULL, 0, 0 }, /* Sq */ { NULL, 0, 0 }, /* Sm */ { NULL, 0, 0 }, /* Sx */ { NULL, TYPE_Sy, 0 }, /* Sy */ { NULL, TYPE_Tn, 0 }, /* Tn */ { NULL, 0, NODE_NOSRC }, /* Ux */ { NULL, 0, 0 }, /* Xc */ { NULL, 0, 0 }, /* Xo */ { parse_mdoc_Fo, 0, 0 }, /* Fo */ { NULL, 0, 0 }, /* Fc */ { NULL, 0, 0 }, /* Oo */ { NULL, 0, 0 }, /* Oc */ { NULL, 0, 0 }, /* Bk */ { NULL, 0, 0 }, /* Ek */ { NULL, 0, 0 }, /* Bt */ { NULL, 0, 0 }, /* Hf */ { NULL, 0, 0 }, /* Fr */ { NULL, 0, 0 }, /* Ud */ { NULL, TYPE_Lb, NODE_NOSRC }, /* Lb */ { NULL, 0, 0 }, /* Lp */ { NULL, TYPE_Lk, 0 }, /* Lk */ { NULL, TYPE_Mt, NODE_NOSRC }, /* Mt */ { NULL, 0, 0 }, /* Brq */ { NULL, 0, 0 }, /* Bro */ { NULL, 0, 0 }, /* Brc */ { NULL, 0, 0 }, /* %C */ { NULL, 0, 0 }, /* Es */ { NULL, 0, 0 }, /* En */ { NULL, TYPE_Dx, NODE_NOSRC }, /* Dx */ { NULL, 0, 0 }, /* %Q */ { NULL, 0, 0 }, /* br */ { NULL, 0, 0 }, /* sp */ { NULL, 0, 0 }, /* %U */ { NULL, 0, 0 }, /* Ta */ { NULL, 0, 0 }, /* ll */ }; int mandocdb(int argc, char *argv[]) { struct manconf conf; struct mparse *mp; struct dba *dba; const char *path_arg, *progname; size_t j, sz; int ch, i; #if HAVE_PLEDGE if (pledge("stdio rpath wpath cpath fattr flock proc exec", NULL) == -1) { warn("pledge"); return (int)MANDOCLEVEL_SYSERR; } #endif #if HAVE_SANDBOX_INIT if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) { warnx("sandbox_init"); return (int)MANDOCLEVEL_SYSERR; } #endif memset(&conf, 0, sizeof(conf)); /* * We accept a few different invocations. * The CHECKOP macro makes sure that invocation styles don't * clobber each other. */ #define CHECKOP(_op, _ch) do \ if (OP_DEFAULT != (_op)) { \ warnx("-%c: Conflicting option", (_ch)); \ goto usage; \ } while (/*CONSTCOND*/0) path_arg = NULL; op = OP_DEFAULT; while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v"))) switch (ch) { case 'a': use_all = 1; break; case 'C': CHECKOP(op, ch); path_arg = optarg; op = OP_CONFFILE; break; case 'D': debug++; break; case 'd': CHECKOP(op, ch); path_arg = optarg; op = OP_UPDATE; break; case 'n': nodb = 1; break; case 'p': warnings = 1; break; case 'Q': mparse_options |= MPARSE_QUICK; break; case 'T': if (strcmp(optarg, "utf8")) { warnx("-T%s: Unsupported output format", optarg); goto usage; } write_utf8 = 1; break; case 't': CHECKOP(op, ch); dup2(STDOUT_FILENO, STDERR_FILENO); op = OP_TEST; nodb = warnings = 1; break; case 'u': CHECKOP(op, ch); path_arg = optarg; op = OP_DELETE; break; case 'v': /* Compatibility with espie@'s makewhatis. */ break; default: goto usage; } argc -= optind; argv += optind; #if HAVE_PLEDGE if (nodb) { if (pledge("stdio rpath", NULL) == -1) { warn("pledge"); return (int)MANDOCLEVEL_SYSERR; } } #endif if (OP_CONFFILE == op && argc > 0) { warnx("-C: Too many arguments"); goto usage; } exitcode = (int)MANDOCLEVEL_OK; mchars_alloc(); mp = mparse_alloc(mparse_options, MANDOCLEVEL_BADARG, NULL, NULL); mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { /* * Most of these deal with a specific directory. * Jump into that directory first. */ if (OP_TEST != op && 0 == set_basedir(path_arg, 1)) goto out; dba = nodb ? dba_new(128) : dba_read(MANDOC_DB); if (dba != NULL) { /* * The existing database is usable. Process * all files specified on the command-line. */ #if HAVE_PLEDGE if (!nodb) { if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) { warn("pledge"); exitcode = (int)MANDOCLEVEL_SYSERR; goto out; } } #endif use_all = 1; for (i = 0; i < argc; i++) filescan(argv[i]); if (nodb == 0) dbprune(dba); } else { /* Database missing or corrupt. */ if (op != OP_UPDATE || errno != ENOENT) say(MANDOC_DB, "%s: Automatically recreating" " from scratch", strerror(errno)); exitcode = (int)MANDOCLEVEL_OK; op = OP_DEFAULT; if (0 == treescan()) goto out; dba = dba_new(128); } if (OP_DELETE != op) mpages_merge(dba, mp); if (nodb == 0) dbwrite(dba); dba_free(dba); } else { /* * If we have arguments, use them as our manpaths. * If we don't, use man.conf(5). */ if (argc > 0) { conf.manpath.paths = mandoc_reallocarray(NULL, argc, sizeof(char *)); conf.manpath.sz = (size_t)argc; for (i = 0; i < argc; i++) conf.manpath.paths[i] = mandoc_strdup(argv[i]); } else manconf_parse(&conf, path_arg, NULL, NULL); if (conf.manpath.sz == 0) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "Empty manpath"); } /* * First scan the tree rooted at a base directory, then * build a new database and finally move it into place. * Ignore zero-length directories and strip trailing * slashes. */ for (j = 0; j < conf.manpath.sz; j++) { sz = strlen(conf.manpath.paths[j]); if (sz && conf.manpath.paths[j][sz - 1] == '/') conf.manpath.paths[j][--sz] = '\0'; if (0 == sz) continue; if (j) { mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); } if ( ! set_basedir(conf.manpath.paths[j], argc > 0)) continue; if (0 == treescan()) continue; dba = dba_new(128); mpages_merge(dba, mp); if (nodb == 0) dbwrite(dba); dba_free(dba); if (j + 1 < conf.manpath.sz) { mpages_free(); ohash_delete(&mpages); ohash_delete(&mlinks); } } } out: manconf_free(&conf); mparse_free(mp); mchars_free(); mpages_free(); ohash_delete(&mpages); ohash_delete(&mlinks); return exitcode; usage: progname = getprogname(); fprintf(stderr, "usage: %s [-aDnpQ] [-C file] [-Tutf8]\n" " %s [-aDnpQ] [-Tutf8] dir ...\n" " %s [-DnpQ] [-Tutf8] -d dir [file ...]\n" " %s [-Dnp] -u dir [file ...]\n" " %s [-Q] -t file ...\n", progname, progname, progname, progname, progname); return (int)MANDOCLEVEL_BADARG; } /* * To get a singly linked list in alpha order while inserting entries * at the beginning, process directory entries in reverse alpha order. */ static int #if HAVE_FTS_COMPARE_CONST fts_compare(const FTSENT *const *a, const FTSENT *const *b) #else fts_compare(const FTSENT **a, const FTSENT **b) #endif { return -strcmp((*a)->fts_name, (*b)->fts_name); } /* * Scan a directory tree rooted at "basedir" for manpages. * We use fts(), scanning directory parts along the way for clues to our * section and architecture. * * If use_all has been specified, grok all files. * If not, sanitise paths to the following: * * [./]man*[/ ]/ . * or * [./]cat [/ ]/ .0 * * TODO: accommodate for multi-language directories. */ static int treescan(void) { char buf[PATH_MAX]; FTS *f; FTSENT *ff; struct mlink *mlink; int gzip; enum form dform; char *dsec, *arch, *fsec, *cp; const char *path; const char *argv[2]; argv[0] = "."; - argv[1] = (char *)NULL; + argv[1] = NULL; f = fts_open((char * const *)argv, FTS_PHYSICAL | FTS_NOCHDIR, fts_compare); if (f == NULL) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fts_open"); return 0; } dsec = arch = NULL; dform = FORM_NONE; while ((ff = fts_read(f)) != NULL) { path = ff->fts_path + 2; switch (ff->fts_info) { /* * Symbolic links require various sanity checks, * then get handled just like regular files. */ case FTS_SL: if (realpath(path, buf) == NULL) { if (warnings) say(path, "&realpath"); continue; } if (strstr(buf, basedir) != buf #ifdef HOMEBREWDIR && strstr(buf, HOMEBREWDIR) != buf #endif ) { if (warnings) say("", "%s: outside base directory", buf); continue; } /* Use logical inode to avoid mpages dupe. */ if (stat(path, ff->fts_statp) == -1) { if (warnings) say(path, "&stat"); continue; } /* FALLTHROUGH */ /* * If we're a regular file, add an mlink by using the * stored directory data and handling the filename. */ case FTS_F: if ( ! strcmp(path, MANDOC_DB)) continue; if ( ! use_all && ff->fts_level < 2) { if (warnings) say(path, "Extraneous file"); continue; } gzip = 0; fsec = NULL; while (fsec == NULL) { fsec = strrchr(ff->fts_name, '.'); if (fsec == NULL || strcmp(fsec+1, "gz")) break; gzip = 1; *fsec = '\0'; fsec = NULL; } if (fsec == NULL) { if ( ! use_all) { if (warnings) say(path, "No filename suffix"); continue; } } else if ( ! strcmp(++fsec, "html")) { if (warnings) say(path, "Skip html"); continue; } else if ( ! strcmp(fsec, "ps")) { if (warnings) say(path, "Skip ps"); continue; } else if ( ! strcmp(fsec, "pdf")) { if (warnings) say(path, "Skip pdf"); continue; } else if ( ! use_all && ((dform == FORM_SRC && strncmp(fsec, dsec, strlen(dsec))) || (dform == FORM_CAT && strcmp(fsec, "0")))) { if (warnings) say(path, "Wrong filename suffix"); continue; } else fsec[-1] = '\0'; mlink = mandoc_calloc(1, sizeof(struct mlink)); if (strlcpy(mlink->file, path, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(path, "Filename too long"); free(mlink); continue; } mlink->dform = dform; mlink->dsec = dsec; mlink->arch = arch; mlink->name = ff->fts_name; mlink->fsec = fsec; mlink->gzip = gzip; mlink_add(mlink, ff->fts_statp); continue; case FTS_D: case FTS_DP: break; default: if (warnings) say(path, "Not a regular file"); continue; } switch (ff->fts_level) { case 0: /* Ignore the root directory. */ break; case 1: /* * This might contain manX/ or catX/. * Try to infer this from the name. * If we're not in use_all, enforce it. */ cp = ff->fts_name; if (ff->fts_info == FTS_DP) { dform = FORM_NONE; dsec = NULL; break; } if ( ! strncmp(cp, "man", 3)) { dform = FORM_SRC; dsec = cp + 3; } else if ( ! strncmp(cp, "cat", 3)) { dform = FORM_CAT; dsec = cp + 3; } else { dform = FORM_NONE; dsec = NULL; } if (dsec != NULL || use_all) break; if (warnings) say(path, "Unknown directory part"); fts_set(f, ff, FTS_SKIP); break; case 2: /* * Possibly our architecture. * If we're descending, keep tabs on it. */ if (ff->fts_info != FTS_DP && dsec != NULL) arch = ff->fts_name; else arch = NULL; break; default: if (ff->fts_info == FTS_DP || use_all) break; if (warnings) say(path, "Extraneous directory part"); fts_set(f, ff, FTS_SKIP); break; } } fts_close(f); return 1; } /* * Add a file to the mlinks table. * Do not verify that it's a "valid" looking manpage (we'll do that * later). * * Try to infer the manual section, architecture, and page name from the * path, assuming it looks like * * [./]man*[/ ]/ . * or * [./]cat [/ ]/ .0 * * See treescan() for the fts(3) version of this. */ static void filescan(const char *file) { char buf[PATH_MAX]; struct stat st; struct mlink *mlink; char *p, *start; assert(use_all); if (0 == strncmp(file, "./", 2)) file += 2; /* * We have to do lstat(2) before realpath(3) loses * the information whether this is a symbolic link. * We need to know that because for symbolic links, * we want to use the orginal file name, while for * regular files, we want to use the real path. */ if (-1 == lstat(file, &st)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&lstat"); return; } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "Not a regular file"); return; } /* * We have to resolve the file name to the real path * in any case for the base directory check. */ if (NULL == realpath(file, buf)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&realpath"); return; } if (OP_TEST == op) start = buf; else if (strstr(buf, basedir) == buf) start = buf + strlen(basedir); #ifdef HOMEBREWDIR else if (strstr(buf, HOMEBREWDIR) == buf) start = buf; #endif else { exitcode = (int)MANDOCLEVEL_BADARG; say("", "%s: outside base directory", buf); return; } /* * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * and use the original name. * This implies passing stuff like "cat1/../man1/foo.1" * on the command line won't work. So don't do that. * Note the stat(2) can still fail if the link target * doesn't exist. */ if (S_IFLNK & st.st_mode) { if (-1 == stat(buf, &st)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&stat"); return; } if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { say(file, "Filename too long"); return; } start = buf; if (OP_TEST != op && strstr(buf, basedir) == buf) start += strlen(basedir); } mlink = mandoc_calloc(1, sizeof(struct mlink)); mlink->dform = FORM_NONE; if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(start, "Filename too long"); free(mlink); return; } /* + * In test mode or when the original name is absolute + * but outside our tree, guess the base directory. + */ + + if (op == OP_TEST || (start == buf && *start == '/')) { + if (strncmp(buf, "man/", 4) == 0) + start = buf + 4; + else if ((start = strstr(buf, "/man/")) != NULL) + start += 5; + else + start = buf; + } + + /* * First try to guess our directory structure. * If we find a separator, try to look for man* or cat*. * If we find one of these and what's underneath is a directory, * assume it's an architecture. */ if (NULL != (p = strchr(start, '/'))) { *p++ = '\0'; if (0 == strncmp(start, "man", 3)) { mlink->dform = FORM_SRC; mlink->dsec = start + 3; } else if (0 == strncmp(start, "cat", 3)) { mlink->dform = FORM_CAT; mlink->dsec = start + 3; } start = p; if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { *p++ = '\0'; mlink->arch = start; start = p; } } /* * Now check the file suffix. * Suffix of `.0' indicates a catpage, `.1-9' is a manpage. */ p = strrchr(start, '\0'); while (p-- > start && '/' != *p && '.' != *p) /* Loop. */ ; if ('.' == *p) { *p++ = '\0'; mlink->fsec = p; } /* * Now try to parse the name. * Use the filename portion of the path. */ mlink->name = start; if (NULL != (p = strrchr(start, '/'))) { mlink->name = p + 1; *p = '\0'; } mlink_add(mlink, &st); } static void mlink_add(struct mlink *mlink, const struct stat *st) { struct inodev inodev; struct mpage *mpage; unsigned int slot; assert(NULL != mlink->file); mlink->dsec = mandoc_strdup(mlink->dsec ? mlink->dsec : ""); mlink->arch = mandoc_strdup(mlink->arch ? mlink->arch : ""); mlink->name = mandoc_strdup(mlink->name ? mlink->name : ""); mlink->fsec = mandoc_strdup(mlink->fsec ? mlink->fsec : ""); if ('0' == *mlink->fsec) { free(mlink->fsec); mlink->fsec = mandoc_strdup(mlink->dsec); mlink->fform = FORM_CAT; } else if ('1' <= *mlink->fsec && '9' >= *mlink->fsec) mlink->fform = FORM_SRC; else mlink->fform = FORM_NONE; slot = ohash_qlookup(&mlinks, mlink->file); assert(NULL == ohash_find(&mlinks, slot)); ohash_insert(&mlinks, slot, mlink); memset(&inodev, 0, sizeof(inodev)); /* Clear padding. */ inodev.st_ino = st->st_ino; inodev.st_dev = st->st_dev; slot = ohash_lookup_memory(&mpages, (char *)&inodev, sizeof(struct inodev), inodev.st_ino); mpage = ohash_find(&mpages, slot); if (NULL == mpage) { mpage = mandoc_calloc(1, sizeof(struct mpage)); mpage->inodev.st_ino = inodev.st_ino; mpage->inodev.st_dev = inodev.st_dev; mpage->form = FORM_NONE; mpage->next = mpage_head; mpage_head = mpage; ohash_insert(&mpages, slot, mpage); } else mlink->next = mpage->mlinks; mpage->mlinks = mlink; mlink->mpage = mpage; } static void mlink_free(struct mlink *mlink) { free(mlink->dsec); free(mlink->arch); free(mlink->name); free(mlink->fsec); free(mlink); } static void mpages_free(void) { struct mpage *mpage; struct mlink *mlink; while ((mpage = mpage_head) != NULL) { while ((mlink = mpage->mlinks) != NULL) { mpage->mlinks = mlink->next; mlink_free(mlink); } mpage_head = mpage->next; free(mpage->sec); free(mpage->arch); free(mpage->title); free(mpage->desc); free(mpage); } } /* * For each mlink to the mpage, check whether the path looks like * it is formatted, and if it does, check whether a source manual * exists by the same name, ignoring the suffix. * If both conditions hold, drop the mlink. */ static void mlinks_undupe(struct mpage *mpage) { char buf[PATH_MAX]; struct mlink **prev; struct mlink *mlink; char *bufp; mpage->form = FORM_CAT; prev = &mpage->mlinks; while (NULL != (mlink = *prev)) { if (FORM_CAT != mlink->dform) { mpage->form = FORM_NONE; goto nextlink; } (void)strlcpy(buf, mlink->file, sizeof(buf)); bufp = strstr(buf, "cat"); assert(NULL != bufp); memcpy(bufp, "man", 3); if (NULL != (bufp = strrchr(buf, '.'))) *++bufp = '\0'; (void)strlcat(buf, mlink->dsec, sizeof(buf)); if (NULL == ohash_find(&mlinks, ohash_qlookup(&mlinks, buf))) goto nextlink; if (warnings) say(mlink->file, "Man source exists: %s", buf); if (use_all) goto nextlink; *prev = mlink->next; mlink_free(mlink); continue; nextlink: prev = &(*prev)->next; } } static void mlink_check(struct mpage *mpage, struct mlink *mlink) { struct str *str; unsigned int slot; /* * Check whether the manual section given in a file * agrees with the directory where the file is located. * Some manuals have suffixes like (3p) on their * section number either inside the file or in the * directory name, some are linked into more than one * section, like encrypt(1) = makekey(8). */ if (FORM_SRC == mpage->form && strcasecmp(mpage->sec, mlink->dsec)) say(mlink->file, "Section \"%s\" manual in %s directory", mpage->sec, mlink->dsec); /* * Manual page directories exist for each kernel * architecture as returned by machine(1). * However, many manuals only depend on the * application architecture as returned by arch(1). * For example, some (2/ARM) manuals are shared * across the "armish" and "zaurus" kernel * architectures. * A few manuals are even shared across completely * different architectures, for example fdformat(1) * on amd64, i386, and sparc64. */ if (strcasecmp(mpage->arch, mlink->arch)) say(mlink->file, "Architecture \"%s\" manual in " "\"%s\" directory", mpage->arch, mlink->arch); /* * XXX * parse_cat() doesn't set NAME_TITLE yet. */ if (FORM_CAT == mpage->form) return; /* * Check whether this mlink * appears as a name in the NAME section. */ slot = ohash_qlookup(&names, mlink->name); str = ohash_find(&names, slot); assert(NULL != str); if ( ! (NAME_TITLE & str->mask)) say(mlink->file, "Name missing in NAME section"); } /* * Run through the files in the global vector "mpages" * and add them to the database specified in "basedir". * * This handles the parsing scheme itself, using the cues of directory * and filename to determine whether the file is parsable or not. */ static void mpages_merge(struct dba *dba, struct mparse *mp) { struct mpage *mpage, *mpage_dest; struct mlink *mlink, *mlink_dest; struct roff_man *man; char *sodest; char *cp; int fd; for (mpage = mpage_head; mpage != NULL; mpage = mpage->next) { mlinks_undupe(mpage); if ((mlink = mpage->mlinks) == NULL) continue; name_mask = NAME_MASK; mandoc_ohash_init(&names, 4, offsetof(struct str, key)); mandoc_ohash_init(&strings, 6, offsetof(struct str, key)); mparse_reset(mp); man = NULL; sodest = NULL; if ((fd = mparse_open(mp, mlink->file)) == -1) { say(mlink->file, "&open"); goto nextpage; } /* * Interpret the file as mdoc(7) or man(7) source * code, unless it is known to be formatted. */ if (mlink->dform != FORM_CAT || mlink->fform != FORM_CAT) { mparse_readfd(mp, fd, mlink->file); close(fd); + fd = -1; mparse_result(mp, &man, &sodest); } if (sodest != NULL) { mlink_dest = ohash_find(&mlinks, ohash_qlookup(&mlinks, sodest)); if (mlink_dest == NULL) { mandoc_asprintf(&cp, "%s.gz", sodest); mlink_dest = ohash_find(&mlinks, ohash_qlookup(&mlinks, cp)); free(cp); } if (mlink_dest != NULL) { /* The .so target exists. */ mpage_dest = mlink_dest->mpage; while (1) { mlink->mpage = mpage_dest; /* * If the target was already * processed, add the links * to the database now. * Otherwise, this will * happen when we come * to the target. */ if (mpage_dest->dba != NULL) dbadd_mlink(mlink); if (mlink->next == NULL) break; mlink = mlink->next; } /* Move all links to the target. */ mlink->next = mlink_dest->next; mlink_dest->next = mpage->mlinks; mpage->mlinks = NULL; } goto nextpage; } else if (man != NULL && man->macroset == MACROSET_MDOC) { mdoc_validate(man); mpage->form = FORM_SRC; mpage->sec = man->meta.msec; mpage->sec = mandoc_strdup( mpage->sec == NULL ? "" : mpage->sec); mpage->arch = man->meta.arch; mpage->arch = mandoc_strdup( mpage->arch == NULL ? "" : mpage->arch); mpage->title = mandoc_strdup(man->meta.title); } else if (man != NULL && man->macroset == MACROSET_MAN) { man_validate(man); - mpage->form = FORM_SRC; - mpage->sec = mandoc_strdup(man->meta.msec); - mpage->arch = mandoc_strdup(mlink->arch); - mpage->title = mandoc_strdup(man->meta.title); - } else { + if (*man->meta.msec != '\0' || + *man->meta.msec != '\0') { + mpage->form = FORM_SRC; + mpage->sec = mandoc_strdup(man->meta.msec); + mpage->arch = mandoc_strdup(mlink->arch); + mpage->title = mandoc_strdup(man->meta.title); + } else + man = NULL; + } + + assert(mpage->desc == NULL); + if (man == NULL) { mpage->form = FORM_CAT; mpage->sec = mandoc_strdup(mlink->dsec); mpage->arch = mandoc_strdup(mlink->arch); mpage->title = mandoc_strdup(mlink->name); - } - - assert(mpage->desc == NULL); - if (man != NULL && man->macroset == MACROSET_MDOC) + parse_cat(mpage, fd); + } else if (man->macroset == MACROSET_MDOC) parse_mdoc(mpage, &man->meta, man->first); - else if (man != NULL) - parse_man(mpage, &man->meta, man->first); else - parse_cat(mpage, fd); - if (mpage->desc == NULL) - mpage->desc = mandoc_strdup(mpage->mlinks->name); + parse_man(mpage, &man->meta, man->first); + if (mpage->desc == NULL) { + mpage->desc = mandoc_strdup(mlink->name); + if (warnings) + say(mlink->file, "No one-line description, " + "using filename \"%s\"", mlink->name); + } - if (warnings && !use_all) - for (mlink = mpage->mlinks; mlink; - mlink = mlink->next) + for (mlink = mpage->mlinks; + mlink != NULL; + mlink = mlink->next) { + putkey(mpage, mlink->name, NAME_FILE); + if (warnings && !use_all) mlink_check(mpage, mlink); + } dbadd(dba, mpage); - mlink = mpage->mlinks; nextpage: ohash_delete(&strings); ohash_delete(&names); } } static void parse_cat(struct mpage *mpage, int fd) { FILE *stream; - char *line, *p, *title; + struct mlink *mlink; + char *line, *p, *title, *sec; size_t linesz, plen, titlesz; ssize_t len; int offs; - stream = (-1 == fd) ? - fopen(mpage->mlinks->file, "r") : - fdopen(fd, "r"); - if (NULL == stream) { - if (-1 != fd) + mlink = mpage->mlinks; + stream = fd == -1 ? fopen(mlink->file, "r") : fdopen(fd, "r"); + if (stream == NULL) { + if (fd != -1) close(fd); if (warnings) - say(mpage->mlinks->file, "&fopen"); + say(mlink->file, "&fopen"); return; } line = NULL; linesz = 0; - /* Skip to first blank line. */ + /* Parse the section number from the header line. */ - while (getline(&line, &linesz, stream) != -1) + while (getline(&line, &linesz, stream) != -1) { if (*line == '\n') + continue; + if ((sec = strchr(line, '(')) == NULL) break; + if ((p = strchr(++sec, ')')) == NULL) + break; + free(mpage->sec); + mpage->sec = mandoc_strndup(sec, p - sec); + if (warnings && *mlink->dsec != '\0' && + strcasecmp(mpage->sec, mlink->dsec)) + say(mlink->file, + "Section \"%s\" manual in %s directory", + mpage->sec, mlink->dsec); + break; + } + /* Skip to first blank line. */ + + while (line == NULL || *line != '\n') + if (getline(&line, &linesz, stream) == -1) + break; + /* * Assume the first line that is not indented * is the first section header. Skip to it. */ while (getline(&line, &linesz, stream) != -1) if (*line != '\n' && *line != ' ') break; /* * Read up until the next section into a buffer. * Strip the leading and trailing newline from each read line, * appending a trailing space. * Ignore empty (whitespace-only) lines. */ titlesz = 0; title = NULL; while ((len = getline(&line, &linesz, stream)) != -1) { if (*line != ' ') break; offs = 0; while (isspace((unsigned char)line[offs])) offs++; if (line[offs] == '\0') continue; title = mandoc_realloc(title, titlesz + len - offs); memcpy(title + titlesz, line + offs, len - offs); titlesz += len - offs; title[titlesz - 1] = ' '; } free(line); /* * If no page content can be found, or the input line * is already the next section header, or there is no * trailing newline, reuse the page title as the page * description. */ if (NULL == title || '\0' == *title) { if (warnings) - say(mpage->mlinks->file, - "Cannot find NAME section"); + say(mlink->file, "Cannot find NAME section"); fclose(stream); free(title); return; } title[titlesz - 1] = '\0'; /* * Skip to the first dash. * Use the remaining line as the description (no more than 70 * bytes). */ if (NULL != (p = strstr(title, "- "))) { for (p += 2; ' ' == *p || '\b' == *p; p++) /* Skip to next word. */ ; } else { if (warnings) - say(mpage->mlinks->file, - "No dash in title line"); + say(mlink->file, "No dash in title line, " + "reusing \"%s\" as one-line description", title); p = title; } plen = strlen(p); /* Strip backspace-encoding from line. */ while (NULL != (line = memchr(p, '\b', plen))) { len = line - p; if (0 == len) { memmove(line, line + 1, plen--); continue; } memmove(line - 1, line + 1, plen - len); plen -= 2; } mpage->desc = mandoc_strdup(p); fclose(stream); free(title); } /* * Put a type/word pair into the word database for this particular file. */ static void putkey(const struct mpage *mpage, char *value, uint64_t type) { putkeys(mpage, value, strlen(value), type); } /* * Grok all nodes at or below a certain mdoc node into putkey(). */ static void putmdockey(const struct mpage *mpage, const struct roff_node *n, uint64_t m, int taboo) { for ( ; NULL != n; n = n->next) { if (n->flags & taboo) continue; if (NULL != n->child) putmdockey(mpage, n->child, m, taboo); if (n->type == ROFFT_TEXT) putkey(mpage, n->string, m); } } static void parse_man(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { const struct roff_node *head, *body; char *start, *title; char byte; size_t sz; if (n == NULL) return; /* * We're only searching for one thing: the first text child in * the BODY of a NAME section. Since we don't keep track of * sections in -man, run some hoops to find out whether we're in * the correct section or not. */ if (n->type == ROFFT_BODY && n->tok == MAN_SH) { body = n; if ((head = body->parent->head) != NULL && (head = head->child) != NULL && head->next == NULL && head->type == ROFFT_TEXT && strcmp(head->string, "NAME") == 0 && body->child != NULL) { /* * Suck the entire NAME section into memory. * Yes, we might run away. * But too many manuals have big, spread-out * NAME sections over many lines. */ title = NULL; deroff(&title, body); if (NULL == title) return; /* * Go through a special heuristic dance here. * Conventionally, one or more manual names are * comma-specified prior to a whitespace, then a * dash, then a description. Try to puzzle out * the name parts here. */ start = title; for ( ;; ) { sz = strcspn(start, " ,"); if ('\0' == start[sz]) break; byte = start[sz]; start[sz] = '\0'; /* * Assume a stray trailing comma in the * name list if a name begins with a dash. */ if ('-' == start[0] || ('\\' == start[0] && '-' == start[1])) break; putkey(mpage, start, NAME_TITLE); if ( ! (mpage->name_head_done || strcasecmp(start, meta->title))) { putkey(mpage, start, NAME_HEAD); mpage->name_head_done = 1; } if (' ' == byte) { start += sz + 1; break; } assert(',' == byte); start += sz + 1; while (' ' == *start) start++; } if (start == title) { putkey(mpage, start, NAME_TITLE); if ( ! (mpage->name_head_done || strcasecmp(start, meta->title))) { putkey(mpage, start, NAME_HEAD); mpage->name_head_done = 1; } free(title); return; } while (isspace((unsigned char)*start)) start++; if (0 == strncmp(start, "-", 1)) start += 1; else if (0 == strncmp(start, "\\-\\-", 4)) start += 4; else if (0 == strncmp(start, "\\-", 2)) start += 2; else if (0 == strncmp(start, "\\(en", 4)) start += 4; else if (0 == strncmp(start, "\\(em", 4)) start += 4; while (' ' == *start) start++; mpage->desc = mandoc_strdup(start); free(title); return; } } for (n = n->child; n; n = n->next) { if (NULL != mpage->desc) break; parse_man(mpage, meta, n); } } static void parse_mdoc(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { assert(NULL != n); for (n = n->child; NULL != n; n = n->next) { if (n->flags & mdocs[n->tok].taboo) continue; switch (n->type) { case ROFFT_ELEM: case ROFFT_BLOCK: case ROFFT_HEAD: case ROFFT_BODY: case ROFFT_TAIL: if (NULL != mdocs[n->tok].fp) if (0 == (*mdocs[n->tok].fp)(mpage, meta, n)) break; if (mdocs[n->tok].mask) putmdockey(mpage, n->child, mdocs[n->tok].mask, mdocs[n->tok].taboo); break; default: assert(n->type != ROFFT_ROOT); continue; } if (NULL != n->child) parse_mdoc(mpage, meta, n); } } static int parse_mdoc_Fd(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { char *start, *end; size_t sz; if (SEC_SYNOPSIS != n->sec || NULL == (n = n->child) || n->type != ROFFT_TEXT) return 0; /* * Only consider those `Fd' macro fields that begin with an * "inclusion" token (versus, e.g., #define). */ if (strcmp("#include", n->string)) return 0; if ((n = n->next) == NULL || n->type != ROFFT_TEXT) return 0; /* * Strip away the enclosing angle brackets and make sure we're * not zero-length. */ start = n->string; if ('<' == *start || '"' == *start) start++; if (0 == (sz = strlen(start))) return 0; end = &start[(int)sz - 1]; if ('>' == *end || '"' == *end) end--; if (end > start) putkeys(mpage, start, end - start + 1, TYPE_In); return 0; } static void parse_mdoc_fname(struct mpage *mpage, const struct roff_node *n) { char *cp; size_t sz; if (n->type != ROFFT_TEXT) return; /* Skip function pointer punctuation. */ cp = n->string; while (*cp == '(' || *cp == '*') cp++; sz = strcspn(cp, "()"); putkeys(mpage, cp, sz, TYPE_Fn); if (n->sec == SEC_SYNOPSIS) putkeys(mpage, cp, sz, NAME_SYN); } static int parse_mdoc_Fn(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { if (n->child == NULL) return 0; parse_mdoc_fname(mpage, n->child); for (n = n->child->next; n != NULL; n = n->next) if (n->type == ROFFT_TEXT) putkey(mpage, n->string, TYPE_Fa); return 0; } static int parse_mdoc_Fo(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { if (n->type != ROFFT_HEAD) return 1; if (n->child != NULL) parse_mdoc_fname(mpage, n->child); return 0; } static int parse_mdoc_Va(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { char *cp; if (n->type != ROFFT_ELEM && n->type != ROFFT_BODY) return 0; if (n->child != NULL && n->child->next == NULL && n->child->type == ROFFT_TEXT) return 1; cp = NULL; deroff(&cp, n); if (cp != NULL) { putkey(mpage, cp, TYPE_Vt | (n->tok == MDOC_Va || n->type == ROFFT_BODY ? TYPE_Va : 0)); free(cp); } return 0; } static int parse_mdoc_Xr(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { char *cp; if (NULL == (n = n->child)) return 0; if (NULL == n->next) { putkey(mpage, n->string, TYPE_Xr); return 0; } mandoc_asprintf(&cp, "%s(%s)", n->string, n->next->string); putkey(mpage, cp, TYPE_Xr); free(cp); return 0; } static int parse_mdoc_Nd(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { if (n->type == ROFFT_BODY) deroff(&mpage->desc, n); return 0; } static int parse_mdoc_Nm(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { if (SEC_NAME == n->sec) putmdockey(mpage, n->child, NAME_TITLE, 0); else if (n->sec == SEC_SYNOPSIS && n->type == ROFFT_HEAD) { if (n->child == NULL) putkey(mpage, meta->name, NAME_SYN); else putmdockey(mpage, n->child, NAME_SYN, 0); } if ( ! (mpage->name_head_done || n->child == NULL || n->child->string == NULL || strcasecmp(n->child->string, meta->title))) { putkey(mpage, n->child->string, NAME_HEAD); mpage->name_head_done = 1; } return 0; } static int parse_mdoc_Sh(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { return n->sec == SEC_CUSTOM && n->type == ROFFT_HEAD; } static int parse_mdoc_head(struct mpage *mpage, const struct roff_meta *meta, const struct roff_node *n) { return n->type == ROFFT_HEAD; } /* * Add a string to the hash table for the current manual. * Each string has a bitmask telling which macros it belongs to. * When we finish the manual, we'll dump the table. */ static void putkeys(const struct mpage *mpage, char *cp, size_t sz, uint64_t v) { struct ohash *htab; struct str *s; const char *end; unsigned int slot; int i, mustfree; if (0 == sz) return; mustfree = render_string(&cp, &sz); if (TYPE_Nm & v) { htab = &names; v &= name_mask; if (v & NAME_FIRST) name_mask &= ~NAME_FIRST; if (debug > 1) say(mpage->mlinks->file, "Adding name %*s, bits=0x%llx", (int)sz, cp, (unsigned long long)v); } else { htab = &strings; if (debug > 1) for (i = 0; i < KEY_MAX; i++) if ((uint64_t)1 << i & v) say(mpage->mlinks->file, "Adding key %s=%*s", mansearch_keynames[i], (int)sz, cp); } end = cp + sz; slot = ohash_qlookupi(htab, cp, &end); s = ohash_find(htab, slot); if (NULL != s && mpage == s->mpage) { s->mask |= v; return; } else if (NULL == s) { s = mandoc_calloc(1, sizeof(struct str) + sz + 1); memcpy(s->key, cp, sz); ohash_insert(htab, slot, s); } s->mpage = mpage; s->mask = v; if (mustfree) free(cp); } /* * Take a Unicode codepoint and produce its UTF-8 encoding. * This isn't the best way to do this, but it works. * The magic numbers are from the UTF-8 packaging. * They're not as scary as they seem: read the UTF-8 spec for details. */ static size_t utf8(unsigned int cp, char out[7]) { size_t rc; rc = 0; if (cp <= 0x0000007F) { rc = 1; out[0] = (char)cp; } else if (cp <= 0x000007FF) { rc = 2; out[0] = (cp >> 6 & 31) | 192; out[1] = (cp & 63) | 128; } else if (cp <= 0x0000FFFF) { rc = 3; out[0] = (cp >> 12 & 15) | 224; out[1] = (cp >> 6 & 63) | 128; out[2] = (cp & 63) | 128; } else if (cp <= 0x001FFFFF) { rc = 4; out[0] = (cp >> 18 & 7) | 240; out[1] = (cp >> 12 & 63) | 128; out[2] = (cp >> 6 & 63) | 128; out[3] = (cp & 63) | 128; } else if (cp <= 0x03FFFFFF) { rc = 5; out[0] = (cp >> 24 & 3) | 248; out[1] = (cp >> 18 & 63) | 128; out[2] = (cp >> 12 & 63) | 128; out[3] = (cp >> 6 & 63) | 128; out[4] = (cp & 63) | 128; } else if (cp <= 0x7FFFFFFF) { rc = 6; out[0] = (cp >> 30 & 1) | 252; out[1] = (cp >> 24 & 63) | 128; out[2] = (cp >> 18 & 63) | 128; out[3] = (cp >> 12 & 63) | 128; out[4] = (cp >> 6 & 63) | 128; out[5] = (cp & 63) | 128; } else return 0; out[rc] = '\0'; return rc; } /* * If the string contains escape sequences, * replace it with an allocated rendering and return 1, * such that the caller can free it after use. * Otherwise, do nothing and return 0. */ static int render_string(char **public, size_t *psz) { const char *src, *scp, *addcp, *seq; char *dst; size_t ssz, dsz, addsz; char utfbuf[7], res[6]; int seqlen, unicode; res[0] = '\\'; res[1] = '\t'; res[2] = ASCII_NBRSP; res[3] = ASCII_HYPH; res[4] = ASCII_BREAK; res[5] = '\0'; src = scp = *public; ssz = *psz; dst = NULL; dsz = 0; while (scp < src + *psz) { /* Leave normal characters unchanged. */ if (strchr(res, *scp) == NULL) { if (dst != NULL) dst[dsz++] = *scp; scp++; continue; } /* * Found something that requires replacing, * make sure we have a destination buffer. */ if (dst == NULL) { dst = mandoc_malloc(ssz + 1); dsz = scp - src; memcpy(dst, src, dsz); } /* Handle single-char special characters. */ switch (*scp) { case '\\': break; case '\t': case ASCII_NBRSP: dst[dsz++] = ' '; scp++; continue; case ASCII_HYPH: dst[dsz++] = '-'; /* FALLTHROUGH */ case ASCII_BREAK: scp++; continue; default: abort(); } /* * Found an escape sequence. * Read past the slash, then parse it. * Ignore everything except characters. */ scp++; if (mandoc_escape(&scp, &seq, &seqlen) != ESCAPE_SPECIAL) continue; /* * Render the special character * as either UTF-8 or ASCII. */ if (write_utf8) { unicode = mchars_spec2cp(seq, seqlen); if (unicode <= 0) continue; addsz = utf8(unicode, utfbuf); if (addsz == 0) continue; addcp = utfbuf; } else { addcp = mchars_spec2str(seq, seqlen, &addsz); if (addcp == NULL) continue; if (*addcp == ASCII_NBRSP) { addcp = " "; addsz = 1; } } /* Copy the rendered glyph into the stream. */ ssz += addsz; dst = mandoc_realloc(dst, ssz + 1); memcpy(dst + dsz, addcp, addsz); dsz += addsz; } if (dst != NULL) { *public = dst; *psz = dsz; } /* Trim trailing whitespace and NUL-terminate. */ while (*psz > 0 && (*public)[*psz - 1] == ' ') --*psz; if (dst != NULL) { (*public)[*psz] = '\0'; return 1; } else return 0; } static void dbadd_mlink(const struct mlink *mlink) { dba_page_alias(mlink->mpage->dba, mlink->name, NAME_FILE); dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->dsec); dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->fsec); dba_page_add(mlink->mpage->dba, DBP_ARCH, mlink->arch); dba_page_add(mlink->mpage->dba, DBP_FILE, mlink->file); } /* * Flush the current page's terms (and their bits) into the database. * Also, handle escape sequences at the last possible moment. */ static void dbadd(struct dba *dba, struct mpage *mpage) { struct mlink *mlink; struct str *key; char *cp; uint64_t mask; size_t i; unsigned int slot; int mustfree; mlink = mpage->mlinks; if (nodb) { for (key = ohash_first(&names, &slot); NULL != key; key = ohash_next(&names, &slot)) free(key); for (key = ohash_first(&strings, &slot); NULL != key; key = ohash_next(&strings, &slot)) free(key); if (0 == debug) return; while (NULL != mlink) { fputs(mlink->name, stdout); if (NULL == mlink->next || strcmp(mlink->dsec, mlink->next->dsec) || strcmp(mlink->fsec, mlink->next->fsec) || strcmp(mlink->arch, mlink->next->arch)) { putchar('('); if ('\0' == *mlink->dsec) fputs(mlink->fsec, stdout); else fputs(mlink->dsec, stdout); if ('\0' != *mlink->arch) printf("/%s", mlink->arch); putchar(')'); } mlink = mlink->next; if (NULL != mlink) fputs(", ", stdout); } printf(" - %s\n", mpage->desc); return; } if (debug) say(mlink->file, "Adding to database"); cp = mpage->desc; i = strlen(cp); mustfree = render_string(&cp, &i); mpage->dba = dba_page_new(dba->pages, *mpage->arch == '\0' ? mlink->arch : mpage->arch, cp, mlink->file, mpage->form); if (mustfree) free(cp); dba_page_add(mpage->dba, DBP_SECT, mpage->sec); while (mlink != NULL) { dbadd_mlink(mlink); mlink = mlink->next; } for (key = ohash_first(&names, &slot); NULL != key; key = ohash_next(&names, &slot)) { assert(key->mpage == mpage); dba_page_alias(mpage->dba, key->key, key->mask); free(key); } for (key = ohash_first(&strings, &slot); NULL != key; key = ohash_next(&strings, &slot)) { assert(key->mpage == mpage); i = 0; for (mask = TYPE_Xr; mask <= TYPE_Lb; mask *= 2) { if (key->mask & mask) dba_macro_add(dba->macros, i, key->key, mpage->dba); i++; } free(key); } } static void dbprune(struct dba *dba) { struct dba_array *page, *files; char *file; dba_array_FOREACH(dba->pages, page) { files = dba_array_get(page, DBP_FILE); dba_array_FOREACH(files, file) { if (*file < ' ') file++; if (ohash_find(&mlinks, ohash_qlookup(&mlinks, file)) != NULL) { if (debug) say(file, "Deleting from database"); dba_array_del(dba->pages); break; } } } } /* * Write the database from memory to disk. */ static void dbwrite(struct dba *dba) { char tfn[32]; int status; pid_t child; if (dba_write(MANDOC_DB "~", dba) != -1) { if (rename(MANDOC_DB "~", MANDOC_DB) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "&rename"); unlink(MANDOC_DB "~"); } return; } (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn)); if (mkdtemp(tfn) == NULL) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&%s", tfn); return; } (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn)); if (dba_write(tfn, dba) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; say(tfn, "&dba_write"); goto out; } switch (child = fork()) { case -1: exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fork cmp"); return; case 0: execlp("cmp", "cmp", "-s", tfn, MANDOC_DB, (char *)NULL); say("", "&exec cmp"); exit(0); default: break; } if (waitpid(child, &status, 0) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&wait cmp"); } else if (WIFSIGNALED(status)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "cmp died from signal %d", WTERMSIG(status)); } else if (WEXITSTATUS(status)) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "Data changed, but cannot replace database"); } out: *strrchr(tfn, '/') = '\0'; switch (child = fork()) { case -1: exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fork rm"); return; case 0: execlp("rm", "rm", "-rf", tfn, (char *)NULL); say("", "&exec rm"); exit((int)MANDOCLEVEL_SYSERR); default: break; } if (waitpid(child, &status, 0) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&wait rm"); } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "%s: Cannot remove temporary directory", tfn); } } static int set_basedir(const char *targetdir, int report_baddir) { static char startdir[PATH_MAX]; static int getcwd_status; /* 1 = ok, 2 = failure */ static int chdir_status; /* 1 = changed directory */ char *cp; /* * Remember the original working directory, if possible. * This will be needed if the second or a later directory * on the command line is given as a relative path. * Do not error out if the current directory is not * searchable: Maybe it won't be needed after all. */ if (0 == getcwd_status) { if (NULL == getcwd(startdir, sizeof(startdir))) { getcwd_status = 2; (void)strlcpy(startdir, strerror(errno), sizeof(startdir)); } else getcwd_status = 1; } /* * We are leaving the old base directory. * Do not use it any longer, not even for messages. */ *basedir = '\0'; /* * If and only if the directory was changed earlier and * the next directory to process is given as a relative path, * first go back, or bail out if that is impossible. */ if (chdir_status && '/' != *targetdir) { if (2 == getcwd_status) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "getcwd: %s", startdir); return 0; } if (-1 == chdir(startdir)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&chdir %s", startdir); return 0; } } /* * Always resolve basedir to the canonicalized absolute * pathname and append a trailing slash, such that * we can reliably check whether files are inside. */ if (NULL == realpath(targetdir, basedir)) { if (report_baddir || errno != ENOENT) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "&%s: realpath", targetdir); } return 0; } else if (-1 == chdir(basedir)) { if (report_baddir || errno != ENOENT) { exitcode = (int)MANDOCLEVEL_BADARG; say("", "&chdir"); } return 0; } chdir_status = 1; cp = strchr(basedir, '\0'); if ('/' != cp[-1]) { if (cp - basedir >= PATH_MAX - 1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "Filename too long"); return 0; } *cp++ = '/'; *cp = '\0'; } return 1; } static void say(const char *file, const char *format, ...) { va_list ap; int use_errno; if ('\0' != *basedir) fprintf(stderr, "%s", basedir); if ('\0' != *basedir && '\0' != *file) fputc('/', stderr); if ('\0' != *file) fprintf(stderr, "%s", file); use_errno = 1; if (NULL != format) { switch (*format) { case '&': format++; break; case '\0': format = NULL; break; default: use_errno = 0; break; } } if (NULL != format) { if ('\0' != *basedir || '\0' != *file) fputs(": ", stderr); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } if (use_errno) { if ('\0' != *basedir || '\0' != *file || NULL != format) fputs(": ", stderr); perror(NULL); } else fputc('\n', stderr); } Index: vendor/mdocml/dist/manpath.c =================================================================== --- vendor/mdocml/dist/manpath.c (revision 313955) +++ vendor/mdocml/dist/manpath.c (revision 313956) @@ -1,284 +1,326 @@ -/* $Id: manpath.c,v 1.31 2016/07/19 22:40:33 schwarze Exp $ */ +/* $Id: manpath.c,v 1.33 2017/02/10 15:45:28 schwarze Exp $ */ /* - * Copyright (c) 2011, 2014, 2015 Ingo Schwarze