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 (¯o($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 (¯o($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 manptchrist@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);
Xtchrist@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 archivetchrist@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 "
<