The Answer to All Man's Problems (part 2 of 6)

Tom Christiansen tchrist at convex.COM
Tue Jan 8 09:17:51 AEST 1991


	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 at 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 at 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 at 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 at manpath = split(/:/,$manpath);
	X
	X$opt_x =~ /^:/ && ( $opt_x = $manpath . $opt_x );
	X at xrefpath = $opt_x ? split(/:/,$opt_x) : @manpath;
	X
	X$debug = $opt_d;
	X$use_DBM = $opt_f;
	X
	X at sections = $opt_s ? split(/ */,$opt_s) : 1..8;
	X
	Xif ($debug) {
	X    $" = ':';
	X    print "manpath is @manpath\n";
	X    print "xrefpath is @xrefpath\n";
	X    $" = ' ';
	X} 
	X
	Xfile:    foreach $file ( $#ARGV >= $[ ? @ARGV : '*.*' ) {
	X	     printf STDERR "considering %s\n", $file if $debug & 1;
	X	     $bingo = 0;
	Xtree:        foreach $tree ( @manpath ) {
	X		 print "ROOT is $tree\n" if $debug;
	X		 if (!chdir $tree) {
	X		    warn "cannot chdir to $tree: $!";
	X		    next tree;
	X		 } 
	X		 $rootdir = $tree;
	X		 if ( $file =~ m#^/# ) {
	X		    &read_manpages($file); 
	X		    next file;
	X		 } 
	Xsection:         foreach $section ( @sections ) {
	X		    &scan_section($tree,$section,$file);
	X		 }
	X	     } 
	X	     print "no man pages matched \"$file\"\n" unless $bingo;
	X	  }
	X
	X
	Xexit 0;
	X
	X############################################################################
	X#
	X# scan_section()
	X#
	X#	checks a given man tree (like /usr/local/man) in a 
	X#	certain subsection (like '1'), checking for a certain
	X#	file, like 'tty' (which mean 'tty.*', 'system.3*', or '*.*'.
	X#
	X#	will recurse on a subsection name contaning a shell meta-character
	X#
	X############################################################################
	X
	Xsub scan_section {
	X    local ( $manroot, $subsec, $files ) = @_;
	X    local ( $mandir );
	X
	X    $mandir = "man" . $subsec;
	X
	X
	X    # subsec may have been ? or *; if so, recurse!
	X    if ( &has_meta($mandir) ) {  
	X	for (<${mandir}>) {
	X	    if (&has_meta($_)) { 
	X		warn "bad glob of $mandir"; 
	X		last; 
	X	    } 
	X	    s/^man//;
	X	    &scan_section($manroot,$_,$files);
	X	} 
	X	return;
	X    } 
	X
	X    $files = "$files.*" unless $files =~ /\./;
	X
	X    if (!chdir $mandir) {
	X	warn "couldn't chdir to $mandir: $!\n" if $debug;
	X	return;
	X    } 
	X
	X    printf STDERR "chdir to %s of %s\n", $mandir, $manroot if $debug & 1;
	X
	X    &read_manpages ( &has_meta($files) ? &glob($files) : ($files));
	X
	X    chdir('..');
	X} 
	X
	X############################################################################
	X#
	X# read_manpages()
	X#
	X#	passed a list of filename, which are man pages.  opens each one
	X#	verifying that the file really is in the place that the .TH line.
	X#	skips to SEE ALSO section and then verifies existence of each 
	X#	referenced man page.
	X############################################################################
	X
	X
	Xsub read_manpages {
	X    local (@pages) = @_;
	X
	X    local ($junk, $sopage, $basename, $line, $page, $pname, $pext, $gotTH);
	X    local(%seen);
	X
	X
	Xpage:
	X    foreach $page ( @pages ) {
	X	next page if $page =~ /\.(BAK|OLD)$/i;
	X
	X	if ($seen{$page}++) {
	X	    print "already saw $page\n" if $debug & 1;
	X	    next page;
	X	}
	X
	X	if (!open page) {
	X	    warn "couldn't open $page: $!\n";
	X	    next page;
	X	}
	X
	X	$bingo = 1; # global var
	X
	X	print "checking $page\n" if $debug & 1;
	X
	X	$gotTH = 0;
	X	$line = 0;
	X	$sopage = '';
	X
	Xline:   while (<page>) {
	X	    print if $debug & 16;
	X	    next line if /^'''/ || /^\.\\"/;
	X
	X	    # deal with .so's on the first line.
	X	    # /usr/ucb/man uses this instead of links.
	X	    if (!($line++) && /^\.so\s+(.*)/) {
	X		$sopage = $1;
	X		print "$page -> $sopage\n" if $debug & 1;
	X		($basename = $sopage) =~ s%.*/%%;
	X		if ($seen{$basename}++) {
	X		    print "already saw $basename\n" if $debug & 1;
	X		    next page;
	X		} 
	X		if (!open(page,"../$sopage")) {
	X		    print "$page: cannot open $sopage: $!\n";
	X		    next page;
	X		} 
	X		$page = $basename;
	X		next line;
	X	    } 
	X
	X	    # check for internally consistent .TH line
	X	    if ( /^\.(TH|SC)/ ) { # SC is for mh
	X		 $gotTH++;
	X		 printf STDERR "TH checking %s", $_ if $debug & 4;
	X		 do flush();
	X		 s/"+//g;
	X		 ($junk, $pname, $pext) = split;
	X		 if (&macro($pname)) {
	X			printf STDERR "%s: can't resolve troff macro in .TH: %s\n",
	X			    $page, $pname;
	X			next line;
	X		 } 
	X		 $pext =~ y/A-Z/a-z/;
	X		 $pname =~ s/\\-/-/g;
	X		 $pname =~ y/A-Z/a-z/ if $pname =~ /^[\$0-9A-Z_\055]+$/;
	X		 ($pexpr = $page) =~ s/([.+])/\\$1/g;
	X		 $pexpr =~ s%.*/%%;
	X		 if ( "$pname.$pext" !~ /^$pexpr$/i) {
	X		      printf "%s: thinks it's in %s(%s)\n", 
	X			  $page, $pname, $pext;
	X		 } 
	X		 next line;
	X	    }
	X
	X	    next line unless /^\.S[Hh]\s+"*SEE ALSO"*/ 
	X		|| /^\.S[Hh]\s+REFERENCES/	# damn posix
	X		|| /^\.Sa\s*$/; 		# damn mh
	X
	X	    # finally found the cross-references
	Xxref:       while (<page>) {
	X		print if $debug & 16;
	X		last line if /^\.(S[Hh]|Co|Hi|Bu)/; # i really hate mh macros
	X		next xref unless /\(/;
	X		next xref if /^.PP/;
	X		chop;
	X		s/\\f[RIPB]//g;
	X		s/\\\|//g;
	X		s/\\-/-/g;
	Xentry:          foreach $entry ( split(/,/) ) {
	X		    #print "got entry $entry\n";
	X		    next entry unless $entry =~ /\(.*\)/;
	X		    $pname = ''; $pext = '';
	X		    $1 = ''; $2 = '';
	X		    ($pname, $pext) = 
	X			($entry =~ /([A-Za-z0-9\$._\-]+)\s*\(([^)]+)\).*$/); 
	X		    if ($debug & 8) {
	X			printf STDERR "entry was %s, pname is %s, pext is %s\n",
	X			    $entry, $pname, $pext;
	X		    }     
	X		    if (&macro($pname)) {
	X			printf "%s: can't resolve troff macro in SEE ALSO: %s\n",
	X			    $page, $pname;
	X			next entry;
	X		    } 
	X		    next entry if !$pname || !$pext || $pext !~ /^\w+$/;
	X		    $pext =~ y/A-Z/a-z/;
	X		    $pname =~ y/A-Z/a-z/ if $pname =~ /^[A-Z_0-9\-]+$/;
	X		    #($psect = $pext) =~ s/^(.).*/$1/;
	X		    do check_xref($page,$pname,$pext);
	X
	X		}	# entry: foreach $entry ( split(/,/) ) 
	X	    }		# xref:  while (<page>)
	X	}		# line:  while (<page>) 
	X	printf "%s: missing .TH\n", $page if (!$gotTH);
	X    }  			# page:  foreach $page ( @pages )
	X}     			# sub    read_manapages
	X
	X
	X###########################################################################
	X#
	X# check_xref()
	X#
	X#	given the name of the page we're looking for, check for a
	X#	cross reference of a given man page and its assumed subsection
	X#
	X###########################################################################
	X
	Xsub check_xref {
	X    local ($name, $target, $section) = @_;
	X    local ($basesec, $subsec, $newsec );
	X
	X    printf STDERR " xref of %s(%s)\n", $target, $section if $debug & 2;
	X
	X    return if &pathcheck($target,$section);
	X
	X
	X    # if we get this far, something's wrong, so begin notify
	X    printf "%s: %s(%s)", $name, $target, $section;
	X
	X    ($basesec, $subsec) = ($section =~ /^(\d)(.*)$/);
	X
	X    if ($name =~ /\.\d*([nlp])$/ && ($section == 1 || $section == 8)
	X	    && ($newsec = &pathcheck($target,$1))) { # hack for manl idiocy
	X	&really($target,$newsec);
	X	return;
	X    }
	X
	X    # first check if page.Xn is really in page.X
	X    if ( $subsec && ($newsec = &pathcheck($target,$basesec))) {
	X	&really($target,$newsec);
	X	return;
	X    } 
	X
	X    if ( $basesec == 1 && &pathcheck($target,8))  {
	X	&really($target,8);
	X	return;
	X    }
	X
	X    if ( $basesec == 8 && &pathcheck($target,1))  {
	X	&really($target,1);
	X	return;
	X    }
	X
	X    # maybe it thinks it's in 8 but got erroneously in 1
	X    if ( $basesec =~ /[18]/ && ($newsec = &pathcheck($target,'l')))  {
	X	&really($target,$newsec);
	X	return;
	X    } 
	X
	X    # maybe page.X is really in page.Xn; this is expensive
	X    if ( !$subsec && ($newsec = &pathcheck($target,$basesec.'*'))) {
	X	&really($target,$newsec);
	X	return;
	X    } 
	X
	X    printf " missing\n";
	X    do flush();
	X}
	X
	X###########################################################################
	X#
	X# pathcheck()
	X#
	X#	takes a name (like 'tty') and a section (like '1d')
	X#	and looks for 'tty.1d' first in the current root, 
	X#	then in all other elements of @xrefpath.  the section
	X#	may have a meta-character in it (like '8*').
	X#
	X#	returns the subsection in which we found the page, or
	X#	null if we failed.
	X#
	X###########################################################################
	X
	Xsub pathcheck {
	X    local ( $name, $section ) = @_;
	X    local ( $basesec, $metasec, $fullpath, @expansion, $tree, %checked  ); 
	X    local ( $return ) = 0;
	X
	X    $metasec = &has_meta($section);
	X
	X    ($basesec) = ($section =~ /^(.)/);
	X
	X    foreach $tree ( $rootdir, @xrefpath ) {
	X	next if !$tree || $checked{$tree}++;  # only check each tree once
	X
	X	$fullpath = "$tree/man$basesec/$name.$section";  
	X
	X	print "   testing $fullpath\n" if $debug & 8;
	X
	X	if (!$metasec) {
	X	    if (-e $fullpath) {
	X		$return = $section;
	X	    }
	X	} else {
	X	    if ($use_DBM && &dbmopen($tree)) {
	X		next;
	X	    }
	X	    open(SAVERR, '>&STDERR');  # csh globbing brain damage
	X	    close STDERR;
	X	    if ((@expansion = <${fullpath}>) && !&has_meta($expansion[0])) {
	X	    			# redundant meta check due to sh brain-damage
	X		#for (@expansion) { s/.*\.//; } 
	X		#$section = join(' or ', at expansion);
	X		($section) = ($expansion[0] =~ /([^.]+)$/);
	X		$return = $section;
	X	    }
	X	    open(STDERR, '>&SAVERR');  # csh globbing brain damage
	X	    close SAVERR;



More information about the Alt.sources mailing list