diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile new file mode 100644 index 000000000000..3e37b596ef64 --- /dev/null +++ b/usr.bin/man/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +SCRIPTS= man.sh +LINKS= ${BINDIR}/man ${BINDIR}/apropos \ + ${BINDIR}/man ${BINDIR}/manpath \ + ${BINDIR}/man ${BINDIR}/whatis + +MAN= man.1 manpath.1 apropos.1 man.conf.5 +MLINKS= apropos.1 whatis.1 + +.include diff --git a/usr.bin/man/apropos.1 b/usr.bin/man/apropos.1 new file mode 100644 index 000000000000..4b79ada8ee34 --- /dev/null +++ b/usr.bin/man/apropos.1 @@ -0,0 +1,88 @@ +.\"- +.\" Copyright (c) 2010 Gordon Tetlow +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 1, 2010 +.Dt APROPOS 1 +.Os +.Sh NAME +.Nm apropos , +.Nm whatis +.Nd keyword search whatis documentation databases +.Sh SYNOPSIS +.Nm +.Op Fl d +.Ar keyword ... +.Nm whatis +.Op Fl d +.Ar keyword ... +.Sh DESCRIPTION +The +.Nm +utility searches a set of databases looking for documentation matching each +.Ar keyword +and displays the results. +The +.Nm whatis +utility does the same search but only on complete words. +.Bl -tag -width ".Fl d" +.It Fl d +Print extra debugging information. +.El +.Pp +The +.Ar keyword +is simply passed to +.Xr grep 1 +allowing for extended regular expression matches. +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm : +.Bl -tag -width ".Ev PAGER" +.It Ev MANLOCALES , MANPATH , PATH +Used to find the location of the +.Nm whatis +database files. See +.Xr manpath 1 +for additional information. +.It Ev PAGER +Program used to display files. +If unset, +.Ic "more -s" +is used. +.El +.Sh DIAGNOSTICS +The +.Nm +utility exits 0 if a keyword matched and 1 if no keywords are matched or no +.Nm whatis +databases are found. +.Sh SEE ALSO +.Xr grep 1 , +.Xr makewhatis 1 , +.Xr man 1 , +.Xr manpath 1 , +.Xr man.conf 5 diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1 new file mode 100644 index 000000000000..751ea467f55b --- /dev/null +++ b/usr.bin/man/man.1 @@ -0,0 +1,299 @@ +.\"- +.\" Copyright (c) 2010 Gordon Tetlow +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 1, 2010 +.Dt MAN 1 +.Os +.Sh NAME +.Nm man +.Nd display online manual documentation pages +.Sh SYNOPSIS +.Nm +.Op Fl adho +.Op Fl t | Fl w +.Op Fl M Ar manpath +.Op Fl P Ar pager +.Op Fl S Ar mansect +.Op Fl m Ar arch Ns Op : Ns Ar machine +.Op Fl p Op Ar eprtv +.Op Ar mansect +.Ar page ... +.Nm +.Fl f +.Ar keyword ... +.Nm +.Fl k +.Ar keyword ... +.Sh DESCRIPTION +The +.Nm +utility finds and displays online manual documentation pages. +If +.Ar mansect +is provided, +.Nm +restricts the search to the specific section of the manual. +.Pp +Options that +.Nm +understands: +.Bl -tag -width indent +.It Fl M Ar manpath +Forces a specific colon separated manual path instead of the default +search path. +See +.Xr manpath 1 . +Overrides the +.Ev MANPATH +environment variable. +.It Fl P Ar pager +Use specified pager. +Defaults to +.Ic "more -s" . +Overrides the +.Ev PAGER +environment variable. +.It Fl S Ar mansect +Restricts manual sections searched to the specified colon delimited list. +Defaults to +.Va 1:1aout:8:2:3:n:4:5:6:7:9:l . +Overrides the +.Ev MANSECT +environment variable. +.It Fl a +Display all manual pages instead of just the first found for each +.Ar page +argument. +.It Fl d +Print extra debugging information. +Repeat for increased verbosity. +Does not display the manual page. +.It Fl f +Emulate +.Xr whatis 1 . +.It Fl h +Display short help message and exit. +.It Fl k +Emulate +.Xr apropos 1 . +.It Fl m Ar arch Ns Op : Ns Ar machine +Override the default architecture and machine settings allowing lookup of +other platform specific manual pages. +See +.Sx IMPLEMENTATION NOTES +for how this option changes the default behavior. +Overrides the +.Ev MACHINE_ARCH +and +.Ev MACHINE +environment variables. +.It Fl o +Force use of non-localized manual pages. +See +.Sx IMPLEMENTATION NOTES +for how locale specific searches work. +Overrides the +.Ev LC_ALL , LC_CTYPE , +and +.Ev LANG +environment variables. +.It Fl p Op Ar eprtv +Use the list of given preprocessors before running +.Xr nroff 1 +or +.Xr troff 1 . +Valid preprocessors arguments: +.Bl -tag -width indent -compact +.It Cm e +.Xr eqn 1 +.It Cm p +.Xr pic 1 +.It Cm r +.Xr refer 1 +.It Cm t +.Xr tbl 1 +.It Cm v +.Xr vgrind 1 +.El +Overrides the +.Ev MANROFFSEQ +environment variable. +.It Fl t +Send manual page source through +.Xr troff 1 +allowing transformation of the manual pages to other formats. +.It Fl w +Display the location of the manual page instead of the contents of +the manual page. +.El +.Sh IMPLEMENTATION NOTES +.Ss Locale Specific Searches +The +.Nm +utility supports manual pages in different locales. +The search behavior is dictated by the first of three +environment variables with a nonempty string: +.Ev LC_ALL , +.Ev LC_CTYPE , +or +.Ev LANG . +If set, +.Nm +will search for locale specific manual pages using the following logic: +.Bl -item -compact -offset indent +.Sm off +.It +.Va lang _ +.Va country . +.Va charset +.It +.Va lang . +.Va charset +.It +.Li en . +.Va charset +.Sm on +.El +For example, if +.Ev LC_ALL +is set to +.Va ja_JP.eucJP , +.Nm +will search the following paths when considering section 1 manual pages in +.Pa /usr/share/man : +.Bl -item -compact -offset indent +.It +.Pa /usr/share/man/ja_JP.eucJP/man1 +.It +.Pa /usr/share/man/ja.eucJP/man1 +.It +.Pa /usr/share/man/en.eucJP/man1 +.It +.Pa /usr/share/man/man1 +.El +.Ss Platform Specific Searches +The +.Nm +utility supports platform specific manual pages. +The search behavior is dictated by the +.Fl m +option or the +.Ev MACHINE_ARCH +and +.Ev MACHINE +environment variables. +For example, if +.Ev MACHINE_ARCH +is set to +.Va i386 +and +.Ev MACHINE +is set to +.Va pc98 , +.Nm +will search the following paths when considering section 4 manual pages in +.Pa /usr/share/man : +.Bl -item -compact -offset indent +.It +.Pa /usr/share/man/man4/pc98 +.It +.Pa /usr/share/man/man4/i386 +.It +.Pa /usr/share/man/man4 +.El +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm : +.Bl -tag -width ".Ev MANPATH" +.It Ev LC_ALL , LC_CTYPE , LANG +Used to find locale specific manual pages. +Valid values can be found by running the +.Xr locale 1 +command. +See +.Sx IMPLEMENTATION NOTES +for details. +Influenced by the +.Fl o +option. +.It Ev MACHINE_ARCH , MACHINE +Used to find platform specific manual pages. +If unset, the output of +.Ic "sysctl hw.machine_arch" +and +.Ic "sysctl hw.machine" +is used respectively. +See +.Sx IMPLEMENTATION NOTES +for details. +Corresponds to the +.Fl m +option. +.It Ev MANPATH +Used to find the location of the manual files. +See +.Xr manpath 1 +for additional information. +Corresponds to the +.Fl M +option. +.It Ev MANROFFSEQ +Used to determine the preprocessors for the manual source before running +.Xr nroff 1 +or +.Xr troff 1 . +If unset, defaults to +.Xr tbl 1 . +Corresponds to the +.Fl p +option. +.It Ev MANSECT +Restricts manual sections searched to the specified colon delimited list. +Corresponds to the +.Fl S +option. +.It Ev PAGER +Program used to display files. +If unset, +.Ic "more -s" +is used. +.El +.Sh FILES +.Bl -tag -width indent -compact +.It Pa /etc/man.conf +System configuration file. +.It Pa /usr/local/etc/man.d/*.conf +Local configuration files. +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr locale 1 , +.Xr manpath 1 , +.Xr man.conf 5 , +.Xr nroff 1 , +.Xr troff 1 , +.Xr whatis 1 diff --git a/usr.bin/man/man.conf.5 b/usr.bin/man/man.conf.5 new file mode 100644 index 000000000000..6326bc1ebf1d --- /dev/null +++ b/usr.bin/man/man.conf.5 @@ -0,0 +1,142 @@ +.\"- +.\" Copyright (c) 2010 Gordon Tetlow +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 1, 2010 +.Os +.Dt MAN.CONF 5 +.Sh NAME +.Nm man.conf +.Nd +.Xr man 1 +and +.Xr manpath 1 +configuration files +.Sh DESCRIPTION +The +.Nm +file is used to configure the manual search path, locales, and utility set for +.Xr man 1 +and its related utilities. +During initialization, +.Xr man 1 +reads the configuration files located at +.Pa /usr/local/etc/man.d/*.conf +and +.Pa /etc/man.conf . +.Pp +The files contained in +.Pa /usr/local/etc/man.d/*.conf +are intended to be used by the +.Xr ports 7 +system for extending the manual set to support additional paths and locales. +.Pa /etc/man.conf +is intended to be used by the local administrator to set additional policy. +.Pp +Currently supported configuration variables include: +.Bl -tag -offset indent +.It MANCONFIG +Overrides the default location to import additional manual configuration files. +Defaults to +.Pa /usr/local/etc/man.d/*.conf . +.It MANPATH +Adds the specified directory to the manual search path. +.It MANLOCALE +Indicates support is available for the given locale. +.El +.Pp +For pages in a given language, overriding the default toolset for +display is supported via the following definitions: +.Bl -tag -offset indent -compact +.It EQN Ns _ Ns Va LANG +.It COL Ns _ Ns Va LANG +.It NROFF Ns _ Ns Va LANG +.It PIC Ns _ Ns Va LANG +.It TBL Ns _ Ns Va LANG +.It TROFF Ns _ Ns Va LANG +.It REFER Ns _ Ns Va LANG +.It VGRIND Ns _ Ns Va LANG +.El +.Pp +See the +.Sx EXAMPLES +section for how to use these variables. +.Sh IMPLEMENTATION NOTES +The parser used for this utility is very basic and only supports comment +characters (#) at the beginning of a line. +.Sh FILES +.Bl -tag -compact +.It Pa /etc/man.conf +System configuration file. +.It Pa /usr/local/etc/man.d/*.conf +Local configuration files. +.El +.Sh EXAMPLES +A perl port that needs to install additional manual pages outside of the +default location could install a file in +.Pa /usr/local/etc/man.d/perl.conf +with the following contents: +.Bd -literal -offset indent +# Add perl man pages to search path +MANPATH /usr/local/lib/perl5/5.8.9/man +MANPATH /usr/local/lib/perl5/5.8.9/perl/man +.Ed +.Pp +A Japanese localization port could install a custom toolset and include a +file in +.Pa /usr/local/etc/man.d/ja-man-doc.conf +with the following contents: +.Bd -literal -offset indent +# Setup Japanese toolset +MANLOCALE ja_JP.eucJP +EQN_JA /usr/local/bin/gepn +PIC_JA /usr/local/bin/gpic +TBL_JA /usr/local/bin/gtbl +NROFF_JA /usr/local/bin/groff -man -dlang=ja_JP.eucJP +TROFF_JA /usr/local/bin/groff -man -dlang=ja_JP.euc.jp +.Ed +.Pp +If the system administrator decides to override the +.Va LOCALBASE +.Xr make 1 +variable causing all +.Xr ports 7 +to be installed into +.Pa /opt +instead of +.Pa /usr/local , +specifying the following in +.Pa /etc/man.conf +will accommodate this change: +.Bd -literal -offset indent +# Look for additional configuration files +MANCONFIG /opt/etc/man.d/*.conf +.Ed +.Sh SEE ALSO +.Xr apropos 1 , +.Xr man 1 , +.Xr manpath 1 , +.Xr whatis 1 diff --git a/usr.bin/man/man.sh b/usr.bin/man/man.sh new file mode 100755 index 000000000000..f8da4daf3d8e --- /dev/null +++ b/usr.bin/man/man.sh @@ -0,0 +1,847 @@ +#! /bin/sh +# +# Copyright (c) 2010 Gordon Tetlow +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# +# $FreeBSD$ + +# Usage: add_to_manpath path +# Adds a variable to manpath while ensuring we don't have duplicates. +# Returns true if we were able to add something. False otherwise. +add_to_manpath() { + case "$manpath" in + *:$1) decho " Skipping duplicate manpath entry $1" 2 ;; + $1:*) decho " Skipping duplicate manpath entry $1" 2 ;; + *:$1:*) decho " Skipping duplicate manpath entry $1" 2 ;; + *) if [ -d "$1" ]; then + decho " Adding $1 to manpath" + manpath="$manpath:$1" + return 0 + fi + ;; + esac + + return 1 +} + +# Usage: build_manlocales +# Builds a correct MANLOCALES variable. +build_manlocales() { + # If the user has set manlocales, who are we to argue. + if [ -n "$MANLOCALES" ]; then + return + fi + + parse_configs + + # Trim leading colon + MANLOCALES=${manlocales#:} + + decho "Available manual locales: $MANLOCALES" +} + +# Usage: build_manpath +# Builds a correct MANPATH variable. +build_manpath() { + local IFS + + # If the user has set a manpath, who are we to argue. + if [ -n "$MANPATH" ]; then + return + fi + + search_path + + decho "Adding default manpath entries" + IFS=: + for path in $man_default_path; do + add_to_manpath "$path" + done + unset IFS + + parse_configs + + # Trim leading colon + MANPATH=${manpath#:} + + decho "Using manual path: $MANPATH" +} + +# Usage: check_cat catglob +# Checks to see if a cat glob is available. +check_cat() { + if exists "$1"; then + use_cat=yes + catpage=$found + decho " Found catpage $catpage" + return 0 + else + return 1 + fi +} + +# Usage: check_man manglob catglob +# Given 2 globs, figures out if the manglob is available, if so, check to +# see if the catglob is also available and up to date. +check_man() { + if exists "$1"; then + # We have a match, check for a cat page + manpage=$found + decho " Found manpage $manpage" + + if exists "$2" && is_newer $found $manpage; then + # cat page found and is newer, use that + use_cat=yes + catpage=$found + decho " Using catpage $catpage" + else + # no cat page or is older + unset use_cat + decho " Skipping catpage: not found or old" + fi + return 0 + fi + + return 1 +} + +# Usage: decho "string" [debuglevel] +# Echoes to stderr string prefaced with -- if high enough debuglevel. +decho() { + if [ $debug -ge ${2:-1} ]; then + echo "-- $1" >&2 + fi +} + +# Usage: exists glob +# Returns true if glob resolves to a real file. +exists() { + local IFS + + # Don't accidentally inherit callers IFS (breaks perl manpages) + unset IFS + + # Use some globbing tricks in the shell to determine if a file + # exists or not. + set +f + set -- "$1" $1 + set -f + + if [ "$1" != "$2" -a -r "$2" ]; then + found="$2" + return 0 + fi + + return 1 +} + +# Usage: find_file path section subdir pagename +# Returns: true if something is matched and found. +# Search the given path/section combo for a given page. +find_file() { + local manroot catroot mann man0 catn cat0 + + manroot="$1/man$2" + catroot="$1/cat$2" + if [ -n "$3" ]; then + manroot="$manroot/$3" + catroot="$catroot/$3" + fi + + if [ ! -d "$manroot" ]; then + return 1 + fi + decho " Searching directory $manroot" 2 + + mann="$manroot/$4.$2*" + man0="$manroot/$4.0*" + catn="$catroot/$4.$2*" + cat0="$catroot/$4.0*" + + # This is the behavior as seen by the original man utility. + # Let's not change that which doesn't seem broken. + if check_man "$mann" "$catn"; then + return 0 + elif check_man "$man0" "$cat0"; then + return 0 + elif check_cat "$catn"; then + return 0 + elif check_cat "$cat0"; then + return 0 + fi + + return 1 +} + +# Usage: is_newer file1 file2 +# Returns true if file1 is newer than file2 as calculated by mtime. +is_newer() { + if [ $(stat -f %m $1) -gt $(stat -f %m $2) ]; then + decho " mtime: $1 newer than $2" 3 + return 0 + else + decho " mtime: $1 older than $2" 3 + return 1 + fi +} + +# Usage: manpath_parse_args "$@" +# Parses commandline options for manpath. +manpath_parse_args() { + local cmd_arg + + while getopts 'Ldq' cmd_arg; do + case "${cmd_arg}" in + L) Lflag=Lflag ;; + d) debug=$(( $debug + 1 )) ;; + q) qflag=qflag ;; + *) manpath_usage ;; + esac + done >&2 +} + +# Usage: manpath_usage +# Display usage for the manpath(1) utility. +manpath_usage() { + echo 'usage: manpath [-Ldq]' >&2 + exit 1 +} + +# Usage: manpath_warnings +# Display some warnings to stderr. +manpath_warnings() { + if [ -z "$Lflag" -a -n "$MANPATH" ]; then + echo "(Warning: MANPATH environment variable set)" >&2 + fi + + if [ -n "$Lflag" -a -n "$MANLOCALES" ]; then + echo "(Warning: MANLOCALES environment variable set)" >&2 + fi +} + +# Usage: man_display_page +# Display either the manpage or catpage depending on the use_cat variable +man_display_page() { + local EQN COL NROFF PIC TBL TROFF REFER VGRIND + local IFS l nroff_dev pipeline preproc_arg tool + + # We are called with IFS set to colon. This causes really weird + # things to happen for the variables that have spaces in them. + unset IFS + + # If we are supposed to use a catpage and we aren't using troff(1) + # just zcat the catpage and we are done. + if [ -z "$tflag" -a -n "$use_cat" ]; then + if [ -n "$wflag" ]; then + echo "$catpage (source: $manpage)" + ret=0 + else + if [ $debug -gt 0 ]; then + decho "Command: $ZCAT $catpage | $PAGER" + ret=0 + else + eval "$ZCAT $catpage | $PAGER" + ret=$? + fi + fi + return + fi + + # Okay, we are using the manpage, do we just need to output the + # name of the manpage? + if [ -n "$wflag" ]; then + echo "$manpage" + ret=0 + return + fi + + # So, we really do need to parse the manpage. First, figure out the + # device flag (-T) we have to pass to eqn(1) and groff(1). Then, + # setup the pipeline of commands based on the user's request. + + # Apparently the locale flags are switched on where the manpage is + # found not just the locale env variables. + nroff_dev="ascii" + case "X${use_locale}X${manpage}" in + XyesX*/${man_lang}*${man_charset}/*) + # I don't pretend to know this; I'm just copying from the + # previous version of man(1). + case "$man_charset" in + KOI8-R) nroff_dev="koi8-r" ;; + ISO8859-1) nroff_dev="latin1" ;; + ISO8859-15) nroff_dev="latin1" ;; + UTF-8) nroff_dev="utf8" ;; + *) nroff_dev="ascii" ;; + esac + + NROFF="$NROFF -T$nroff_dev -dlocale=$man_lang.$man_charset" + EQN="$EQN -T$nroff_dev" + + # Allow language specific calls to override the default + # set of utilities. + l=$(echo $man_lang | tr [:lower:] [:upper:]) + for tool in EQN COL NROFF PIC TBL TROFF REFER VGRIND; do + eval "$tool=\${${tool}_$l:-\$$tool}" + done + ;; + *) NROFF="$NROFF -Tascii" + EQN="$EQN -Tascii" + ;; + esac + + if [ -n "$MANROFFSEQ" ]; then + set -- -$MANROFFSEQ + while getopts 'egprtv' preproc_arg; do + case "${preproc_arg}" in + e) pipeline="$pipeline | $EQN" ;; + g) ;; # Ignore for compatability. + p) pipeline="$pipeline | $PIC" ;; + r) pipeline="$pipeline | $REFER" ;; + t) pipeline="$pipeline | $TBL"; use_col=yes ;; + v) pipeline="$pipeline | $VGRIND" ;; + *) usage ;; + esac + done + # Strip the leading " | " from the resulting pipeline. + pipeline="${pipeline#" | "}" + else + pipeline="$TBL" + use_col=yes + fi + + if [ -n "$tflag" ]; then + pipeline="$pipeline | $TROFF" + else + pipeline="$pipeline | $NROFF" + + if [ -n "$use_col" ]; then + pipeline="$pipeline | $COL" + fi + + pipeline="$pipeline | $PAGER" + fi + + if [ $debug -gt 0 ]; then + decho "Command: $ZCAT $manpage | $pipeline" + ret=0 + else + eval "$ZCAT $manpage | $pipeline" + ret=$? + fi +} + +# Usage: man_find_and_display page +# Search through the manpaths looking for the given page. +man_find_and_display() { + local found_page locpath p path sect + + IFS=: + for sect in $MANSECT; do + decho "Searching section $sect" 2 + for path in $MANPATH; do + for locpath in $locpaths; do + p=$path/$locpath + p=${p%/.} # Rid ourselves of the trailing /. + + # Check if there is a MACHINE specific manpath. + if find_file $p $sect $MACHINE "$1"; then + found_page=yes + man_display_page + if [ -z "$aflag" ]; then + return + fi + fi + + # Check if there is a MACHINE_ARCH + # specific manpath. + if find_file $p $sect $MACHINE_ARCH "$1"; then + found_page=yes + man_display_page + if [ -z "$aflag" ]; then + return + fi + fi + + # Check plain old manpath. + if find_file $p $sect '' "$1"; then + found_page=yes + man_display_page + if [ -z "$aflag" ]; then + return + fi + fi + done + done + done + unset IFS + + # Nothing? Well, we are done then. + if [ -z "$found_page" ]; then + echo "No manual entry for $1" >&2 + ret=1 + return + fi +} + +# Usage: man_parse_args "$@" +# Parses commandline options for man. +man_parse_args() { + local IFS cmd_arg + + while getopts 'M:P:S:adfhkm:op:tw' cmd_arg; do + case "${cmd_arg}" in + M) MANPATH=$OPTARG ;; + P) PAGER=$OPTARG ;; + S) MANSECT=$OPTARG ;; + a) aflag=aflag ;; + d) debug=$(( $debug + 1 )) ;; + f) fflag=fflag ;; + h) man_usage 0 ;; + k) kflag=kflag ;; + m) mflag=$OPTARG ;; + o) oflag=oflag ;; + p) MANROFFSEQ=$OPTARG ;; + t) tflag=tflag ;; + w) wflag=wflag ;; + *) man_usage ;; + esac + done >&2 + + shift $(( $OPTIND - 1 )) + + # Check the args for incompatible options. + case "${fflag}${kflag}${tflag}${wflag}" in + fflagkflag*) echo "Incompatible options: -f and -k"; man_usage ;; + fflag*tflag*) echo "Incompatible options: -f and -t"; man_usage ;; + fflag*wflag) echo "Incompatible options: -f and -w"; man_usage ;; + *kflagtflag*) echo "Incompatible options: -k and -t"; man_usage ;; + *kflag*wflag) echo "Incompatible options: -k and -w"; man_usage ;; + *tflagwflag) echo "Incompatible options: -t and -w"; man_usage ;; + esac + + # Short circuit for whatis(1) and apropos(1) + if [ -n "$fflag" ]; then + do_whatis "$@" + exit + fi + + if [ -n "$kflag" ]; then + do_apropos "$@" + exit + fi + + IFS=: + for sect in $man_default_sections; do + if [ "$sect" = "$1" ]; then + decho "Detected manual section as first arg: $1" + MANSECT="$1" + shift + break + fi + done + unset IFS + + pages="$*" +} + +# Usage: man_setup +# Setup various trivial but essential variables. +man_setup() { + # Setup machine and architecture variables. + if [ -n "$mflag" ]; then + MACHINE_ARCH=${mflag%%:*} + MACHINE=${mflag##*:} + fi + if [ -z "$MACHINE_ARCH" ]; then + MACHINE_ARCH=$(sysctl -n hw.machine_arch) + fi + if [ -z "$MACHINE" ]; then + MACHINE=$(sysctl -n hw.machine) + fi + decho "Using architecture: $MACHINE_ARCH:$MACHINE" + + setup_pager + + # Setup manual sections to search. + if [ -z "$MANSECT" ]; then + MANSECT=$man_default_sections + fi + decho "Using manual sections: $MANSECT" + + build_manpath + man_setup_locale +} + +# Usage: man_setup_locale +# Setup necessary locale variables. +man_setup_locale() { + # Setup locale information. + if [ -n "$oflag" ]; then + decho "Using non-localized manpages" + unset use_locale + elif [ -n "$LC_ALL" ]; then + parse_locale "$LC_ALL" + elif [ -n "$LC_CTYPE" ]; then + parse_locale "$LC_CTYPE" + elif [ -n "$LANG" ]; then + parse_locale "$LANG" + fi + + if [ -n "$use_locale" ]; then + locpaths="${man_lang}_${man_country}.${man_charset}" + locpaths="$locpaths:$man_lang.$man_charset" + if [ "$man_lang" != "en" ]; then + locpaths="$locpaths:en.$man_charset" + fi + locpaths="$locpaths:." + else + locpaths="." + fi + decho "Using locale paths: $locpaths" +} + +# Usage: man_usage [exitcode] +# Display usage for the man utility. +man_usage() { + echo 'Usage:' + echo ' man [-adho] [-t | -w] [-M manpath] [-P pager] [-S mansect]' + echo ' [-m arch[:machine]] [-p [eprtv]] [mansect] page [...]' + echo ' man -f page [...] -- Emulates whatis(1)' + echo ' man -k page [...] -- Emulates apropos(1)' + + # When exit'ing with -h, it's not an error. + exit ${1:-1} +} + +# Usage: parse_configs +# Reads the end-user adjustable config files. +parse_configs() { + local IFS file files + + if [ -n "$parsed_configs" ]; then + return + fi + + unset IFS + + # Read the global config first in case the user wants + # to override config_local. + if [ -r "$config_global" ]; then + parse_file "$config_global" + fi + + # Glob the list of files to parse. + set +f + files=$(echo $config_local) + set -f + + for file in $files; do + if [ -r "$file" ]; then + parse_file "$file" + fi + done + + parsed_configs='yes' +} + +# Usage: parse_file file +# Reads the specified config files. +parse_file() { + local file line tstr var + + file="$1" + decho "Parsing config file: $file" + while read line; do + decho " $line" 2 + case "$line" in + \#*) decho " Comment" 3 + ;; + MANPATH*) decho " MANPATH" 3 + trim "${line#MANPATH}" + add_to_manpath "$tstr" + ;; + MANLOCALE*) decho " MANLOCALE" 3 + trim "${line#MANLOCALE}" + manlocales="$manlocales:$tstr" + ;; + MANCONFIG*) decho " MANCONFIG" 3 + trim "${line#MANCONF}" + config_local="$tstr" + ;; + # Set variables in the form of FOO_BAR + *_*[\ \ ]*) var="${line%%[\ \ ]*}" + trim "${line#$var}" + eval "$var=\"$tstr\"" + decho " Parsed $var" 3 + ;; + esac + done < "$file" +} + +# Usage: parse_locale localestring +# Setup locale variables for proper parsing. +parse_locale() { + local lang_cc + + case "$1" in + C) ;; + POSIX) ;; + [a-z][a-z]_[A-Z][A-Z]\.*) lang_cc="${1%.*}" + man_lang="${1%_*}" + man_country="${lang_cc#*_}" + man_charset="${1#*.}" + use_locale=yes + return 0 + ;; + *) echo 'Unknown locale, assuming C' >&2 + ;; + esac + + unset use_locale +} + +# Usage: search_path +# Traverse $PATH looking for manpaths. +search_path() { + local IFS p path + + decho "Searching PATH for man directories" + + IFS=: + for path in $PATH; do + # Do a little special casing since the base manpages + # are in /usr/share/man instead of /usr/man or /man. + case "$path" in + /bin|/usr/bin) add_to_manpath "/usr/share/man" ;; + *) if add_to_manpath "$path/man"; then + : + elif add_to_manpath "$path/MAN"; then + : + else + case "$path" in + */bin) p="${path%/bin}/man" + add_to_manpath "$p" + ;; + *) ;; + esac + fi + ;; + esac + done + unset IFS + + if [ -z "$manpath" ]; then + decho ' Unable to find any manpaths, using default' + manpath=$man_default_path + fi +} + +# Usage: search_whatis cmd [arglist] +# Do the heavy lifting for apropos/whatis +search_whatis() { + local IFS bad cmd f good key keywords loc opt out path rval wlist + + cmd="$1" + shift + + whatis_parse_args "$@" + + build_manpath + build_manlocales + setup_pager + + if [ "$cmd" = "whatis" ]; then + opt="-w" + fi + + f='whatis' + + IFS=: + for path in $MANPATH; do + if [ \! -d "$path" ]; then + decho "Skipping non-existent path: $path" 2 + continue + fi + + if [ -f "$path/$f" -a -r "$path/$f" ]; then + decho "Found whatis: $path/$f" + wlist="$wlist $path/$f" + fi + + for loc in $MANLOCALES; do + if [ -f "$path/$loc/$f" -a -r "$path/$loc/$f" ]; then + decho "Found whatis: $path/$loc/$f" + wlist="$wlist $path/$loc/$f" + fi + done + done + unset IFS + + if [ -z "$wlist" ]; then + echo "$cmd: no whatis databases in $MANPATH" >&2 + exit 1 + fi + + rval=0 + for key in $keywords; do + out=$(grep -Ehi $opt -- "$key" $wlist) + if [ -n "$out" ]; then + good="$good\\n$out" + else + bad="$bad\\n$key: nothing appropriate" + rval=1 + fi + done + + # Strip leading carriage return. + good=${good#\\n} + bad=${bad#\\n} + + if [ -n "$good" ]; then + echo -e $good | $PAGER + fi + + if [ -n "$bad" ]; then + echo -e $bad >&2 + fi + + exit $rval +} + +# Usage: setup_pager +# Correctly sets $PAGER +setup_pager() { + # Setup pager. + if [ -z "$PAGER" ]; then + PAGER="more -s" + fi + decho "Using pager: $PAGER" +} + +# Usage: trim string +# Trims whitespace from beginning and end of a variable +trim() { + tstr=$1 + while true; do + case "$tstr" in + [\ \ ]*) tstr="${tstr##[\ \ ]}" ;; + *[\ \ ]) tstr="${tstr%%[\ \ ]}" ;; + *) break ;; + esac + done +} + +# Usage: whatis_parse_args "$@" +# Parse commandline args for whatis and apropos. +whatis_parse_args() { + local cmd_arg + while getopts 'd' cmd_arg; do + case "${cmd_arg}" in + d) debug=$(( $debug + 1 )) ;; + *) whatis_usage ;; + esac + done >&2 + + shift $(( $OPTIND - 1 )) + + keywords="$*" +} + +# Usage: whatis_usage +# Display usage for the whatis/apropos utility. +whatis_usage() { + echo "usage: $cmd [-d] keyword [...]" + exit 1 +} + + + +# Supported commands +do_apropos() { + search_whatis apropos "$@" +} + +do_man() { + man_parse_args "$@" + if [ -z "$pages" ]; then + echo 'What manual page do you want?' >&2 + exit 1 + fi + man_setup + + for page in $pages; do + decho "Searching for $page" + man_find_and_display "$page" + done + + exit ${ret:-0} +} + +do_manpath() { + manpath_parse_args "$@" + if [ -z "$qflag" ]; then + manpath_warnings + fi + if [ -n "$Lflag" ]; then + build_manlocales + echo $MANLOCALES + else + build_manpath + echo $MANPATH + fi + exit 0 +} + +do_whatis() { + search_whatis whatis "$@" +} + +EQN=/usr/bin/eqn +COL=/usr/bin/col +NROFF='/usr/bin/groff -S -Wall -mtty-char -man' +PIC=/usr/bin/pic +TBL=/usr/bin/tbl +TROFF='/usr/bin/groff -S -man' +REFER=/usr/bin/refer +VGRIND=/usr/bin/vgrind +ZCAT='/usr/bin/zcat -f' + +debug=0 +man_default_sections='1:1aout:8:2:3:n:4:5:6:7:9:l' +man_default_path='/usr/share/man:/usr/share/openssl/man:/usr/local/man' + +config_global='/etc/man.conf' + +# This can be overridden via a setting in /etc/man.conf. +config_local='/usr/local/etc/man.d/*.conf' + +# Set noglobbing for now. I don't want spurious globbing. +set -f + +case "$0" in +*apropos) do_apropos "$@" ;; +*manpath) do_manpath "$@" ;; +*whatis) do_whatis "$@" ;; +*) do_man "$@" ;; +esac diff --git a/usr.bin/man/manpath.1 b/usr.bin/man/manpath.1 new file mode 100644 index 000000000000..2cf39abc804f --- /dev/null +++ b/usr.bin/man/manpath.1 @@ -0,0 +1,123 @@ +.\"- +.\" Copyright (c) 2010 Gordon Tetlow +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 1, 2010 +.Dt MANPATH 1 +.Os +.Sh NAME +.Nm manpath +.Nd display search path for manual pages +.Sh SYNOPSIS +.Nm +.Op Fl Ldq +.Sh DESCRIPTION +The +.Nm +utility determines the user's manual search path from +the user's +.Ev PATH , +and local configuration files. +This result is echoed to the standard output. +.Bl -tag -width ".Fl d" +.It Fl L +Output manual locales list instead of the manual path. +.It Fl d +Print extra debugging information. +.It Fl q +Suppresses warning messages. +.El +.Sh IMPLEMENTATION NOTES +The +.Nm +utility constructs the manual path from two sources: +.Bl -enum -compact +.It +From each component of the user's +.Ev PATH +for the first of: +.Bl -dash -compact +.It +.Pa pathname/man +.It +.Pa pathname/MAN +.It +If pathname ends with /bin: +.Pa pathname/../man +.El +Note: Special logic exists to make +.Pa /bin +and +.Pa /usr/bin +look in +.Pa /usr/share/man +for manual files. +.It +The configuration files listed in the +.Sx FILES +section for +.Va MANPATH +entries. +.El +The information from these locations is then concatenated together. +.Pp +If the +.Fl L +flag is set, the +.Nm +utility will search the configuration files listed in the +.Sx FILES +section for +.Va MANLOCALE +entries. +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm : +.Bl -tag -width ".Ev MANLOCALES" +.It Ev MANLOCALES +If set with the +.Fl L +flag, causes the utility to display a warning and the value, overriding any +other configuration found on the system. +.It Ev MANPATH +If set, causes the utility to display a warning and the value, overriding +any other configuration found on the system. +.It Ev PATH +Influences the manual path as described in the +.Sx IMPLEMENTATION NOTES . +.El +.Sh FILES +.Bl -tag -width indent -compact +.It Pa /etc/man.conf +System configuration file. +.It Pa /usr/local/etc/man.d/*.conf +Local configuration files. +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr man 1 , +.Xr man.conf 5 , +.Xr whatis 1