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

Tom Christiansen tchrist at convex.COM
Tue Jan 8 09:19:54 AEST 1991


	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 at 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 at 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 at convex.com\fP
	X.AB no
	X.ps -1
	XThe \s-1UNIX\s0 on-line manual system was designed many years ago to suit
	Xthe needs of systems at the time, but 
	Xdespite the growth in complexity of typical
	Xsystems and the need for more sophisticated software,
	Xfew modifications have been
	Xmade to it since then.
	XThis paper
	Xpresents the results of a complete rewrite of the man system.  The
	Xthree principal goals were to effect substantial gains in
	Xfunctionality, extensibility, and speed.  The secondary goal was to
	Xrewrite a basic \s-1UNIX\s0 utility in the perl programming language to
	Xobserve how perl affected development time, execution time, and design
	Xdecisions.
	X.PP
	X.ps -1
	XExtensions to the original man system include storing the whatis
	Xdatabase in \s-1DBM\s0 format for quicker access, intelligent handling of
	Xentries with multiple names (via \fB.so\fP inclusion, links, or the \s-1NAME\s0
	Xsection), embedded tbl and eqn directives, multiple man trees,
	Xextensible section naming possibilities,
	Xuser-definable section and sub-section search ordering, 
	Xan indexing mechanism
	Xfor long man pages,
	Xtypesetting of man pages,
	Xtext-previewer support for bit mapped displays, 
	Xautomatic validity checks on the \s-1SEE\s0 \s-1ALSO\s0 sections, 
	Xsupport for compressed man pages to conserve disk usage, 
	Xper-tree man macro definitions, 
	Xand support for man pages for multiple
	Xarchitectures or software versions from the same host.
	X.ps +1
	X.AE
	X.NH 
	XIntroduction
	X.PP
	XThe \s-1UNIX\s0 on-line manual system was 
	Xdesigned many years ago to suit the needs of the systems
	Xat the time.  Since then, 
	Xdespite 
	Xthe growth in complexity of 
	Xtypical systems and the need for more sophisticated software
	Xto support them,
	Xfew modifications of major significance
	Xhave been
	Xmade to the program.
	XThis paper describes problems inherent
	Xin earlier versions of the \fIman\fP program, proposes solutions 
	Xto these problems, and outlines one implementation of these solutions.
	X.NH
	XThe Problem
	X.NH 2
	XThe Monolithic Approach
	X.PP
	XOne of the most serious problems with the \fIman\fP program up to 
	Xand including the \s-1BSD\s04.2 release was that 
	Xall man pages on the entire system were expected to reside
	Xunder a common directory, 
	X\fI/usr/man\fR.
	XThere was no 
	Xnotion of separate sets of man pages installed on
	Xthe same machine in different subdirectories.
	XAt large installations, 
	Xsituations commonly arise in which this 
	Xfunctionality is desirable.  
	XA site may wish to keep vendor-supplied man pages
	Xseparate from man pages that were developed locally 
	Xor acquired from some third party.  
	XAn individual or group may wish to maintain their own set 
	Xof man pages.
	XMultiple versions of the same software package
	Xmight be simultaneously installed on the same machine.  
	XA heterogeneous environment may want to be able to view man pages for
	Xall available architectures from any machine.
	XGiven the requirement that all man pages live in the 
	Xsame directory, these scenarios are difficult to impossible to 
	Xsupport.
	X.PP
	XThe \fIman\fP program distributed in the \s-1BSD\s04.3 release 
	Xincluded the concept of a \s-1MANPATH\s0,
	Xa colon-delimited 
	Xlist of complete man trees taken either from the user's
	Xenvironment or supplied on the command line.
	XWhile this was a vast improvement over the previous monolithic approach,
	Xseveral significant problems remained.
	XFor one thing, the program still had to use
	Xthe 
	X.M access 2
	Xsystem call on all possible paths to find out 
	Xwhere the man page for a particular topic 
	Xexisted.  
	XWhen the user has a \s-1MANPATH\s0 containing multiple
	Xcomponents, the time needed for the \fIman\fP program to locate
	Xa man page is often noticeable, particularly when the target
	Xman page does not exist.
	X.NH 2
	XHard-coded Section Names
	X.PP
	XAnother problem with the \fIman\fP program unresolved by 
	Xthe \s-1BSD\s04.3 release
	Xwas that all possible sections in which a man page could 
	Xreside were hard-coded 
	Xinto the program.  This means that while a 
	X.B manp



More information about the Alt.sources mailing list