Xref: utzoo alt.sources:2973 comp.lang.perl:3480 Path: utzoo!utgpu!cs.utexas.edu!uunet!convex!usenet From: tchrist@convex.COM (Tom Christiansen) Newsgroups: alt.sources,comp.lang.perl Subject: The Answer to All Man's Problems (part 6 of 6) Keywords: man perl Message-ID: <1991Jan07.222531.10907@convex.com> Date: 7 Jan 91 22:25:31 GMT References: <1991Jan07.220902.9726@convex.com> Sender: usenet@convex.com (news access account) Reply-To: tchrist@convex.COM (Tom Christiansen) Followup-To: alt.sources.d Organization: CONVEX Software Development, Richardson, TX Lines: 846 Nntp-Posting-Host: pixel.convex.com 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 () { X if (/^\.EQ/) { X $_ = ; X $eqn = 1 unless /\.(if|nr)/; # has eqn output not input X } X if (/^\.TS/) { X $_ = ; 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 for $#ssindex subsections via pager: "; X $_ = ; 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 = ; 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 () { 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 () { 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 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 $why: "; X $_ = ; 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 () {\n"; X X for (@ARGV) { X s#/#\\/#g; X $code .= < ) { X unless (chdir($dir)) { X warn "can't chdir to $root/$dir: $!"; X $status++; X next; X } X unless (opendir(DIR, '.')) { X warn "can't opendir $root/$dir: $!"; X $status++; X next; X } X foreach $FILE ( readdir(DIR) ) { X next if $FILE eq '.' || $FILE eq '..'; X $path = "$root/$dir/$FILE"; X if ($FILE !~ /\S\.\S/ || !-f $FILE) { X print "skipping non-man file: $path\n" if $debug; X next; X } X if ($FILE =~ /\.Z$/ || $dir =~ /\.Z$/) { X $FILE = "$ZCAT $FILE|"; X } X print STDERR "grepping $path\n" if $debug; X unless (open FILE) { X warn "can't open $root/$dir/$FILE: $!"; X $status++; X next; X } X eval $code; X die $@ if $@; X } X unless (chdir ($root)) { X warn "can't return to $root: $!"; X $status++; X last; X } X } X } X exit ($status || !$found); X} SHAR_EOF if test 39119 -ne "`wc -c < 'man'`" then echo shar: "error transmitting 'man'" '(should have been 39119 characters)' fi chmod 755 'man' fi echo shar: "extracting 'catwhatis'" '(553 characters)' if test -f 'catwhatis' then echo shar: "will not over-write existing file 'catwhatis'" else sed 's/^ X//' << \SHAR_EOF > 'catwhatis' X#!/usr/local/bin/perl X X$manpath = shift || $ENV{'MANPATH'} || '/usr/man/'; X Xfor $manroot (split(/:/, $manpath)) { X X unless (dbmopen(manroot, "$manroot/whatis", undef)) { X warn("Can't dbmopen $manroot/whatis: $!\n"); X next; X } X X print "$manroot:\n"; X X while (($key,$value) = each %manroot) { X X for (split(/\002/, $value)) { X if (/\001/) { X ($cmd, $page, $section, $desc) = split(/\001/); X printf("%-30s - %s\n", "$cmd ($section)", $desc); X } else { X printf("%-30s > %s\n", "$key", $_); X } X } X } X X dbmclose(manroot); X} SHAR_EOF if test 553 -ne "`wc -c < 'catwhatis'`" then echo shar: "error transmitting 'catwhatis'" '(should have been 553 characters)' fi chmod 775 'catwhatis' fi echo shar: "extracting 'countman'" '(326 characters)' if test -f 'countman' then echo shar: "will not over-write existing file 'countman'" else sed 's/^ X//' << \SHAR_EOF > 'countman' X#!/usr/local/bin/perl X# X# countman -- count the number of entries in a man tree X X$| = 1; Xfor (split(/:/, shift || $ENV{'MANPATH'} || '/usr/man')) { X dbmopen(%whatis, "$_/whatis", undef) || (warn "$0: dbmopen $_: $!\n",next); X print $_, ': '; X for ($count = 0; each %whatis; $count++) { } X print $count, "\n"; X} SHAR_EOF if test 326 -ne "`wc -c < 'countman'`" then echo shar: "error transmitting 'countman'" '(should have been 326 characters)' fi chmod 775 'countman' fi echo shar: "extracting 'cfman.8'" '(6282 characters)' if test -f 'cfman.8' then echo shar: "will not over-write existing file 'cfman.8'" else sed 's/^ X//' << \SHAR_EOF > 'cfman.8' X.TH CFMAN 8L \fIConvex-Internal\fP "15 November 1989" "\fBDocumentation Administration Toolkit\fP" X.de Sh X.br X.PP X.ne 4 X.ti -.5i X\fB\\$1\fR X.PP X.. X.de LB \" little and bold X.ft B X.if !"\\$1"" \&\s-1\\$1\s+1 \\$2 \\$3 \\$4 \\$5 \\$6 X.ft R X.. X.de Sp X.if t .sp .5v X.if n .sp X.. X.ds lq \&"\" X.ds rq \&"\" X.if t \ X. ds lq `` X.if t \ X. ds rq '' X.de Q X\*(lq\\$1\*(rq\\$2 X.. X.Sh NAME Xcfman \- cross-reference man pages for internal consistency X.Sh SYNOPSIS X.B cfman X[ X.B \-d Xlevel X] X[ X.B \-s Xsections X] X[ X.B \-p Xmanpath X] X[ X.B \-x Xxrefpath X] X[ pattern | pathname ] ... X.br X.Sh DESCRIPTION X.I XCfman Xis a X.I perl Xprogram that checks that man page sources Xare mutually consistent in their X.LB "SEE ALSO" Xreferences. XIt will also report any X.LB ".TH" Xline that claims the Xman page is in a different place than X.I cfman Xfound it. X.PP XWhen supplied with no arguments, X.I cfman Xwill check all files (matching *.*) it finds in each man directory in Xyour colon-delimited X.LB "$MANPATH" Xenvariable if set, or in X.I /usr/man Xotherwise. It first verifies that the X.LB ".TH" Xsays Xthe man page is really where it should be, e.g. if the Xline is X.br X.in +.5i X.nf X\f(TA X\&.TH\ \ WIDGET\ \ 8 X.in -.5i X\fP X.fi X.br Xthen \fIwidget.8\fP should be the filename currently Xbeing examined. All upper-case will map to all lower-case, Xbut mixed case will be preserved for compatibility with Xthe X.LB X11 Xman pages. X.PP X.I Cfman Xthen skips ahead to the X.LB "SEE ALSO" Xsection and retrieves Xall comma-delimited entries of the general Xform \fIpagename(section)\fP. It first looks in the file X\&../man\fIsection/pagename.section\fP. If this fails Xand the current file ended in one of \fB[npl]\fP, but the X.I section Xreferenced is either X\fB1\fP or \fB8\fP, then it will check in X.I ../man8. XFailing this, X.I cfman Xchecks to see whether the referenced man page has been Xinstalled stripped of its subsection, e.g. \fIuucp\fP(1c) Xhas found its way into \fIuucp\fP(1). It then checks Xto see whether something in section \fB1\fP has been mis-installed Xin section \fB8\fP, or vice versa, or either one in section \fBl\fP Xmis-installed in the Xin section \fB8\fP and vice-versa. If all else fails, X.I cfman Xwill guess that a man page is referenced without its Xproper subsection, as in a reference to \fIrcp(1)\fP Xthat should really have been to \fIrcp(1c)\fP. If it finds Xthe misplaced man page, it reports where the reference Xthought it was and where it really was. Otherwise it Xreports the man page as missing. X.PP XThe X.LB $MANPATH Xvariable may be overridden by Xthe \fB-p\fP option. XAll checks will Xbe performed across each subtree specified in the manpath X(either from the environment of the command line), Xunless altered with the \fB-x\fP option. As a short-cut, Xthe \fIxrefpath\fP may have a leading colon to indicate Xthat it is to be concatenation of the \fImanpath\fP Xand the supplied \fIxrefpath\fP. X.PP XYou can restrict the sections checked with the \fB-s\fP Xswitch. By default, sections 1 through 8 will be examined. XThe section may be a shell metacharacter expression, Xlike X.Q ? Xor X.Q [18lpn] . X.PP XYou may restrict the individual man pages cross-referenced Xby specifying which ones you're interested in on the command Xline. These may be full pathnames, simple names like X.Q tty , Xor a shell metacharacter expression like X.Q *net . XIf Xno period occurs in the simple name, it is assumed to mean that Xthe name may have any extension. If you list specific Xman pages on the command line and X.I cfman Xfinds none matching your specification, it will report this fact. XSee the X.LB "EXAMPLES" Xsection. X.PP XMan pages that are linked by placing a \fB.so\fP directive Xon the first line will be correctly followed, and no man page Xin the same subtree. Very limited support for alternate Xman macros is provided: the X.I "\fIRand MH Message Handling System\fP" 's Xman macro set are recognized, as is Larry Wall's X.LB .Sh Xreplacement for X.LB .SH. X.Sh DIAGNOSTICS XRequires X.I perl Xto be at least version 3.0, patchlevel 1 to run. The Xprogram will abort if you try to run it with an Xearlier version of perl X.PP XFive different tracing levels can be specified with the \fB-d\fP Xoption. If any debugging is turned on, the walk through Xthe different components of the manpath are traced. XDebug values are numeric and additive, and are interpreted Xthis way: X.Sp X.in +.5i X.nf X.ne 5 X 1 Trace each man page examined X 2 Trace each cross reference examined X 4 Trace each \s-1\fB.TH\s+1\fP check X 8 Trace each file-existence test X 16 Trace each line X'in -.5i X.fi X.Sp XTracing information and other warnings are printed to X\fIstderr\fP, but normal messages about bad cross references Xare printed to \fIstdout\fP as that is \fIcfman\fP's principle Xtask. X.PP XEmbedded X.I troff Xstring macros starting \e*( cannot be resolved, and they Xwill trigger a warning message if found in the X.LB .TH Xor X.LB "SEE ALSO" Xsections. X.Sh EXAMPLES X.nf X\f(TA Xcfman # do all in $MANPATH Xcfman -p /export/exec/sun3/share/man # sun man pages Xcfman -p $HOME/man:/usr/local/mh/man:/usr/local/man:/usr/man Xcfman -p /usr/local/man -x :/usr/man # xref also in /usr/man Xcfman -s 18nlp # only these sections Xcfman '*tty*' fubar # check for *tty*.* and fubar.* Xcfman `pwd`/*.[1-8] # just check these files Xcfman -s 23 'sys*' # sys*.* files in sections 2,3 Xcfman -s 1 -p /export/exec/sun3/share/man X.fi X\fR X.PP XThe last command produced this output on my machine: X.nf X\f(TA Xbanner.1v: thinks it's in banner(1) Xfoption.1: skyversion(8) missing Xfrom.1: prmail(1) missing Xmake.1: rstat(8c) missing Xman.1: apropos(1) missing Xold-perfmon.1: missing .TH Xoldperfmon.1: missing .TH Xoldsetkeys.1: thinks it's in setkeys(1) Xorganizer.1: restore(1v) really in restore(8) Xsunview.1: traffic(1) really in traffic(1c) Xsort.1v: thinks it's in sort(1) Xsum.1v: thinks it's in sum(1) X.fi X\fR X.Sh ENVIRONMENT XThe default manpath will be taken from X.LB $MANPATH Xif set. X.Sh "SEE ALSO" Xman(1), troff(1), perl(1), man(7). X.Sh BUGS XDue to the current implentation of globbing in X.I perl, Xyou can get X.Q "Arguments too long" Xerrors. The workaround is to run X.I cfman Xfirst on X.Q [a-m]* , Xand then on X.Q [n-z]* . X.Sh AUTHOR XTom Christiansen, \s-1CONVEX\s+1 Computer Corporation. SHAR_EOF if test 6282 -ne "`wc -c < 'cfman.8'`" then echo shar: "error transmitting 'cfman.8'" '(should have been 6282 characters)' fi chmod 644 'cfman.8' fi echo shar: "extracting 'FEATURES'" '(2356 characters)' if test -f 'FEATURES' then echo shar: "will not over-write existing file 'FEATURES'" else sed 's/^ X//' << \SHAR_EOF > 'FEATURES' XFeatures include but are not limited to: X X * almost always faster than standard man (try 'man me') X X * take much less diskspace for catpages X X * supports per-tree tmac macros X X * compressed man and cat files X X * user-definable man path via $MANPATH or -M optoin X X * $MANPATH autoconfigged based on $PATH if not set X X * user-definable section search order via -S or $MANSECT. Thus X programmers can get stty(3) before stty(1). X X * $PAGER support X X * show all the places you would find a man page (-w option) X and in what order. X X * display all available man page on a topic (-a option) X X * no limits on what subsections go where (if you want to add 7x, ok) X X * support for multi-char sections like man1m/*.1m or manavs/*.avs X X * man -K for regexp apropos X X * grep through all the man pages in $MANPATH X X * section and subsection indexing for long man pages X X * support for alternate architectures docs on same machine X X * ability to run man on a local file X X * ability to easily troff (or preview) a man page X X * recognizes embedded filter directives for tbl and eqn X X * does the right thing for man tree that don't have DBM whatis files X X * support for connecting online problem reports to right man page X X * there's an extended usage message (man -U) for further help X and to show current defaults. X X XHere are some features of this version of makewhatis: X X * it's faster. X X * tries hard to make pretty output, stripping troff directives. X X * doesn't blow up on more files in a man directory X than the shell will glob. X X * accepts troff string macros for the dashes in the X the NAME section. X X * prints a diagnostic for a malformed NAME section. X X * detects linked (hard, soft, or via .so) man pages X X * finds *all* references in the NAME section. X X * recognizes MH's man macros (and .Sh from lwall). X X * many other things that makewhatis used to do wrong X XHere are some supporting utilities that are included: X X * catman -- new version that groks compressed files X X * catwhatis -- display the whatis databases X X * straycats -- find cat pages with no man page ancestor X X * countman -- find how many man pages you can get at X X * cfman -- find bad SEE ALSO references in man pages SHAR_EOF if test 2356 -ne "`wc -c < 'FEATURES'`" then echo shar: "error transmitting 'FEATURES'" '(should have been 2356 characters)' fi chmod 664 'FEATURES' fi chmod 775 . echo shar: "done with directory 'man'" cd .. exit 0 # End of shell archive