[alt.sources] The Answer to All Man's Problems

tchrist@convex.com (Tom Christiansen) (01/08/91)

Here is a deluxe man program and associated utilities written in perl.
It does everything any other man program does that I've ever seen,
plus quite a bit more.  There is documentation in the form of man 
pages as well as the paper I presented at the USENIX Large 
Installation Systems Administrations conference.  

There will be six parts to the distribution.  Cat them together
and then unshar.  Here is a list of features.  Note that the
whatis databases are stored in ndbm format for rapid access, so 
you'll need ndbm.  

Changes since the last posting include:

    *   extra utils

    *   grep feature

    *   online problem report feature

    *   dynamic determination of MANPATH

    *   bug fixes

Thanks to everyone who's sent me bug reports or fixes.

--tom

Features include but are not limited to:

    *   almost always faster than standard man (try 'man me')

    *   take much less diskspace for catpages

    *   supports per-tree tmac macros
    
    *   compressed man and cat files

    *   user-definable man path via $MANPATH or -M optoin
    
    *  $MANPATH autoconfigged based on $PATH if not set

    *   user-definable section search order via -S or $MANSECT.  Thus
        programmers can get stty(3) before stty(1).
    
    *   $PAGER support

    *   show all the places you would find a man page (-w option)
        and in what order.

    *   display all available man page on a topic (-a option)
    
    *   no limits on what subsections go where (if you want to add 7x, ok)

    *   support for multi-char sections like man1m/*.1m or manavs/*.avs

    *   man -K for regexp apropos

    *   grep through all the man pages in $MANPATH

    *   section and subsection indexing for long man pages

    *   support for alternate architectures docs on same machine

    *   ability to run man on a local file 

    *   ability to easily troff (or preview) a man page

    *   recognizes embedded filter directives for tbl and eqn

    *   does the right thing for man tree that don't have DBM whatis files

    *   support for connecting online problem reports to right man page
   
    *   there's an extended usage message (man -U) for further help
        and to show current defaults.


Here are some features of this version of makewhatis:

    *   it's faster.

    *   tries hard to make pretty output, stripping troff directives.

    *   doesn't blow up on more files in a man directory 
        than the shell will glob.  

    *   accepts troff string macros for the dashes in the
        the NAME section.

    *   prints a diagnostic for a malformed NAME section.

    *   detects linked (hard, soft, or via .so) man pages

    *   finds *all* references in the NAME section.

    *   recognizes MH's man macros (and .Sh from lwall).

    *   many other things that makewhatis used to do wrong

Here are some supporting utilities that are included:

    *   catman -- new version that groks compressed files

    *   catwhatis -- display the whatis databases

    *   straycats -- find cat pages with no man page ancestor

    *   countman -- find how many man pages you can get at

    *   cfman -- find bad SEE ALSO references in man pages
--
Tom Christiansen                tchrist@convex.com      convex!tchrist

    Perl programming is an *empirical* science!  
            --Larry Wall in <10226@jpl-devvax.JPL.NASA.GOV>

tchrist@convex.COM (Tom Christiansen) (01/08/91)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	man
# This archive created: Mon Jan  7 15:58:51 1991
export PATH; PATH=/bin:/usr/bin:$PATH
if test ! -d 'man'
then
	echo shar: "creating directory 'man'"
	mkdir 'man'
fi
echo shar: "entering directory 'man'"
cd 'man'
echo shar: "extracting 'makewhatis.8'" '(9442 characters)'
if test -f 'makewhatis.8'
then
	echo shar: "will not over-write existing file 'makewhatis.8'"
else
sed 's/^	X//' << \SHAR_EOF > 'makewhatis.8'
	X.TH MAKEWHATIS 8
	X.CX		\" probably only makes sense on Convex machines
	X.\"
	X.de SB		\" small and bold
	X.if !"\\$1"" \\s-1\\fB\&\\$1\\s0\\fR\\$2 \\$3 \\$4 \\$5
	X..
	X.\"
	X.de T		\" switch to typewriter font
	X.ft TA		\" probably want CW if you don't have TA font
	X..
	X.\"
	X.de TY		\" put $1 in typewriter font
	X.if t .T
	X.if n ``\c
	X\\$1\c
	X.if t .ft P
	X.if n \&''\c
	X\\$2
	X..
	X.\"
	X.de M		\" man page reference
	X\\fI\\$1\\fR\\|(\\$2\)\\$3
	X..
	X.SH NAME
	Xmakewhatis \- rebuild the whatis database
	X.SH SYNOPSIS
	X.B /usr/lib/makewhatis 
	X[
	X.B \-v
	X] [
	X.B \-n
	X] [
	X.B \-y
	X] [[
	X.B \-M
	X] manpath ]
	X.SH DESCRIPTION
	XThe
	X.I makewhatis
	Xprogram
	Xrebuilds the text and 
	X.M dbm 3X
	Xforms of the 
	X.I whatis
	Xdatabase 
	Xfrom the on-line manual pages for a given
	X\fImanpath\fP, or from 
	X.I /usr/man
	Xif none is supplied.
	XThese files are 
	Xused by the
	X.M man 1 , 
	X.M whatis 1 , 
	Xand 
	X.M apropos 1
	Xprograms to locate man pages and to print out the one-line 
	Xdescriptions given by 
	X.I whatis
	Xand 
	X.I apropos\c
	X\&.
	XThe
	X.I makewhatis
	Xprogram
	Xshould be run whenever new man pages are added so these programs can 
	Xfind them.  
	X.PP
	XEach component of the colon-delimited 
	X.I manpath 
	Xis interpreted to be the root
	Xof a new tree containing directories of the form \fIman*\fP,  
	Xwithin which are unformatted man pages of the form \fI*.*\fP.  A separate 
	X.I whatis
	Xfile and corresponding 
	X.I dbm 
	Xfiles, 
	X.I whatis.pag
	Xand 
	X\fIwhatis.dir\fP,
	Xwill be placed at the root of that man tree.
	XFiles or directories ending in tilde (\fB~\fP) or in \fB.bak\fP or \fB.old\fP (in either case)
	Xwill be skipped, as will a section named \fBman0\fP, should it exist. 
	X.PP
	XIn general, files in section \fBmanX\fP should end in \fB.X*\fP, where
	X\fBX\fP is a section of the manual like \fB1\fP or \fB3x11\fP.  This means
	Xit's ok to put things ending in \fB1c\fP in \fBman1\fP, but not things 
	Xending in \fB3s\fP.  Any man page whose name doesn't match these criteria
	Xwill generate a warning, but will be processed anyway.  The exception
	Xto this is the \fBmano\fP subdirectory, which has 
	Xtraditionally been the receptacle of all obsolete
	Xman pages irrespective of their file extension.  A better way to do that
	Xwould be to have an entire man tree dedicated to this, as in 
	X\fI/usr/old/man/\fP.
	X.PP
	XMultiple entries occurring in the 
	X.SB NAME
	Xsection or as links (hard, soft, or via \fB.so\fP inclusion) will all be
	Xstored under the same man page name.  This method can save significant amounts
	Xof disk space because it guarantees that only one cat page need be generated, 
	Xregardless of how many ways you can get at the corresponding man page.  
	XMaintaining aliases as links or via a one-line file that \fB.so\fP's the 
	Xreal man page (once the only way to do this) is still supported although
	Xno longer
	Xrequired; mere inclusion in the 
	X.SB NAME
	Xsection is sufficient.
	X.PP
	XEmbedded point and font changes will be removed from the output, and
	X\fItroff\fP string macros for hyphens and underscores will be 
	Xtranslated into their corresponding 
	X\s-1ASCII\s0
	Xrepresentations.  In 
	Xgeneral, all that can be done will be done to produce nicely readable output for
	X.M whatis 1 .
	X.PP
	XThe 
	X.B \-v
	Xflag will generate verbose output, reporting such things as each new man tree
	Xexamined, each subdirectory, each file that is opened, and each entry in the 
	X.SB NAME
	Xsection that is found and stored.  Simple tracing information is printed
	Xto the standard output, while to standard error are directed
	Xmore serious warnings.
	X.PP
	XThe 
	X.B \-n
	Xoption can be used to check whether the database needs rebuilding.  It 
	Xwill report on the first file in each man path that is out of date.  Note
	Xthat this is the only automated way to determine this; neither 
	X.I makewhatis
	Xnor 
	X.I man
	Xwill realize that the database is out of date, so you should be careful
	Xto rebuild it whenever new man pages are added.
	X.PP
	XThe
	X.B \-y
	Xoption is similar, but after finding something out of date, 
	X.I makewhatis
	Xwill automatically rebuild its database.
	X.PP
	XSupport for compressed man pages is provided in the following way:
	Xif the name of the subdirectory itself ends in \fB.Z\fP, as in \fBman8.Z\fP,
	Xthen all its files are assumed to have been compressed with 
	X.M compress 1L .
	XAlternatively, individual files ending in \fB.Z\fP are also 
	Xconsidered to be compressed.
	XCompressed files are processed with 
	X.M zcat 1L .  
	X.SH EXAMPLES
	X.ft TA
	X.nf 
	X.ta 3i
	X% makewhatis -y	# build /usr/man database only if out of date
	X% makewhatis /usr/local/man	# make local man page database
	X% makewhatis -v ~/man	# make personal man page database verbosely
	X% makewhatis -n $MANPATH	# tell if any dbase in $MANPATH out of date
	X.ft P
	X.fi
	X.SH FILES
	X.nf
	X.ta \w'/usr/man/whatis.pag  'u
	X\fI/usr/man/whatis\fP	default whatis database, text version
	X\fI/usr/man/whatis.pag\fP	\fIdbm\fP index file for default whatis database
	X\fI/usr/man/whatis.dir\fP	\fIdbm\fP data file for default whatis database
	X\fI/usr/man/man*/*.*\fR	default (unformatted) man pages
	X\fI/usr/man/cat*/*.*\fR	formatted man pages
	X.SH "SEE ALSO"
	Xman(1),
	Xwhatis(1),
	Xapropos(1),
	Xperl(1),
	Xcompress(1L),
	Xdbm(3X),
	Xman(7),
	Xcatman(8)
	X.SH DIAGNOSTICS
	XNumerous diagnostics may be issued, especially if the 
	X.B \-v
	Xflag has been given.  Here are messages that may require
	Xfurther attention.  The 
	X.B %s 
	Xand 
	X.B %d
	Xfields
	Xin the descriptions indicate strings and integers respectively
	Xthat will be filled
	Xin appropriately at run time, while 
	X.B %m
	Xindicates a standard system error message as
	Xdescribed in 
	X.M intro 2 .
	XSee the source for further details.
	X.sp
	X.TY "Skipping non-man file: %s"
	X.in +5n
	XA file was found in a man directory that has no dot 
	Xin its name.
	X.in -5n
	X.sp 
	X.TY "%s has a funny extension to be in %s"
	X.in +5n
	XAn apparent man file was found whose extension doesn't
	Xmatch the name of the man file in which it was found.
	XThis restriction does not apply to 
	X.B mano
	Xsubdirectories.
	X.in -5n
	X.sp 
	X.TY "can't stat %s: %m"
	X.in +5n
	XThe 
	X.M stat 2
	Xcall returned an error.  This can be caused by a
	Xsymbolic link pointing to a non-existent file.
	X.in -5n
	X.sp 
	X.TY "can't open %s: %m"
	X.in +5n
	XThe
	X.M open 2
	Xcall returned an error, which is listed.
	X.in -5n
	X.sp 
	X.TY "can't chdir back to %s: %m"
	X.in +5n
	XAfter changing directory to one of the man subdirectories, 
	X.I makewhatis
	Xcouldn't return to the initial root man tree.  This is
	Xa fatal error.
	X.in -5n
	X.sp 
	X.TY "makewhatis: %s: found %d entries in %d files"
	X.in +5n
	XFor the given root man directory, this many separate
	Xentry points were found for this many different files.
	X.in -5n
	X.sp 
	X.TY "%s .so references non-existent %s"
	X.in +5n
	XA file contains a 
	X.B .so
	Xreference to a missing man page.
	X.in -5n
	X.sp 
	X.TY "%s's .TH thinks it's in %s"
	X.in +5n
	XThe man page's internal
	X.SB TH
	Xsection has a different idea of where it lives
	Xthan the actual file name.
	X.in -5n
	X.sp 
	X.TY "trimmed troff string macro in NAME section of %s"
	X.in +5n
	XAn irresolvable 
	X.I troff
	Xstring macro was found within the
	X.SB NAME
	Xsection of the man page.  This may cause peculiar
	X.I whatis
	Xoutput.
	X.in -5n
	X.sp 
	X.TY "%s: no separated dash in %s"
	X.in +5n
	XThe 
	X.SB NAME
	Xsection contained no dash in it to separate the list
	Xof man entries from their descriptions.  This will also 
	Xcause odd 
	X.I whatis
	Xoutput.
	X.in -5n
	X.sp 
	X.TY "%s: truncating cmdlist from %d to %d bytes for DBM's sake"
	X.in +5n
	XThe 
	X.SB NAME
	Xsection contained too many characters.  Due to built-in 
	Xlimitations of 
	X.M dbm 3X
	Xdata entries, this entry was truncated.  It will appear
	Xin 
	X.I whatis
	Xoutput with a trailing 
	X.TY \&...
	Xat the point of truncation.
	X.in -5n
	X.sp 
	X.TY "%s: forgot my own name!"
	X.in +5n
	XA man page is stored in a file name that doesn't
	Xcorrespond to any of the entries in its
	X.SB NAME
	Xsection.
	X.in -5n
	X.sp 
	X.TY "%s: no NAME lines, so has no whatis description!"
	X.in +5n
	XNo parsable entries in the 
	X.SB NAME
	Xsection were discovered.
	X.in -5n
	X.sp 
	X.TY "%s was a .so alias for %s, but %s's NAME section doesn't know it!"
	X.in +5n
	XA 
	X.B .so 
	Xreference was found to point to a man page whose own 
	X.SB NAME
	Xsection didn't contain the name of the 
	X.B .so
	Xfile.  This may be due to old man page aliases that were 
	Xnever de-installed when the base man page changed.
	X.in -5n
	X.sp 
	X.TY "can't store %s: would break DBM"
	X.in +5n
	XThere were too many entries for a topic for it
	Xto be stored without exceeding inherent
	X.M dbm 3X
	Xdata size limitations.
	X.in -5n
	X.SH NOTES
	XThis version of 
	X.I makewhatis
	Xis written entirely in the
	X.I perl
	Xprogramming language; it requires that
	X.I perl
	Xbe installed on the system to run.
	X.SH RESTRICTIONS
	XThe 
	X.SB NAME
	Xsection should be a comma-separated list
	Xof aliases for this man page, followed by white space, 
	Xa dash, more white space, and then the description.  Pages
	Xnot conforming to this rule will still be found, but a diagnostic
	Xwill be printed and their descriptions will be left blank.
	X.PP
	XMan pages should be formatted using the 
	X.M man 7
	Xmacro set.  The only exceptions to this are the man pages
	Xfrom the \fIRand MH Message Handling System\fP,
	Xwhose own internal macro set is also recognized, and Larry 
	XWall's \fB.Sh\fP section header macro.
	X.PP
	X.I makewhatis
	Xtakes much longer to run if man pages are stored in compressed form.
	X.SH BUGS
	XNot all systems have 
	X.I compress
	Xinstalled on them.
	X.SH AUTHOR
	XTom Christiansen 
	X\fI<tchrist@convex.com>\fP,
	X\s-1CONVEX\s0 Computer Corporation 
	X.SH COPYRIGHT
	XCopyright 1990 
	X\s-1CONVEX\s0 Computer Corporation.
	XYou are free to use, modify, and redistribute these scripts
	Xas you wish for non-commercial purposes provided that this
	Xnotice remains intact.
SHAR_EOF
if test 9442 -ne "`wc -c < 'makewhatis.8'`"
then
	echo shar: "error transmitting 'makewhatis.8'" '(should have been 9442 characters)'
fi
chmod 664 'makewhatis.8'
fi
echo shar: "extracting 'man.1'" '(20992 characters)'
if test -f 'man.1'
then
	echo shar: "will not over-write existing file 'man.1'"
else
sed 's/^	X//' << \SHAR_EOF > 'man.1'
	X.TH MAN 1 
	X.CX		\" probably only makes sense on Convex machines
	X.\"
	X.de SB		\" small and bold
	X.if !"\\$1"" \\s-2\\fB\&\\$1\\s0\\fR\\$2 \\$3 \\$4 \\$5
	X..
	X.\"
	X.de T		\" switch to typewriter font
	X.ft TA		\" probably want CW if you don't have TA font
	X..
	X.\"
	X.de TY		\" put $1 in typewriter font
	X.if t .T
	X.if n ``\c
	X\\$1\c
	X.if t .ft P
	X.if n \&''\c
	X\\$2
	X..
	X.\"
	X.de M		\" man page reference
	X\\fI\\$1\\fR\\|(\\$2\)\\$3
	X..
	X.SH NAME
	Xman, apropos, whatis \- display on-line reference manual information
	X.SH SYNOPSIS
	X.B man
	X[ 
	X.B \-ltfikwuvthdgK
	X]
	X[
	X.B \-M
	X.I manpath
	X]
	X[
	X\fB-S\fIsections\fR
	X] 
	X.if n .br
	X.if n .ti +4n
	X[
	X\fB-T\fItroffproc\fR
	X] 
	X.if t .br
	X.if t .ti +4n
	X[
	X.I hwtype
	X]
	X[
	X.I section
	X]
	X.if n .br 
	X.if n .ti +4n
	X\fItopic\fB[/\fIindex\fR]
	X\&...
	X.sp
	X.B apropos
	X[
	X.B \-uvdDK
	X]
	X[
	X.B \-M
	X.I manpath
	X]
	X[
	X.I hwtype
	X] 
	X.I keyword 
	X.sp
	X.B whatis
	X[
	X.B \-uvdhD
	X]
	X[
	X.B \-M
	X.I manpath
	X]
	X[
	X.I hwtype
	X]
	X.I topic 
	X\&...
	X.SH DESCRIPTION
	XThe 
	X.I man 
	Xprogram (and its links, 
	X.I apropos
	Xand 
	X.I whatis\c
	X) formats and displays information from the on-line Programmer's
	XReference Manual.  It can display complete entries selected
	Xby topic or brief one-line summaries selected by keyword 
	X(as
	X.I apropos
	Xor with the
	X.B \-k
	Xor 
	X.B \-K
	Xflags) or by topic 
	X(as
	X.I whatis 
	Xor with
	Xthe
	X.B \-f
	Xflag).
	XWhen invoked in the simplest way, without any options and with a topic,
	Xit displays 
	Xthe corresponding manual page formatted with 
	X.M nroff 1 \&.  
	XIf the
	Xstandard output is to a terminal,
	Xthe man page is piped by default through 
	X.M more 1 , 
	Xor else through
	Xthe program specified in the user's 
	X.SB PAGER
	Xenvironment variable if set. 
	X.PP
	XMan pages are stored on-line as 
	X.I nroff
	Xsource files and are reformatted 
	Xwhenever the unformatted
	Xversion is missing or has a newer modification date than the formatted version.
	XThe formatted version of the man page stored in
	Xthe corresponding 
	X.B cat*/
	Xdirectory if it exists
	Xand is writable by the current user.
	XThus 
	X.I man1/stty.1 
	Xonce formatted
	Xis stored in 
	X.I cat1/stty.1\c
	X\&.
	XOtherwise manual pages must be reformatted each time a user wishes
	Xto view them.  
	XIf the root of the man tree from which the man page is taken contains
	Xthe file 
	X.I tmac.an\c
	X, then this file is used in lieu of the 
	Xstandard 
	X.M man 7
	Xmacros to reformat the page.  Appropriate calls to 
	X.M tbl 1
	Xand either 
	X.M neqn 1
	Xor 
	X.M eqn 1
	Xare automatically
	Xinserted as needed.
	X.SS "Section Selection"
	XSome topics occur in more than one section of the manual.
	XTo select a man page from a particular section, put the section name 
	Xin front of the topic, as in 
	X.TY "man 4 tty"\c
	X\&.  
	XFor backwards compatibility with other 
	X.I man 
	Xprograms, certain 
	Xnon-numeric sections are recognized by longer aliases: 
	X.B local
	Xmeans section 
	X.B l\c
	X, 
	X.B new
	Xmeans section
	X.B n\c
	X, 
	X.B public
	Xmeans section 
	X.B p\c
	X, and 
	X.B old
	Xmeans section 
	X.B o\c
	X\&.  
	XIn other words, 
	X.TY "man new csh"
	Xon the command line will search for a man page on the
	X.I csh
	Xin section 
	X.I n
	Xof the manual.
	X.PP
	XThe applicable topics for each component in the current
	X.SB MANPATH
	Xare sorted according to their section.
	XThe default ordering is 
	X.B ln16823457po\c
	X\&.  Thus, 
	X.M stty 1
	Xwould be displayed before 
	X.M stty 3 .
	XThis ordering can be
	Xoverridden on a site-wide basis by the system manager (see 
	X.SB NOTES
	Xsection below), a per-user basis using the 
	X.SB MANSECT
	Xenvironment variable, 
	Xor a per-command basis using the \fB-S \fIsections\fR switch.  Programmers
	Xmay prefer a default ordering beginning 
	X.B 231
	Xso that when they type 
	X.TY "man wait"
	Xthey see the man page for the system call rather than the one for the shell command
	Xof the same name.
	XIf multi-character sections exist (\fIe.g.\fR some systems have a 
	X.B man1m
	Xsection)
	Xor subsection ordering is desired, then colons must be used to separate
	Xthe sections.  A \s-1FORTRAN\s0 programmer might prefer to set his 
	X.SB MANSECT
	Xto 
	X.B 3f:2:3:1:6:8\c
	X, so that when he asks for the 
	Xmanual page for 
	X.I system\c
	X, he gets 
	X.M system 3F
	Xrather than the
	XC function of the same name.
	X.PP 
	XCase is significant for
	Xthe topic itself, but not for the section.  This allows 
	Xyou to make a distinction between 
	X.M cc 1 , 
	Xthe C compiler,
	Xand 
	X.M CC 1 , 
	Xthe C++ translator.  However, section 
	X.B 3S
	Xis interpreted as section 
	X.B 3s
	Xto accommodate users
	Xwho see a reference to the man page for 
	X.M printf 3S ,
	Xand don't realize that it's stored in the file 
	X.B printf.3s\c
	X\&.
	X.SS "Search Strategy"
	XThe 
	X.I man
	Xprogram by default consults the 
	X.SB MANPATH 
	Xenvironment variable for a list of complete man trees.
	XIf this variable is not set, then the user's 
	X.SB PATH
	Xenvironment variable will be consulted instead.  This way, if
	X.I /usr/local/bin 
	Xis before
	X.I /usr/bin
	Xin the PATH variable, then 
	X.I /usr/local/man 
	Xwill be before
	X.I /usr/man
	Xin the dynamically determined
	X.SB MANPATH
	Xvariable.  This is nice because it means the user need only add a 
	X.SB PATH
	Xcomponent, and the 
	X.SB MANPATH
	Xwill be automatically updated apppropriately.
	XThis search
	Xorder can be overridden on a site-wide basis by the system manager (see
	X.SB NOTES
	Xsection below) or on a per-user basis by setting the 
	Xenvironment variable 
	X.SB MANPATH .  
	XThis is a colon-separated 
	Xlist of directory names that contains subdirectories
	Xof the form 
	X.B man*/\c
	X\&.  A user keeping his own man pages under his home directory 
	Xmight place a line like the following in his 
	X.BI ~ /.cshrc 
	Xfile:
	X.in +5n
	X.TY "setenv MANPATH $HOME/man:/usr/local/man:/usr/man"
	X.in -5n
	XThe 
	X.SB MANPATH
	Xmay also be set on a per-command basis by using the 
	X.B \-M
	Xcommand-line.
	X.PP
	XIt is possible to install several complete sets
	Xof man pages on one host.  These alternate sets are 
	Xassumed to be installed in 
	X\fB\s-2MANALT\s0\fI/subdir\fR, where 
	X.SB MANALT
	Xis an environment variable defaulting to 
	X.I /usr/local/man
	Xand
	X.I subdir
	Xis assumed to be a complete man tree.  
	XIf at least non-switch two arguments are supplied,
	Xthen the 
	X.SB MANALT
	Xdirectory is consulted
	Xto see whether it contains a sub-directory whose names is the
	Xfirst argument.  This makes it easy to say things like 
	X.TY "man sun csh"
	Xto read the 
	X.I csh
	Xman page from the installed set of 
	X.I sun
	Xman pages.  This is equivalent to saying 
	X.TY "man -M /usr/local/bin/sun csh" " (assuming"
	X.SB MANALT
	Xis not set).
	X.SS "Indexing"
	XVery long man pages are often broken up into several major 
	Xsubsections, whose tables of contents can be listed with the 
	X.B \-i
	Xswitch.
	XTo access one of these subsections directly,
	Xspecify \fB/\fIindex\fR,
	Xwhere 
	X.I /index
	Xis the name of the subsection.
	XFor example, 
	Xto see the section on expressions in the 
	X.M csh 1
	Xman page, use 
	X.TY "man csh/expr" 
	Xto skip to that section first.
	XIf 
	X.I index
	Xis not a valid section or 
	Xsubsection header, or if none is given, 
	Xa list of all valid section
	Xheaders is displayed and the user is prompted 
	Xto select one.  The special form of a missing subsection 
	Xafter the slash means to go into continuous menu mode, returning to the
	Xsection menu after the manual page has been displayed.
	X.PP
	XThe match against the index entries is not case-sensitive,
	Xnor is it required to be at the beginning
	Xof the string.  For example, this allows
	X.TY /expr
	Xto be 
	Xused to look up the
	X.TY "Regular Expressions"
	Xsection.
	XHowever, matches that 
	X.I do
	Xstart at the front of the 
	Xheader are selected before those that do not.  A 
	Xnumeric section index as listed by the 
	X.B \-i
	Xflag may also be given, with an index of 
	X.B 0
	Xmeaning the whole page.  
	X.PP
	XMan page indices (tables of contents) are generated automatically when needed.
	XTo save time,
	Xif a writable 
	X.B idx*/
	Xsubdirectory exists in the root
	Xof the man tree, the index is left there under the
	Xsame name as its man page for quicker
	Xaccess in the future.  Indices older than their 
	Xparent man page are rebuilt upon demand.
	X.PP 
	XAs a special case of indexing, the form
	X.I //string
	Xstarts the 
	X.SB PAGER
	Xat an arbitrary 
	X.I string
	Xin the text of the man page
	Xrather than at a subsection index.
	X.PP
	XNote that the 
	X.I /index
	Xfeature may not be used in conjunction with the 
	X.B \-l
	X(local file) switch.  The 
	X.B \-i
	Xswitch may still 
	Xbe used with local files.
	X.SS "Online Problem Reports"
	XSome vendors provide their customers with electronically
	Xreadable problem reports; also, some sites may wish to 
	Xmaintain their own list of known problems. 
	XTo support inclusion of these in 
	Xthe man pages, the 
	X.I man
	Xprogram knows to look in a set of 
	X.B pr*
	Xsubdirectories for additional source.  For example, if you
	Xare reading the man page contained in 
	X\fI/usr/man/man1/foo.1\fP,
	Xthen if there exists a file called 
	X\fI/usr/man/pr1/foo.1\fP, this will be appended to the
	Xdata passed to 
	X.I nroff
	Xor 
	X.I troff
	Xas an additional 
	X.SB "KNOWN PROBLEMS"
	Xsection.  These files should be in 
	X.I nroff
	Xformat.  
	X.SH OPTIONS
	X.TP 
	X.BI \-M manpath
	XChanges the search path for finding man pages for just
	Xthis command, overriding the current 
	X.SB MANPATH
	Xenvironment variable or system default.
	XSee the section on 
	X.SB "Search Strategy"
	Xfor further details.
	X.TP 
	X.BI \-S sections
	Xsets the section and subsection ordering for just
	Xthis command, overriding the 
	Xcurrent 
	X.SB MANSECT 
	Xenvironment variables or system default.
	XSee the 
	X.B "Section Selection"
	Xfor further details.
	X.TP
	X.B \-f
	Xdirects
	X.I man
	Xto print out the applicable one-line descriptions
	Xfor each corresponding 
	X.I topic\c
	X\&.  
	XThese one-line descriptions are stored in separate 
	X.I whatis
	Xfiles for each component in the 
	Xspecified 
	X.SB MANPATH
	X\&.  If 
	X.M dbm 3X
	Xversions of these
	Xdatabases exist, these are used for more rapid location of manual
	Xpages.  See 
	X.M makewhatis 8
	Xfor directions on generating the 
	X.I "whatis"
	Xdatabases.  
	XThis switch is automatically enabled if 
	X.I man
	Xis invoked as 
	X.IR whatis .
	X.TP
	X.B \-k
	Xdirect
	X.I man
	Xto search the 
	X.I whatis
	Xdatabases for any entry 
	Xmatching the given 
	X.I keyword\c
	X\&.  
	XThis is useful when the precise topic is unknown; it reports
	Xthe topics that have to do with the given keywords, according
	Xto their 
	X.I whatis
	Xdescriptions.
	XOutput will be sent to the user's
	X.SB PAGER .
	XThis switch is automatically enabled if 
	X.I man
	Xis invoked as 
	X.IR apropos .
	X.TP 
	X.B \-K
	Xsame as
	X.B \-k
	Xexcept regular expressions as in 
	X.M egrep 1
	Xmay be used in the 
	X.I keywords\c
	X\&.
	X.TP 
	X.B \-g
	XGrep through all man pages for one or more 
	X.I perl
	Xexpressions.  Compressed man pages will be 
	Xcorrectly processed
	Xby 
	X.M zcat 1L .
	X.TP
	X.B \-t
	Xis used to typeset the manual pages, usually
	Xusing 
	X.M troff 1L
	Xor a front-end for the same.
	XIf the 
	Xdefault system typesetter is not desired, it can be overridden 
	Xeither with the 
	X.SB TROFF
	Xenvironment variable or the 
	X.B \-T
	Xflag.
	X.TP 
	X.BI \-T troffproc
	Xselects an alternate typesetting program, overriding
	Xthe current 
	X.SB TROFF
	Xenvironment variable or system default.
	XIt is most 
	Xoften used to select a previewer
	Xfor workstations that support previewing of 
	X.I troff
	Xoutput.
	X.TP
	X.B \-l
	Xis used with specific file names rather than with
	Xtopics.  The same processing as 
	X.I man
	Xwould perform on a system 
	Xmanual page will be performed on the given file.  This is useful
	Xwhen first writing new manual pages or viewing ones not yet installed.
	XIt may be used in conjunction with other switches, such as 
	X.B \-t\c
	X\&.  The indexing feature is not available for local man pages except
	Xvia the 
	X.B \-i
	Xswitch.
	X.TP
	X.B \-w
	Xis used to determine the full pathnames of 
	Xthe files that 
	X.I man 
	Xwould display for the given topic.  This is useful
	Xwhen the same topic occurs in more than one section of the manual or
	Xin more than one of the components in the supplied 
	X.SB MANPATH .
	XThe files are listed in the order in which they would be presented
	Xto the user.  
	X.TP
	X.B \-a
	Xdisplays all man pages for all possible matches for a given 
	Xtopic.  Its effect is similar to calling 
	X.I man 
	Xon all the files returned by the
	X.B \-w
	Xswitch.
	X.TP 
	X.B \-i
	Xdisplays
	Xthe complete section and subsection index
	Xfor a given topic.  
	X.TP
	X.B \-v
	Xprints out the current revision of the program
	Xas stored by 
	X.M rcs 1 .
	X.TP
	X.B \-u
	Xdisplays a long usage message, piped through the user's 
	X.SB PAGER .  
	XIt includes the current defaults for 
	X.SB PAGER ,
	X.SB MANPATH ,
	X.SB MANSECT ,
	X.SB MANALT ,
	Xand 
	X.SB TROFF .
	X.TP 
	X.B \-h
	Xforces 
	X.I "man"
	Xto ignore the 
	X.I whatis
	Xdatabases and search for its manual pages the slow way.  This is the
	Xdefault behavior if no 
	X.I whatis
	Xdatabase has been created for 
	Xa particular man tree.
	X.TP
	X.B \-d
	Xtriggers debugging output.  All externally invoked programs
	Xsuch as 
	X.I nroff
	Xand 
	X.I egrep
	Xwill have their arguments displayed
	Xbefore they are called.  Certain other debugging information is also printed.
	X.TP 
	X.B \-D
	Xcauses embedded backspaces and their preceding characters
	Xin cat pages to be stripped upon 
	Xoutput.  This is useful for producing utterly clean output,
	Xsuch as when being viewed in an 
	X.M emacs 1
	Xwindow.
	X.SH EXAMPLES
	X.T
	X.nf
	X.ta 20
	X.\"	.ne 5
	Xman stty	# show me the first stty man page
	Xman -w stty	# which stty man pages are there?
	Xman 3 stty	# show me the one from section 3
	Xman -a tty	# show all man pages on tty

tchrist@convex.COM (Tom Christiansen) (01/08/91)

	X
	X.\"	.ne 3
	Xman old makewhatis	# show me the makewhatis man page from mano
	Xman new csh	# show me the csh man page from mann
	X
	X.\"	.ne 3
	Xman sun csh	# show me the csh man page for suns 
	X	(assumes $MANALT/sun contains sun man pages)
	X
	X.\"	.ne 6
	Xman -i uucp	# get list of sections in uucp man page
	Xman -ai tty	# give list of sections on all tty pages
	Xman adb/bugs	# check for BUGS section of adb man page
	Xman man/exam	# start at this example section
	Xman ksh/	# select from section menu for ksh man page
	Xman sun cron/files	# go to the FILES section of the sun cron man page
	X
	X.\"	.ne 4
	Xman -f rcs	# what is rcs?
	Xwhatis rcs	# same as previous
	Xwhatis vax rcs	# same as previous, but only vax man pages
	X
	X.\"	.ne 4
	Xman -k rcs	# what knows about rcs?
	Xapropos rcs	# same as previous
	Xapropos sun rcs	# same as previous, but only sun man pages
	X
	X.\"	.ne 3
	Xman -S231 wait	# override system section ordering 
	Xman -S3f:3s:3:2:1 system	# subsection ordering
	X
	X.\"	.ne 2
	Xman -M\ \ ~/man hack	# use ~/man as only man tree
	X
	X.\"	.ne 4
	Xman -t 4 tty	# troff tty(4) man page
	Xman -at tty	# troff all tty pages
	Xman -Tpnitroff perl	# preview perl man page
	Xman -lTpnitroff ../foo.1	# preview local file as man page
	X
	X.\"	.ne 3
	Xman -l file.x	# run man on local file
	Xman -tl file.x	# print local file as man would
	Xman -il file.x	# get section index on local page
	X.fi
	X.ft R
	X.\"	.ne 5
	X.SH ENVIRONMENT
	X.I Man
	Xexplicitly checks for the following environment variables; if they are
	Xpresent, they
	Xwill override its internal defaults:
	X.IP \fBMANPATH\fP 15
	XThe colon-separated list of man trees for locating man pages.  This
	Xmay itself be overridden by the command line flags 
	X.B \-M
	Xor by
	Xspecifying an alternate hardware type.
	X.IP \fBMANSECT\fP 15
	XThe default ordering for section and subsection sorting.  It need only
	Xbe separated by colons if multi-character sections such as 
	X.B man1m
	Xexist 
	Xor if subsection ordering is desired, such as to place subsection 
	X.B 3f
	Xin front of section 
	X.B 3 .
	X.IP \fBMANALT\fP 15
	XThe directory to check for alternate sets of man trees.  This is useful
	Xfor storing the man pages for several different machine architectures or 
	Xversions of the operating system.  See the 
	X.B Search Strategy
	Xsection
	Xfor details.
	X.IP \fBPAGER\fP 15
	XThe user's preferred viewer of paged output.  Note that often the pager
	Xitself will consult the environment; for example, 
	X.I more
	Xlooks for a
	X.SB MORE
	Xvariable
	Xand 
	X.I less
	Xlooks for a
	X.SB LESS
	Xvariable
	Xin the environment.
	X.IP \fBTROFF\fP 15
	XThe preferred typesetter program.  It should recognize
	X.I troff
	Xinput and switches.
	XThis may be overridden using the 
	X.B \-T
	Xcommand line flag.
	X.SH FILES
	X.nf
	X.ta \w'/usr/lib/perl/getopts.pl   'u
	X\fI/usr/man\fR	default man tree
	X\fI/usr/man/man*/*.*\fR	unformatted (nroff source) man pages
	X\fI/usr/man/cat*/*.*\fR	formatted man pages
	X\fI/usr/man/idx*/*.*\fR	indices for section headers
	X\fI/usr/man/whatis\fR	default whatis database, text version
	X\fI/usr/man/whatis.dir\fR	\fIdbm\fP index file for default \fIwhatis\fP database
	X\fI/usr/man/whatis.pag\fR	\fIdbm\fP data file for default \fIwhatis\fP database
	X\fI/usr/lib/perl/getopts.pl\fR	required \fIperl\fR library file
	X\fI/usr/lib/perl/stat.pl\fR	required \fIperl\fR library file
	X.fi
	X.SH "SEE ALSO"
	X.M egrep 1 ,
	X.M perl 1 ,
	X.M more 1 ,
	X.M eqn 1 ,
	X.M tbl 1 ,
	X.M nroff 1 ,
	X.M troff 1L ,
	X.M compress 1L ,
	X.M dbm 3X ,
	X.M man 7 ,
	X.M catman 8 ,
	X.M makewhatis 8 
	X.SH NOTES
	XThis version of 
	X.I man
	Xis written entirely in the 
	X.M perl 1
	Xprogramming language
	Xand thus requires that 
	X.I perl
	Xbe properly installed on your system to run.  
	XBecause 
	X.I man
	Xis distributed in a script, it can be easily 
	Xreconfigured by the system managers to suit their site's particular style.
	XThis is good, as some of the default settings are somewhat
	Xidiosyncratic to the set-up at the \s-1CONVEX\s0 home office.
	X.sp
	X.in +5n
	X\fBBe sure to save a copy of the 
	Xoriginal \fIman\fP
	Xprogram before any modification.\fR
	X.in -5n
	X.ft R
	X.sp
	X.ne 4
	XThings that can be configured include:
	X.in +5n
	X.IP \(bu 5
	Xthe default 
	X.SB PAGER 
	Xand its flags (alternate flags can be provided for 
	X.I less\c
	X)
	X.IP \(bu 5
	Xsystem defaults for 
	X.SB MANPATH , 
	X.SB MANSECT , 
	X.SB MANALT , 
	Xand 
	X.SB TROFF .
	X.IP \(bu 5
	Xpaths for
	X\fItroff\fP,
	X\fInroff\fP,
	X\fItbl\fP,
	X\fIeqn\fP,
	X\fIneqn\fP,
	X\fIul\fP,
	X\fIcol\fP,
	X\fIegrep\fP, and
	X\fIcompress\fP
	X.IP \(bu 5
	Xwhether you have compressed man pages and how they are stored
	X.IP \(bu 5
	Xwhich section aliases you want (as in 
	X.B public
	Xbeing recognized 
	Xas section \fBp\fP)
	X.IP \(bu 5
	Xwhether to recognize man pages whose 
	X.SB NAME 
	Xsections don't mention their
	Xown names
	X.in -5n
	X.PP 
	XTo save disk space at the expense of execution time, a site may 
	Xwish to run 
	X.M compress 1L 
	Xon the manual entries where available.  The
	X.I man
	Xprogram
	Xunderstands how to read compressed man 
	Xpages, and knows to create a compressed cat page if the source
	Xman page was compressed to start with.
	X.PP 
	XWhen running on slow \s-1CPU\s0s, the start-up time for parsing the
	Xscript may be annoying.  These sites can skip this parsing phase
	Xat each invocation of 
	X.M man 1
	Xby using \fIperl\fP's 
	X.B \-u
	Xflag to dump a binary image of the interpreter.
	X.SH DIAGNOSTICS
	XSeveral self-explanatory diagnostics are possible, such as 
	X.TY "No manual entry for xyzzy" ,
	Xbut the following warnings may not be intuitive:
	X.sp
	X.TY "But what do you want from section %s?"
	X.in +5n
	XA section was specified but no topic.
	X.in -5n
	X.sp 
	X.TY "No dbm file for %s: %m"
	X.in +5n
	X.br 
	XThis means that the named man tree does not have an
	Xaccessible 
	X.I dbm
	Xversion 
	Xof its 
	X.I whatis
	Xdatabase and that 
	X.M makewhatis 8
	Xshould be run on it.  This only shows up when 
	X.B \-d
	Xoption is used. \fB%m\fP is the appropriate
	X.M perror 3
	Xmessage.
	X.in -5n
	X.sp
	X.TY "%s has disappeared -- rerun makewhatis"
	X.in +5n
	X.br
	XA man page has been removed since 
	X.I makewhatis
	Xwas last run.
	XNote that new man pages being added will NOT be detected.
	X.in -5n
	X.sp
	X.TY "nroff of %s failed"
	X.br
	X.in +5n
	XFor some reason 
	X.I nroff
	Xdid not exit correctly.  The disk may
	Xbe mounted read-only, it might be full, or 
	X.I nroff
	Xmay 
	Xnot be installed on your system.  Contact your system manager.
	X.in -5n
	X.sp 
	X.TY "%s was length 0; disk full?"
	X.br
	X.in +5n
	XA cat page was empty.  
	XThe
	X.I man
	Xprogram
	Xunlinks the useless cat page,
	Xissues this warning, and exits non-zero.  Rerun the command and
	Xif the problem persists, contact your system manager.
	X.in -5n
	X.sp
	X.TY "/tmp not writable"
	X.br
	X.in +5n
	XThe 
	X.I /tmp 
	Xand
	X.I /usr/man/cat*
	Xdirectories are not writable by you. Contact your system manager.
	X.in -5n
	X.sp
	X.TY "No whatis databases found, please run makewhatis"
	X.br
	X.in +5n
	XThe
	X.I man
	Xprogram was unable to find a 
	X.I whatis
	Xdatabase for use with
	X.I apropos
	Xor
	X.I man -k
	Xin any of the directories listed in the 
	X.BR MANPATH .
	XHave your system manager run
	X.I makewhatis
	Xon the system manual page directories, and run
	X.I makewhatis
	Xon any personal manual page directories.
	X.in -5n
	X.sp
	X.TY "bad eval: %s"
	X.br
	X.TY "can't eval %s: %s"
	X.br
	X.in +5n
	XThese are internal errors that should never occur.  Contact
	Xyour system manager, who should submit a problem report.
	X.in -5n
	X.SH RESTRICTIONS 
	XOnce a 
	X.M dbm 3X
	X.I whatis
	Xdatabase has been created for a particular man root, 
	Xnew man pages will not be found unless the 
	X.B \-h
	Xflag is
	Xused or until 
	X.I makewhatis
	Xis rerun.
	X.PP
	XIf your 
	X.SB PAGER
	Xis 
	X.M more 1 ,
	Xbold text shows up in the normal font; 
	Xhowever,
	X.M less 1
	Xdoes display bold text in your terminal's bold font.
	X.SH BUGS
	XThe manual is supposed to be reproducible either on the phototypesetter
	Xor on an \s-1ASCII\s0 terminal.
	XHowever, on a terminal, some information is necessarily lost.
	X.PP
	XThe
	X.B \-t
	Xflag for
	X.I man
	Xonly works if the host system supports
	X.IR troff .
	X.PP
	XNot all systems have 
	X.I compress
	Xinstalled on them.
	X.SH AUTHOR
	XTom Christiansen
	X.I <tchrist@convex.com>\c
	X.SH COPYRIGHT
	XCopyright 1990 
	X\s-1CONVEX\s0 Computer Corporation.
	XYou are free to use, modify, and redistribute these scripts
	Xas you wish for non-commercial purposes provided that this
	Xnotice remains intact.
SHAR_EOF
if test 20992 -ne "`wc -c < 'man.1'`"
then
	echo shar: "error transmitting 'man.1'" '(should have been 20992 characters)'
fi
chmod 644 'man.1'
fi
echo shar: "extracting 'catman'" '(4457 characters)'
if test -f 'catman'
then
	echo shar: "will not over-write existing file 'catman'"
else
sed 's/^	X//' << \SHAR_EOF > 'catman'
	X#!/usr/bin/perl
	X#
	X# perl rewrite of catman 
	X# author: tom christiansen <tchrist@convex.com>
	X#
	X# Copyright 1990 Convex Computer Corporation.
	X# All rights reserved.
	X
	X$| = 1;
	X
	X$TBL	    = "tbl -D";
	X$EQN	    = "eqn";
	X$MAKEWHATIS = "/usr/lib/makewhatis";
	X$COMPRESS   = "compress";
	X$NROFF	    = "nroff";
	X$COL 	    = "col";
	X$CAT	    = "cat";
	X$ZCAT	    = "zcat";
	X
	X# Command to format man pages to be viewed on a tty or printed on a line printer
	X$CATSET	  = "$NROFF -h -man -";
	X$CATSET  .= " | $COL" if $COL;
	X
	Xumask 022;
	X
	Xdo 'getopts.pl' || die("can't do getopts.pl", $@?$@:$!, "\n");
	Xdo 'stat.pl' || die("can't do stat.pl ", $@?$@:$!, "\n");
	Xdo 'ctime.pl' || die("can't do ctime.pl ", $@?$@:$!, "\n");
	X
	X
	X# -Z flag is planning for the future
	Xunless (&Getopts('dpnwZP:M:') && $ARGV <= 1) {
	X    die "usage: $0 [-pnwZ] [-M manpath] [sections]\n";
	X}
	X
	X$debug      =  $opt_d;
	X$makewhatis = !$opt_n;
	X$catman     = !$opt_w;
	X$fakeout    =  $opt_p;
	X$compress   =  $opt_Z;
	X
	X($sections = shift) &&  
	X    (@sections = split($sections =~ /:/ ? ':' : '', $sections));
	X
	X($manpath = $opt_P) || 
	X    ($manpath = $opt_M) || 
	X    ($manpath = "/usr/man");
	X
	Xpath: foreach $path (split(/:/,$manpath)) {
	X    unless (chdir $path) {
	X	warn "can't chdir to $path: $!";
	X	$status = 1;
	X	next path;
	X    }
	X    &run ("$MAKEWHATIS $path") if $makewhatis;
	X    next unless $catman;
	X    print "chdir $path\n" if $debug;
	X
	Xmandir: foreach $mandir (<man*>) {
	X	next if $sections && !grep($mandir =~ /man$_/, @sections);
	X	print "considering $mandir\n" if $debug;
	X	$found++;
	X	($catdir = $mandir) =~ s/man/cat/;
	X	$catdir = "$path/$catdir";
	X	next unless -w $catdir;
	X	opendir(mandir,$mandir);
	X
	Xmanpage: foreach $manpage ( readdir(mandir) ) {
	X	    local(@st_man, @st_cat);
	X	    next manpage if $manpage =~ /^\.{1,2}/;
	X
	X	    if ($manpage !~ /\S\.\S/) {
	X		print "skipping non man file: $manpage\n" if $debug;
	X		next manpage;
	X	    } 
	X
	X	    next manpage if $manpage =~ /\.(old|bak|out)$/i;
	X	    next manpage if $manpage =~ /~$/;
	X
	X	    $zpage = $zdir || $manpage =~ /\.Z$/;
	X
	X	    $manpage = "$path/$mandir/$manpage";
	X
	X	    ($catpage = $manpage) 
	X		=~ s,^(.*)/man([^\.]*)(\.Z)?/([^/]*)$,$1/cat$2$3/$4,;
	X
	X	    @st_man = &Stat($manpage);
	X	    @st_cat = &Stat($catpage);
	X
	X	    if ($st_cat[$ST_MTIME] < $st_man[$ST_MTIME]) {
	X		$command = (($manpage =~ m:\.Z:) ? $ZCAT : $CAT)
	X			    . " < $manpage | $CATSET";
	X
	X		$command = &insert_filters($command, $manpage);
	X		$command =~ s,-man,$path/tmac.an, if -e "$path/tmac.an";
	X
	X		$command .= "| $COMPRESS " if $catpage =~ /\.Z/;
	X
	X		$command .= "> $catpage";
	X
	X		&reformat($command);
	X	    }
	X	}
	X    } 
	X}
	X
	Xsub insert_filters {
	X    local($filters,$eqn, $tbl, $_);
	X    local(*PAGE);
	X    local($command, $PAGE) = @_;
	X
	X
	X    $PAGE = "$ZCAT < $PAGE|" if $PAGE =~ /\.Z/;
	X
	X    (open PAGE) || die ("can't open $page to check filters: $!\n");
	X
	X    while (<PAGE>) {
	X	if (/^\.EQ/) {
	X	    $_ = <PAGE>;
	X	    $eqn = 1 unless /\.(if|nr)/;  # has eqn output not input
	X	} 
	X	if (/^\.TS/) {
	X	    $_ = <PAGE>;
	X	    $tbl = 1 unless /\.(if|nr)/;  # has tbl output not input
	X	} 
	X	last if $eqn && $tbl;
	X    } 
	X    close PAGE;
	X
	X    $eqn && $_[0] =~ s/(\S+roff)/$EQN | $1/;
	X    $tbl && $_[0] =~ s/(\S+roff)/$TBL | $1/;
	X
	X    $_[0];
	X} 
	X
	X
	Xsub run {
	X    local($command) = $_[0];
	X
	X    $command =~ s/^\s*cat\s*<?\s*([^\s|]+)\s*\|\s*([^|]+)/$2 < $1/;
	X    $command =~ s/^([^|<]+)<([^Z|<]+)$/$1 $2/;
	X    print STDERR "$command\n" if $debug || $fakeout;
	X    if (!$fakeout && system $command) {
	X	$status = 1;
	X	printf STDERR "\"%s\" exited %d, sig %d\n", $command, 
	X	    ($? >> 8), ($? & 255) if $debug;
	X    }
	X    return ($? == 0);
	X}
	X
	Xsub print {
	X    local($_) = @_;
	X
	X    if (!$inbold) {
	X	print;
	X    } else {
	X	for (split(//)) {
	X	    print /[!-~]/ ? $_."\b".$_ : $_;
	X	} 
	X    } 
	X}
	X
	Xsub reformat {
	X    local($_) = @_;
	X    local($nroff, $col);
	X    local($inbold) = 0;
	X
	X    s/^\s*cat\s*<?\s*([^\s|]+)\s*\|\s*([^|]+)/$2 < $1/;
	X    s/^([^|<]+)<([^Z|<]+)$/$1 $2/;
	X    ($nroff, $col) = m!(.*)\|\s*($COL.*)!;
	X
	X    print "$nroff | (this proc) | $col\n" if $debug;
	X
	X    open (NROFF, "$nroff |");
	X    open (COL, "| $col");
	X
	X    select(COL);
	X
	X    while (<NROFF>) {
	X	s/\033\+/\001/;
	X	s/\033\,/\002/;
	X	if ( /^([^\001]*)\002/ || /^([^\002]*)\001/ )  {
	X	    &print($1);
	X	    $inbold = !$inbold;
	X	    $_ = $';
	X	    redo;
	X	}   
	X	&print($_);
	X    }
	X
	X    close NROFF;
	X    if ($?) {
	X	warn "$program: \"$nroff\" failed!\n";
	X	$status++;
	X    } 
	X    close COL;
	X    if ($?) {
	X	warn "$program: \"$col\" failed!\n";
	X	$status++;
	X    }
	X    select(STDOUT);
	X}
SHAR_EOF
if test 4457 -ne "`wc -c < 'catman'`"
then
	echo shar: "error transmitting 'catman'" '(should have been 4457 characters)'
fi
chmod 755 'catman'
fi
echo shar: "extracting 'WISHES'" '(538 characters)'
if test -f 'WISHES'
then
	echo shar: "will not over-write existing file 'WISHES'"
else
sed 's/^	X//' << \SHAR_EOF > 'WISHES'
	Xincremental makewhatis
	Xnew man(5) page
	Xnew catman(8) page
	Xadd to makewhatis(8) section on whatis format?
	Xcatman new switches
	X    z	compress
	X    u	uncompress
	X    i	initialize -- create cat* and idx* directories
	X    r	remove old cat pages first, warn of cat pages w/o man pages
	X    R	remove recursivley old cat dirs first, then invoke -i 
	Xman - collapse case on lookups
	Xcfman - option to use dbm files 
	Xman - use optimized perl wth eval trick for apropos unless GNU grep
	Xman - option to ask which page out of many; i.e. -a + selection menu
SHAR_EOF
if test 538 -ne "`wc -c < 'WISHES'`"
then
	echo shar: "error transmitting 'WISHES'" '(should have been 538 characters)'
fi
chmod 664 'WISHES'
fi
echo shar: "extracting 'BUGS'" '(268 characters)'
if test -f 'BUGS'
then
	echo shar: "will not over-write existing file 'BUGS'"
else
sed 's/^	X//' << \SHAR_EOF > 'BUGS'
	Xcatman needs to reference the database instead
	Xof globbing to find manpage names to avoid duplicating 
	Xoutput from .so's.
	X
	Xcfman doesn't use the database, so its idea of what
	Xman can find is wrong.
	X
	Xif man is invoked as whman, it doesn't get the right
	Xoptions string.
SHAR_EOF
if test 268 -ne "`wc -c < 'BUGS'`"
then
	echo shar: "error transmitting 'BUGS'" '(should have been 268 characters)'
fi
chmod 664 'BUGS'
fi
echo shar: "extracting 'README'" '(636 characters)'
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
sed 's/^	X//' << \SHAR_EOF > 'README'
	Xread the man pages first.  then check out the configuration section
	Xin man to make sure it's as you like it.
	X
	Xyou have to run makewhatis to build the database to make this work.
	Xyou should actually remove your catpages and rerun catman,
	Xwho will call /usr/lib/makewhatis, which it assumes to be mine.
	Xthe catpages may be in a slightly different format than you are used to.
	X
	Xyou will need ndbm.
	X
	Xyou might want to create idx* directories for speed.
	X
	Xless makes a better pager than more.
	X
	Xyou can read the paper at your leisure.  it's not essential.
	X
	Xcopying is ok, just don't remove my name or try to sell it.
	Xread the man pages first.
SHAR_EOF
if test 636 -ne "`wc -c < 'README'`"
then
	echo shar: "error transmitting 'README'" '(should have been 636 characters)'
fi
chmod 664 'README'
fi
echo shar: "extracting 'cfman'" '(10347 characters)'
if test -f 'cfman'
then
	echo shar: "will not over-write existing file 'cfman'"
else
sed 's/^	X//' << \SHAR_EOF > 'cfman'
	X#!/usr/bin/perl
	X#
	X# cfman v2.0: man page cross-referencer
	X# author: Tom Christiansen <tchrist@convex.com>
	X# date: 15 November 89
	X#
	X# usage: cfman [ -d debug-devel ] [ -s sub-sections ] 
	X#	       [ -p manpath ] [ -x xrefpath ] 
	X
	X($iam = $0) =~ s%.*/%%;
	X 
	X$] =~ /(\d+\.\d+).*\nPatch level: (\d+)/;
	Xdie "$iam: requires at least perl version 3.0, patchlevel 1 to run correctly\n"
	X	if $1 < 3.0 || ($1 == 3.0 && $2 < 1);
	X
	X&Getopts('fd:s:p:P:x:') || &usage;
	X
	X$manpath = $opt_p if defined $opt_p;
	X$manpath = $opt_P if defined $opt_P;
	X$manpath = $ENV{'MANPATH'} unless $manpath;
	X$manpath = "/usr/man" unless $manpath;
	X@manpath = split(/:/,$manpath);
	X
	X$opt_x =~ /^:/ && ( $opt_x = $manpath . $opt_x );
	X@xrefpath = $opt_x ? split(/:/,$opt_x) : @manpath;
	X
	X$debug = $opt_d;
	X$use_DBM = $opt_f;
	X
	X@sections = $opt_s ? split(/ */,$opt_s) : 1..8;
	X
	Xif ($debug) {
	X    $" = ':';
	X    print "manpath is @manpath\n";
	X    print "xrefpath is @xrefpath\n";
	X    $" = ' ';
	X} 
	X
	Xfile:    foreach $file ( $#ARGV >= $[ ? @ARGV : '*.*' ) {
	X	     printf STDERR "considering %s\n", $file if $debug & 1;
	X	     $bingo = 0;
	Xtree:        foreach $tree ( @manpath ) {
	X		 print "ROOT is $tree\n" if $debug;
	X		 if (!chdir $tree) {
	X		    warn "cannot chdir to $tree: $!";
	X		    next tree;
	X		 } 
	X		 $rootdir = $tree;
	X		 if ( $file =~ m#^/# ) {
	X		    &read_manpages($file); 
	X		    next file;
	X		 } 
	Xsection:         foreach $section ( @sections ) {
	X		    &scan_section($tree,$section,$file);
	X		 }
	X	     } 
	X	     print "no man pages matched \"$file\"\n" unless $bingo;
	X	  }
	X
	X
	Xexit 0;
	X
	X############################################################################
	X#
	X# scan_section()
	X#
	X#	checks a given man tree (like /usr/local/man) in a 
	X#	certain subsection (like '1'), checking for a certain
	X#	file, like 'tty' (which mean 'tty.*', 'system.3*', or '*.*'.
	X#
	X#	will recurse on a subsection name contaning a shell meta-character
	X#
	X############################################################################
	X
	Xsub scan_section {
	X    local ( $manroot, $subsec, $files ) = @_;
	X    local ( $mandir );
	X
	X    $mandir = "man" . $subsec;
	X
	X
	X    # subsec may have been ? or *; if so, recurse!
	X    if ( &has_meta($mandir) ) {  
	X	for (<${mandir}>) {
	X	    if (&has_meta($_)) { 
	X		warn "bad glob of $mandir"; 
	X		last; 
	X	    } 
	X	    s/^man//;
	X	    &scan_section($manroot,$_,$files);
	X	} 
	X	return;
	X    } 
	X
	X    $files = "$files.*" unless $files =~ /\./;
	X
	X    if (!chdir $mandir) {
	X	warn "couldn't chdir to $mandir: $!\n" if $debug;
	X	return;
	X    } 
	X
	X    printf STDERR "chdir to %s of %s\n", $mandir, $manroot if $debug & 1;
	X
	X    &read_manpages ( &has_meta($files) ? &glob($files) : ($files));
	X
	X    chdir('..');
	X} 
	X
	X############################################################################
	X#
	X# read_manpages()
	X#
	X#	passed a list of filename, which are man pages.  opens each one
	X#	verifying that the file really is in the place that the .TH line.
	X#	skips to SEE ALSO section and then verifies existence of each 
	X#	referenced man page.
	X############################################################################
	X
	X
	Xsub read_manpages {
	X    local (@pages) = @_;
	X
	X    local ($junk, $sopage, $basename, $line, $page, $pname, $pext, $gotTH);
	X    local(%seen);
	X
	X
	Xpage:
	X    foreach $page ( @pages ) {
	X	next page if $page =~ /\.(BAK|OLD)$/i;
	X
	X	if ($seen{$page}++) {
	X	    print "already saw $page\n" if $debug & 1;
	X	    next page;
	X	}
	X
	X	if (!open page) {
	X	    warn "couldn't open $page: $!\n";
	X	    next page;
	X	}
	X
	X	$bingo = 1; # global var
	X
	X	print "checking $page\n" if $debug & 1;
	X
	X	$gotTH = 0;
	X	$line = 0;
	X	$sopage = '';
	X
	Xline:   while (<page>) {
	X	    print if $debug & 16;
	X	    next line if /^'''/ || /^\.\\"/;
	X
	X	    # deal with .so's on the first line.
	X	    # /usr/ucb/man uses this instead of links.
	X	    if (!($line++) && /^\.so\s+(.*)/) {
	X		$sopage = $1;
	X		print "$page -> $sopage\n" if $debug & 1;
	X		($basename = $sopage) =~ s%.*/%%;
	X		if ($seen{$basename}++) {
	X		    print "already saw $basename\n" if $debug & 1;
	X		    next page;
	X		} 
	X		if (!open(page,"../$sopage")) {
	X		    print "$page: cannot open $sopage: $!\n";
	X		    next page;
	X		} 
	X		$page = $basename;
	X		next line;
	X	    } 
	X
	X	    # check for internally consistent .TH line
	X	    if ( /^\.(TH|SC)/ ) { # SC is for mh
	X		 $gotTH++;
	X		 printf STDERR "TH checking %s", $_ if $debug & 4;
	X		 do flush();
	X		 s/"+//g;
	X		 ($junk, $pname, $pext) = split;
	X		 if (&macro($pname)) {
	X			printf STDERR "%s: can't resolve troff macro in .TH: %s\n",
	X			    $page, $pname;
	X			next line;
	X		 } 
	X		 $pext =~ y/A-Z/a-z/;
	X		 $pname =~ s/\\-/-/g;
	X		 $pname =~ y/A-Z/a-z/ if $pname =~ /^[\$0-9A-Z_\055]+$/;
	X		 ($pexpr = $page) =~ s/([.+])/\\$1/g;
	X		 $pexpr =~ s%.*/%%;
	X		 if ( "$pname.$pext" !~ /^$pexpr$/i) {
	X		      printf "%s: thinks it's in %s(%s)\n", 
	X			  $page, $pname, $pext;
	X		 } 
	X		 next line;
	X	    }
	X
	X	    next line unless /^\.S[Hh]\s+"*SEE ALSO"*/ 
	X		|| /^\.S[Hh]\s+REFERENCES/	# damn posix
	X		|| /^\.Sa\s*$/; 		# damn mh
	X
	X	    # finally found the cross-references
	Xxref:       while (<page>) {
	X		print if $debug & 16;
	X		last line if /^\.(S[Hh]|Co|Hi|Bu)/; # i really hate mh macros
	X		next xref unless /\(/;
	X		next xref if /^.PP/;
	X		chop;
	X		s/\\f[RIPB]//g;
	X		s/\\\|//g;
	X		s/\\-/-/g;
	Xentry:          foreach $entry ( split(/,/) ) {
	X		    #print "got entry $entry\n";
	X		    next entry unless $entry =~ /\(.*\)/;
	X		    $pname = ''; $pext = '';
	X		    $1 = ''; $2 = '';
	X		    ($pname, $pext) = 
	X			($entry =~ /([A-Za-z0-9\$._\-]+)\s*\(([^)]+)\).*$/); 
	X		    if ($debug & 8) {
	X			printf STDERR "entry was %s, pname is %s, pext is %s\n",
	X			    $entry, $pname, $pext;
	X		    }     
	X		    if (&macro($pname)) {
	X			printf "%s: can't resolve troff macro in SEE ALSO: %s\n",
	X			    $page, $pname;
	X			next entry;
	X		    } 
	X		    next entry if !$pname || !$pext || $pext !~ /^\w+$/;
	X		    $pext =~ y/A-Z/a-z/;
	X		    $pname =~ y/A-Z/a-z/ if $pname =~ /^[A-Z_0-9\-]+$/;
	X		    #($psect = $pext) =~ s/^(.).*/$1/;
	X		    do check_xref($page,$pname,$pext);
	X
	X		}	# entry: foreach $entry ( split(/,/) ) 
	X	    }		# xref:  while (<page>)
	X	}		# line:  while (<page>) 
	X	printf "%s: missing .TH\n", $page if (!$gotTH);
	X    }  			# page:  foreach $page ( @pages )
	X}     			# sub    read_manapages
	X
	X
	X###########################################################################
	X#
	X# check_xref()
	X#
	X#	given the name of the page we're looking for, check for a
	X#	cross reference of a given man page and its assumed subsection
	X#
	X###########################################################################
	X
	Xsub check_xref {
	X    local ($name, $target, $section) = @_;
	X    local ($basesec, $subsec, $newsec );
	X
	X    printf STDERR " xref of %s(%s)\n", $target, $section if $debug & 2;
	X
	X    return if &pathcheck($target,$section);
	X
	X
	X    # if we get this far, something's wrong, so begin notify
	X    printf "%s: %s(%s)", $name, $target, $section;
	X
	X    ($basesec, $subsec) = ($section =~ /^(\d)(.*)$/);
	X
	X    if ($name =~ /\.\d*([nlp])$/ && ($section == 1 || $section == 8)
	X	    && ($newsec = &pathcheck($target,$1))) { # hack for manl idiocy
	X	&really($target,$newsec);
	X	return;
	X    }
	X
	X    # first check if page.Xn is really in page.X
	X    if ( $subsec && ($newsec = &pathcheck($target,$basesec))) {
	X	&really($target,$newsec);
	X	return;
	X    } 
	X
	X    if ( $basesec == 1 && &pathcheck($target,8))  {
	X	&really($target,8);
	X	return;
	X    }
	X
	X    if ( $basesec == 8 && &pathcheck($target,1))  {
	X	&really($target,1);
	X	return;
	X    }
	X
	X    # maybe it thinks it's in 8 but got erroneously in 1
	X    if ( $basesec =~ /[18]/ && ($newsec = &pathcheck($target,'l')))  {
	X	&really($target,$newsec);
	X	return;
	X    } 
	X
	X    # maybe page.X is really in page.Xn; this is expensive
	X    if ( !$subsec && ($newsec = &pathcheck($target,$basesec.'*'))) {
	X	&really($target,$newsec);
	X	return;
	X    } 
	X
	X    printf " missing\n";
	X    do flush();
	X}
	X
	X###########################################################################
	X#
	X# pathcheck()
	X#
	X#	takes a name (like 'tty') and a section (like '1d')
	X#	and looks for 'tty.1d' first in the current root, 
	X#	then in all other elements of @xrefpath.  the section
	X#	may have a meta-character in it (like '8*').
	X#
	X#	returns the subsection in which we found the page, or
	X#	null if we failed.
	X#
	X###########################################################################
	X
	Xsub pathcheck {
	X    local ( $name, $section ) = @_;
	X    local ( $basesec, $metasec, $fullpath, @expansion, $tree, %checked  ); 
	X    local ( $return ) = 0;
	X
	X    $metasec = &has_meta($section);
	X
	X    ($basesec) = ($section =~ /^(.)/);
	X
	X    foreach $tree ( $rootdir, @xrefpath ) {
	X	next if !$tree || $checked{$tree}++;  # only check each tree once
	X
	X	$fullpath = "$tree/man$basesec/$name.$section";  
	X
	X	print "   testing $fullpath\n" if $debug & 8;
	X
	X	if (!$metasec) {
	X	    if (-e $fullpath) {
	X		$return = $section;
	X	    }
	X	} else {
	X	    if ($use_DBM && &dbmopen($tree)) {
	X		next;
	X	    }
	X	    open(SAVERR, '>&STDERR');  # csh globbing brain damage
	X	    close STDERR;
	X	    if ((@expansion = <${fullpath}>) && !&has_meta($expansion[0])) {
	X	    			# redundant meta check due to sh brain-damage
	X		#for (@expansion) { s/.*\.//; } 
	X		#$section = join(' or ',@expansion);
	X		($section) = ($expansion[0] =~ /([^.]+)$/);
	X		$return = $section;
	X	    }
	X	    open(STDERR, '>&SAVERR');  # csh globbing brain damage
	X	    close SAVERR;

tchrist@convex.COM (Tom Christiansen) (01/08/91)

	X	}
	X    } 
	X    printf STDERR "   pathcheck returns $section\n" if $debug & 8;
	X    $return;
	X} 
	X
	X#---------------------------------------------------------------------------
	X
	Xsub flush {
	X    $| = 1; 
	X    print ''; 
	X    $| = 0;
	X}
	X
	Xsub has_meta {
	X    $_[0] =~ /[[*?]/;
	X} 
	X
	Xsub macro {
	X    @_[0] =~ /^\\\*\(/;
	X} 
	X
	Xsub really {
	X    local($was,$is) = @_;
	X    print " really in $was($is)\n";
	X}
	X
	Xsub usage {
	X    die "usage: $iam [-d debug-level] [-s sub-sections] [-p manpath] 
	X    	[-x xrefpath] [pattern ...] \n";
	X}
	X
	Xsub glob {
	X    local($expr) = @_;
	X    local(@retlist) = ();
	X    local(*METADIR);				# paranoia
	X
	X    die "glob: null expr" unless $expr;		# assert
	X
	X    if ($expr =~ /\//) {
	X	warn "glob: \"$expr\" has slashes, punting...";
	X	return <${expr}>;
	X    } 
	X
	X    $expr =~ s/\*/.*/g;
	X    $expr =~ s/\?/./g;
	X
	X    unless (opendir(METADIR, '.')) {
	X	warn "glob: can't opendir ".": $!\n";
	X    } else {
	X	@retlist = sort grep(/$expr/o, grep(!/^\./, readdir(METADIR)));
	X	closedir METADIR;
	X    }
	X    return @retlist;
	X} 
	X
	Xsub dbmopen {
	X    local($tree) = $_[0];
	X    # globals: %dbmopened, %warned
	X    return 1 if $dbmopened{$tree};
	X
	X    unless (-f "
	X
	X} 
SHAR_EOF
if test 10347 -ne "`wc -c < 'cfman'`"
then
	echo shar: "error transmitting 'cfman'" '(should have been 10347 characters)'
fi
chmod 775 'cfman'
fi
echo shar: "extracting 'cfman.8l'" '(6219 characters)'
if test -f 'cfman.8l'
then
	echo shar: "will not over-write existing file 'cfman.8l'"
else
sed 's/^	X//' << \SHAR_EOF > 'cfman.8l'
	X.TH CFMAN 8L "" "15 November 1989" 
	X.de Sh
	X.br
	X.PP
	X.ne 4
	X.ti -.5i
	X\fB\\$1\fR
	X.PP
	X..
	X.de LB		\" little and bold
	X.ft B
	X.if !"\\$1"" \&\s-1\\$1\s+1 \\$2 \\$3 \\$4 \\$5 \\$6
	X.ft R
	X..
	X.de Sp
	X.if t .sp .5v
	X.if n .sp
	X..
	X.ds lq \&"\"
	X.ds rq \&"\"
	X.if t \
	X.       ds lq ``
	X.if t \
	X.       ds rq ''
	X.de Q
	X\*(lq\\$1\*(rq\\$2
	X..
	X.Sh NAME
	Xcfman \- cross-reference man pages for internal consistency
	X.Sh SYNOPSIS
	X.B cfman
	X[
	X.B \-d
	Xlevel
	X] 
	X[
	X.B \-s
	Xsections
	X] 
	X[
	X.B \-p
	Xmanpath
	X] 
	X[
	X.B \-x
	Xxrefpath
	X] 
	X[ pattern | pathname ] ...
	X.br 
	X.Sh DESCRIPTION
	X.I 
	XCfman 
	Xis a 
	X.I perl
	Xprogram that checks that man page sources 
	Xare mutually consistent in their 
	X.LB "SEE ALSO"
	Xreferences.
	XIt will also report any 
	X.LB ".TH"
	Xline that claims the
	Xman page is in a different place than 
	X.I cfman
	Xfound it.
	X.PP
	XWhen supplied with no arguments, 
	X.I cfman
	Xwill check all files (matching *.*) it finds in each man directory in 
	Xyour colon-delimited 
	X.LB "$MANPATH"
	Xenvariable if set, or in 
	X.I /usr/man
	Xotherwise.  It first verifies that the 
	X.LB ".TH"
	Xsays
	Xthe man page is really where it should be, e.g. if the
	Xline is 
	X.br
	X.in +.5i
	X.nf
	X\f(TA
	X\&.TH\ \ WIDGET\ \ 4
	X.in -.5i
	X\fR
	X.fi
	X.br
	Xthen \fIwidget.8\fP should be the filename currently 
	Xbeing examined.  All upper-case will map to all lower-case,
	Xbut mixed case will be preserved for compatibility with
	Xthe 
	X.LB X11
	Xman pages.
	X.PP
	X.I Cfman
	Xthen skips ahead to the 
	X.LB "SEE ALSO"
	Xsection and retrieves
	Xall comma-delimited entries of the general 
	Xform \fIpagename(section)\fP.  It first looks in the file
	X\&../man\fIsection/pagename.section\fP.  If this fails
	Xand the current file ended in one of \fB[npl]\fP, but the
	X.I section
	Xreferenced is either 
	X\fB1\fP or \fB8\fP, then it will check in 
	X.I ../man8.
	XFailing this, 
	X.I cfman
	Xchecks to see whether the referenced man page has been 
	Xinstalled stripped of its subsection, e.g. \fIuucp\fP(1c)
	Xhas found its way into \fIuucp\fP(1).  It then checks
	Xto see whether something in section \fB1\fP has been mis-installed
	Xin section \fB8\fP, or vice versa, or either one in section \fBl\fP
	Xmis-installed in the 
	Xin section \fB8\fP and vice-versa.  If all else fails, 
	X.I cfman
	Xwill guess that a man page is referenced without its
	Xproper subsection, as in a reference to \fIrcp(1)\fP
	Xthat should really have been to \fIrcp(1c)\fP.  If it finds
	Xthe misplaced man page, it reports where the reference
	Xthought it was and where it really was.  Otherwise it
	Xreports the man page as missing.  
	X.PP
	XThe 
	X.LB $MANPATH
	Xvariable may be overridden by 
	Xthe \fB-p\fP option.  
	XAll checks will 
	Xbe performed across each subtree specified in the manpath
	X(either from the environment of the command line),
	Xunless altered with the \fB-x\fP option.  As a short-cut, 
	Xthe \fIxrefpath\fP may have a leading colon to indicate
	Xthat it is to be concatenation of the \fImanpath\fP
	Xand the supplied \fIxrefpath\fP.  
	X.PP
	XYou can restrict the sections checked with the \fB-s\fP
	Xswitch.  By default, sections 1 through 8 will be examined.
	XThe section may be a shell metacharacter expression, 
	Xlike 
	X.Q ?
	Xor 
	X.Q [18lpn] .
	X.PP
	XYou may restrict the individual man pages cross-referenced
	Xby specifying which ones you're interested in on the command
	Xline.  These may be full pathnames, simple names like 
	X.Q tty ,
	Xor a shell metacharacter expression like 
	X.Q *net . 
	XIf
	Xno period occurs in the simple name, it is assumed to mean that
	Xthe name may have any extension.  If you list specific 
	Xman pages on the command line and 
	X.I cfman
	Xfinds none matching your specification, it will report this fact.
	XSee the 
	X.LB "EXAMPLES"
	Xsection.  
	X.PP
	XMan pages that are linked by placing a \fB.so\fP directive
	Xon the first line will be correctly followed, and no man page
	Xin the same subtree.  Very limited support for alternate 
	Xman macros is provided: the 
	X.I "\fIRand MH Message Handling System\fP" 's
	Xman macro set are recognized, as is Larry Wall's 
	X.LB .Sh
	Xreplacement for 
	X.LB .SH.
	X.Sh DIAGNOSTICS
	XRequires 
	X.I perl 
	Xto be at least version 3.0, patchlevel 1 to run.  The 
	Xprogram will abort if you try to run it with an 
	Xearlier version of perl
	X.PP
	XFive different tracing levels can be specified with the \fB-d\fP
	Xoption.  If any debugging is turned on, the walk through
	Xthe different components of the manpath are traced.
	XDebug values are numeric and additive, and are interpreted
	Xthis way:
	X.Sp
	X.in +.5i
	X.nf
	X.ne 5
	X	1	Trace each man page examined
	X	2	Trace each cross reference examined
	X	4	Trace each \s-1\fB.TH\s+1\fP check
	X	8	Trace each file-existence test 
	X	16	Trace each line
	X'in -.5i
	X.fi
	X.Sp
	XTracing information and other warnings are printed to 
	X\fIstderr\fP, but normal messages about bad cross references
	Xare printed to \fIstdout\fP as that is \fIcfman\fP's principle
	Xtask. 
	X.PP
	XEmbedded 
	X.I troff
	Xstring macros starting \e*( cannot be resolved, and they
	Xwill trigger a warning message if found in the 
	X.LB .TH
	Xor 
	X.LB "SEE ALSO"
	Xsections.
	X.Sh EXAMPLES
	X.nf
	X\f(TA
	Xcfman							# do all in $MANPATH
	Xcfman -p /export/exec/sun3/share/man		# sun man pages
	Xcfman -p $HOME/man:/usr/local/mh/man:/usr/local/man:/usr/man
	Xcfman -p /usr/local/man	 -x :/usr/man	# xref also in /usr/man
	Xcfman -s 18nlp					# only these sections
	Xcfman '*tty*' fubar		    			# check for *tty*.* and fubar.*
	Xcfman `pwd`/*.[1-8]		    		# just check these files
	Xcfman -s 23 'sys*'					# sys*.* files in sections 2,3
	Xcfman -s 1 -p /export/exec/sun3/share/man
	X.fi
	X\fR
	X.PP
	XThe last command produced this output on my machine:
	X.nf
	X\f(TA
	Xbanner.1v: thinks it's in banner(1)
	Xfoption.1: skyversion(8) missing
	Xfrom.1: prmail(1) missing
	Xmake.1: rstat(8c) missing
	Xman.1: apropos(1) missing
	Xold-perfmon.1: missing .TH
	Xoldperfmon.1: missing .TH
	Xoldsetkeys.1: thinks it's in setkeys(1)
	Xorganizer.1: restore(1v) really in restore(8)
	Xsunview.1: traffic(1) really in traffic(1c)
	Xsort.1v: thinks it's in sort(1)
	Xsum.1v: thinks it's in sum(1)
	X.fi 
	X\fR
	X.Sh ENVIRONMENT
	XThe default manpath will be taken from 
	X.LB $MANPATH
	Xif set.
	X.Sh "SEE ALSO"
	Xman(1), troff(1), perl(1), man(7).
	X.Sh BUGS
	XDue to the current implentation of globbing in 
	X.I perl,
	Xyou can get 
	X.Q "Arguments too long"
	Xerrors.  The workaround is to run 
	X.I cfman
	Xfirst on 
	X.Q [a-m]* ,
	Xand then on 
	X.Q [n-z]* .
	X.Sh AUTHOR
	XTom Christiansen, \s-1CONVEX\s+1 Computer Corporation.
SHAR_EOF
if test 6219 -ne "`wc -c < 'cfman.8l'`"
then
	echo shar: "error transmitting 'cfman.8l'" '(should have been 6219 characters)'
fi
chmod 664 'cfman.8l'
fi
echo shar: "extracting 'makewhatis'" '(11184 characters)'
if test -f 'makewhatis'
then
	echo shar: "will not over-write existing file 'makewhatis'"
else
sed 's/^	X//' << \SHAR_EOF > 'makewhatis'
	X#!/usr/local/bin/perl
	X#
	X# makewhatis: perl rewrite for makewhatis
	X# author: tom christiansen <tchrist@convex.com>
	X#
	X# Copyright 1990 Convex Computer Corporation.
	X# All rights reserved.
	X
	Xeval "exec /usr/bin/perl -S $0 $*"    # some bozo called us with 'sh foo'
	X    if $running_under_some_shell;     #   'catman -w' likes to do this; sigh
	X
	X
	X&source('stat.pl');
	X
	X($program = $0) =~ s,.*/,,;
	X
	X$UNCOMPRESS = "uncompress";
	X
	X$MAXWHATISLEN =  300;   
	X$MAXDATUM     = 1024; 	# DBM is such a pain
	X
	Xumask 022;
	X
	X&source('getopts.pl');
	X
	Xdo Getopts('ynvdP:M:') || &usage;
	X
	X$opt_P = shift if $#ARGV >= 0;
	X
	X&usage if $#ARGV > -1;
	X
	Xsub usage { die "usage: $program [-n] [-y] [-v] [[-M] manpath]\n"; } 
	X
	X$nflag = $opt_n;
	X$yflag = $opt_y;
	X
	X$manpath = $opt_M if $opt_M;
	X$manpath = $opt_P if $opt_P;		# backwards contemptibility
	X$manpath = "/usr/man" unless $manpath;
	X@manpath = split(/:/,$manpath);
	X
	X$| = $debug = ($opt_d || $opt_v);
	X
	X$SIG{'INT'}  = 'CLEANUP';
	X$SIG{'TERM'} = 'CLEANUP';
	X
	X$SIG{'HUP'}  = 'INGORE';
	X
	Xchop($cwd = `pwd`);
	X
	X$WHATIS = "whatis";
	X
	X# ---------------------------------------------------------------------------
	X# main loop
	X#
	X# chdir to each root in man path.  save mtime of dbase for later compares
	X# with files in case of nflag or yflag.   
	X# ---------------------------------------------------------------------------
	X
	Xforeach $root ( @manpath ) {
	X    local($dbtime, $filecount, $entries);
	X
	X    $root = "$cwd/$root" if $root !~ m:^/:;  # normalize to fullpathname
	X    chdir $root || die "can't chdir to $root: $!";
	X
	X    print "root to $root\n" if $debug;
	X
	X    if ($nflag || $yflag) { 
	X	unless (&Stat('whatis.pag')) {
	X	    print "couldn't stat $root/whatis DBM file\n" if $debug;
	X	    &rebuild(0, 0) if $yflag;
	X	    next;
	X	}
	X	$dbtime = $st_mtime;
	X    }
	X    &rebuild($nflag, $yflag);
	X}
	X
	Xexit $status;
	X
	X# ---------------------------------------------------------------------------
	X# rebuild -- open a new whatis database, store all references in files in 
	X# 	     this root to it.  if dont_touch or test_stale parms set, just
	X#	     do the checks.  if test_stale, recurse on a real rebuild.
	X# ---------------------------------------------------------------------------
	X
	Xsub rebuild {
	X    local($dont_touch, $test_stale) = @_;
	X
	X    local(%seen);		# {dev,ino} pairs of files seen
	X    local(%so);			# the .so references seen
	X    local(@WHATIS);		# whatis list
	X    local($entries, $filecount) = (0,0);
	X
	X    unless ($dont_touch || $test_stale) {
	X	if (!open (WHATIS, "> $WHATIS.$$")) {
	X	    warn "can't open $root/$WHATIS.$$: $!\n";
	X	    $status = 1;
	X	    return;;
	X	}
	X	if (!dbmopen(WHATIS, "$WHATIS.$$", 0644)) {
	X	    warn "can't dbmopen $root/$WHATIS: $!\n";
	X	    $status = 1;
	X	    return;
	X	}
	X    }
	X
	X    foreach $mandir ( <man?*> ) {
	X	next if $mandir =~ /man0.*/;
	X	next if $mandir =~ /\.(old|bak)$/i;
	X	next if $mandir =~ /~$/;
	X	next unless -d $mandir;
	X
	X	if (!chdir $mandir) {
	X	    warn "can't chdir to $root/$mandir: $!\n";
	X	    next;
	X	}
	X
	X	($dirext) = $mandir =~ /man(.*)$/;
	X	$dirext =~ s/\.Z$//;
	X
	X	print "subdir is $mandir\n" if $debug;
	X
	X	if (!opendir(mandir,'.')) {
	X	    warn "can't opendir('$root/$mandir'): $!\n";
	X	    next;
	X	}
	X
	X	# read each file in directory.  use readdir not globbing
	X	# because we don't want to blow up on huge directories
	XFILE:	while ($FILE = readdir(mandir)) {
	X	    $compressed = $mandir =~ m:.*\.Z:;
	X	    next FILE if $FILE =~ /^\.{1,2}/;
	X
	X	    if ($FILE !~ /\S\.\S/) {
	X		print STDERR "Skipping non-man file: $FILE\n";
	X		next FILE;
	X	    } 
	X
	X	    # this will be optimized into a case statement
	X	    if      ($FILE =~ /\.old$/i) {
	X		next;
	X	    } elsif ($FILE =~ /\.bak$/i) {
	X		next;
	X	    } elsif ($FILE =~ /\.out$/i) {
	X		next;
	X	    } elsif ($FILE =~ /~$/) {
	X		next;
	X	    }
	X
	X	    ($tmpfile = $FILE) =~ s/\.Z$//;
	X
	X	    ($filenam, $filext) = 
	X		$tmpfile =~ /^(\S+)\.([^.]+)$/;
	X
	X	    #if ($filext eq '.Z') {
	X		#($filenam, $filext) = $filenam =~ /^(\S+)\.([^.]+)(\.Z)?$/;
	X	    #}
	X
	X	    if ($filext !~ /^${dirext}.*/ && $mandir ne 'mano') {
	X		print STDERR "$FILE has a funny extension ($filext) to be in $mandir\n";
	X	    }
	X
	X	    unless (&Stat($FILE)) {
	X		warn "can't stat $root/$mandir/$FILE: $!\n";
	X		next FILE;
	X	    } 
	X
	X	    if ($dont_touch || $test_stale) {
	X		next unless $st_mtime > $dbtime;
	X		print "$root/$mandir/$FILE newer than its dbm whatis file\n";
	X		closedir mandir;
	X		chdir $root;
	X		&rebuild(0,0) if $test_stale;
	X		return;
	X	    }
	X
	X	    if ($apage = $seen{$st_dev,$st_ino}) {
	X		printf "already saw %s, linked to %s\n", $FILE, $apage
	X		    if $debug;
	X		&chopext($page = $FILE);
	X		unless ($WHATIS{$page}) {
	X		    print "forgot $page\n" if $debug;
	X		    &store_indirect($page, $apage);
	X		}
	X		next FILE;
	X	    } 
	X	    $seen{$st_dev,$st_ino} = $FILE;
	X
	X	    $compressed |= $FILE =~ /\.Z$/;
	X	    
	X	    if (!open(FILE, $compressed ? "$UNCOMPRESS < $FILE |" : $FILE)) {
	X		warn "can't open $FILE: $!\n";
	X		next FILE; 
	X	    }
	X
	X	    $filecount++;
	X	    print "opened $root/$mandir/$FILE\n" if $debug;
	X
	X	    &extract_names;
	X	} 
	X	closedir mandir;
	X	chdir $root || die "can't chdir back to $root: $!";
	X    } 
	X
	X    unless ($dont_touch || $test_stale) {
	X	$, = "\n";
	X	print WHATIS (sort @WHATIS),'';
	X	$, = '';
	X	close WHATIS || warn "can't close $WHATIS.$$: $!";
	X	rename ("$WHATIS.$$", $WHATIS) 
	X	    || warn "can't rename $WHATIS.$$ to $WHATIS: $!";
	X	&check_sos();
	X	dbmclose(WHATIS) || warn  "can't dbmclose $WHATIS: $!";
	X	for $ext ( 'pag', 'dir' ) {
	X	    unlink "$WHATIS.$ext"; 
	X	    rename("$WHATIS.$$.$ext", "$WHATIS.$ext")
	X		|| warn "can't rename $WHATIS.$$.$ext:  $!";
	X	} 
	X	print "$program: $root: found $entries entries in $filecount files\n";
	X    } 
	X} 
	X
	X
	X# in case we get interrupted
	X#
	Xsub CLEANUP {
	X    print stderr "<<INTERRUPTED>> reading $FILE\n";
	X    chdir $root;
	X    unlink "$WHATIS.$$", "$WHATIS.$$.pag", "$WHATIS.$$.dir";
	X    exit 1;
	X} 
	X
	X# get next line from FILE, honoring escaped newlines
	X#
	Xsub getline {
	X    local ($_);
	X
	X    $_ = <FILE>;
	X    {
	X        chop;
	X        if (/\\$/) {
	X            chop;
	X            $_ .= ' ';
	X            $_ .= <FILE>;
	X            redo;
	X        }
	X    }
	X    $_;
	X}
	X
	Xsub extract_names {
	X    local($_);
	X    local($needcmdlist) = 0;
	X    local($foundname) = 0;
	X    local(@lines);
	X    local($page, $page2, $indirect, $foundname, @lines, $nameline);
	X    local($cmdlist, $ocmdlist, $tmpfile, $section);
	X    local($prototype, $seenpage);
	X
	X    unless (-T FILE) {
	X	print STDERR "$FILE: not a text file\n";
	X	next;
	X    } 
	X
	X
	X    $_ = <FILE>; 	#   first check for leading .so reference
	X    if (/^\.so\s+(man.+\/\S+)/) {
	X	local($indirect, $indirect2);
	X	$indirect = $1;
	X	($page)  = $FILE     =~ m:([^.]+)\.[^.]*$:;
	X	($page2) = $indirect =~ m:.*/([^/]+)$:;
	X	($indirect2 = $indirect) =~ s!/!.Z/!;
	X	if (-e "../$indirect" || -e "../$indirect.Z" || -e $indirect2) {
	X	    $so{$page} = $page2;
	X	    print "$FILE: .so alias for $indirect\n" if $debug;
	X	} else {
	X	    print STDERR "$FILE .so references non-existent $indirect\n";
	X	}
	X	return;
	X    } else {
	X	/^\.TH\s+(\S*)\s+(\S+)/ && &doTH($1, $2);
	X    } 
	X
	XLINE: while (<FILE>) {
	X	/^\.TH\s+(\S*)\s+(\S+)/ && &doTH($1, $2);
	X	next LINE unless /^\.SH\s+"?NAME"?/i || /^\.NA\s?/;
	X	$foundname = 1;
	X	@lines = ();
	X	$nameline = '';
	XNAME:	while ($_ = &getline()) {
	X	    last NAME if /^\.(S[hHYS])\s?/;  # MH support
	X	    if ( $_ eq '.br' ) {
	X		push(@lines, $nameline) if $nameline;
	X		$nameline = '';
	X		next NAME;
	X	    } 
	X	    s/^\.[IB]\b//;	# Kill Bold and Italics
	X	    next if /^\./;
	X	    $nameline .= ' ' if $nameline;
	X	    $nameline .= $_;
	X	} 
	X
	X	push(@lines, $nameline);
	X
	X	for ( @lines ) {
	X	    next unless ord;
	X	    s/\\f([PBIR]|\(..)//g;	# kill font changes
	X	    s/\\s[+-]?\d+//g;		# kill point changes
	X	    s/\\&//g;			# and \&
	X	    s/\\\((ru|ul)/_/g;		# xlate to '_'
	X	    s/\\\((mi|hy|em)/-/g;	# xlate to '-'
	X	    s/\\\*\(..//g  &&		# no troff strings
	X		print STDERR "trimmed troff string macro in NAME section of $FILE\n";
	X	    s/\\//g;		   	# kill all remaining backslashes 
	X	    s/^\.\\"\s*//;		# comments
	X	    if (!/\s+-+\s+/) {
	X		#   ^ otherwise L-devices would be L
	X		printf STDERR "$FILE: no separated dash in \"%s\"\n", $_;
	X		$needcmdlist = 1;   	# forgive their braindamage
	X		s/.*-//;
	X		$desc = $_;
	X	    } else {
	X		($cmdlist, $desc) = ( $`, $' );
	X		$cmdlist =~ s/^\s+//;
	X	    }
	X
	X	    # need this for two reasons: sprintf might blow up and so 
	X	    # might the dbm store due to 1k limit
	X	    #
	X	    $ocmdlist = $cmdlist;  # before truncation
	X	    if (length($cmdlist) > $MAXWHATISLEN) {
	X		printf STDERR "$FILE: truncating cmdlist from %d to %d bytes for DBM's sake\n",
	X			length($cmdlist), $MAXWHATISLEN;
	X		$cmdlist = substr($cmdlist,0,$MAXWHATISLEN) . "...";
	X	    } 
	X
	X	    ($tmpfile = $FILE) =~ s/\.Z$//;
	X	    ($page, $section) = $tmpfile =~ /^(\S+)\.(\S+)$/;
	X	    $cmdlist = $page if $needcmdlist; 
	X
	X	    $prototype = ''; $seenpage = 0;
	X
	X	    foreach $cmd (split(/[\s,]+/,$ocmdlist)) {
	X		next unless $cmd;
	X		$seenpage |= ($cmd eq $page);
	X		if (! $prototype) {
	X		    &store_direct($cmd, $cmdlist, $tmpfile, $dirext, $desc);
	X		    $prototype = $cmd;
	X		} else {
	X		    &store_indirect($cmd, "$prototype.$filext");
	X		} 
	X	    } 
	X	    unless ($seenpage) {
	X		print "$FILE: forgot my own name!\n" if $debug;
	X		if ($prototype) {
	X		    &store_indirect($page, "$prototype.$filext");
	X		} else {
	X		    &store_direct($page, $page, $FILE, $dirext, '');
	X		}
	X	    }
	X	}
	X    }  
	X    unless ($foundname) {
	X	print STDERR "$FILE: no NAME lines, so has no whatis description!\n";
	X	($tmpfile = $FILE) =~ s/\.Z$//;
	X	($page, $section) = $tmpfile =~ /^(\S+)\.(\S+)$/;
	X	&store_direct($page, $page, $tmpfile, $dirext, 'NO DESCRIPTION');
	X    } 
	X}
	X
	X# --------------------------------------------------------------------------
	Xsub source {
	X    local($file) = @_;
	X    local($return) = 0;
	X
	X
	X    $return = do $file;
	X    die  "couldn't parse \"$file\": $@" if $@;
	X    die  "couldn't do \"$file\": $!" unless defined $return;
	X    warn "couldn't run \"$file\"" unless $return;
	X}
	X
	X
	Xsub chopext {
	X    $_[0] =~ s/\.[^.]+$//;
	X} 
	X
	Xsub check_sos {
	X    local($key);
	X
	X    foreach $key (keys %so) {
	X	unless (defined $WHATIS{$key}) {
	X	    printf STDERR 
	X		"%s was a .so alias for %s, but %s's NAME section doesn't know it!\n",
	X		$key, $so{$key}, $so{$key};
	X	    &store_indirect($key, $so{$key});
	X	} 
	X    } 
	X} 
	X
	Xsub store_direct {
	X    local($cmd, $list, $page, $section, $desc) = @_;
	X    local($datum);
	X
	X    push(@WHATIS,sprintf("%-20s - %s", "$list ($filext)", $desc));
	X
	X    $datum = join("\001", $list, $page, $section, $desc);
	X
	X    if (defined $WHATIS{$cmd}) {
	X	if (length($WHATIS{$cmd}) + length($datum) + 1 > $MAXDATUM) {
	X	    print STDERR "can't store $page -- would break DBM\n";
	X	    return;
	X	} 
	X	$WHATIS{$cmd} .= "\002";
	X    } 
	X
	X    print "storing $cmd\n" if $debug;
	X    $WHATIS{$cmd} .= $datum;
	X    $entries++;
	X} 
	X
	Xsub store_indirect {
	X    local($indirect, $real) = @_;
	X
	X    print "storing $indirect as reference to $real\n"
	X	if $debug;
	X
	X    $WHATIS{$indirect} .= "\002" if $WHATIS{$indirect};
	X    $WHATIS{$indirect} .= $real;
	X    $entries++;
	X} 
	X
	Xsub doTH {
	X    local($THname, $THext) = @_;
	X    local($int_name, $ext_name);
	X
	X    ($int_name = "$THname.$THext") =~ tr/A-Z/a-z/;
	X    ($ext_name = "$filenam.$filext") =~ tr/A-Z/a-z/;
	X
	X    if ($int_name ne $ext_name && $debug) {
	X	print STDERR "${FILE}'s .TH thinks it's in $int_name\n";
	X    } 
	X} 
SHAR_EOF
if test 11184 -ne "`wc -c < 'makewhatis'`"
then
	echo shar: "error transmitting 'makewhatis'" '(should have been 11184 characters)'
fi
chmod 755 'makewhatis'
fi
echo shar: "extracting 'straycats'" '(476 characters)'
if test -f 'straycats'
then
	echo shar: "will not over-write existing file 'straycats'"
else
sed 's/^	X//' << \SHAR_EOF > 'straycats'
	X#!/usr/local/bin/perl
	X
	X$manpath = shift || $ENV{'MANPATH'} || '/usr/man/';
	X
	Xfor $root (split(/:/, $manpath)) {
	X
	X    chdir($root) || die "can't chdir to $root: $!\n";
	X
	X    foreach $catdir ( <cat*> ) {
	X	opendir (catdir, $catdir) || die "can't opendir $dir: $!";
	X	($mandir = $catdir) =~ s/cat/man/;
	X	foreach $file ( readdir(catdir) ) {
	X	    next if $file eq '.' || $file eq '..';
	X	    next if -e "$mandir/$file";
	X	    print "no man page for $root/$catdir/$file\n";
	X	} 
	X    } 
	X
	X}
SHAR_EOF
if test 476 -ne "`wc -c < 'straycats'`"
then
	echo shar: "error transmitting 'straycats'" '(should have been 476 characters)'
fi
chmod 775 'straycats'
fi
echo shar: "extracting 'man.ms'" '(34978 characters)'
if test -f 'man.ms'
then
	echo shar: "will not over-write existing file 'man.ms'"
else
sed 's/^	X//' << \SHAR_EOF > 'man.ms'
	X.\" --------------------------------------------------------
	X.de CW	\" macro to begin constant-width font
	X\" I like TA better, but whatever looks nicest should be used
	X.ft TA
	X..
	X.\" --------------------------------------------------------
	X.de CE	\" macro to end constant-width font
	X.ft R	\" maybe should really be .ft P?
	X..
	X.\" --------------------------------------------------------
	X.de M           \" man page reference
	X\\fI\\$1\\fR\\|(\\$2\)\\$3
	X..
	X.\" --------------------------------------------------------
	X.\" Here begins a mostly successful attempt at
	X.\" defining a macro to output a boxed, centered 
	X.\" figure that includes an auto-increment figure #N
	X.\" line using -ms macros.
	X.\" It doesn't actually do the centering. sigh.
	X.nr FN 0 1
	X.de BF 	\" begin figure
	X.ds FN \\$1 
	X.KF
	X.nf
	X.na
	X.sp
	X.B1
	X.CW
	X..
	X.\" --------------------------------------------------------
	X.de EF	\" end figure
	X.sp .5v
	X.B2
	X.CE
	X.ce
	X\\fBFigure \\n+(FN \\(em \\*(FN\\fR
	X.sp
	X.fi 
	X.ad
	X.KE
	X..
	X.\" --------------------------------------------------------
	X.de LB          \" little and bold
	X.ft B
	X.if !"\\$1"" \&\s-1\\$1\s+1 \\$2 \\$3 \\$4 \\$5 \\$6
	X.ft R
	X..
	X.\" End macro definitions
	X.\" --------------------------------------------------------
	X.TL
	X\s+2The Answer to All Man's Problems\s-2
	X.AU
	X\s+2\fITom Christiansen\fP\s-2
	X.AI
	X\s-1CONVEX\s+1 Computer Corporation
	XPOB 833851
	X3000 Waterview Parkway
	XRichardson, TX  75083-3851
	X.sp
	X\fI{uunet,uiucdcs,sun}!convex!tchrist
	Xtchrist@convex.com\fP
	X.AB no
	X.ps -1
	XThe \s-1UNIX\s0 on-line manual system was designed many years ago to suit
	Xthe needs of systems at the time, but 
	Xdespite the growth in complexity of typical
	Xsystems and the need for more sophisticated software,
	Xfew modifications have been
	Xmade to it since then.
	XThis paper
	Xpresents the results of a complete rewrite of the man system.  The
	Xthree principal goals were to effect substantial gains in
	Xfunctionality, extensibility, and speed.  The secondary goal was to
	Xrewrite a basic \s-1UNIX\s0 utility in the perl programming language to
	Xobserve how perl affected development time, execution time, and design
	Xdecisions.
	X.PP
	X.ps -1
	XExtensions to the original man system include storing the whatis
	Xdatabase in \s-1DBM\s0 format for quicker access, intelligent handling of
	Xentries with multiple names (via \fB.so\fP inclusion, links, or the \s-1NAME\s0
	Xsection), embedded tbl and eqn directives, multiple man trees,
	Xextensible section naming possibilities,
	Xuser-definable section and sub-section search ordering, 
	Xan indexing mechanism
	Xfor long man pages,
	Xtypesetting of man pages,
	Xtext-previewer support for bit mapped displays, 
	Xautomatic validity checks on the \s-1SEE\s0 \s-1ALSO\s0 sections, 
	Xsupport for compressed man pages to conserve disk usage, 
	Xper-tree man macro definitions, 
	Xand support for man pages for multiple
	Xarchitectures or software versions from the same host.
	X.ps +1
	X.AE
	X.NH 
	XIntroduction
	X.PP
	XThe \s-1UNIX\s0 on-line manual system was 
	Xdesigned many years ago to suit the needs of the systems
	Xat the time.  Since then, 
	Xdespite 
	Xthe growth in complexity of 
	Xtypical systems and the need for more sophisticated software
	Xto support them,
	Xfew modifications of major significance
	Xhave been
	Xmade to the program.
	XThis paper describes problems inherent
	Xin earlier versions of the \fIman\fP program, proposes solutions 
	Xto these problems, and outlines one implementation of these solutions.
	X.NH
	XThe Problem
	X.NH 2
	XThe Monolithic Approach
	X.PP
	XOne of the most serious problems with the \fIman\fP program up to 
	Xand including the \s-1BSD\s04.2 release was that 
	Xall man pages on the entire system were expected to reside
	Xunder a common directory, 
	X\fI/usr/man\fR.
	XThere was no 
	Xnotion of separate sets of man pages installed on
	Xthe same machine in different subdirectories.
	XAt large installations, 
	Xsituations commonly arise in which this 
	Xfunctionality is desirable.  
	XA site may wish to keep vendor-supplied man pages
	Xseparate from man pages that were developed locally 
	Xor acquired from some third party.  
	XAn individual or group may wish to maintain their own set 
	Xof man pages.
	XMultiple versions of the same software package
	Xmight be simultaneously installed on the same machine.  
	XA heterogeneous environment may want to be able to view man pages for
	Xall available architectures from any machine.
	XGiven the requirement that all man pages live in the 
	Xsame directory, these scenarios are difficult to impossible to 
	Xsupport.
	X.PP
	XThe \fIman\fP program distributed in the \s-1BSD\s04.3 release 
	Xincluded the concept of a \s-1MANPATH\s0,
	Xa colon-delimited 
	Xlist of complete man trees taken either from the user's
	Xenvironment or supplied on the command line.
	XWhile this was a vast improvement over the previous monolithic approach,
	Xseveral significant problems remained.
	XFor one thing, the program still had to use
	Xthe 
	X.M access 2
	Xsystem call on all possible paths to find out 
	Xwhere the man page for a particular topic 
	Xexisted.  
	XWhen the user has a \s-1MANPATH\s0 containing multiple
	Xcomponents, the time needed for the \fIman\fP program to locate
	Xa man page is often noticeable, particularly when the target
	Xman page does not exist.
	X.NH 2
	XHard-coded Section Names
	X.PP
	XAnother problem with the \fIman\fP program unresolved by 
	Xthe \s-1BSD\s04.3 release
	Xwas that all possible sections in which a man page could 
	Xreside were hard-coded 
	Xinto the program.  This means that while a 
	X.B manp

tchrist@convex.COM (Tom Christiansen) (01/08/91)

	Xsection would be recognized, a 
	X.B manq
	Xdirectory would not be, and while a 
	X.B man3f
	Xdirectory would be recognized, a
	X.B man3x11
	Xdirectory would not be.  
	X.PP
	XLikewise, the possible subsections
	Xfor a man page were also embedded in the source code, so 
	Xa man page named something like 
	X.I /usr/man/man3/XmLabel.3x11
	Xwould not be found because 
	X.B 3x11
	Xwas not in the hard-coded list of viable subsections.
	XSome systems install all man pages stripped of subsection
	Xcomponents in the file name.  This situation is less than optimal
	Xbecause it proves useful to be able
	Xto supply both a 
	X.M getc 3f
	Xand a 
	X.M getc 3s .
	XDistinguishing between subsections is 
	Xparticularly convenient with the ``intro'' man pages;
	Xa vendor could supply
	X.M intro 3
	X.M intro 3a ,
	X.M intro 3c ,
	X.M intro 3f ,
	X.M intro 3m ,
	X.M intro 3n ,
	X.M intro 3r ,
	X.M intro 3s ,
	Xand
	X.M intro 3x 
	Xas introductory man pages for the various libraries.
	XHowever, the task of running
	X.M access 2
	Xon all possible subsections is slow and tedious, requiring
	Xrecompilation whenever a new subsection is invented.
	X.NH
	XReferences in the Filesystem
	X.PP
	XThe existing man system had no elegant way to handle
	Xman pages containing more than one entry.  For example, 
	X.M string 3
	Xcontains references to 
	X.M strcat 3 ,
	X.M strcpy 3 ,
	Xamongst others.  Because the \fIman\fP program looks for
	Xentries only in the file system, these extra references must be
	Xrepresented as files that reference the base man page.  The most
	Xcommon practice is to have a file consisting of
	Xa single line
	Xtelling
	X.I troff
	Xto source the other man page.
	XThis file would read something like:
	X.sp
	X.ti 5
	X.CW
	X\&.so man3/string.3
	X.CE
	X.sp 
	XOccasionally,
	Xextra references are created with a link in the file 
	Xsystem (either a hard link or a symbolic one).  Except when 
	Xusing 
	Xhard links, this method wastes
	Xdisk blocks and inodes.  In any case,
	Xthe directory gains more entries, slowing
	Xdown accesses to files in those directories.  Logic 
	Xmust be built into the \fIman\fP program to 
	Xdetect these extra references.
	XIf not, when man pages are reformatted into their 
	Xcat directories, separate formatted man pages are stored 
	Xon disk, wasting substantial amounts of disk space 
	Xon duplicate information.
	XOn systems with numerous man pages, the directories can grow 
	Xso large that all man 
	Xpages for a given section cannot be listed on the command line 
	Xat one time because of kernel restrictions on the total length of the
	Xarguments to 
	X.M exec 2 .
	XBecause of the need to store reference information 
	Xin the file system, the problem is only made worse.
	XThis often happens in 
	Xsection 3 after the man pages for the X 
	Xlibrary have been installed, but
	Xcan occur in other sections as well.
	X.PP
	XThe 
	X.M makewhatis 8
	Xprogram is a Bourne shell script that generates the 
	X.I /usr/lib/whatis
	Xindex, and is used by 
	X.M apropos 1
	Xand 
	X.M whatis 1
	Xto provide one-line summaries of man pages.  These
	Xprograms are part of the 
	X.I man 
	Xsystem
	Xand are often links to each other and sometimes to 
	X.I man
	Xitself.
	XIf any of 
	Xthe man subdirectories contain more files than the shell 
	Xcan successfully expand on the command line, the 
	X.I makewhatis
	Xscript fails
	Xand no index is generated.  When this occurs, 
	X.I whatis
	Xand 
	X.I apropos
	Xstop working.  The 
	X.M catman 8 
	Xprogram, used to pre-format raw man pages, suffers
	Xfrom the same problem.
	X.PP
	XOf course,
	X.I makewhatis
	Xwasn't working all that well, anyway. 
	XIt was a wrapper around many calls to little programs
	Xthat each did a small piece of the work, making it
	Xrun slowly.
	XIt, too, had a hard-coded pathname for where man pages resided
	Xon disk and which sections were permitted.
	X.I Makewhatis
	Xdidn't always extract the proper information 
	Xfrom the man page's \s-1NAME\s0
	Xsection.  When it did, this information was sometimes 
	Xgarbled due to embedded
	X.I troff
	Xformatting information.
	XBut even garbled information was better
	Xthan none at all.  
	XEven so, these programs left some things to be desired.
	X.I Apropos 
	Xdidn't understand regular expression searches, and both
	Xit and 
	X.I whatis
	Xpreferred to do their own lookups using basic, unoptimized C functions
	Xlike 
	X.M index 3
	Xrather than using a general-purpose optimized string search program
	Xlike 
	X.M egrep 1 .
	X.NH
	XThe Solution 
	X.NH 2
	XA Real Database
	X.PP
	XThe problem in all these cases appeared to be that the filesystem
	Xwas being used as a database, and that this paradigm did not hold
	Xup well to expansion.  Therefore the solution was to move
	Xthis information into a database for more rapid access.  
	XUsing this database, 
	X.I man
	Xand 
	X.I whatis
	Xneed no longer call
	X.M access 2
	Xto test all possible locations for the desired man page.
	XTo solve the other problems,
	X.M makewhatis 8
	Xwould be recoded so it didn't rely on the shell 
	Xfor looking at directories.
	X.NH 2
	XCoding in Perl
	X.PP
	XWhen the project was first contemplated, the
	Xperl programming language by Larry Wall was rapidly 
	Xgaining popularity as an alternative to C for tasks that
	Xwere either too slow when written as shell scripts, or
	Xsimply exceeded the shells' somewhat limited capabilities.
	XSince perl was 
	Xoptimized for parsing text, had convenient
	X.M dbm 3x 
	Xsupport built in to it, and the task really didn't seem complex 
	Xenough to merit a full-blown treatment in C or C++,
	Xperl was selected as the language of choice.
	XHaving all code written in perl would also help support 
	Xheterogeneous environments because the resulting scripts could
	Xbe copied and run on any hardware or software platform supporting
	Xperl.  No recompilation would be required.
	X.PP
	XSome concern existed about choosing
	Xan interpreted language when one of the issues to address was 
	Xthat of speed.  It was decided to do the prototype in perl
	Xand, if necessary, translate this into C should performance 
	Xprove unacceptable.
	X.PP
	XThe first task was to recode 
	X.M makewhatis 8
	Xto generate the new
	X.I whatis
	Xdatabase using \fIdbm\fP.  The
	X.M directory 3
	Xroutines were used rather than shell globbing to circumvent
	Xthe problem of large directories breaking shell wildcard
	Xexpansions.  Perl proved to be an appropriate choice for this
	Xtype of text processing (see Figure 1).
	X.BF "\fImakewhatis\fP excerpt #1"
	Xs/\e\ef([PBIR]|\e(..)//g;      # kill font changes
	Xs/\e\es[+-]?\ed+//g;           # kill point changes
	Xs/\e\e&//g;                   # and \e&
	Xs/\e\e\e((ru|ul)/_/g;          # xlate to '_'
	Xs/\e\e\e((mi|hy|em)/-/g;       # xlate to '-'
	Xs/\e\e\e*\e(..//g  &&           # no troff strings
	X    print STDERR "trimmed troff string macro in NAME section of $FILE\en";
	Xs/\e\e//g;                    # kill all remaining backslashes
	Xs/^\e.\e\e"\es*//;              # kill comments
	Xif (!/\es+-+\es+/) {
	X    #   ^ otherwise L-devices would be L
	X    print STDERR "$FILE: no separated dash in $_\en";
	X    $needcmdlist = 1;       # forgive their braindamage
	X    s/.*-//;
	X    $desc = $_;
	X} else {
	X    ($cmdlist, $desc) = ( $`, $' );
	X    $cmdlist =~ s/^\es+//;
	X}
	X.EF
	X.NH 2
	XDatabase Format
	X.PP
	XThe database entries themselves are conveniently
	Xaccessed as arrays from perl.  To save space and
	Xaccommodate man pages with multiple references, two
	Xkinds of database entries exist: direct and indirect.
	XIndirect entries are simply references to direct entries.
	XFor example, indirect entries for 
	X.M getc 3s ,
	X.M getchar 3s ,
	X.M fgetc 3s , 
	Xand 
	X.M getw 3s
	Xall point to the real entry, which is
	X.M getc 3s .
	XIndirect entries are created for multiple entries in 
	Xthe \s-1NAME\s0 section, for symbolic and hard links, and
	Xfor 
	X.B \&.so
	Xreferences.  Using the \s-1NAME\s0 section is the preferred 
	Xmethod; the others are supported for backwards compatibility.
	X.PP
	X.ne 4
	XAssuming that the \s-1WHATIS\s0 array has been bound to the
	Xappropriate
	X.I dbm
	Xfile, storing indirect entries is trivial:
	X.sp
	X.CW	
	X.ti 1i
	X$WHATIS{'fgetc'} = 'getc.3s';
	X.sp
	X.CE
	XWhen a program encounters an indirect entry, such as
	Xfor \fIfgetc\fP, it must make another lookup based on 
	Xthe return value of first lookup (stripped of its 
	Xtrailing extension) until it finds a direct entry.  The
	Xtrailing extension is kept so that an indirect reference
	Xto 
	X.M gtty 3c
	Xdoesn't accidentally pull out 
	X.M stty 1
	Xwhen it really wanted 
	X.M stty 3c .
	X.PP
	XThe format of a direct entry is more complicated, because
	Xit needs to encode the description to be used by 
	X.M whatis 1
	Xas well as the section and subsection information.
	XIt can be distinguished from an indirect entry because
	Xit contains four fields delimited by control-A's (\s-1ASCII 001\s0), 
	Xwhich are themselves prohibited from being in any
	Xof the fields.  The fields are as follows:
	X.br
	X.in +5n
	X.IP 1
	XList of references that point to this man page; this
	Xis usually everything to the left of the hyphen
	Xin the \s-1NAME\s0 section.
	X.IP 2
	XRelative pathname of the file the man page is kept in;
	Xthis is stored for the indirect entries.
	X.IP 3
	XTrailing component of the directory in which the
	Xman page can be found, such as 
	X.B 3
	Xfor \fBman3\fP.  
	X.IP 4
	XDescription of the man page for use by 
	Xthe 
	X.I whatis 
	Xand 
	X.I apropos
	Xprograms; basically everything to the right of the hyphen in the
	XN\s-1AME\s0 section.
	X.in -5n
	X.PP
	XAt first glance, the third field would
	Xseem redundant.  It would appear that you could 
	Xderive it from the character after the dot in the second field.
	XHowever, to support arbitrary subdirectories like
	X.B man3f
	Xor 
	X\fBman3x11\fP, you must also know the name of the 
	Xdirectory so you don't look in
	X.B man3 
	Xinstead.  Additionally, a long-standing tradition exists 
	Xof using the
	X.B mano
	Xsection 
	Xto store old man pages from arbitrary sections.  
	XFurthermore, man pages are sometimes installed in the
	Xwrong section.  To support these scenarios, restrictions
	Xregarding the format of filenames used for man pages were
	Xrelaxed in \fIman\fR,
	X\fImakewhatis\fR, and \fIcatman\fR,
	Xbut warnings would be issued by 
	X.I makewhatis
	Xfor man pages installed in directories that don't have
	Xthe same suffix as the man pages.
	X.NH 2
	XMultiple References to the Same Topic
	X.PP
	XA problem arises from the fact that the same topic 
	Xmay exist in more than one section of the manual.
	XWhen a lookup is performed on a topic,
	Xyou want to retrieve all possible man page locations
	Xfor that topic.  The 
	X.I whatis
	Xprogram wants to display them all to the user, while
	Xthe 
	X.I man
	Xprogram will either show all the man pages 
	X(if the 
	X.B \-a
	Xflag is given) or
	Xsort what it has retrieved according to a particular section and
	Xsubsection precedence, by default showing entries from section
	X1 before those from section 2, and so forth.  Therefore, 
	Xeach lookup may actually return a list of direct and
	Xindirect lookups.  This list is delimited by control-B's
	X(\s-1ASCII 002\s0), which are stripped from the data fields, should
	Xthey somehow contain any.  The code for storing a direct entry
	Xin the 
	X.I whatis
	Xdatabase is featured in Figure 2.
	X.BF "\fImakewhatis\fP excerpt #2"
	Xsub store_direct {
	X    local($cmd, $list, $page, $section, $desc) = @_; # args
	X    local($datum);
	X
	X    $datum = join("\e001", $list, $page, $section, $desc);
	X
	X    if (defined $WHATIS{$cmd}) {
	X        if (length($WHATIS{$cmd}) + length($datum) + 1 > $MAXDATUM) {
	X            print STDERR "can't store $page -- would break DBM\en";
	X            return;
	X        }
	X        $WHATIS{$cmd} .= "\e002";  # append separator
	X    }
	X    $WHATIS{$cmd} .= $datum;  # append entry
	X}
	X.EF
	X.KE
	X.PP
	XNotice the check of the new datum's
	Xlength against the value of \s-1MAXDATUM.\s0  This is because of the
	Xinherent limitations in the implementation of the 
	X.M dbm 3x
	Xroutines.  This is 1k for 
	X.I dbm 
	Xand 4k for
	X.I ndbm .
	XThis restriction will be relaxed 
	Xif a \fIdbm\fR-compatible set of routines is written without 
	Xthese size limitations.  The \s-1GNU\s0 
	X.I gdbm 
	Xroutines hold promise, but they were released after the 
	Xwriting of these programs and haven't been investigated yet.
	XIn practice, these limits are seldom if ever reached, especially 
	Xwhen 
	X.I ndbm 
	Xis used.
	X.NH 
	XOther Problems, Other Solutions
	X.PP
	XThe rewrite of 
	X.I makewhatis ,
	X.I catman ,
	Xand 
	X.I man
	Xto understand multiple man trees and to use a database
	Xfor topic-to-pathname mapping
	Xdid much to alleviate the most important problems
	Xin the existing man system, but several minor problems 
	Xremained.  Since this was a complete rewrite of the entire
	Xsystem, it seemed an appropriate time to address these as well.
	X.NH 2
	XIndexing Long Pages
	X.PP
	XSeveral of the most frequently consulted man pages on the system 
	Xhave grown beyond the scope of a quick reference guide, 
	Xinstead filling the function of a detailed user manual.
	XMan pages of this sort include those for shells, window
	Xmanagers, 
	Xgeneral purpose 
	Xutilities such as awk and perl,
	Xand the \s-1X11\s0 man pages. 
	XAlthough these man pages
	Xare internally organized into sections and subsections that
	Xare easily visible on a hard-copy printout, the on-line 
	Xman system could not recognize these internal
	Xsections.  Instead, the user was forced to search through pages
	Xof output looking for the section of the man page containing
	Xthe desired information.  
	X.PPe
	XTo alleviate this time-consuming tedium, the man program 
	Xwas taught to parse the 
	X.I nroff
	Xsource for man pages in order to build up an index of these sections
	Xand present them to the user on demand.  
	XSee Figure 3 for an excerpt from the 
	X.M ksh 1
	Xindex page, displayable via the new
	X.B \-i 
	Xswitch.
	X.BF "\fIksh\fP index excerpt"
	XIdx  Subsections in ksh.1                   Lines
	X 1   NAME                                       3
	X 2   SYNOPSIS                                  22
	X 3   DESCRIPTION                               15
	X 4   Definitions.                              43
	X 5   Commands.                                338
	X 6   Comments.                                  6
	X 7   Aliasing.                                107
	X 8   Tilde Substitution.                       47
	X 9   Command Substitution.                     28
	X10   Process Substitution.                     49
	X11   Parameter Substitution.                  645
	X12   Blank Interpretation.                     15
	X13   File Name Generation.                     87
	X.EF
	X.PP
	XThe 
	X.I /usr/man/idx*/
	Xdirectories
	Xserve the
	Xsame function for saved indices
	Xas
	X.I /usr/man/cat*/
	Xdirectories do for saved formatted man pages.
	XThese are regenerated as needed according the 
	Xthe same criteria used to regenerate the cat pages.
	XThey can be used to index into a given man page or
	Xto list a man page's subsections.  
	XTo begin at a given subsection, the user appends
	Xthe desired subsection to the name of the man page
	Xon the command line,
	Xusing a forward slash as a delimiter.   Alternatively, 
	Xthe user can just supply a trailing slash on the man page
	Xname, in which case they are presented with the index listing
	Xlike the one the
	X.B \-i
	Xswitch provides, then prompted for the section 
	Xin which they are interested.  A double slash indicates
	Xan arbitrary regular expression, not a section name.
	XThis is merely a short-hand notation for first running
	Xman and then typing 
	X.CW
	X/expr
	X.CE 
	Xfrom within the user's pager.
	XSee Figure 4
	Xfor example usages of the indexing features.  
	X.BF "Index Examples"
	Xman -i ksh      # show sections
	Xman ksh/        # show sections, prompt for which one
	X
	Xman ksh/tilde
	Xman ksh/8       # equivalent to preceding line
	X
	Xman ksh/file
	Xman ksh/generat # equivalent to preceding line
	Xman ksh/13      # so is this
	X
	Xman ksh//hangup # start at this string
	X.EF
	X.PP
	XThis indexing scheme is implemented by searching the index stored in 
	X.I /usr/man/idx1/ksh.1
	Xif it exists, or generated dynamically otherwise,
	Xfor the requested subsection.  A numeric subsection is
	Xeasily handled.  For strings, a case-insensitive
	Xpattern match is first
	Xmade anchored to the front of the string, then \(em failing
	Xthat \(em anywhere in the section description.  This way
	Xthe user doesn't need to type the full section title.
	XThe 
	X.I man 
	Xprogram starts up the pager with a 
	Xleading argument to begin at that section.  Both
	X.M more 1
	Xand 
	X.M less 1
	Xunderstand this particular notation.
	XIn the first
	Xexample given above, this would be
	X.sp
	X.CW
	X.ti +.5i
	Xless '+/^[ \et]*Tilde Substitution' /usr/man/cat1/ksh.1
	X.sp
	X.CE
	X.PP
	XOnce again, perl proved 
	Xuseful for coding this algorithm concisely.  The 
	Xsubroutine for doing this is given in 
	XFigure 5.  Given an expression such as ``5''
	Xor ``tilde'' or ``file'' and a pathname of the man 
	Xpage,
	X.I man
	Xloads
	Xan array of subsection
	Xindex titles and quickly retrieves the proper
	Xheader to pass on to the pager.  Perl's built-in 
	X.B grep
	Xroutine for selecting from arrays those elements 
	Xconforming to certain criteria made the coding easy.
	X.BF "Locate Subsection by Index"
	Xsub find_index {
	X    local($expr, $path) = @_;  # subroutine args
	X    local(@matches, @ssindex);
	X    @ssindex = &load_index($path);
	X
	X    if ($expr > 0) {            # test for numeric section
	X        return $ssindex[$expr];
	X    } else {
	X        if (@matches = grep (/^$expr/i, @ssindex)) {
	X            return $matches[0];
	X        } elsif (@matches = grep (/$expr/i, @ssindex)) {
	X            return $matches[0];
	X        } else {
	X            return '';
	X        }
	X    }
	X}
	X.EF
	X.NH 2
	XConditional Tbl and Eqn Inclusion
	X.PP
	XSeveral other relatively minor enhancements were made 
	Xto the man system in the course of its rewrite.  
	XOne of these
	Xwas to include calls to 
	X.M eqn 1
	Xand 
	X.M tbl 1
	Xwhere appropriate.  For instance, the \s-1X11\s0 man pages use 
	X.I tbl
	Xdirectives to construct a number of tables.
	XIt was not sufficient to supply 
	Xthese extra filters for all man pages.  Besides the
	Xslight performance degradation this would incur, a 
	Xmore serious problem exists: some systems have man pages that 
	Xcontain embedded
	X.LB .TS
	Xand 
	X.LB .TE
	Xdirectives; however, the data between them was not
	X.I tbl 
	Xinput, but rather its output.  They have already 
	Xbeen pre-processed in the unformatted versions.
	XTo do so again causes 
	X.I tbl 
	Xto complain bitterly, so heuristics to check for this condition
	Xwere built in to the function that determines which filters 
	Xare needed.
	X.PP
	XTo support tables and equations in man pages when viewed on-line,
	Xthe output must be run through
	X.M col 1
	Xto be legible.  Unfortunately, this strips the man pages
	Xof any bold font changes, which is undesirable because it is 
	Xoften important to distinguish between bold and italics for 
	Xclarity.  Therefore, before the formatted man page is fed to 
	X\fIcol\fP, all text in bold (between escape sequences)
	Xis converted to character-backspace-character combinations.  These
	Xcombinations
	Xcan be recognized by the user's pager as a character in 
	Xa bold font, just as underbar-backspace-character is recognized
	Xas an italic (or underlined) one.  Unfortunately, while 
	X.I less
	Xdoes recognize this convention, 
	X.I more
	Xdoes not.  By storing the formatted versions with all escape-sequences
	Xremoved, the user's pager can be invoked without a pipe to 
	X.I ul 
	Xor
	X.I col
	Xto fix the reverse line motion directives.  This provides the pager with
	Xa handle on the pathname of the cat page, allowing users to back up
	Xto the start of man pages, even exceptionally long ones, without exiting the 
	X.I man 
	Xprogram.  This would not be feasible if the pager were being fed
	Xfrom a pipe.
	X.NH 2
	XTroffing and Previewing Man Pages
	X.PP
	XNow that many sites have high-quality laser printers
	Xand bit-mapped displays, it seemed desirable for 
	X.I man
	Xto understand how to direct 
	X.I troff
	Xoutput to these.  A new option, \fB-t\fR,
	Xwas added to mean that 
	X.I troff 
	Xshould be used instead of 
	X\fInroff\fR.
	XThis way users can easily get pretty-printed versions of
	Xtheir man pages.
	X.PP
	XFor workstation or X-terminal users,
	X.I man
	Xwill recognize
	Xa \s-1TROFF\s0 environment variable or 
	Xcommand line argument to indicate an 
	Xalternate program to use for typesetting.  
	X(This presumes that the program recognizes 
	X.I troff
	Xoptions.)  This method often produces more legible output
	Xthan 
	X.I nroff
	Xwould, allows the user to stay in their office, and saves
	Xtrees as well.
	X.NH 2
	XSection Ordering
	X.PP 
	XThe same topic can occur in more than one section of 
	Xthe manual, but
	Xnot all users on the system want the same default
	Xsection ordering that 
	X.I man 
	Xuses to sort these possible pages.
	XFor instance,
	XC programmers who want to look up the man page for
	X.M sleep 3
	Xor 
	X.M stty 3
	Xfind that by default, 
	X.I man 
	Xgives them 
	X.M sleep 1
	Xand
	X.M stty 1
	Xinstead.  A \s-1FORTRAN\s0 programmer may want to see
	X.M system 3f ,
	Xbut instead gets 
	X.M system 3 .
	XTo accommodate these needs, the 
	X.I man 
	Xprogram will honor a \s-1MANSECT\s0 environment 
	Xvariable (or a 
	X.B \-S 
	Xcommand line switch) containing a list of section suffixes.
	XIf subsection or multi-character section ordering 
	Xis desired, this string should be colon-delimited.
	XThe default ordering is ``ln16823457po''.  
	XA C programmer might set his \s-1MANSECT\s0 to be ``231'' instead to access
	Xsubroutines and system calls before commands of the same name.
	XA \s-1FORTRAN\s0 programmer might prefer ``3f:2:3:1'' to get
	Xat the \s-1FORTRAN\s0 versions of subroutines before the standard
	XC versions.
	XSections absent from the \s-1MANSECT\s0 have a sorting priority 
	Xlower than any that are present.
	X.NH 2
	XCompressed Man Pages
	X.PP
	XBecause man pages are \s-1ASCII\s0 text files, they stand to benefit from 
	Xbeing run through the 
	X.M compress 1
	Xprogram.
	XCompressing man pages 
	Xtypically yields disk space savings of around 60%.
	XThe start-up time for decompressing the man page when 
	Xviewing is not enough to be bothersome.  However, running
	X.I makewhatis
	Xacross compressed man pages takes significantly longer
	Xthan running it over uncompressed ones, so some sites may wish to 
	Xkeep only the formatted pages compressed, not the unformatted
	Xones.
	X.PP
	XTwo different
	Xways of indicating compressed man pages seem to exist
	Xtoday.  One is where the man page itself has an attached
	X.B .Z 
	Xsuffix, yielding pathnames like
	X\fI/usr/man/man1/who.1.Z\fR.  
	XThe other way is to have 
	Xthe section directory contain the 
	X.B .Z 
	Xsuffix
	Xand have the files named normally, as in 
	X\fI/usr/man/man1.Z/who.1\fR.  
	XEither strategy is supported to ease porting 
	Xthe program to other systems.
	XAll programs dealing with man pages have been updated to 
	Xunderstand man pages stored in compressed form.
	X.NH 2
	XAutomated Consistency Checking
	X.PP
	XAfter receiving a half-dozen or so bug reports regarding 
	Xnon-existent man pages referenced in \s-1SEE\s0 \s-1ALSO\s0 sections,
	Xit became apparent that the only way to verify that all
	Xbugs of this nature had really been expurgated would be to automate the process.
	XThe 
	X.I cfman
	Xprogram
	Xverifies that man pages
	Xare mutually consistent in their \s-1SEE\s0 \s-1ALSO\s0 references.  It
	Xalso reports man pages whose
	X.LB .TH 
	Xline claims the man page is in
	Xa different place than 
	X.I cfman 
	Xfound it.  
	X.I Cfman
	Xcan locate man pages
	Xthat are improperly referenced rather than merely missing.  It 
	Xcan be run on an entire man tree, or on individual files as 
	Xan aid to developers writing new man pages.
	X.BF "Sample \fIcfman\fP run"
	Xat.1: cron(8) really in cron(1)
	Xbinmail.1: xsend(1) missing
	Xdbadd.1: dbm(3) really in dbm(3x)
	Xksh.1: exec(2) missing
	Xksh.1: signal(2) missing
	Xksh.1: ulimit(2) missing
	Xksh.1: rand(3) really in rand(3c)
	Xksh.1: profile(5) missing
	Xld.1: fc(1) really in fc(1f)
	Xsccstorcs.1: thinks it's in ci(1)
	Xuuencode.1c: atob(n) missing
	Xyppasswd.1: mkpasswd(5) missing
	Xfstream.3: thinks it's in fstream(3c++)
	Xftpd.8c: syslog(8) missing
	Xnfmail.8: delivermail(8) missing
	Xversatec.8: vpr(1) missing
	X.EF
	X.PP
	XThe amount of output produced by 
	X.I cfman 
	Xis startling.
	XA portion of the output of a sample run 
	Xis seen in Figure 6.
	XSome of its complaints are relatively harmless, such as
	X.I dbm
	Xbeing in section 
	X.B 3x
	Xrather than section 
	X\fB3\fR, because the 
	X.I man 
	Xprogram can find entries with the subsection left off.
	XHaving inconsistent
	X.LB .TH
	Xheaders is also harmless, although the printed
	Xman pages will have headers that do not reflect their
	Xfilenames on the disk.
	XHowever, entries that refer to pages that are truly absent, like
	X.M exec 2
	Xor 
	X.M delivermail 8 ,
	Xmerit closer attention.
	X.NH 2
	XMultiple Architecture Support
	X.PP
	XAs mentioned in the discussion of the need for a \s-1MANPATH\s0, 
	Xa site may for various reasons wish to maintain several 
	Xcomplete sets of man pages on the same machine.  Of course,
	Xa user could know to specify the full pathname of the 
	Xalternate tree on the command line 
	Xor set up their environment appropriately, but this is
	Xinconvenient.  Instead, it is preferable
	Xto specify the machine type on the command line and let
	Xthe system worry about pathnames.  
	X.ne 5
	XConsider these examples:
	X.br
	X.CW
	X.nf
	X.na
	X.in +.5i
	Xman vax csh
	Xapropos sun rpc
	Xwhatis tahoe man
	X.in -.5i
	X.CE
	X.ad 
	X.fi
	X.PP 
	XTo implement this, 
	Xwhen presented with more than one argument,
	X.I man
	X(in any of its three guises)
	Xchecks to see whether the first non-switch argument
	Xis a directory beneath
	X.I /usr/man .  
	XIf so, it automatically adjusts its \s-1MANPATH\s0 to that subdirectory.
	X.PP 
	XNot all vendors use precisely the same set of 
	X.M man 7
	Xmacros for formatting their man pages.  Furthermore, it's 
	Xhelpful to see in the header of the man page which manual
	Xit came from.  The 
	X.I man 
	Xprogram therefore looks for a local 
	X.I tmac.an
	Xfile in the root of the current man tree for alternate macro
	Xdefinitions.  If this file exists, it will be used rather than
	Xthe system defaults for passing to 
	X.I nroff
	Xor 
	X.I troff
	Xwhen reformatting.
	X.NH 
	XPerformance Analysis
	X.PP
	XThe 
	X.I man
	Xprogram is one that is often used on the system, 
	Xso users are sensitive to any significant degradation
	Xin response time.  Because it is written in perl (an 
	Xinterpreted language) this was cause for concern.
	XOn a \s-1CONVEX C2\s0, the C version runs faster when only
	Xone element is present in the \s-1MANPATH\s0.
	XHowever, when the \s-1MANPATH\s0 contains four
	Xelements, the C version bogs down considerably because of
	Xthe large number of 
	X.M access 2
	Xcalls it must make.  
	X.PP
	XThe start-up time on the parsing
	Xof the script, now just over 1300 lines long, is around
	X0.6 seconds.  This time can be reduced by dumping the 
	Xparse tree that perl generates to disk and executing that instead.
	XThe expense of this action is disk space, as the current implementation
	Xrequires that the whole perl interpreter be included in the 
	Xnew executable, not just the parse tree.  This method
	Xyields performance superior to that of the C version,
	Xirrespective of the number of components in the user's \s-1MANPATH\s0,
	Xexcept occasionally on the initial run.  This is because the 
	Xprogram needs to be loaded
	Xinto memory the first time.  If perl itself is installed ``sticky''
	Xso it is memory resident, start-up time improves considerably.  
	XIn any case, the 
	Xtotal variance (on a \s-1CONVEX\s0) is 
	Xless than two seconds in the worst case (and often 
	Xunder one second), so it was deemed acceptable, particularly
	Xconsidering the additional functionality the perl version offers.
	X.PP
	XNothing in the algorithms employed in the
	X.I man 
	Xprogram require that it be written in perl;
	Xit was just easier this way.  It could be rewritten in C 
	Xusing 
	X.M dbm 3x
	Xroutines, although the development time would probably 
	Xbe much longer.  
	X.PP
	XThe 
	X.I makewhatis
	Xprogram was originally a conglomeration of man calls to various individual
	Xutilities such as 
	X\fIsed\fP,
	X\fIexpand\fP,
	X\fIsort\fP, and others.  The perl rewrite runs in less than half the time
	Xof the original, and does a much better job.  There are two
	Xreasons for the speed increase.  The first is the cost of the numerous 
	X.M exec 2
	Xcalls made via the shell script used by the old version of 
	X.I makewhatis .
	XThe second is that 
	Xperl is optimized for text processing, which is most of what
	X.I makewhatis
	Xis doing.
	X.PP
	XTotal development time was only a few weeks, 
	Xwhich was much shorter than originally anticipated.  The short
	Xdevelopment cycle was chiefly attributable to
	Xthe ease of text processing in perl, the many built-in 
	Xroutines for doing things that in C would have required 
	Xextensive library development, and, last but not at all least,
	Xthe omission of the compilation stage in the normal edit-compile-test
	Xcycle of development when working with non-interpreted languages.
	X.NH
	XConclusions
	X.PP
	XThe system described above has been in operation for the last
	Xsix months on a large local network consisting of three dozen 
	X\s-1CONVEX\s0 machines, a token \s-1VAX\s0, quite a few \s-1HP\s0 workstations
	Xand servers, and innumerable Sun workstations, all running different
	Xflavors of \s-1UNIX\s0.  Despite this heterogeneity,
	Xthe same code runs on all systems without alterations.
	XFew problems have been seen, and those that did arise were quickly
	Xfixed in the scripts, which could be immediately redistributed
	Xto the network.  The principal project goals of improved functionality, 
	Xextensibility, and execution time were adequately met, and the 
	Xexperience of rewriting a set of standard \s-1UNIX\s0 utilities
	Xin perl was an educational one.
	XMan pages stand a much better chance of being internally consistent
	Xwith each other.
	XResponse from the user and development community has 
	Xbeen favorable. They have
	Xbeen relieved by the many bug fixes and pleasantly surprised
	Xby the new functionality.  The suite of man programs will replace
	Xthe old man system in the next release of \s-1CONVEX\s0 utilities.
	X.\" Should be .BB here but that seems to mutilate my last BF figure
	X.sp 3
	X.QP
	X.I 
	X.SM
	XTom Christiansen left the University of Wisconsin with an \s-1MS-CS\s0
	Xin 1987
	Xwhere he had been a system administrator for 6 years to join
	X\s-1CONVEX\s0
	XComputer Corporation in Richardson, Texas.
	XHe is a software development engineer
	Xin the Internal Tools Group there, designing software tools
	Xto streamline software development and systems administration
	Xand to improve overall system security.
	X.BE
SHAR_EOF
if test 34978 -ne "`wc -c < 'man.ms'`"
then
	echo shar: "error transmitting 'man.ms'" '(should have been 34978 characters)'
fi
chmod 664 'man.ms'
fi
echo shar: "extracting 'COPYING'" '(151 characters)'
if test -f 'COPYING'
then
	echo shar: "will not over-write existing file 'COPYING'"
else
sed 's/^	X//' << \SHAR_EOF > 'COPYING'
	X#	You are free to use, modify, and redistribute these scripts
	X#	as you wish for non-commercial purposes provided that this 
	X#	notice remains intact.  
SHAR_EOF
if test 151 -ne "`wc -c < 'COPYING'`"
then
	echo shar: "error transmitting 'COPYING'" '(should have been 151 characters)'
fi
chmod 664 'COPYING'
fi
echo shar: "extracting 'man'" '(39119 characters)'
if test -f 'man'
then
	echo shar: "will not over-write existing file 'man'"
else
sed 's/^	X//' << \SHAR_EOF > 'man'
	X#!/usr/local/bin/perl 
	X# 
	X# man - perl rewrite of man system
	X# tom christiansen <tchrist@convex.com>
	X#
	X# Copyright 1990 Convex Computer Corporation.
	X# All rights reserved.
	X#
	X# --------------------------------------------------------------------------
	X# begin configuration section
	X#
	X# this should be adequate for CONVEX systems.  if you copy this script 
	X# to non-CONVEX systems, or have a particularly outre local setup, you may
	X# wish to alter some of the defaults.
	X# --------------------------------------------------------------------------
	X
	X$PAGER = $ENV{'PAGER'} || 'more';
	X
	X# assume "less" pagers want -sf flags, all others must accept -s.
	X# note: some less's prefer -r to -f.  you might also add -i if supported.
	X#
	X$is_less = $PAGER =~ /^\S*less(\s+-\S.*)?$/;
	X$PAGER    .= $is_less ? ' -si' : ' -s';       # add -f if using "ul"
	X
	X# man roots to look in; you would really rather use a separate tree than 
	X# manl and mann!  see %SECTIONS and $MANALT if you do.
	X$MANPATH  = &config_path;
	X
	X# default section precedence
	X$MANSECT  = $ENV{'MANSECT'} || 'ln16823457po';
	X
	X# colons optional unless you have multi-char section names
	X# note that HP systems want this:
	X#	$MANSECT  = $ENV{'MANSECT'} || '1:1m:6:8:2:3:4:5:7';
	X
	X# alternate architecture man pages in 

tchrist@convex.COM (Tom Christiansen) (01/08/91)

	X# ${MANALT}/${machine}/man(.+)/*.\11*
	X$MANALT = $ENV{'MANALT'} || '/usr/local/man';
	X
	X# default program for -t command 
	X$TROFF    = $ENV{'TROFF'} || 'nitroff';
	X
	X$NROFF    = 'nroff';
	X$NROFF_CAN_BOLD = 0;	# if nroff puts out bold as H\bH
	X
	X# this are used if filters are needed
	X$TBL	  = 'tbl';
	X$NTBL 	  = "$TBL -D";	# maybe you need -TX instead
	X$NEQN	  = 'neqn';
	X$EQN	  = 'eqn';
	X$SED	  = 'sed';
	X
	X# define this if you don't have/want UL;
	X# without ul, you probably need COL defined unless your PAGER is very smart
	X# you also must use col instead of ul if you've any tbl'd man pages, such 
	X# as from the X man pages or the eqnchar.7 page.
	X$COL	  = 'col';  
	X$UL	  = '';		# set to '' if you haven't got ul
	Xdie 'need either $UL or $COL' unless $UL || $COL;
	X
	X# need these for .Z files or dirs
	X$COMPRESS = 'compress';
	X$ZCAT	  = 'zcat';
	X$CAT	  = 'cat';
	X
	X# define COMPRESS_DIR if pages might have moved to manX.Z/page.X (like HPs)
	X$COMPRESS_DIR = 1;
	X# define COMPRESS_PAGE if pages might have moved to manX/page.X.Z  (better)
	X$COMPRESS_PAGE = 1;
	X
	X# Command to format man pages to be viewed on a tty or printed on a line printer
	X$CATSET	  = "$NROFF -h -man -";
	X
	X$CATSET  .= " | $COL" if $COL;
	X
	X# Command to typeset a man page
	X$TYPESET  = "$TROFF -man";
	X
	X
	X# flags: GNU likes -i, BSD doesn't; both like -h, but BSD doesn't document it
	X# if you don't put -i here, i'll make up for it later the hard way
	X$EGREP = '/usr/local/bin/egrep';	
	Xif (-x $EGREP) {
	X    $EGREP .= ' -i -h';
	X} else {
	X    $EGREP = '/usr/bin/egrep';
	X    unless (-x $EGREP) {
	X	$EGREP = '';
	X    } else {
	X	$EGREP .= ' -h';
	X    }
	X} 
	X
	X# sections that have verbose aliases
	X# if you change this, change the usage message
	X#
	X# if you put any of these in their own trees, comment them out and make 
	X# a link in $MANALT so people can still say 'man local foo'; for local,
	X#	cd $MANALT; ln -s . local
	X# for the other trees (new, old, public) put either them or links
	X# to them in $MANALT
	X#
	X%SECTIONS = (				
	X    'local',	'l',
	X    'new',	'n',
	X    'old',	'o',
	X    'public',	'p' );
	X
	X# turn this on if you want linked (via ".so" or otherwise) man pages
	X# to be found even if the thing they are linked to doesn't know it's
	X# being linked to -- that is, its NAME section doesn't have reference
	X# to it.  eg, if you call a man page 'gnugrep' but it's own NAME section
	X# just calls it grep, then you need this.  usually a good idea.
	X#
	X$STUPID_SO	= 1;  
	X
	X# --------------------------------------------------------------------------
	X# end configuration section
	X# --------------------------------------------------------------------------
	X
	X# CONVEX RCS keeps CHeader; others may prefer Header
	X($bogus, $version) = split(/:\s*/,'$CHeader: man 0.40 91/01/07 15:40:15 $',2);
	Xchop($version); chop($version);
	X
	Xrequire 'getopts.pl';
	X
	X# could do this via ioctl(0,$TIOCGETP,$sgtty) if I were really concerned
	X#
	X$rows = ($ENV{'TERMCAP'} =~ /:li#(\d+):/) ? $1 : 24;
	X
	X%options = (
	X    'man',	'T:m:P:M:c:s:S:fkltvwdguhaiDK',
	X    'apropos',	'm:P:MvduaK',
	X    'whatis',	'm:P:M:vduh',
	X    'whereis',	'm:P:M:vduh'
	X);
	X
	X($program = $0) =~ s,.*/,,;
	X
	X$apropos = $program eq 'apropos';
	X$whatis  = $program eq 'whatis';
	X$whereis = $program eq 'whman';
	X$program = 'man' unless $program;
	X
	X&Getopts($options = $options{$program}) || &usage;
	X
	Xif ($opt_u) {
	X    &version if $opt_v;
	X    &usage;
	X    # not reached
	X} 
	X
	Xif ($opt_v) {
	X    &version;
	X    exit 0;
	X}
	X
	X&usage if $#ARGV < 0;
	X
	X$MANPATH = $opt_P 	if $opt_P;	# backwards contemptibility
	X$MANPATH = $opt_M 	if $opt_M;
	X
	X$want_section = $opt_c 	if $opt_c;	# backwards contemptibility
	X$want_section = $opt_s 	if $opt_s;
	X
	X$hard_way = $opt_h	if $opt_h;
	X
	Xif ($opt_T) {
	X    $opt_t = 1;
	X    $TYPESET =~ s/$TROFF/$opt_T/;
	X    $TROFF = $opt_T;
	X} 
	X
	X$MANPATH = "$MANALT/$opt_m"		# want different machine type (undoc)
	X			if $machine = $opt_m;
	X
	X$MANSECT = $opt_S	if $opt_S;	# prefer our own section ordering
	X
	X$whatis = 1		if $opt_f;
	X$apropos = 1		if $opt_k || $opt_K;
	X$fromfile = 1		if $opt_l;
	X$whereis = 1  		if $opt_w;
	X$grepman = 1		if $opt_g;	
	X$| = $debug = 1		if $opt_d;
	X$full_index = 1 	if $opt_i;
	X$show_all = 1		if $opt_a;
	X$stripBS = 1		if $opt_D;
	X
	X$roff = $opt_t ? 'troff' : 'nroff';   # for indirect function call
	X
	X
	X# maybe they said something like 'man vax ls'
	Xif ($#ARGV > 0) {
	X    local($machdir) = $MANALT . '/' . $ARGV[0];
	X    if (-d $machdir) {
	X	$MANPATH = $machdir;
	X	$machine = shift;
	X    } 
	X} 
	X
	X@MANPATH = split(/:/,$MANPATH);
	X
	X# assign priorities to the sections he cares about
	X# the nearer the front the higher the sorting priority
	X$secidx = 0;
	X$delim = ($MANSECT =~ /:/) ? ':' : ' *';
	Xfor (reverse split(/$delim/, $MANSECT)) {
	X    if ($_ eq '') {
	X	warn "null section in $MANSECT\n";
	X	next;
	X    } 
	X    $MANSECT{$_} = ++$secidx;
	X} 
	X
	X
	Xif ($whatis) {
	X    &whatis;
	X} elsif ($apropos) {
	X    &apropos;
	X} elsif ($whereis) {
	X    &whereis;
	X} elsif ($grepman) {
	X    &grepman;
	X} else {
	X    &man;
	X} 
	X
	Xexit $status;
	X
	X# --------------------------------------------------------------------------
	X# fill out @whatis array with all possible names of whatis files
	X# --------------------------------------------------------------------------
	Xsub genwhatis {
	X    local($elt,$whatis);
	X
	X    for $elt (@MANPATH) {
	X	$whatis = "$elt/whatis";
	X	push(@whatis, $whatis) if -f $whatis;
	X    } 
	X
	X    die "$program: No whatis databases found, please run makewhatis\n" 
	X	if $#whatis < 0;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# run whatis (man -f)
	X# --------------------------------------------------------------------------
	Xsub whatis {
	X    local($target, %seeking, $section, $desc, @entries);
	X
	X    &genwhatis;
	X
	X    for $target (@ARGV) { $seeking{$target} = 1; } 
	X
	X    if ($hard_way) {
	X	&slow_whatis;
	X    } else { 
	X	&fast_whatis;
	X    }
	X
	X    for $target (keys %seeking) {
	X	print "$program: $target: not found.\n";
	X	$status = 1;
	X    } 
	X} 
	X
	X# --------------------------------------------------------------------------
	X# do whatis lookup against dbm file(s)
	X# --------------------------------------------------------------------------
	Xsub fast_whatis {
	X    local($entry, $cmd, $page, $section, $desc, @entries);
	X
	X    for $INDEX (@whatis) {
	X	unless (-f "$INDEX.pag" && dbmopen(INDEX,$INDEX,0444)) {
	X	    warn "$program: No dbm file for $INDEX: $!\n" if $debug; 
	X	    #$status = 1;
	X	    if (-f $INDEX) {
	X		local(@whatis) = ($INDEX);  # dynamic scoping obfuscation
	X		&slow_whatis;
	X	    }
	X	    next;
	X	} 
	X       	for $target (@ARGV) {
	X	    local($ext);
	X	    @entries = &quick_fetch($target,'INDEX');
	X	    next if $#entries < 0;
	X	    # $target =~ s/([^\w])/\\$1/g;
	X	    for $entry (@entries) {
	X		($cmd, $page, $section, $desc) = split(/\001/, $entry);
	X		#  STUPID_SO is one that .so's that reference things that
	X		#  don't know they are being referenced.  STUPID_SO may cause
	X		#  some peculiarities.
	X		unless ($STUPID_SO) {
	X		    next unless $cmd =~ /$target/i || $cmd =~ /\.{3}/;
	X		}
	X
	X		delete $seeking{$target};
	X		($ext) = $page =~ /\.([^.]*)$/;
	X		printf("%-20s - %s\n", "$cmd ($ext)", $desc);
	X	    }
	X	} 
	X	dbmclose(INDEX);
	X    } 
	X    
	X} 
	X
	X# --------------------------------------------------------------------------
	X# do whatis lookup the hard way
	X# --------------------------------------------------------------------------
	Xsub slow_whatis {
	X    local($query);
	X    local($WHATIS);
	X
	X    for (@ARGV) { s/([^\w])/\\$1/g; } 
	X
	X    $query = '^[^-]*\b?(' . join('|',@ARGV) . ')\b[^-]* -';
	X
	X    if ($EGREP)  {
	X	if (&run("$EGREP '$query' @whatis")) {
	X	    # pity can't tell which i found
	X	    %seeking = ();
	X	}
	X    } else {
	X	foreach $WHATIS (@whatis)  {
	X	    unless (open WHATIS) {
	X		warn "can't open $WHATIS: $!\n";
	X		next;
	X	    } 
	X	    while (<WHATIS>) {
	X		next unless /$query/i;
	X		($target = $+) =~ y/A-Z/a-z/;
	X		delete $seeking{$target};
	X		print;
	X	    } 
	X	    close WHATIS;
	X	} 
	X    } 
	X} 
	X
	X# --------------------------------------------------------------------------
	X# run apropos (man -k)
	X# --------------------------------------------------------------------------
	Xsub apropos {
	X    local($_, %seeking, $target, $query);
	X    &genwhatis;  
	X
	X    # fold case on apropos args
	X    for (@ARGV) { 
	X	y/A-Z/a-z/; 
	X	$seeking{$_} = 1; 
	X	s/(\W)/\\$1/g unless $opt_K;
	X    } 
	X    $query = join('|',@ARGV);
	X
	X
	X    if ($EGREP)  {
	X	# need to fake a -i flag?
	X	unless ($EGREP =~ /-\w*i/) {
	X	    local($C);
	X	    local(@pat) = split(//,$query);
	X	    for (@pat) {
	X		($C = $_) =~ y/a-z/A-Z/ && ($_ = '[' . $C . $_ . ']');
	X	    } 
	X	    $query = join('',@pat);
	X	} 
	X	if (&run("$EGREP '$query' @whatis | $PAGER")) {
	X	    %seeking = ();
	X	} 
	X    } else {  # use perl
	X	foreach $WHATIS (@whatis) {
	X	    unless (open WHATIS) {
	X		warn "can't open $WHATIS: $!\n";
	X		next;
	X	    } 
	XWHATIS:	    while (<WHATIS>) {
	X		next unless /$query/io;	      # /o ok, because only called once
	X		$target = $+;
	X		$target =~ s/\\//g;
	X		delete $seeking{$query};
	X		print;
	X	    } 
	X	    close WHATIS;
	X	} 
	X
	X    } 
	X
	X    for $target (keys %seeking) {
	X	warn "$program: $target: nothing appropriate\n";
	X	$status = 1;
	X    }
	X}
	X
	X# --------------------------------------------------------------------------
	X# print out usage message via pager and exit
	X# --------------------------------------------------------------------------
	Xsub usage {
	X    unless ($opt_u) {
	X	warn "usage: $program [-flags] topic ...\n";
	X	warn "        (use -u for long usage message)\n";
	X    } else {
	X	open (PIPE, "| $PAGER");
	X	print PIPE <<USAGE;  # in case he wants a page
	XUSAGE SUMMARY: 
	X    man [-flags] [section] page[/index] ...
	X	(section is [1-8lnop], or "new", "local", "public", "old")
	X	(index is section or subsection header)
	X
	X    man [-flags] -f topic ...  
	X	(aka "whatis")
	X
	X    man [-flags] -k keyword ...
	X	(aka "apropos")
	X
	XFLAGS: (most only make sense when invoked as 'man')
	X    -a		show all possible man pages for this topic
	X    -l file	do man processing on local file
	X    -f topic	list table of contents entry for topic
	X    -k keyword	give table of contents entries containing keyword
	X    -K pattern  as -K but allow regexps
	X    -g pattern  grep through all man pages for patterns
	X    -w topic    which files would be shown for a given topic
	X    -i topic    show section and subsection index for use with topic/index
	X
	X    -M path	use colon-delimited man path for searching (also as -P)
	X    -S sects	define new section precedence 
	X
	X    -t		troff the man page
	X    -T path	call alternate typesetter on the man page
	X
	X    -d		print out all system() commands before running them
	X    -h		do all lookups the hard way, ignoring any DBM files
	X    -u		this message
	X    -v		print version string
	X    -D		strip backspaces from output
	X
	XENVIRONMENT:
	X    \$PAGER	pager to pipe terminal-destined output through 
	X    \$MANPATH	like -M path
	X    \$MANSECT	like -S sects
	X    \$MANALT	used for alternate hardware types (or obsolete -m flag)
	X    \$TROFF	like -T path
	X
	XCURRENT DEFAULTS:
	X    PAGER	$PAGER
	X    MANPATH	$MANPATH
	X    MANSECT	$MANSECT
	X    MANALT	$MANALT
	X    TROFF	$TROFF
	X
	XNOTES:  (\$manroot is each component in \$MANPATH)
	X    * If \$manroot/whatis DBM files do not exist, a warning will be 
	X	printed (if -d flag) and -h will be assumed for that \$manroot only.
	X    * If \$manroot/tmac.an exists, it will be used for formatting 
	X	instead of the normal -man macros.
	X    * Man pages may be compressed either in (for example) man1.Z/who.1 
	X        or man1/who.1.Z; cat pages will go into corresponding places.
	X    * If the man page contains .EQ or .TS directives, eqn and/or tbl
	X        will be invoked as needed at format time.
	XUSAGE
	X	close PIPE;
	X    }
	X    warn "couldn't run long usage message thru $PAGER?!?!\n" if $?;
	X    exit 1;
	X}
	X
	X# --------------------------------------------------------------------------
	X# lookup a given key in the given man root; returns list of hits
	X# --------------------------------------------------------------------------
	Xsub fetch {
	X    local($key,$root) = @_;
	X    local(%recursed);
	X
	X    return $dbmopened{$root}
	X	? &quick_fetch($key,$dbm{$root})
	X	: &slow_fetch($key,$root);
	X}
	X
	X# --------------------------------------------------------------------------
	X# do a quick fetch of a key in the dbm file, recursing on indirect references
	X# --------------------------------------------------------------------------
	Xsub quick_fetch {
	X    local($key,$array) = @_;
	X    local(@retlist) = ();
	X    local(@tmplist) = ();
	X    local($_, $entry);
	X    local($name, $ext);
	X    local(@newlist);
	X
	X    return @retlist unless $entry = eval "\$$array".'{$key};';
	X
	X    if ($@) { chop $@; die "bad eval: $@"; }
	X
	X    @tmplist = split(/\002/, $entry);
	X    for (@tmplist) {
	X	if (/\001/) {
	X	    push(@retlist, $_);
	X	} else {
	X	    ($name, $ext) = /(.+)\.([^.]+)/;
	X	    push(@retlist, 
	X		grep(/[^\001]+\001[^\001]+\001${ext}\001/ || /[^\001]+${ext}\001/,
	X			&quick_fetch($name, $array)))
	X		    unless $recursed{$name}++;
	X	# explain and diction are near duplicate man pages referencing
	X	# each other, requiring the $recursed check.  one should be removed
	X	}
	X    } 
	X    return @retlist;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# do a slow fetch for target using perl's globbing notation
	X# --------------------------------------------------------------------------
	Xsub slow_fetch {
	X    local($target,$root) = @_;
	X    local($glob, $stem, $entry);
	X
	X    if ($want_section) {
	X	if ($MANSECT{$want_section}) {
	X	    $stem = $want_section;
	X	} else {
	X	    $stem = substr($want_section,0,1);
	X        } 
	X	$glob = "man$stem*";
	X    } else {
	X	$glob = 'man*';
	X    } 
	X    $glob = "$root/$glob/$target.*";
	X    return <${glob}>;
	X}
	X
	X# --------------------------------------------------------------------------
	X# run 'man -w'
	X# --------------------------------------------------------------------------
	Xsub whereis {
	X    local($target, @files);
	X
	X    foreach $target (@ARGV) {
	X	@files = &find_files($target);
	X	if ($#files < $[) {
	X	    warn "$program: $target not found\n";
	X	    $status = 1;
	X	} else {
	X	    print "$target: ";
	X	    for (@files) { print " ", &verify($_); }
	X	    print "\n";
	X	}
	X    } 
	X} 
	X
	X
	X# --------------------------------------------------------------------------
	X# what are the file names matching this target?
	X# --------------------------------------------------------------------------
	Xsub find_files {
	X    local($target) = @_;
	X    local($root, $entry);
	X    local(@retlist) = ();
	X    local(@tmplist) = ();
	X    local(@entries) = ();
	X    local($tar_regx);
	X    local($found) = 0;
	X    # globals: $vars, $called_before, %dbm, $hard_way (kinda)
	X
	X    $vars = 'dbm00';  # var for magic autoincrementation
	X
	X    ($tar_regx = $target) =~ s/(\W)/\\$1/g;  # quote meta
	X
	X    if (!$hard_way && !$called_before++) {
	X	# generate dbm names
	X	for $root (@MANPATH) {
	X	    $dbm{$root} = $vars++; # magic incr
	X	    $string = "dbmopen($dbm{$root},\"$root/whatis\",0444);";
	X	    unless (-f "$root/whatis.pag" && eval $string) {
	X		if ($@) { 
	X		    chop $@;
	X		    warn "Can't eval $string: $@";
	X		} else {
	X		    warn "No dbm file for $root/whatis: $!\n" if $debug;
	X		}
	X		#$status = 1;
	X		next;
	X	    } 
	X	    $dbmopened{$root} = 1;
	X	}
	X    } 
	X
	X    for $root (@MANPATH) {
	X	local($fullname);
	X	@tmplist = ();
	X	if ($hard_way || !$dbmopened{$root})  {
	X	    next unless -d $root;
	X	    warn "slow fetch on $target in $root\n" if $debug;
	X	    @tmplist = &slow_fetch($target,$root);
	X	} else {
	X	    @entries = &fetch($target,$root);
	X	    next if $#entries < 0;
	X
	X	    for $entry (sort @entries) {
	X		($cmd, $page, $section, $desc) = split(/\001/, $entry);
	X
	X		# STUPID_SO is so that .so's that reference things that
	X		# don't know they are being referenced.  STUPID_SO may
	X		# cause peculiarities.
	X		unless ($STUPID_SO) {
	X		    next unless $cmd =~ /$tar_regx/i || $cmd =~ /\.{3}/;
	X		}
	X		push(@tmplist, "$root/man$section/$page");
	X	    }
	X	}
	X	push(@retlist, sort bysection @tmplist);
	X	last if $#retlist >= 0 && $hard_way;
	X    }
	X#    unless (@retlist || $hard_way) {
	X#	# shameless (ab?)use of dynamic scoping
	X#	local($hard_way) = 1;
	X#	warn "recursing on find_files\n" if $debug;
	X#	return &find_files($target);
	X#    } 
	X     return &trimdups(@retlist);
	X} 
	X
	X# --------------------------------------------------------------------------
	X# run a normal man command
	X# --------------------------------------------------------------------------
	Xsub man {
	X    local($target,$page);
	X    $isatty = -t STDOUT;
	X
	X    &get_section;
	X
	X    while ($target = shift(@ARGV)) {
	X	undef $idx_topic;
	X
	X	if (!$fromfile && $target =~ m!^([^/]+)/(.*)!) {
	X	    if (!$isatty) {
	X		warn "$program: no tty, so no pager to prime with index\n";
	X		$target = $1;
	X	    }  else {
	X		($target, $idx_topic) = ($1, $2);
	X	    } 
	X	} else {
	X	    undef $idx_topic;
	X	} 
	X
	X	if ($show_all) {
	X	    local(@pages);
	X	    local($was_defined) = defined $idx_topic;
	X	    @pages = &find_files($target);
	X	    if (!@pages) {
	X		&no_entry($target);
	X		next;
	X	    } 
	X	    while ($tpage = shift @pages) {
	X		undef $idx_topic unless $was_defined;
	X		do $roff(&verify($tpage));
	X		&prompt_RTN("to read $pages[0]") 
	X		    if $roff eq 'nroff' && @pages;
	X	    } 
	X	} else {
	X	    $target = &get_page($target) unless $fromfile;
	X	    do $roff($target) if $target;
	X	}
	X	&prompt_RTN("to read man page for $ARGV[0]") 
	X	    if $roff eq 'nroff' && @ARGV;
	X    } 
	X} 
	X
	X# --------------------------------------------------------------------------
	X# find out if he wants a special section and save in $want_section
	X# --------------------------------------------------------------------------
	Xsub get_section {
	X    if (!$want_section) {
	X	local($section) = $ARGV[0];
	X	$section =~ tr/A-Z/a-z/;
	X
	X	if ($want_section = $SECTIONS{$section}) {
	X	    shift @ARGV;
	X	}  elsif (defined($MANSECT{$section}) || $section =~ /^\d\S*$/i) { 
	X	    $want_section = shift @ARGV;
	X	} 
	X    }
	X    $want_section =~ tr/A-Z/a-z/;
	X
	X    die "But what do you want from section $want_section?\n" 
	X	if $want_section && $#ARGV < 0;
	X}
	X
	X# --------------------------------------------------------------------------
	X# pick the first page matching his target and search orders
	X# --------------------------------------------------------------------------
	Xsub get_page {
	X    local($target) = @_;
	X    local(@found, @want);
	X
	X    unless (@found = &find_files($target)) {
	X	&no_entry($target);
	X	return '';
	X    } 
	X
	X    if (!$want_section) {
	X	@want = @found;
	X    } else {{
	X	local($patsect); # in case it's section 3c++ 
	X	($patsect = $want_section) =~ s/(\W)/\\$1/g;
	X
	X	# try exact match first
	X	last if @want = grep (/\.$patsect$/, @found);
	X
	X	# otherwise how about a subsection
	X	last if @want = grep (/\.$patsect[^.]*$/, @found);
	X
	X	# maybe it's in the wrong place (mano is notorious for this)
	X	last if @want = grep (/man$patsect[^.]*\//, @found);
	X
	X	&no_entry($target);
	X	return '';
	X    }}
	X
	X    do {
	X	($found = &verify($want[0])) || shift @want;
	X    } until $found || $#want < 0;
	X
	X    return $found;
	X}
	X
	X# --------------------------------------------------------------------------
	X# figure out full path name of man page, which may have been compressed
	X# --------------------------------------------------------------------------
	Xsub verify {
	X    local($path) = @_;
	X    local($orig) = $path;
	X
	X    return $path if -f $path;
	X
	X    if ($COMPRESS_PAGE) {
	X	$path .= '.Z';
	X	return $path if -f $path;
	X	$path =~ s/.Z//;
	X    } 
	X
	X    if ($COMPRESS_DIR) {
	X	$path =~ s-(/[^/]*)$-.Z$1-;
	X	return $path if -f $path;
	X    } 
	X
	X    warn "$program: $orig has disappeared -- rerun makewhatis\n";
	X    $status = 1;
	X    return '';
	X}
	X
	X
	X# --------------------------------------------------------------------------
	X# whine about something not being found
	X# --------------------------------------------------------------------------
	Xsub no_entry {
	X    print STDERR "No manual entry for $_[0]";
	X    if ($machine || $want_section) {
	X	print STDERR " in";
	X	print STDERR " section $want_section of" if $want_section;
	X	print STDERR " the";
	X	print STDERR " $machine" if $machine;
	X	print STDERR " manual";
	X    }
	X    print STDERR ".\n";
	X    $status = 1;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# order by section.  if the complete extension has a section
	X# priority, use that.  otherwise use the first char of extension
	X# only.  undefined priorities are lower than any defined one.
	X# --------------------------------------------------------------------------
	Xsub bysection {
	X    local ($e1, $e2, $p1, $p2, $s1, $s2);
	X
	X    ($s1, $e1) = $a =~ m:.*/man([^/]+)/.*\.([^.]+)(\.Z)?$:;
	X    ($s2, $e2) = $b =~ m:.*/man([^/]+)/.*\.([^.]+)(\.Z)?$:;
	X
	X    $e1 = $s1 if $e1 !~ /^${s1}.*/;
	X    $e2 = $s2 if $e2 !~ /^${s2}.*/;
	X
	X    $p1 = $MANSECT{$e1} || $MANSECT{substr($e1,0,1)};
	X
	X    $p2 = $MANSECT{$e2} || $MANSECT{substr($e2,0,1)};
	X
	X    $p1 == $p2 ? $a cmp $b : $p2 <=> $p1;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# see whether they want to start at a subsection, then run the command
	X# --------------------------------------------------------------------------
	Xsub run_topic {
	X    local($_);
	X    local($menu_rtn) = defined $idx_topic && $idx_topic eq '';
	X    {
	X	&append_sub_topic;
	X	last if $idx_topic eq "\004";
	X	if ($idx_topic eq '0') {
	X	    $menu_rtn = 0;
	X	    $idx_topic = '';
	X	    $command =~ s: '\+/[^']*'::;
	X	}
	X	$fromfile ? &reformat($command) : &run($command);
	X	if ($menu_rtn) {
	X	    $idx_topic = '';
	X	    &prompt_RTN("to return to the index");
	X	    $command =~ s! '\+/.*$!!;
	X	    redo;
	X	} 
	X    }
	X    
	X} 
	X
	X# --------------------------------------------------------------------------
	X# run through the typesetter
	X# --------------------------------------------------------------------------
	Xsub troff {
	X    local ($file) = $_[0];
	X    local ($command);
	X    local ($manroot);
	X    local ($macros);
	X
	X    ($manroot) = $file =~ m,^(.*)/man([^\.]*)(\.Z)?/([^/]*),;
	X
	X    $command = ((($file =~ m:\.Z:) 
	X			? $ZCAT 
	X			: $CAT) 
	X		. " < $file | $TYPESET");
	X
	X    $command =~ s,-man,$manroot/tmac.an, if -e "$manroot/tmac.an";
	X
	X    &insert_filters($command,$file);
	X    &run($command);
	X} 
	X
	X# --------------------------------------------------------------------------
	X# just run a regular nroff, possibly showing the index first.
	X# --------------------------------------------------------------------------
	Xsub nroff {
	X    local($manpage) = $_[0];
	X    local($catpage);
	X    local($tmppage);
	X    local($command);
	X    local(@saveidx);
	X    local($manroot);
	X    local($macros);
	X    local($intmp);
	X    local(@st_cat, @st_man);
	X
	X    die "trying to nroff a null man page" if $manpage eq '';
	X
	X    umask 022;
	X
	X    if ($full_index) {
	X	&show_index($manpage);
	X	return;
	X    } 
	X    if ($fromfile) {
	X	$command = (($manpage =~ m:\.Z/:) ? $ZCAT : $CAT)
	X			. " < $manpage | $CATSET";
	X	&insert_filters($command, $manpage);
	X    } else {
	X	require 'stat.pl' unless defined &Stat;   
	X	# compiled version has this already
	X
	X
	X	($catpage = $manpage) 
	X	    =~ s,^(.*)/man([^\.]*)(\.Z)?/([^/]*)$,$1/cat$2/$4,;
	X
	X	$manroot = $1;
	X
	X	# Does the cat page exist?
	X	if (! -f $catpage && $COMPRESS_DIR){
	X	    # No, maybe it is compressed?
	X	    if (-f "$1/cat$2.Z/$4"){
	X		# Yes it was.
	X		$catpage = "$1/cat$2.Z/$4";
	X	    } else {
	X		# Nope, the cat file doesn't exist.
	X	    	# Prefer the compressed cat directory if it exists.
	X	    	$catpage = "$1/cat$2.Z/$4" 
	X		    if $catpage !~ /\.Z$/ && -d "$1/cat$2.Z";
	X	    }
	X	}
	X
	X
	X	@st_man = &Stat($manpage);
	X	@st_cat = &Stat($catpage);
	X
	X	if ($st_cat[$ST_MTIME] < $st_man[$ST_MTIME]) {
	X
	X	    $command = (($manpage =~ m:\.Z:) ? $ZCAT : $CAT)
	X			. " < $manpage | $CATSET";
	X
	X	    $command = &insert_filters($command, $manpage);
	X	    $command =~ s,-man,$manroot/tmac.an, if -e "$manroot/tmac.an";
	X
	X	    ($catdir = $catpage) =~ s!^(.*/?cat[^/]+)/[^/]*!$1!;
	X
	X	    chdir $manroot;
	X
	X	    $tmppage = "$catpage.$$";
	X
	X	    unless (-d $catdir && -w _ 
	X		    && open(tmppage, ">$tmppage") # usually EROFS
	X		    && close(tmppage) )
	X	    {
	X		$catpage = $tmppage = "/tmp/man.$$";
	X		$intmp = 1;
	X	    }
	X
	X	    printf STDERR "Reformatting page.  Please wait ... " if $isatty;
	X
	X	    $command .= "| $COMPRESS" if $catpage =~ /\.Z/;
	X	    $command .= "> $tmppage";
	X
	X	    $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = $SIG{'TERM'} 
	X		= 'tmp_cleanup';
	X
	XREFORMAT:   { unless (&reformat($command)) {
	X		warn "$program: nroff of $manpage into $tmppage failed\n";
	X		unlink $tmppage;
	X		if (!$intmp++) {
	X		    $catpage = $tmppage = "/tmp/man.$$";
	X		    warn "$program: hang on... retrying into $tmppage\n";
	X		    $command =~ s/> \S+$/> $tmppage/;
	X		    $status = 0;
	X		    redo REFORMAT;
	X		} else {
	X		    #$status = 1;
	X		    return;
	X		}
	X	    }} 
	X	    warn "done\n" if $isatty;
	X
	X	    $intmp || rename($tmppage,$catpage) || 
	X		die "couldn't rename $tmppage to $catpage: $!\n";
	X	    
	X	    $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = $SIG{'TERM'} 
	X		= 'DEFAULT';
	X
	X	} 
	X	$command = (($catpage =~ m:\.Z:)
	X			? $ZCAT
	X			: $CAT)
	X		    . " < $catpage";
	X    }
	X    if (-z $catpage) {
	X	unlink $catpage;
	X	die "$program: $catpage was length 0; disk full?\n";
	X    } 
	X    $command .= "| $UL" 		if $UL;
	X    $command .= "| $SED 's/.\b//g'" 	if $stripBS;
	X    $command .= "| $PAGER"  		if $isatty;
	X
	X    &run_topic;
	X    unlink($tmppage) if $intmp;
	X} 
	X
	X
	X# --------------------------------------------------------------------------
	X# modify $command to prime the pager with the subsection they want
	X# --------------------------------------------------------------------------
	Xsub append_sub_topic {
	X    if (defined $idx_topic)  {{
	X	local($key);
	X	last if $idx_topic eq '0';
	X	unless ($idx_topic) {
	X	    $idx_topic = &pick_index;
	X	    last if $idx_topic eq "\004" || $idx_topic eq '0';
	X	}
	X	if ($idx_topic =~ m!^/!) {
	X	    $command .= " '+$idx_topic'";
	X	    last;
	X	}
	X	unless ($key = &find_index($manpage, $idx_topic)) {
	X	    warn "No subsection $idx_topic for $manpage\n\n";
	X	    $idx_topic = '';
	X	    redo;
	X	}
	X	$key =~ s/([!-~])/$1.$1/g unless $is_less;
	X	$command .= " '+/^[ \t]*$key'";
	X    }}
	X}
	X
	X
	X# --------------------------------------------------------------------------
	X# present subsections and let user select one
	X# --------------------------------------------------------------------------
	Xsub pick_index {
	X     local($_);
	X     print "Valid sections for $page follow.  Choose the section\n";
	X     print "index number or string pattern. (0 for full page, RTN to quit.)\n\n";
	X     &show_index;
	X     print "\nWhich section would you like? ";
	X     ($_ = <>) ? chop : ($_ = "\004");
	X     $_ = "\004" if 'quit' =~ /^$_/;
	X     return $_;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# strip arg of extraneous cats and redirects	
	X# --------------------------------------------------------------------------
	Xsub unshell {
	X    $_[0] =~ s/^\s*cat\s*<?\s*([^\s|]+)\s*\|\s*([^|]+)/$2 < $1/;
	X    $_[0] =~ s/^([^|<]+)<([^Z|<]+)$/$1 $2/;
	X    ($roff eq 'troff') && $_[0] =~ s#(/usr/man/pr\S+)\s+(\S+)#$2 $1#;
	X}
	X
	X# --------------------------------------------------------------------------
	X# call system on command arg, stripping of sh-isms and echoing for debugging
	X# --------------------------------------------------------------------------
	Xsub run {
	X    local($command) = $_[0];
	X
	X    &unshell($command);
	X
	X    warn "running: $command\n" if $debug;
	X    if (system $command) {
	X	$status = 1;
	X	printf STDERR "\"%s\" exited %d, sig %d\n", $command, 
	X	    ($? >> 8), ($? & 255) if $debug;
	X    }
	X    return ($? == 0);
	X} 
	X
	X# --------------------------------------------------------------------------
	X# check if page needs tbl or eqn, modifying command if needed
	X# add known problems for PR directory if applicable
	X# --------------------------------------------------------------------------
	Xsub insert_filters {
	X    local($filters,$eqn, $tbl, $_);
	X    local(*PAGE);
	X    local($c, $PAGE) = @_;
	X    local($page,$sect, $prs);
	X

tchrist@convex.COM (Tom Christiansen) (01/08/91)

	X    ( $page = $PAGE ) =~ s/\.Z//;
	X    $page =~ s#.*/([^/]+)$#$1#;
	X
	X    $PAGE = "$ZCAT < $PAGE|" if $PAGE =~ /\.Z/;
	X
	X    (open PAGE) || die ("$program: can't open $PAGE to check filters: $!\n");
	X    warn "open $PAGE to check for filters in $_[0]\n" if $debug;
	X
	X    while (<PAGE>) {
	X	if (/^\.EQ/) {
	X	    $_ = <PAGE>;
	X	    $eqn = 1 unless /\.(if|nr)/;  # has eqn output not input
	X	} 
	X	if (/^\.TS/) {
	X	    $_ = <PAGE>;
	X	    $tbl = 1 unless /\.(if|nr)/;  # has tbl output not input
	X	} 
	X	last if $eqn && $tbl;
	X    } 
	X    close PAGE;
	X
	X    if ($roff eq 'troff') {
	X	$eqn && $_[0] =~ s/(\S+roff)/$EQN | $1/;
	X	$tbl && $_[0] =~ s/(\S+roff)/$TBL | $1/;
	X    } else { # nroff
	X	$eqn && $_[0] =~ s/(\S+roff)/$NEQN | $1/;
	X	$tbl && $_[0] =~ s/(\S+roff)/$NTBL | $1/;
	X    }
	X
	X    ($sect) = $page =~ /\.(\d)[^.]*$/;
	X    $prs = "/usr/man/pr$sect/$page";
	X    if (-e $prs) {
	X	warn "found PRs for $page\n" if $debug;
	X	if ($roff eq 'nroff')  {
	X	    $_[0] =~ s/ - / - $prs/;
	X	} else {
	X	    $_[0] .= " $prs";
	X	} 
	X    } else {
	X	print "no PRS for $page in $prs\n" if $debug;
	X    } 
	X    $_[0];
	X} 
	X
	X# --------------------------------------------------------------------------
	X# due to aliasing the dbase sometimes has the same thing twice
	X# --------------------------------------------------------------------------
	Xsub trimdups {
	X    local(%seen) = ();
	X    local(@retlist) = ();
	X
	X    while ($file = shift) {
	X	push(@retlist,$file) unless $seen{$file}++;
	X    } 
	X    return @retlist;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# just print the version
	X# --------------------------------------------------------------------------
	Xsub version  {
	X    warn "$program: version is \"$version\"\n" ;
	X}
	X
	X# --------------------------------------------------------------------------
	X# create and display subsection index via pager
	X# --------------------------------------------------------------------------
	Xsub show_index {
	X    local($_);
	X    &load_index($_[0]);
	X    if ($#ssindex > ($rows - 4) && $isatty) {
	X	print "Hit <RTN> for $#ssindex subsections via pager: ";
	X	$_ = <STDIN>;
	X	if ($no_idx_file) {
	X	    open (PAGER, "| $PAGER");
	X	    print PAGER @ssindex;
	X	    close PAGER;
	X	} else {
	X	    &run("$PAGER $idx_file");
	X	} 
	X    } else {
	X	print STDOUT @ssindex;
	X    }
	X} 
	X
	X# --------------------------------------------------------------------------
	X# find closest match on index selection in full index
	X# --------------------------------------------------------------------------
	Xsub find_index {
	X    local($manpage, $expr) = @_;
	X    local($_, @matches);
	X
	X    &load_index($manpage);
	X
	X    $expr =~ s!^/+!!;
	X
	X    for (@ssindex) { 
	X	s/^\s*\d+\s+//; 
	X	s/\s+\d+\s*$//; 
	X    }
	X
	X    if ($expr > 0) {
	X	return $ssindex[$expr];
	X    } else {
	X	$ssindex[0] = '';
	X	if (@matches = grep (/^$expr/i, @ssindex)) {
	X	    return $matches[0];
	X	} elsif (@matches = grep (/$expr/i, @ssindex)) {
	X	    return $matches[0];
	X	} else {
	X	    return '';
	X	}
	X    } 
	X} 
	X
	X# --------------------------------------------------------------------------
	X# read in subsection index into @ssindex
	X# --------------------------------------------------------------------------
	Xsub load_index {
	X    local($manpage)  = @_;
	X    $no_idx_file = 0;
	X    &getidx($manpage) if $#saveidx < 0;
	X    @ssindex = @saveidx;
	X    die "should have have an index for $manpage" if $#saveidx < 0;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# create subsection index is out of date wrt source man page
	X# --------------------------------------------------------------------------
	Xsub getidx {
	X    local($manpage) = @_;
	X    local($is_mh);
	X    local($_, $i, %lines, %sec, $sname, @snames);
	X    local(@retlist, $maxlen, $header, @idx , @st_man, @st_idx);
	X    # global no_idx_file, idx_file
	X
	X    ( $idx_file = $manpage ) =~ s:/man(\w+)(\.Z)?/:/idx$1/:;
	X    $idx_file =~ s/\.Z//;
	X
	X    require 'stat.pl' unless defined &Stat;
	X
	X    @st_man = &Stat($manpage);
	X    @st_idx = &Stat($idx_file);
	X
	X    if ($st_man[$ST_MTIME] < $st_idx[$ST_MTIME]) {
	X	unless (open idx_file) {
	X	    warn "$program: can't open $idx_file: $!\n";
	X	    return ();
	X	} 
	X	@retlist = <idx_file>;
	X	close idx_file;
	X	return @saveidx = @retlist;
	X    } 
	X
	X    if (!open(manpage, $manpage =~ /\.Z/ ? "$ZCAT < $manpage|" : $manpage)) {
	X    	warn "$program: can't open $manpage: $!\n";
	X	return ();
	X    }
	X    warn "building section index\n" if $debug;
	X    ($header = "Subsections in $manpage")  =~ s!/?\S*/!!;
	X    $maxlen = length($header);
	X    push(@snames, $sname = 'preamble');;
	X
	X    # MH has these alias for sections and subsectdions
	X    if ($is_mh = $manpage =~ m:/mh/:) {
	X	%mh_sections = (
	X	    "NA", "NAME",
	X	    "SY", "SYNOPSIS",
	X	    "DE", "DESCRIPTION",
	X	    "Fi", "FILES",
	X	    "Pr", "PROFILE",
	X	    "Sa", "SEE ALSO",
	X	    "De", "DEFAULTS",
	X	    "Co", "CONTEXT",
	X	    "Hh", "HELPFUL HINTS",
	X	    "Hi", "HISTORY",
	X	    "Bu", "BUGS"
	X	);
	X	$mh_expr = join('|',keys %mh_sections);
	X    } 
	X
	X    while (<manpage>) {
	X	if ($is_mh && /^\.($mh_expr)/) {
	X	    $sname = $mh_sections{$+};
	X	    $maxlen = length($sname) if $maxlen < length($sname); 
	X	    push(@snames,$sname);
	X	} 
	X	if (/^\.s[sh]\s+(.*)/i ) {
	X	    $line = $_;
	X	    $_ = $1;
	X	    s/"//g;
	X	    s/\\f([PBIR]|\(..)//g;	# kill font changes
	X	    s/\\s[+-]?\d+//g;		# kill point changes
	X	    s/\\&//g;			# and \&
	X	    s/\\\((ru|ul)/_/g;		# xlate to '_'
	X	    s/\\\((mi|hy|em)/-/g;	# xlate to '-'
	X	    s/\\\*\(..//g; 		# no troff strings
	X	    s/\\//g;		   	# kill all remaining backslashes 
	X	    $sname = $_;
	X	    $_ = $line;
	X	    $maxlen = length($sname) if $maxlen < length($sname); 
	X	    push(@snames,$sname);
	X	} 
	X	$lines{$sname}++;
	X    } 
	X
	X    $mask = sprintf("%%2d   %%-%ds %%5d\n", $maxlen + 2);
	X
	X    $no_idx_file = $idx_file eq $manpage || !open(idx, ">$idx_file");
	X
	X    $line = sprintf(sprintf("Idx  %%-%ds Lines\n", $maxlen + 2), $header);
	X    @retlist = ($line);
	X
	X    for ($i = 1; $i <= $#snames; $i++)  {
	X	push(@retlist, sprintf($mask, $i, $snames[$i], $lines{$snames[$i]}));
	X    } 
	X    if (!$no_idx_file) {
	X	warn "storing section index in $idx_file\n" if $debug;
	X	print idx @retlist;
	X	close idx;
	X    }
	X    return @saveidx = @retlist;
	X}
	X
	X# --------------------------------------------------------------------------
	X# interrupted -- unlink temp page
	X# --------------------------------------------------------------------------
	Xsub tmp_cleanup {
	X    warn "unlink $tmppage\n" if $debug;
	X    unlink $tmppage;
	X    exit -1;
	X} 
	X
	X
	X# --------------------------------------------------------------------------
	X# print line with C\bC style emboldening 
	X# --------------------------------------------------------------------------
	Xsub print {
	X    local($_) = @_;
	X
	X    if (!$inbold) {
	X	print;
	X    } else {
	X	local($last);
	X	for (split(//)) {
	X            if ($last eq "\033") {
	X                print;
	X            } else {
	X                print /[!-~]/ ? $_."\b".$_ : $_;
	X            }
	X            $last = $_;
	X	}
	X    } 
	X}
	X
	X# --------------------------------------------------------------------------
	X# reformat the page with nroff, fixing up bold escapes
	X# --------------------------------------------------------------------------
	Xsub reformat { 
	X    local($_) = @_; 
	X    local($nroff, $col); 
	X    local($inbold) = 0;
	X
	X    if ($NROFF_CAN_BOLD) {
	X	return &run($_);
	X    } 
	X
	X    &unshell($_);
	X    ($nroff, $col) = m!(.*)\|\s*($COL.*)!;
	X
	X    warn "$nroff | (this proc) | $col\n" if $debug;
	X
	X    open (NROFF, "$nroff |");
	X    $colpid = open (COL, "| $col");
	X
	X    select(COL);
	X
	X    while (<NROFF>) {
	X	s/\033\+/\001/;
	X	s/\033\,/\002/;
	X	if ( /^([^\001]*)\002/ || /^([^\002]*)\001/ )  {
	X	    &print($1);
	X	    $inbold = !$inbold;
	X	    $_ = $';
	X	    redo;
	X	}   
	X	&print($_);
	X    }
	X
	X    close NROFF;
	X    if ($?) { 
	X	warn "$program: \"$nroff\" failed!\n" if $debug;
	X	$status++;
	X    } 
	X    close COL;
	X    if ($?) {
	X	warn "$program: \"$col\" failed!\n" if $debug;
	X	$status++;
	X    }
	X    select(STDOUT);
	X    $status == 0;
	X} 
	X
	X# --------------------------------------------------------------------------
	X# prompt for <RET> if we're a tty and have a non-stopping pager
	X# --------------------------------------------------------------------------
	Xsub prompt_RTN {
	X    local($why) = $_[0] || "to continue";
	X    return unless $isatty;
	X    unless ($is_less && $ENV{'LESS'} !~ /E/) {
	X	print "Hit <RTN> $why: ";
	X	$_ = <STDIN>;
	X    }
	X}
	X
	X# --------------------------------------------------------------------------
	X# dynamically determine MANPATH (if unset) according to PATH
	X# --------------------------------------------------------------------------
	Xsub config_path {
	X    local($_);		# for traversing $PATH
	X    local(%seen);	# weed out duplicates
	X    local(*manpath);	# eventual return values
	X
	X    if (defined $ENV{'MANPATH'}) {
	X	$manpath = $ENV{'MANPATH'};
	X    } else {
	X	for (split(/:/, $ENV{'PATH'})) {
	X	    next if $_ eq '.';
	X	    next if $_ eq '..';
	X	    s![^/+]*$!man! && -d && !$seen{$_}++ && push(@manpath,$_);
	X	}
	X	$manpath = join(':', @manpath);
	X    } 
	X    # $manpath;    # last expr is assign to this anyway
	X} 
	X
	X# --------------------------------------------------------------------------
	X# grep through MANPATH for a pattern
	X# --------------------------------------------------------------------------
	Xsub grepman {
	X    local($code, $_, $dir, $root, $FILE, $found);
	X    
	X    $code = "while (<FILE>) {\n";
	X
	X    for (@ARGV) {
	X	s#/#\\/#g;
	X	$code .= <<EOCODE;
	X	    if (/$_/) { 
	X		print "\$path: \$_"; 
	X		\$found++;
	X		next; 
	X	    }
	XEOCODE
	X    } 
	X
	X    $code .= "}\n";
	X
	X    print "grep eval code: $code" if $debug;
	X
	X    
	X    foreach $root ( split(/:/, $MANPATH)) {
	X	unless (chdir($root)) {
	X	    warn "can't chdir to $root: $!";
	X	    $status++;
	X	    next;
	X	} 
	X	foreach $dir ( <man?*> ) {
	X	    unless (chdir($dir)) {
	X		warn "can't chdir to $root/$dir: $!";
	X		$status++;
	X		next;
	X	    } 
	X	    unless (opendir(DIR, '.')) {
	X		warn "can't opendir $root/$dir: $!";
	X		$status++;
	X		next;
	X	    } 
	X	    foreach $FILE ( readdir(DIR) ) {
	X		next if $FILE eq '.' || $FILE eq '..';
	X		$path = "$root/$dir/$FILE";
	X		if ($FILE !~ /\S\.\S/ || !-f $FILE) {
	X		    print "skipping non-man file: $path\n" if $debug;
	X		    next;
	X		} 
	X		if ($FILE =~ /\.Z$/ || $dir =~ /\.Z$/) {
	X		    $FILE = "$ZCAT $FILE|";
	X		} 
	X		print STDERR "grepping $path\n" if $debug;
	X		unless (open FILE) {
	X		    warn "can't open $root/$dir/$FILE: $!";
	X		    $status++;
	X		    next;
	X		} 
	X		eval $code;
	X		die $@ if $@;
	X	    } 
	X	    unless (chdir ($root)) {
	X		warn "can't return to $root: $!";
	X		$status++;
	X		last;
	X	    } 
	X	} 
	X    } 
	X    exit ($status || !$found);
	X} 
SHAR_EOF
if test 39119 -ne "`wc -c < 'man'`"
then
	echo shar: "error transmitting 'man'" '(should have been 39119 characters)'
fi
chmod 755 'man'
fi
echo shar: "extracting 'catwhatis'" '(553 characters)'
if test -f 'catwhatis'
then
	echo shar: "will not over-write existing file 'catwhatis'"
else
sed 's/^	X//' << \SHAR_EOF > 'catwhatis'
	X#!/usr/local/bin/perl
	X
	X$manpath = shift || $ENV{'MANPATH'} || '/usr/man/';
	X
	Xfor $manroot (split(/:/, $manpath)) {
	X
	X    unless (dbmopen(manroot, "$manroot/whatis", undef)) {
	X	warn("Can't dbmopen $manroot/whatis: $!\n");
	X	next;
	X    }
	X
	X    print "$manroot:\n";
	X
	X    while (($key,$value) = each %manroot) {
	X
	X	for (split(/\002/, $value)) {
	X	    if (/\001/) {
	X		($cmd, $page, $section, $desc) = split(/\001/);
	X		printf("%-30s - %s\n", "$cmd ($section)", $desc);
	X	    } else {
	X		printf("%-30s > %s\n", "$key", $_);
	X	    } 
	X	} 
	X    } 
	X
	X    dbmclose(manroot);
	X}
SHAR_EOF
if test 553 -ne "`wc -c < 'catwhatis'`"
then
	echo shar: "error transmitting 'catwhatis'" '(should have been 553 characters)'
fi
chmod 775 'catwhatis'
fi
echo shar: "extracting 'countman'" '(326 characters)'
if test -f 'countman'
then
	echo shar: "will not over-write existing file 'countman'"
else
sed 's/^	X//' << \SHAR_EOF > 'countman'
	X#!/usr/local/bin/perl
	X#
	X# countman -- count the number of entries in a man tree
	X
	X$|  = 1;
	Xfor (split(/:/, shift || $ENV{'MANPATH'} || '/usr/man')) {
	X    dbmopen(%whatis, "$_/whatis", undef) || (warn "$0: dbmopen $_: $!\n",next);
	X    print $_, ': ';
	X    for ($count = 0; each %whatis; $count++) { } 
	X    print $count, "\n";
	X} 
SHAR_EOF
if test 326 -ne "`wc -c < 'countman'`"
then
	echo shar: "error transmitting 'countman'" '(should have been 326 characters)'
fi
chmod 775 'countman'
fi
echo shar: "extracting 'cfman.8'" '(6282 characters)'
if test -f 'cfman.8'
then
	echo shar: "will not over-write existing file 'cfman.8'"
else
sed 's/^	X//' << \SHAR_EOF > 'cfman.8'
	X.TH CFMAN 8L \fIConvex-Internal\fP "15 November 1989" "\fBDocumentation Administration Toolkit\fP"
	X.de Sh
	X.br
	X.PP
	X.ne 4
	X.ti -.5i
	X\fB\\$1\fR
	X.PP
	X..
	X.de LB		\" little and bold
	X.ft B
	X.if !"\\$1"" \&\s-1\\$1\s+1 \\$2 \\$3 \\$4 \\$5 \\$6
	X.ft R
	X..
	X.de Sp
	X.if t .sp .5v
	X.if n .sp
	X..
	X.ds lq \&"\"
	X.ds rq \&"\"
	X.if t \
	X.       ds lq ``
	X.if t \
	X.       ds rq ''
	X.de Q
	X\*(lq\\$1\*(rq\\$2
	X..
	X.Sh NAME
	Xcfman \- cross-reference man pages for internal consistency
	X.Sh SYNOPSIS
	X.B cfman
	X[
	X.B \-d
	Xlevel
	X] 
	X[
	X.B \-s
	Xsections
	X] 
	X[
	X.B \-p
	Xmanpath
	X] 
	X[
	X.B \-x
	Xxrefpath
	X] 
	X[ pattern | pathname ] ...
	X.br 
	X.Sh DESCRIPTION
	X.I 
	XCfman 
	Xis a 
	X.I perl
	Xprogram that checks that man page sources 
	Xare mutually consistent in their 
	X.LB "SEE ALSO"
	Xreferences.
	XIt will also report any 
	X.LB ".TH"
	Xline that claims the
	Xman page is in a different place than 
	X.I cfman
	Xfound it.
	X.PP
	XWhen supplied with no arguments, 
	X.I cfman
	Xwill check all files (matching *.*) it finds in each man directory in 
	Xyour colon-delimited 
	X.LB "$MANPATH"
	Xenvariable if set, or in 
	X.I /usr/man
	Xotherwise.  It first verifies that the 
	X.LB ".TH"
	Xsays
	Xthe man page is really where it should be, e.g. if the
	Xline is 
	X.br
	X.in +.5i
	X.nf
	X\f(TA
	X\&.TH\ \ WIDGET\ \ 8
	X.in -.5i
	X\fP
	X.fi
	X.br
	Xthen \fIwidget.8\fP should be the filename currently 
	Xbeing examined.  All upper-case will map to all lower-case,
	Xbut mixed case will be preserved for compatibility with
	Xthe 
	X.LB X11
	Xman pages.
	X.PP
	X.I Cfman
	Xthen skips ahead to the 
	X.LB "SEE ALSO"
	Xsection and retrieves
	Xall comma-delimited entries of the general 
	Xform \fIpagename(section)\fP.  It first looks in the file
	X\&../man\fIsection/pagename.section\fP.  If this fails
	Xand the current file ended in one of \fB[npl]\fP, but the
	X.I section
	Xreferenced is either 
	X\fB1\fP or \fB8\fP, then it will check in 
	X.I ../man8.
	XFailing this, 
	X.I cfman
	Xchecks to see whether the referenced man page has been 
	Xinstalled stripped of its subsection, e.g. \fIuucp\fP(1c)
	Xhas found its way into \fIuucp\fP(1).  It then checks
	Xto see whether something in section \fB1\fP has been mis-installed
	Xin section \fB8\fP, or vice versa, or either one in section \fBl\fP
	Xmis-installed in the 
	Xin section \fB8\fP and vice-versa.  If all else fails, 
	X.I cfman
	Xwill guess that a man page is referenced without its
	Xproper subsection, as in a reference to \fIrcp(1)\fP
	Xthat should really have been to \fIrcp(1c)\fP.  If it finds
	Xthe misplaced man page, it reports where the reference
	Xthought it was and where it really was.  Otherwise it
	Xreports the man page as missing.  
	X.PP
	XThe 
	X.LB $MANPATH
	Xvariable may be overridden by 
	Xthe \fB-p\fP option.  
	XAll checks will 
	Xbe performed across each subtree specified in the manpath
	X(either from the environment of the command line),
	Xunless altered with the \fB-x\fP option.  As a short-cut, 
	Xthe \fIxrefpath\fP may have a leading colon to indicate
	Xthat it is to be concatenation of the \fImanpath\fP
	Xand the supplied \fIxrefpath\fP.  
	X.PP
	XYou can restrict the sections checked with the \fB-s\fP
	Xswitch.  By default, sections 1 through 8 will be examined.
	XThe section may be a shell metacharacter expression, 
	Xlike 
	X.Q ?
	Xor 
	X.Q [18lpn] .
	X.PP
	XYou may restrict the individual man pages cross-referenced
	Xby specifying which ones you're interested in on the command
	Xline.  These may be full pathnames, simple names like 
	X.Q tty ,
	Xor a shell metacharacter expression like 
	X.Q *net . 
	XIf
	Xno period occurs in the simple name, it is assumed to mean that
	Xthe name may have any extension.  If you list specific 
	Xman pages on the command line and 
	X.I cfman
	Xfinds none matching your specification, it will report this fact.
	XSee the 
	X.LB "EXAMPLES"
	Xsection.  
	X.PP
	XMan pages that are linked by placing a \fB.so\fP directive
	Xon the first line will be correctly followed, and no man page
	Xin the same subtree.  Very limited support for alternate 
	Xman macros is provided: the 
	X.I "\fIRand MH Message Handling System\fP" 's
	Xman macro set are recognized, as is Larry Wall's 
	X.LB .Sh
	Xreplacement for 
	X.LB .SH.
	X.Sh DIAGNOSTICS
	XRequires 
	X.I perl 
	Xto be at least version 3.0, patchlevel 1 to run.  The 
	Xprogram will abort if you try to run it with an 
	Xearlier version of perl
	X.PP
	XFive different tracing levels can be specified with the \fB-d\fP
	Xoption.  If any debugging is turned on, the walk through
	Xthe different components of the manpath are traced.
	XDebug values are numeric and additive, and are interpreted
	Xthis way:
	X.Sp
	X.in +.5i
	X.nf
	X.ne 5
	X	1	Trace each man page examined
	X	2	Trace each cross reference examined
	X	4	Trace each \s-1\fB.TH\s+1\fP check
	X	8	Trace each file-existence test 
	X	16	Trace each line
	X'in -.5i
	X.fi
	X.Sp
	XTracing information and other warnings are printed to 
	X\fIstderr\fP, but normal messages about bad cross references
	Xare printed to \fIstdout\fP as that is \fIcfman\fP's principle
	Xtask. 
	X.PP
	XEmbedded 
	X.I troff
	Xstring macros starting \e*( cannot be resolved, and they
	Xwill trigger a warning message if found in the 
	X.LB .TH
	Xor 
	X.LB "SEE ALSO"
	Xsections.
	X.Sh EXAMPLES
	X.nf
	X\f(TA
	Xcfman							# do all in $MANPATH
	Xcfman -p /export/exec/sun3/share/man		# sun man pages
	Xcfman -p $HOME/man:/usr/local/mh/man:/usr/local/man:/usr/man
	Xcfman -p /usr/local/man	 -x :/usr/man	# xref also in /usr/man
	Xcfman -s 18nlp					# only these sections
	Xcfman '*tty*' fubar		    			# check for *tty*.* and fubar.*
	Xcfman `pwd`/*.[1-8]		    		# just check these files
	Xcfman -s 23 'sys*'					# sys*.* files in sections 2,3
	Xcfman -s 1 -p /export/exec/sun3/share/man
	X.fi
	X\fR
	X.PP
	XThe last command produced this output on my machine:
	X.nf
	X\f(TA
	Xbanner.1v: thinks it's in banner(1)
	Xfoption.1: skyversion(8) missing
	Xfrom.1: prmail(1) missing
	Xmake.1: rstat(8c) missing
	Xman.1: apropos(1) missing
	Xold-perfmon.1: missing .TH
	Xoldperfmon.1: missing .TH
	Xoldsetkeys.1: thinks it's in setkeys(1)
	Xorganizer.1: restore(1v) really in restore(8)
	Xsunview.1: traffic(1) really in traffic(1c)
	Xsort.1v: thinks it's in sort(1)
	Xsum.1v: thinks it's in sum(1)
	X.fi 
	X\fR
	X.Sh ENVIRONMENT
	XThe default manpath will be taken from 
	X.LB $MANPATH
	Xif set.
	X.Sh "SEE ALSO"
	Xman(1), troff(1), perl(1), man(7).
	X.Sh BUGS
	XDue to the current implentation of globbing in 
	X.I perl,
	Xyou can get 
	X.Q "Arguments too long"
	Xerrors.  The workaround is to run 
	X.I cfman
	Xfirst on 
	X.Q [a-m]* ,
	Xand then on 
	X.Q [n-z]* .
	X.Sh AUTHOR
	XTom Christiansen, \s-1CONVEX\s+1 Computer Corporation.
SHAR_EOF
if test 6282 -ne "`wc -c < 'cfman.8'`"
then
	echo shar: "error transmitting 'cfman.8'" '(should have been 6282 characters)'
fi
chmod 644 'cfman.8'
fi
echo shar: "extracting 'FEATURES'" '(2356 characters)'
if test -f 'FEATURES'
then
	echo shar: "will not over-write existing file 'FEATURES'"
else
sed 's/^	X//' << \SHAR_EOF > 'FEATURES'
	XFeatures include but are not limited to:
	X
	X    *   almost always faster than standard man (try 'man me')
	X
	X    *	take much less diskspace for catpages
	X
	X    *	supports per-tree tmac macros
	X    
	X    *	compressed man and cat files
	X
	X    *	user-definable man path via $MANPATH or -M optoin
	X    
	X    *  $MANPATH autoconfigged based on $PATH if not set
	X
	X    *   user-definable section search order via -S or $MANSECT.  Thus
	X	programmers can get stty(3) before stty(1).
	X    
	X    *	$PAGER support
	X
	X    *	show all the places you would find a man page (-w option)
	X	and in what order.
	X
	X    *   display all available man page on a topic (-a option)
	X    
	X    *   no limits on what subsections go where (if you want to add 7x, ok)
	X
	X    *   support for multi-char sections like man1m/*.1m or manavs/*.avs
	X
	X    *   man -K for regexp apropos
	X
	X    *   grep through all the man pages in $MANPATH
	X
	X    *   section and subsection indexing for long man pages
	X
	X    *   support for alternate architectures docs on same machine
	X
	X    *	ability to run man on a local file 
	X
	X    *	ability to easily troff (or preview) a man page
	X
	X    *	recognizes embedded filter directives for tbl and eqn
	X
	X    *	does the right thing for man tree that don't have DBM whatis files
	X
	X    *   support for connecting online problem reports to right man page
	X   
	X    *   there's an extended usage message (man -U) for further help
	X	and to show current defaults.
	X
	X
	XHere are some features of this version of makewhatis:
	X
	X    *	it's faster.
	X
	X    *	tries hard to make pretty output, stripping troff directives.
	X
	X    *   doesn't blow up on more files in a man directory 
	X	than the shell will glob.  
	X
	X    *   accepts troff string macros for the dashes in the
	X	the NAME section.
	X
	X    *   prints a diagnostic for a malformed NAME section.
	X
	X    *   detects linked (hard, soft, or via .so) man pages
	X
	X    *   finds *all* references in the NAME section.
	X
	X    *   recognizes MH's man macros (and .Sh from lwall).
	X
	X    *   many other things that makewhatis used to do wrong
	X
	XHere are some supporting utilities that are included:
	X
	X    *   catman -- new version that groks compressed files
	X
	X    *   catwhatis -- display the whatis databases
	X
	X    * 	straycats -- find cat pages with no man page ancestor
	X
	X    *   countman -- find how many man pages you can get at
	X
	X    *   cfman -- find bad SEE ALSO references in man pages
SHAR_EOF
if test 2356 -ne "`wc -c < 'FEATURES'`"
then
	echo shar: "error transmitting 'FEATURES'" '(should have been 2356 characters)'
fi
chmod 664 'FEATURES'
fi
chmod 775 .
echo shar: "done with directory 'man'"
cd ..
exit 0
#	End of shell archive

tchrist@convex.COM (Tom Christiansen) (01/10/91)

Looks like the cfman program (the SEE ALSO chaser) I jammed into the
package was from my work tree instead of my release tree.  Here's the
necessary patch to back off those minor changes.  Apply to cfman:

16c16
< &Getopts('fd:s:p:P:x:') || &usage;
---
> &Getopts('d:s:p:P:x:') || &usage;
28d27
< $use_DBM = $opt_f;
335,337d333
< 	    if ($use_DBM && &dbmopen($tree)) {
< 		next;
< 	    }
403,411d398
< } 
< 
< sub dbmopen {
<     local($tree) = $_[0];
<     # globals: %dbmopened, %warned
<     return 1 if $dbmopened{$tree};
< 
<     unless (-f "
<