home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / scripts-convex / man.shar / man / man < prev    next >
Encoding:
Text File  |  1991-03-04  |  39.7 KB  |  1,507 lines

  1. #!/usr/local/bin/perl 
  2. # man - perl rewrite of man system
  3. # tom christiansen <tchrist@convex.com>
  4. #
  5. # Copyright 1990 Convex Computer Corporation.
  6. # All rights reserved.
  7. #
  8. # --------------------------------------------------------------------------
  9. # begin configuration section
  10. #
  11. # this should be adequate for CONVEX systems.  if you copy this script 
  12. # to non-CONVEX systems, or have a particularly outre local setup, you may
  13. # wish to alter some of the defaults.
  14. # --------------------------------------------------------------------------
  15.  
  16. $PAGER = $ENV{'MANPAGER'} || $ENV{'PAGER'} || 'more';
  17.  
  18. # assume "less" pagers want -sf flags, all others must accept -s.
  19. # note: some less's prefer -r to -f.  you might also add -i if supported.
  20. #
  21. $is_less = $PAGER =~ /^\S*less(\s+-\S.*)?$/;
  22. $PAGER    .= $is_less ? ' -si' : ' -s';       # add -f if using "ul"
  23.  
  24. # man roots to look in; you would really rather use a separate tree than 
  25. # manl and mann!  see %SECTIONS and $MANALT if you do.
  26. $MANPATH  = &config_path;
  27.  
  28. # default section precedence
  29. $MANSECT  = $ENV{'MANSECT'} || 'ln16823457po';
  30.  
  31. # colons optional unless you have multi-char section names
  32. # note that HP systems want this:
  33. #    $MANSECT  = $ENV{'MANSECT'} || '1:1m:6:8:2:3:4:5:7';
  34.  
  35. # alternate architecture man pages in 
  36. # ${MANALT}/${machine}/man(.+)/*.\11*
  37. $MANALT = $ENV{'MANALT'} || '/usr/local/man';
  38.  
  39. # default program for -t command 
  40. $TROFF    = $ENV{'TROFF'} || 'nitroff';
  41.  
  42. $NROFF    = 'nroff';
  43. $NROFF_CAN_BOLD = 0;    # if nroff puts out bold as H\bH
  44.  
  45. # this are used if filters are needed
  46. $TBL      = 'tbl';
  47. $NTBL       = "$TBL -D";    # maybe you need -TX instead
  48. $NEQN      = 'neqn';
  49. $EQN      = 'eqn';
  50. $SED      = 'sed';
  51.  
  52. # define this if you don't have/want UL;
  53. # without ul, you probably need COL defined unless your PAGER is very smart
  54. # you also must use col instead of ul if you've any tbl'd man pages, such 
  55. # as from the X man pages or the eqnchar.7 page.
  56. $COL      = 'col';  
  57. $UL      = '';        # set to '' if you haven't got ul
  58. die 'need either $UL or $COL' unless $UL || $COL;
  59.  
  60. # need these for .Z files or dirs
  61. $COMPRESS = 'compress';
  62. $ZCAT      = 'zcat';
  63. $CAT      = 'cat';
  64.  
  65. # define COMPRESS_DIR if pages might have moved to manX.Z/page.X (like HPs)
  66. $COMPRESS_DIR = 1;
  67. # define COMPRESS_PAGE if pages might have moved to manX/page.X.Z  (better)
  68. $COMPRESS_PAGE = 1;
  69.  
  70. # Command to format man pages to be viewed on a tty or printed on a line printer
  71. $CATSET      = "$NROFF -h -man -";
  72.  
  73. $CATSET  .= " | $COL" if $COL;
  74.  
  75. # Command to typeset a man page
  76. $TYPESET  = "$TROFF -man";
  77.  
  78.  
  79. # flags: GNU likes -i, BSD doesn't; both like -h, but BSD doesn't document it
  80. # if you don't put -i here, i'll make up for it later the hard way
  81. $EGREP = '/usr/local/bin/egrep';    
  82. if (-x $EGREP) {
  83.     $EGREP .= ' -i -h';
  84. } else {
  85.     $EGREP = '/usr/bin/egrep';
  86.     unless (-x $EGREP) {
  87.     $EGREP = '';
  88.     } else {
  89.     $EGREP .= ' -h';
  90.     }
  91.  
  92. # sections that have verbose aliases
  93. # if you change this, change the usage message
  94. #
  95. # if you put any of these in their own trees, comment them out and make 
  96. # a link in $MANALT so people can still say 'man local foo'; for local,
  97. #    cd $MANALT; ln -s . local
  98. # for the other trees (new, old, public) put either them or links
  99. # to them in $MANALT
  100. #
  101. %SECTIONS = (                
  102.     'local',    'l',
  103.     'new',    'n',
  104.     'old',    'o',
  105.     'public',    'p' );
  106.  
  107. # turn this on if you want linked (via ".so" or otherwise) man pages
  108. # to be found even if the thing they are linked to doesn't know it's
  109. # being linked to -- that is, its NAME section doesn't have reference
  110. # to it.  eg, if you call a man page 'gnugrep' but it's own NAME section
  111. # just calls it grep, then you need this.  usually a good idea.
  112. #
  113. $STUPID_SO    = 1;  
  114.  
  115. # --------------------------------------------------------------------------
  116. # end configuration section
  117. # --------------------------------------------------------------------------
  118.  
  119. # CONVEX RCS keeps CHeader; others may prefer Header
  120. ($bogus, $version) = split(/:\s*/,'$CHeader: man 0.43 91/02/16 10:42:57 $',2);
  121. chop($version); chop($version);
  122.  
  123. require 'getopts.pl';
  124.  
  125. # could do this via ioctl(0,$TIOCGETP,$sgtty) if I were really concerned
  126. #
  127. $rows = ($ENV{'TERMCAP'} =~ /:li#(\d+):/) ? $1 : 24;
  128.  
  129. %options = (
  130.     'man',    'T:m:P:M:c:s:S:fkltvwdguhaAiDK',
  131.     'apropos',    'm:P:MvduaK',
  132.     'whatis',    'm:P:M:vduh',
  133.     'whereis',    'm:P:M:vduh'
  134. );
  135.  
  136. ($program = $0) =~ s,.*/,,;
  137.  
  138. $apropos = $program eq 'apropos';
  139. $whatis  = $program eq 'whatis';
  140. $whereis = $program eq 'whman';
  141. $program = 'man' unless $program;
  142.  
  143. &Getopts($options = $options{$program}) || &usage;
  144.  
  145. if ($opt_u) {
  146.     &version if $opt_v;
  147.     &usage;
  148.     # not reached
  149.  
  150. if ($opt_v) {
  151.     &version;
  152.     exit 0;
  153. }
  154.  
  155. &usage if $#ARGV < 0;
  156.  
  157. $MANPATH = $opt_P     if $opt_P;    # backwards contemptibility
  158. $MANPATH = $opt_M     if $opt_M;
  159.  
  160. $want_section = $opt_c     if $opt_c;    # backwards contemptibility
  161. $want_section = $opt_s     if $opt_s;
  162.  
  163. $hard_way = $opt_h    if $opt_h;
  164.  
  165. if ($opt_T) {
  166.     $opt_t = 1;
  167.     $TYPESET =~ s/$TROFF/$opt_T/;
  168.     $TROFF = $opt_T;
  169.  
  170. $MANPATH = "$MANALT/$opt_m"        # want different machine type (undoc)
  171.             if $machine = $opt_m;
  172.  
  173. $MANSECT = $opt_S    if $opt_S;    # prefer our own section ordering
  174.  
  175. $whatis = 1        if $opt_f;
  176. $apropos = 1        if $opt_k || $opt_K;
  177. $fromfile = 1        if $opt_l;
  178. $whereis = 1          if $opt_w;
  179. $grepman = 1        if $opt_g;    
  180. $| = $debug = 1        if $opt_d;
  181. $full_index = 1     if $opt_i;
  182. $show_all = 1        if $opt_a;
  183. $stripBS = 1        if $opt_D;
  184. $query_all = $opt_A    if $opt_A;
  185.  
  186. $roff = $opt_t ? 'troff' : 'nroff';   # for indirect function call
  187.  
  188.  
  189. # maybe they said something like 'man vax ls'
  190. if ($#ARGV > 0) {
  191.     local($machdir) = $MANALT . '/' . $ARGV[0];
  192.     if (-d $machdir) {
  193.     $MANPATH = $machdir;
  194.     $machine = shift;
  195.     } 
  196.  
  197. @MANPATH = split(/:/,$MANPATH);
  198.  
  199. # assign priorities to the sections he cares about
  200. # the nearer the front the higher the sorting priority
  201. $secidx = 0;
  202. $delim = ($MANSECT =~ /:/) ? ':' : ' *';
  203. for (reverse split(/$delim/, $MANSECT)) {
  204.     if ($_ eq '') {
  205.     warn "null section in $MANSECT\n";
  206.     next;
  207.     } 
  208.     $MANSECT{$_} = ++$secidx;
  209.  
  210.  
  211. if ($whatis) {
  212.     &whatis;
  213. } elsif ($apropos) {
  214.     &apropos;
  215. } elsif ($whereis) {
  216.     &whereis;
  217. } elsif ($grepman) {
  218.     &grepman;
  219. } else {
  220.     &man;
  221.  
  222. exit $status;
  223.  
  224. # --------------------------------------------------------------------------
  225. # fill out @whatis array with all possible names of whatis files
  226. # --------------------------------------------------------------------------
  227. sub genwhatis {
  228.     local($elt,$whatis);
  229.  
  230.     for $elt (@MANPATH) {
  231.     $whatis = "$elt/whatis";
  232.     if (-f $whatis) {
  233.         push(@whatis, $whatis);
  234.     } else {
  235.         warn "$whatis: $!\n";# if $opt_M || $opt_P; # they asked for it
  236.     } 
  237.     } 
  238.  
  239.     die "$program: No whatis databases found, please run makewhatis\n" 
  240.     if $#whatis < 0;
  241.  
  242. # --------------------------------------------------------------------------
  243. # run whatis (man -f)
  244. # --------------------------------------------------------------------------
  245. sub whatis {
  246.     local($target, %seeking, $section, $desc, @entries);
  247.  
  248.     &genwhatis;
  249.  
  250.     for $target (@ARGV) { $seeking{$target} = 1; } 
  251.  
  252.     if ($hard_way) {
  253.     &slow_whatis;
  254.     } else { 
  255.     &fast_whatis;
  256.     }
  257.  
  258.     for $target (keys %seeking) {
  259.     print "$program: $target: not found.\n";
  260.     $status = 1;
  261.     } 
  262.  
  263. # --------------------------------------------------------------------------
  264. # do whatis lookup against dbm file(s)
  265. # --------------------------------------------------------------------------
  266. sub fast_whatis {
  267.     local($entry, $cmd, $page, $section, $desc, @entries);
  268.  
  269.     for $INDEX (@whatis) {
  270.     unless (-f "$INDEX.pag" && dbmopen(INDEX,$INDEX,0444)) {
  271.         warn "$program: No dbm file for $INDEX: $!\n" if $debug; 
  272.         #$status = 1;
  273.         if (-f $INDEX) {
  274.         local(@whatis) = ($INDEX);  # dynamic scoping obfuscation
  275.         &slow_whatis;
  276.         }
  277.         next;
  278.     } 
  279.            for $target (@ARGV) {
  280.         local($ext);
  281.         @entries = &quick_fetch($target,'INDEX');
  282.         next if $#entries < 0;
  283.         # $target =~ s/([^\w])/\\$1/g;
  284.         for $entry (@entries) {
  285.         ($cmd, $page, $section, $desc) = split(/\001/, $entry);
  286.         #  STUPID_SO is one that .so's that reference things that
  287.         #  don't know they are being referenced.  STUPID_SO may cause
  288.         #  some peculiarities.
  289.         unless ($STUPID_SO) {
  290.             next unless $cmd =~ /$target/i || $cmd =~ /\.{3}/;
  291.         }
  292.  
  293.         delete $seeking{$target};
  294.         ($ext) = $page =~ /\.([^.]*)$/;
  295.         printf("%-20s - %s\n", "$cmd ($ext)", $desc);
  296.         }
  297.     } 
  298.     dbmclose(INDEX);
  299.     } 
  300.     
  301.  
  302. # --------------------------------------------------------------------------
  303. # do whatis lookup the hard way
  304. # --------------------------------------------------------------------------
  305. sub slow_whatis {
  306.     local($query);
  307.     local($WHATIS);
  308.  
  309.     for (@ARGV) { s/([^\w])/\\$1/g; } 
  310.  
  311.     $query = '^[^-]*\b?(' . join('|',@ARGV) . ')\b[^-]* -';
  312.  
  313.     if ($EGREP)  {
  314.     if (&run("$EGREP '$query' @whatis")) {
  315.         # pity can't tell which i found
  316.         %seeking = ();
  317.     }
  318.     } else {
  319.     foreach $WHATIS (@whatis)  {
  320.         unless (open WHATIS) {
  321.         warn "can't open $WHATIS: $!\n";
  322.         next;
  323.         } 
  324.         while (<WHATIS>) {
  325.         next unless /$query/i;
  326.         ($target = $+) =~ y/A-Z/a-z/;
  327.         delete $seeking{$target};
  328.         print;
  329.         } 
  330.         close WHATIS;
  331.     } 
  332.     } 
  333.  
  334. # --------------------------------------------------------------------------
  335. # run apropos (man -k)
  336. # --------------------------------------------------------------------------
  337. sub apropos {
  338.     local($_, %seeking, $target, $query);
  339.     &genwhatis;  
  340.  
  341.     # fold case on apropos args
  342.     for (@ARGV) { 
  343.     y/A-Z/a-z/; 
  344.     $seeking{$_} = 1; 
  345.     s/(\W)/\\$1/g unless $opt_K;
  346.     } 
  347.     $query = join('|',@ARGV);
  348.  
  349.  
  350.     if ($EGREP)  {
  351.     # need to fake a -i flag?
  352.     unless ($EGREP =~ /-\w*i/) {
  353.         local($C);
  354.         local(@pat) = split(//,$query);
  355.         for (@pat) {
  356.         ($C = $_) =~ y/a-z/A-Z/ && ($_ = '[' . $C . $_ . ']');
  357.         } 
  358.         $query = join('',@pat);
  359.     } 
  360.     if (&run("$EGREP '$query' @whatis | $PAGER")) {
  361.         %seeking = ();
  362.     } 
  363.     } else {  # use perl
  364.     foreach $WHATIS (@whatis) {
  365.         unless (open WHATIS) {
  366.         warn "can't open $WHATIS: $!\n";
  367.         next;
  368.         } 
  369. WHATIS:        while (<WHATIS>) {
  370.         next unless /$query/io;          # /o ok, because only called once
  371.         $target = $+;
  372.         $target =~ s/\\//g;
  373.         delete $seeking{$query};
  374.         print;
  375.         } 
  376.         close WHATIS;
  377.     } 
  378.  
  379.     } 
  380.  
  381.     for $target (keys %seeking) {
  382.     warn "$program: $target: nothing appropriate\n";
  383.     $status = 1;
  384.     }
  385. }
  386.  
  387. # --------------------------------------------------------------------------
  388. # print out usage message via pager and exit
  389. # --------------------------------------------------------------------------
  390. sub usage {
  391.     unless ($opt_u) {
  392.     warn "usage: $program [-flags] topic ...\n";
  393.     warn "        (use -u for long usage message)\n";
  394.     } else {
  395.     open (PIPE, "| $PAGER");
  396.     print PIPE <<USAGE;  # in case he wants a page
  397. USAGE SUMMARY: 
  398.     man [-flags] [section] page[/index] ...
  399.     (section is [1-8lnop], or "new", "local", "public", "old")
  400.     (index is section or subsection header)
  401.  
  402.     man [-flags] -f topic ...  
  403.     (aka "whatis")
  404.  
  405.     man [-flags] -k keyword ...
  406.     (aka "apropos")
  407.  
  408. FLAGS: (most only make sense when invoked as 'man')
  409.     -a        show all possible man pages for this topic
  410.     -A        ask which selection if several available
  411.  
  412.     -l file    do man processing on local file
  413.     -f topic    list table of contents entry for topic
  414.     -k keyword    give table of contents entries containing keyword
  415.     -K pattern  as -K but allow regexps
  416.     -g pattern  grep through all man pages for patterns
  417.     -w topic    which files would be shown for a given topic
  418.     -i topic    show section and subsection index for use with topic/index
  419.  
  420.     -M path    use colon-delimited man path for searching (also as -P)
  421.     -S sects    define new section precedence 
  422.  
  423.     -t        troff the man page
  424.     -T path    call alternate typesetter on the man page
  425.  
  426.     -d        print out all system() commands before running them
  427.     -h        do all lookups the hard way, ignoring any DBM files
  428.     -u        this message
  429.     -v        print version string
  430.     -D        strip backspaces from output
  431.  
  432. ENVIRONMENT:
  433.     \$PAGER    pager to pipe terminal-destined output through 
  434.     \$MANPATH    like -M path
  435.     \$MANSECT    like -S sects
  436.     \$MANALT    used for alternate hardware types (or obsolete -m flag)
  437.     \$TROFF    like -T path
  438.  
  439. CURRENT DEFAULTS:
  440.     PAGER    $PAGER
  441.     MANPATH    $MANPATH
  442.     MANSECT    $MANSECT
  443.     MANALT    $MANALT
  444.     TROFF    $TROFF
  445.  
  446. NOTES:  (\$manroot is each component in \$MANPATH)
  447.     * If \$manroot/whatis DBM files do not exist, a warning will be 
  448.     printed (if -d flag) and -h will be assumed for that \$manroot only.
  449.     * If \$manroot/tmac.an exists, it will be used for formatting 
  450.     instead of the normal -man macros.
  451.     * Man pages may be compressed either in (for example) man1.Z/who.1 
  452.         or man1/who.1.Z; cat pages will go into corresponding places.
  453.     * If the man page contains .EQ or .TS directives, eqn and/or tbl
  454.         will be invoked as needed at format time.
  455. USAGE
  456.     close PIPE;
  457.     }
  458.     warn "couldn't run long usage message thru $PAGER?!?!\n" if $?;
  459.     exit 1;
  460. }
  461.  
  462. # --------------------------------------------------------------------------
  463. # lookup a given key in the given man root; returns list of hits
  464. # --------------------------------------------------------------------------
  465. sub fetch {
  466.     local($key,$root) = @_;
  467.     local(%recursed);
  468.  
  469.     return $dbmopened{$root}
  470.     ? &quick_fetch($key,$dbm{$root})
  471.     : &slow_fetch($key,$root);
  472. }
  473.  
  474. # --------------------------------------------------------------------------
  475. # do a quick fetch of a key in the dbm file, recursing on indirect references
  476. # --------------------------------------------------------------------------
  477. sub quick_fetch {
  478.     local($key,$array) = @_;
  479.     local(@retlist) = ();
  480.     local(@tmplist) = ();
  481.     local($_, $entry);
  482.     local($name, $ext);
  483.     local(@newlist);
  484.  
  485.     return @retlist unless $entry = eval "\$$array".'{$key};';
  486.  
  487.     if ($@) { chop $@; die "bad eval: $@"; }
  488.  
  489.     @tmplist = split(/\002/, $entry);
  490.     for (@tmplist) {
  491.     if (/\001/) {
  492.         push(@retlist, $_);
  493.     } else {
  494.         ($name, $ext) = /(.+)\.([^.]+)/;
  495.         push(@retlist, 
  496.         grep(/[^\001]+\001[^\001]+\001${ext}\001/ || /[^\001]+${ext}\001/,
  497.             &quick_fetch($name, $array)))
  498.             unless $recursed{$name}++;
  499.     # explain and diction are near duplicate man pages referencing
  500.     # each other, requiring the $recursed check.  one should be removed
  501.     }
  502.     } 
  503.     return @retlist;
  504.  
  505. # --------------------------------------------------------------------------
  506. # do a slow fetch for target using perl's globbing notation
  507. # --------------------------------------------------------------------------
  508. sub slow_fetch {
  509.     local($target,$root) = @_;
  510.     local($glob, $stem, $entry);
  511.  
  512.     if ($want_section) {
  513.     if ($MANSECT{$want_section}) {
  514.         $stem = $want_section;
  515.     } else {
  516.         $stem = substr($want_section,0,1);
  517.         } 
  518.     $glob = "man$stem*";
  519.     } else {
  520.     $glob = 'man*';
  521.     } 
  522.     $glob = "$root/$glob/$target.*";
  523.     return <${glob}>;
  524. }
  525.  
  526. # --------------------------------------------------------------------------
  527. # run 'man -w'
  528. # --------------------------------------------------------------------------
  529. sub whereis {
  530.     local($target, @files);
  531.  
  532.     foreach $target (@ARGV) {
  533.     @files = &find_files($target);
  534.     if ($#files < $[) {
  535.         warn "$program: $target not found\n";
  536.         $status = 1;
  537.     } else {
  538.         print "$target: " if $#ARGV;
  539.         for (@files) { print &verify($_), " "; }
  540.         print "\n";
  541.     }
  542.     } 
  543.  
  544.  
  545. # --------------------------------------------------------------------------
  546. # what are the file names matching this target?
  547. # --------------------------------------------------------------------------
  548. sub find_files {
  549.     local($target) = @_;
  550.     local($root, $entry);
  551.     local(@retlist) = ();
  552.     local(@tmplist) = ();
  553.     local(@entries) = ();
  554.     local($tar_regx);
  555.     local($found) = 0;
  556.     # globals: $vars, $called_before, %dbm, $hard_way (kinda)
  557.  
  558.     $vars = 'dbm00';  # var for magic autoincrementation
  559.  
  560.     ($tar_regx = $target) =~ s/(\W)/\\$1/g;  # quote meta
  561.  
  562.     if (!$hard_way && !$called_before++) {
  563.     # generate dbm names
  564.     for $root (@MANPATH) {
  565.         $dbm{$root} = $vars++; # magic incr
  566.         $string = "dbmopen($dbm{$root},\"$root/whatis\",0444);";
  567.         unless (-f "$root/whatis.pag" && eval $string) {
  568.         if ($@) { 
  569.             chop $@;
  570.             warn "Can't eval $string: $@";
  571.         } else {
  572.             warn "No dbm file for $root/whatis: $!\n" 
  573.             if $opt_M || $opt_P || $debug;
  574.         }
  575.         #$status = 1;
  576.         next;
  577.         } 
  578.         $dbmopened{$root} = 1;
  579.     }
  580.     } 
  581.  
  582.     for $root (@MANPATH) {
  583.     local($fullname);
  584.     @tmplist = ();
  585.     if ($hard_way || !$dbmopened{$root})  {
  586.         next unless -d $root;
  587.         warn "slow fetch on $target in $root\n" if $debug;
  588.         @tmplist = &slow_fetch($target,$root);
  589.     } else {
  590.         @entries = &fetch($target,$root);
  591.         next if $#entries < 0;
  592.  
  593.         for $entry (sort @entries) {
  594.         ($cmd, $page, $section, $desc) = split(/\001/, $entry);
  595.  
  596.         # STUPID_SO is so that .so's that reference things that
  597.         # don't know they are being referenced.  STUPID_SO may
  598.         # cause peculiarities.
  599.         unless ($STUPID_SO) {
  600.             next unless $cmd =~ /$tar_regx/i || $cmd =~ /\.{3}/;
  601.         }
  602.         push(@tmplist, "$root/man$section/$page");
  603.         }
  604.     }
  605.     push(@retlist, sort bysection @tmplist);
  606.     last if $#retlist >= 0 && $hard_way;
  607.     }
  608. #    unless (@retlist || $hard_way) {
  609. #    # shameless (ab?)use of dynamic scoping
  610. #    local($hard_way) = 1;
  611. #    warn "recursing on find_files\n" if $debug;
  612. #    return &find_files($target);
  613. #    } 
  614.      return &trimdups(@retlist);
  615.  
  616. # --------------------------------------------------------------------------
  617. # run a normal man command
  618. # --------------------------------------------------------------------------
  619. sub man {
  620.     local($target,$page);
  621.     $isatty = -t STDOUT;
  622.  
  623.     &get_section;
  624.  
  625.     while ($target = shift(@ARGV)) {
  626.     undef $idx_topic;
  627.  
  628.     if (!$fromfile && $target =~ m!^([^/]+)/(.*)!) {
  629.         if (!$isatty) {
  630.         warn "$program: no tty, so no pager to prime with index\n";
  631.         $target = $1;
  632.         }  else {
  633.         ($target, $idx_topic) = ($1, $2);
  634.         } 
  635.     } else {
  636.         undef $idx_topic;
  637.     } 
  638.  
  639.     if ($show_all) {
  640.         local(@pages);
  641.         local($was_defined) = defined $idx_topic;
  642.         @pages = &find_files($target);
  643.         if (!@pages) {
  644.         &no_entry($target);
  645.         next;
  646.         } 
  647.         while ($tpage = shift @pages) {
  648.         undef $idx_topic unless $was_defined;
  649.         do $roff(&verify($tpage));
  650.         &prompt_RTN("to read $pages[0]") 
  651.             if $roff eq 'nroff' && @pages;
  652.         } 
  653.     } else {
  654.         $target = &get_page($target) unless $fromfile;
  655.         do $roff($target) if $target;
  656.     }
  657.     &prompt_RTN("to read man page for $ARGV[0]") 
  658.         if $roff eq 'nroff' && @ARGV;
  659.     } 
  660.  
  661. # --------------------------------------------------------------------------
  662. # find out if he wants a special section and save in $want_section
  663. # --------------------------------------------------------------------------
  664. sub get_section {
  665.     if (!$want_section) {
  666.     local($section) = $ARGV[0];
  667.     $section =~ tr/A-Z/a-z/;
  668.  
  669.     if ($want_section = $SECTIONS{$section}) {
  670.         shift @ARGV;
  671.     }  elsif (defined($MANSECT{$section}) || $section =~ /^\d\S*$/i) { 
  672.         $want_section = shift @ARGV;
  673.     } 
  674.     }
  675.     $want_section =~ tr/A-Z/a-z/;
  676.  
  677.     die "But what do you want from section $want_section?\n" 
  678.     if $want_section && $#ARGV < 0;
  679. }
  680.  
  681. # --------------------------------------------------------------------------
  682. # pick the first page matching his target and search orders
  683. # --------------------------------------------------------------------------
  684. sub get_page {
  685.     local($target) = @_;
  686.     local(@found, @want);
  687.  
  688.     unless (@found = &find_files($target)) {
  689.     &no_entry($target);
  690.     return '';
  691.     } 
  692.  
  693.     if (!$want_section) {
  694.     @want = @found;
  695.     } else {{
  696.     local($patsect); # in case it's section 3c++ 
  697.     ($patsect = $want_section) =~ s/(\W)/\\$1/g;
  698.  
  699.     # try exact match first
  700.     last if @want = grep (/\.$patsect$/, @found);
  701.  
  702.     # otherwise how about a subsection
  703.     last if @want = grep (/\.$patsect[^.]*$/, @found);
  704.  
  705.     # maybe it's in the wrong place (mano is notorious for this)
  706.     last if @want = grep (/man$patsect[^.]*\//, @found);
  707.  
  708.     &no_entry($target);
  709.     return '';
  710.     }}
  711.  
  712.     for (@want) { $_ = &verify($_) ; }
  713.     $found = $want[0];
  714.  
  715.     if (@want > 1 && $query_all) {
  716.     local($ans, $i);
  717.  
  718.     select(STDERR);
  719.  
  720.     print "There are ", 0+@want, 
  721.         " manual entries available for $target:\n";
  722.     for ($i = 0; $i <= $#want; $i++) {
  723.         printf "%3d\t%s\n", $i+1, $want[$i];
  724.     } 
  725.     {
  726.         print "Which section would you like? (select 0 for all) ";
  727.         ($ans = <STDIN>) ? chop($ans) : ($ans = "\004");
  728.  
  729.         exit if $ans eq "\004";
  730.         redo if $ans eq '';
  731.  
  732.         if ($ans eq '0') {
  733.         # more dynamic scope abuse
  734.         local(@ARGV) = ($target);
  735.         local($show_all) = 1;
  736.         &man;
  737.         return '';
  738.         } 
  739.         if (--$ans > $#want) {
  740.         print "But we only have ",1+$#want, " man pages!\n";
  741.         redo;
  742.         } 
  743.  
  744.         $found = $want[$ans];
  745.     }
  746.     } 
  747.  
  748.  
  749.     select(STDOUT);
  750.  
  751.     $found;
  752. }
  753.  
  754. # --------------------------------------------------------------------------
  755. # figure out full path name of man page, which may have been compressed
  756. # --------------------------------------------------------------------------
  757. sub verify {
  758.     local($path) = @_;
  759.     local($orig) = $path;
  760.  
  761.     return $path if -f $path;
  762.  
  763.     if ($COMPRESS_PAGE) {
  764.     $path .= '.Z';
  765.     return $path if -f $path;
  766.     $path =~ s/.Z//;
  767.     } 
  768.  
  769.     if ($COMPRESS_DIR) {
  770.     $path =~ s-(/[^/]*)$-.Z$1-;
  771.     return $path if -f $path;
  772.     } 
  773.  
  774.     warn "$program: $orig has disappeared -- rerun makewhatis\n";
  775.     $status = 1;
  776.     return '';
  777. }
  778.  
  779.  
  780. # --------------------------------------------------------------------------
  781. # whine about something not being found
  782. # --------------------------------------------------------------------------
  783. sub no_entry {
  784.     print STDERR "No manual entry for $_[0]";
  785.     if ($machine || $want_section) {
  786.     print STDERR " in";
  787.     print STDERR " section $want_section of" if $want_section;
  788.     print STDERR " the";
  789.     print STDERR " $machine" if $machine;
  790.     print STDERR " manual";
  791.     }
  792.     print STDERR ".\n";
  793.     $status = 1;
  794.  
  795. # --------------------------------------------------------------------------
  796. # order by section.  if the complete extension has a section
  797. # priority, use that.  otherwise use the first char of extension
  798. # only.  undefined priorities are lower than any defined one.
  799. # --------------------------------------------------------------------------
  800. sub bysection {
  801.     local ($e1, $e2, $p1, $p2, $s1, $s2);
  802.  
  803.     ($s1, $e1) = $a =~ m:.*/man([^/]+)/.*\.([^.]+)(\.Z)?$:;
  804.     ($s2, $e2) = $b =~ m:.*/man([^/]+)/.*\.([^.]+)(\.Z)?$:;
  805.  
  806.     $e1 = $s1 if $e1 !~ /^${s1}.*/;
  807.     $e2 = $s2 if $e2 !~ /^${s2}.*/;
  808.  
  809.     $p1 = $MANSECT{$e1} || $MANSECT{substr($e1,0,1)};
  810.  
  811.     $p2 = $MANSECT{$e2} || $MANSECT{substr($e2,0,1)};
  812.  
  813.     $p1 == $p2 ? $a cmp $b : $p2 <=> $p1;
  814.  
  815. # --------------------------------------------------------------------------
  816. # see whether they want to start at a subsection, then run the command
  817. # --------------------------------------------------------------------------
  818. sub run_topic {
  819.     local($_);
  820.     local($menu_rtn) = defined $idx_topic && $idx_topic eq '';
  821.     {
  822.     &append_sub_topic;
  823.     last if $idx_topic eq "\004";
  824.     if ($idx_topic eq '0') {
  825.         $menu_rtn = 0;
  826.         $idx_topic = '';
  827.         $command =~ s: '\+/[^']*'::;
  828.     }
  829.     $fromfile ? &reformat($command) : &run($command);
  830.     if ($menu_rtn) {
  831.         $idx_topic = '';
  832.         &prompt_RTN("to return to the index");
  833.         $command =~ s! '\+/.*$!!;
  834.         redo;
  835.     } 
  836.     }
  837.     
  838.  
  839. # --------------------------------------------------------------------------
  840. # run through the typesetter
  841. # --------------------------------------------------------------------------
  842. sub troff {
  843.     local ($file) = $_[0];
  844.     local ($command);
  845.     local ($manroot);
  846.     local ($macros);
  847.  
  848.     ($manroot) = $file =~ m,^(.*)/man([^\.]*)(\.Z)?/([^/]*),;
  849.  
  850.     $command = ((($file =~ m:\.Z:) 
  851.             ? $ZCAT 
  852.             : $CAT) 
  853.         . " < $file | $TYPESET");
  854.  
  855.     $command =~ s,-man,$manroot/tmac.an, if -e "$manroot/tmac.an";
  856.  
  857.     &insert_filters($command,$file);
  858.     &run($command);
  859.  
  860. # --------------------------------------------------------------------------
  861. # just run a regular nroff, possibly showing the index first.
  862. # --------------------------------------------------------------------------
  863. sub nroff {
  864.     local($manpage) = $_[0];
  865.     local($catpage);
  866.     local($tmppage);
  867.     local($command);
  868.     local(@saveidx);
  869.     local($manroot);
  870.     local($macros);
  871.     local($intmp);
  872.     local(@st_cat, @st_man);
  873.  
  874.     die "trying to nroff a null man page" if $manpage eq '';
  875.  
  876.     umask 022;
  877.  
  878.     if ($full_index) {
  879.     &show_index($manpage);
  880.     return;
  881.     } 
  882.     if ($fromfile) {
  883.     $command = (($manpage =~ m:\.Z/:) ? $ZCAT : $CAT)
  884.             . " < $manpage | $CATSET";
  885.     &insert_filters($command, $manpage);
  886.     } else {
  887.     require 'stat.pl' unless defined &Stat;   
  888.     # compiled version has this already
  889.  
  890.  
  891.     ($catpage = $manpage) 
  892.         =~ s,^(.*)/man([^\.]*)(\.Z)?/([^/]*)$,$1/cat$2/$4,;
  893.  
  894.     $manroot = $1;
  895.  
  896.     # Does the cat page exist?
  897.     if (! -f $catpage && $COMPRESS_DIR){
  898.         # No, maybe it is compressed?
  899.         if (-f "$1/cat$2.Z/$4"){
  900.         # Yes it was.
  901.         $catpage = "$1/cat$2.Z/$4";
  902.         } else {
  903.         # Nope, the cat file doesn't exist.
  904.             # Prefer the compressed cat directory if it exists.
  905.             $catpage = "$1/cat$2.Z/$4" 
  906.             if $catpage !~ /\.Z$/ && -d "$1/cat$2.Z";
  907.         }
  908.     }
  909.  
  910.  
  911.     @st_man = &Stat($manpage);
  912.  
  913.     if ($st_man[$ST_SIZE] == 0) {
  914.         warn "$program: $manpage is length 0!\n";
  915.         $status = 1;
  916.         return;
  917.     } 
  918.  
  919.     @st_cat = &Stat($catpage);
  920.  
  921.  
  922.     if ($st_cat[$ST_MTIME] < $st_man[$ST_MTIME]) {
  923.  
  924.         $command = (($manpage =~ m:\.Z:) ? $ZCAT : $CAT)
  925.             . " < $manpage | $CATSET";
  926.  
  927.         $command = &insert_filters($command, $manpage);
  928.         $command =~ s,-man,$manroot/tmac.an, if -e "$manroot/tmac.an";
  929.  
  930.         ($catdir = $catpage) =~ s!^(.*/?cat[^/]+)/[^/]*!$1!;
  931.  
  932.         chdir $manroot;
  933.  
  934.         $tmppage = "$catpage.$$";
  935.  
  936.         unless (-d $catdir && -w _ 
  937.             && open(tmppage, ">$tmppage") # usually EROFS
  938.             && close(tmppage) )
  939.         {
  940.         $catpage = $tmppage = "/tmp/man.$$";
  941.         $intmp = 1;
  942.         }
  943.  
  944.         printf STDERR "Reformatting page.  Please wait ... " if $isatty;
  945.  
  946.         $command .= "| $COMPRESS" if $catpage =~ /\.Z/;
  947.         $command .= "> $tmppage";
  948.  
  949.         $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = $SIG{'TERM'}  
  950.         = 'tmp_cleanup';
  951.  
  952.         $SIG{'PIPE'} = 'PLUMBER';
  953.  
  954. REFORMAT:   { unless (&reformat($command)) {
  955.         warn "$program: nroff of $manpage into $tmppage failed\n"
  956.             unless $@;
  957.         unlink $tmppage;
  958.         if (!$intmp++) {
  959.             $catpage = $tmppage = "/tmp/man.$$";
  960.             warn "$program: hang on... retrying into $tmppage\n";
  961.             $command =~ s/> \S+$/> $tmppage/;
  962.             $status = 0;
  963.             redo REFORMAT;
  964.         } else {
  965.             #$status = 1;
  966.             return;
  967.         }
  968.         }} 
  969.         warn "done\n" if $isatty;
  970.  
  971.         $intmp || rename($tmppage,$catpage) || 
  972.         die "couldn't rename $tmppage to $catpage: $!\n";
  973.         
  974.         $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = $SIG{'TERM'} =
  975.         $SIG{'PIPE'} = 'DEFAULT';
  976.  
  977.     } 
  978.     $command = (($catpage =~ m:\.Z:)
  979.             ? $ZCAT
  980.             : $CAT)
  981.             . " < $catpage";
  982.     }
  983.     if (-z $catpage) {
  984.     unlink $catpage;
  985.     die "$program: $catpage was length 0; disk full?\n";
  986.     } 
  987.     $command .= "| $UL"         if $UL;
  988.     $command .= "| $SED 's/.\b//g'"     if $stripBS;
  989.     $command .= "| $PAGER"          if $isatty;
  990.  
  991.     &run_topic;
  992.     unlink($tmppage) if $intmp;
  993.  
  994.  
  995. # --------------------------------------------------------------------------
  996. # modify $command to prime the pager with the subsection they want
  997. # --------------------------------------------------------------------------
  998. sub append_sub_topic {
  999.     if (defined $idx_topic)  {{
  1000.     local($key);
  1001.     last if $idx_topic eq '0';
  1002.     unless ($idx_topic) {
  1003.         $idx_topic = &pick_index;
  1004.         last if $idx_topic eq "\004" || $idx_topic eq '0';
  1005.     }
  1006.     if ($idx_topic =~ m!^/!) {
  1007.         $command .= " '+$idx_topic'";
  1008.         last;
  1009.     }
  1010.     unless ($key = &find_index($manpage, $idx_topic)) {
  1011.         warn "No subsection $idx_topic for $manpage\n\n";
  1012.         $idx_topic = '';
  1013.         redo;
  1014.     }
  1015.     $key =~ s/([!-~])/$1.$1/g unless $is_less;
  1016.     $command .= " '+/^[ \t]*$key'";
  1017.     }}
  1018. }
  1019.  
  1020.  
  1021. # --------------------------------------------------------------------------
  1022. # present subsections and let user select one
  1023. # --------------------------------------------------------------------------
  1024. sub pick_index {
  1025.      local($_);
  1026.      print "Valid sections for $page follow.  Choose the section\n";
  1027.      print "index number or string pattern. (0 for full page, RTN to quit.)\n\n";
  1028.      &show_index;
  1029.      print "\nWhich section would you like? ";
  1030.      ($_ = <STDIN>) ? chop : ($_ = "\004");
  1031.      $_ = "\004" if 'quit' =~ /^$_/;
  1032.      return $_;
  1033.  
  1034. # --------------------------------------------------------------------------
  1035. # strip arg of extraneous cats and redirects    
  1036. # --------------------------------------------------------------------------
  1037. sub unshell {
  1038.     $_[0] =~ s/^\s*cat\s*<?\s*([^\s|]+)\s*\|\s*([^|]+)/$2 < $1/;
  1039.     $_[0] =~ s/^([^|<]+)<([^Z|<]+)$/$1 $2/;
  1040.     ($roff eq 'troff') && $_[0] =~ s#(/usr/man/pr\S+)\s+(\S+)#$2 $1#;
  1041. }
  1042.  
  1043. # --------------------------------------------------------------------------
  1044. # call system on command arg, stripping of sh-isms and echoing for debugging
  1045. # --------------------------------------------------------------------------
  1046. sub run {
  1047.     local($command) = $_[0];
  1048.  
  1049.     &unshell($command);
  1050.  
  1051.     warn "running: $command\n" if $debug;
  1052.     if (system $command) {
  1053.     $status = 1;
  1054.     printf STDERR "\"%s\" exited %d, sig %d\n", $command, 
  1055.         ($? >> 8), ($? & 255) if $debug;
  1056.     }
  1057.     return ($? == 0);
  1058.  
  1059. # --------------------------------------------------------------------------
  1060. # check if page needs tbl or eqn, modifying command if needed
  1061. # add known problems for PR directory if applicable
  1062. # --------------------------------------------------------------------------
  1063. sub insert_filters {
  1064.     local($filters,$eqn, $tbl, $_);
  1065.     local(*PAGE);
  1066.     local($c, $PAGE) = @_;
  1067.     local($page,$sect, $prs, $prdir);
  1068.  
  1069.     ( $page = $PAGE ) =~ s/\.Z//;
  1070.     ($prdir = $page) =~ s#/[^/]*$##;
  1071.     $prdir =~ s#man([^/]*)$#pr$1#;
  1072.     $page =~ s#.*/([^/]+)$#$1#;
  1073.  
  1074.     $PAGE = "$ZCAT < $PAGE|" if $PAGE =~ /\.Z/;
  1075.  
  1076.     (open PAGE) || die ("$program: can't open $PAGE to check filters: $!\n");
  1077.     warn "open $PAGE to check for filters in $_[0]\n" if $debug;
  1078.  
  1079.     while (<PAGE>) {
  1080.     if (/^\.EQ/) {
  1081.         $_ = <PAGE>;
  1082.         $eqn = 1 unless /\.(if|nr)/;  # has eqn output not input
  1083.     } 
  1084.     if (/^\.TS/) {
  1085.         $_ = <PAGE>;
  1086.         $tbl = 1 unless /\.(if|nr)/;  # has tbl output not input
  1087.     } 
  1088.     last if $eqn && $tbl;
  1089.     } 
  1090.     close PAGE;
  1091.  
  1092.     if ($roff eq 'troff') {
  1093.     $eqn && $_[0] =~ s/(\S+roff)/$EQN | $1/;
  1094.     $tbl && $_[0] =~ s/(\S+roff)/$TBL | $1/;
  1095.     } else { # nroff
  1096.     $eqn && $_[0] =~ s/(\S+roff)/$NEQN | $1/;
  1097.     $tbl && $_[0] =~ s/(\S+roff)/$NTBL | $1/;
  1098.     }
  1099.  
  1100.     ($sect) = $page =~ /\.(\d)[^.]*$/;
  1101.     $prs = "$prdir/$page";
  1102.     if (-e $prs) {
  1103.     warn "found PRs for $page\n" if $debug;
  1104.     if ($roff eq 'nroff')  {
  1105.         $_[0] =~ s/ - / - $prs/;
  1106.     } else {
  1107.         $_[0] .= " $prs";
  1108.     } 
  1109.     } else {
  1110.     print "no PRS for $page in $prs\n" if $debug;
  1111.     } 
  1112.     $_[0];
  1113.  
  1114. # --------------------------------------------------------------------------
  1115. # due to aliasing the dbase sometimes has the same thing twice
  1116. # --------------------------------------------------------------------------
  1117. sub trimdups {
  1118.     local(%seen) = ();
  1119.     local(@retlist) = ();
  1120.  
  1121.     while ($file = shift) {
  1122.     push(@retlist,$file) unless $seen{$file}++;
  1123.     } 
  1124.     return @retlist;
  1125.  
  1126. # --------------------------------------------------------------------------
  1127. # just print the version
  1128. # --------------------------------------------------------------------------
  1129. sub version  {
  1130.     warn "$program: version is \"$version\"\n" ;
  1131. }
  1132.  
  1133. # --------------------------------------------------------------------------
  1134. # create and display subsection index via pager
  1135. # --------------------------------------------------------------------------
  1136. sub show_index {
  1137.     local($_);
  1138.     &load_index($_[0]);
  1139.     if ($#ssindex > ($rows - 4) && $isatty) {
  1140.     print "Hit <RTN> for $#ssindex subsections via pager: ";
  1141.     $_ = <STDIN>;
  1142.     if ($no_idx_file) {
  1143.         open (PAGER, "| $PAGER");
  1144.         print PAGER @ssindex;
  1145.         close PAGER;
  1146.     } else {
  1147.         &run("$PAGER $idx_file");
  1148.     } 
  1149.     } else {
  1150.     print STDOUT @ssindex;
  1151.     }
  1152.  
  1153. # --------------------------------------------------------------------------
  1154. # find closest match on index selection in full index
  1155. # --------------------------------------------------------------------------
  1156. sub find_index {
  1157.     local($manpage, $expr) = @_;
  1158.     local($_, @matches);
  1159.  
  1160.     &load_index($manpage);
  1161.  
  1162.     $expr =~ s!^/+!!;
  1163.  
  1164.     for (@ssindex) { 
  1165.     s/^\s*\d+\s+//; 
  1166.     s/\s+\d+\s*$//; 
  1167.     }
  1168.  
  1169.     if ($expr > 0) {
  1170.     return $ssindex[$expr];
  1171.     } else {
  1172.     $ssindex[0] = '';
  1173.     if (@matches = grep (/^$expr/i, @ssindex)) {
  1174.         return $matches[0];
  1175.     } elsif (@matches = grep (/$expr/i, @ssindex)) {
  1176.         return $matches[0];
  1177.     } else {
  1178.         return '';
  1179.     }
  1180.     } 
  1181.  
  1182. # --------------------------------------------------------------------------
  1183. # read in subsection index into @ssindex
  1184. # --------------------------------------------------------------------------
  1185. sub load_index {
  1186.     local($manpage)  = @_;
  1187.     $no_idx_file = 0;
  1188.     &getidx($manpage) if $#saveidx < 0;
  1189.     @ssindex = @saveidx;
  1190.     die "should have have an index for $manpage" if $#saveidx < 0;
  1191.  
  1192. # --------------------------------------------------------------------------
  1193. # create subsection index is out of date wrt source man page
  1194. # --------------------------------------------------------------------------
  1195. sub getidx {
  1196.     local($manpage) = @_;
  1197.     local($is_mh);
  1198.     local($_, $i, %lines, %sec, $sname, @snames);
  1199.     local(@retlist, $maxlen, $header, @idx , @st_man, @st_idx);
  1200.     # global no_idx_file, idx_file
  1201.  
  1202.     ( $idx_file = $manpage ) =~ s:/man(\w+)(\.Z)?/:/idx$1/:;
  1203.     $idx_file =~ s/\.Z//;
  1204.  
  1205.     require 'stat.pl' unless defined &Stat;
  1206.  
  1207.     @st_man = &Stat($manpage);
  1208.     @st_idx = &Stat($idx_file);
  1209.  
  1210.     if ($st_man[$ST_MTIME] < $st_idx[$ST_MTIME]) {
  1211.     unless (open idx_file) {
  1212.         warn "$program: can't open $idx_file: $!\n";
  1213.         return ();
  1214.     } 
  1215.     @retlist = <idx_file>;
  1216.     close idx_file;
  1217.     return @saveidx = @retlist;
  1218.     } 
  1219.  
  1220.     if (!open(manpage, $manpage =~ /\.Z/ ? "$ZCAT < $manpage|" : $manpage)) {
  1221.         warn "$program: can't open $manpage: $!\n";
  1222.     return ();
  1223.     }
  1224.     warn "building section index\n" if $debug;
  1225.     ($header = "Subsections in $manpage")  =~ s!/?\S*/!!;
  1226.     $maxlen = length($header);
  1227.     push(@snames, $sname = 'preamble');;
  1228.  
  1229.     # MH has these alias for sections and subsectdions
  1230.     if ($is_mh = $manpage =~ m:/mh/:) {
  1231.     %mh_sections = (
  1232.         "NA", "NAME",
  1233.         "SY", "SYNOPSIS",
  1234.         "DE", "DESCRIPTION",
  1235.         "Fi", "FILES",
  1236.         "Pr", "PROFILE",
  1237.         "Sa", "SEE ALSO",
  1238.         "De", "DEFAULTS",
  1239.         "Co", "CONTEXT",
  1240.         "Hh", "HELPFUL HINTS",
  1241.         "Hi", "HISTORY",
  1242.         "Bu", "BUGS"
  1243.     );
  1244.     $mh_expr = join('|',keys %mh_sections);
  1245.     } 
  1246.  
  1247.     while (<manpage>) {
  1248.     if ($is_mh && /^\.($mh_expr)/) {
  1249.         $sname = $mh_sections{$+};
  1250.         $maxlen = length($sname) if $maxlen < length($sname); 
  1251.         push(@snames,$sname);
  1252.     } 
  1253.     if (/^\.s[sh]\s+(.*)/i ) {
  1254.         $line = $_;
  1255.         $_ = $1;
  1256.         s/"//g;
  1257.         s/\\f([PBIR]|\(..)//g;    # kill font changes
  1258.         s/\\s[+-]?\d+//g;        # kill point changes
  1259.         s/\\&//g;            # and \&
  1260.         s/\\\((ru|ul)/_/g;        # xlate to '_'
  1261.         s/\\\((mi|hy|em)/-/g;    # xlate to '-'
  1262.         s/\\\*\(..//g;         # no troff strings
  1263.         s/\\//g;               # kill all remaining backslashes 
  1264.         $sname = $_;
  1265.         $_ = $line;
  1266.         $maxlen = length($sname) if $maxlen < length($sname); 
  1267.         push(@snames,$sname);
  1268.     } 
  1269.     $lines{$sname}++;
  1270.     } 
  1271.  
  1272.     $mask = sprintf("%%2d   %%-%ds %%5d\n", $maxlen + 2);
  1273.  
  1274.     $no_idx_file = $idx_file eq $manpage || !open(idx, ">$idx_file");
  1275.  
  1276.     $line = sprintf(sprintf("Idx  %%-%ds Lines\n", $maxlen + 2), $header);
  1277.     @retlist = ($line);
  1278.  
  1279.     for ($i = 1; $i <= $#snames; $i++)  {
  1280.     push(@retlist, sprintf($mask, $i, $snames[$i], $lines{$snames[$i]}));
  1281.     } 
  1282.     if (!$no_idx_file) {
  1283.     warn "storing section index in $idx_file\n" if $debug;
  1284.     print idx @retlist;
  1285.     close idx;
  1286.     }
  1287.     return @saveidx = @retlist;
  1288. }
  1289.  
  1290. # --------------------------------------------------------------------------
  1291. # interrupted -- unlink temp page
  1292. # --------------------------------------------------------------------------
  1293. sub tmp_cleanup {
  1294.     warn "unlink $tmppage\n" if $debug;
  1295.     unlink $tmppage;
  1296.     die "Interrupted!\n";
  1297.  
  1298. #--------------------------------------------------------------------------
  1299. # in case we die writing to the pipe
  1300. # --------------------------------------------------------------------------
  1301. sub PLUMBER {
  1302.     warn "unlink $tmppage\n" if $debug;
  1303.     unlink $tmppage;
  1304.     die "Broken pipe while reformating $manpage\n" ;
  1305.  
  1306.  
  1307. # --------------------------------------------------------------------------
  1308. # print line with C\bC style emboldening 
  1309. # --------------------------------------------------------------------------
  1310. sub print {
  1311.     local($_) = @_;
  1312.  
  1313.     if (!$inbold) {
  1314.     print;
  1315.     } else {
  1316.     local($last);
  1317.     for (split(//)) {
  1318.             if ($last eq "\033") {
  1319.                 print;
  1320.             } else {
  1321.                 print /[!-~]/ ? $_."\b".$_ : $_;
  1322.             }
  1323.             $last = $_;
  1324.     }
  1325.     } 
  1326. }
  1327.  
  1328. # --------------------------------------------------------------------------
  1329. # reformat the page with nroff, fixing up bold escapes
  1330. # --------------------------------------------------------------------------
  1331. sub reformat { 
  1332.     local($_) = @_; 
  1333.     local($nroff, $col); 
  1334.     local($inbold) = 0;
  1335.  
  1336.     if ($NROFF_CAN_BOLD) {
  1337.     return &run($_);
  1338.     } 
  1339.  
  1340.     &unshell($_);
  1341.     ($nroff, $col) = m!(.*)\|\s*($COL.*)!;
  1342.  
  1343.     warn "$nroff | (this proc) | $col\n" if $debug;
  1344.  
  1345.     open (NROFF, "$nroff |");
  1346.     $colpid = open (COL, "| $col");
  1347.  
  1348.     select(COL);
  1349.  
  1350.     while (<NROFF>) {
  1351.     s/\033\+/\001/;
  1352.     s/\033\,/\002/;
  1353.     if ( /^([^\001]*)\002/ || /^([^\002]*)\001/ )  {
  1354.         &print($1);
  1355.         $inbold = !$inbold;
  1356.         $_ = $';
  1357.         redo;
  1358.     }   
  1359.     &print($_);
  1360.     }
  1361.  
  1362.     close NROFF;
  1363.     if ($?) { 
  1364.     warn "$program: \"$nroff\" failed!\n" if $debug;
  1365.     $status++;
  1366.     } 
  1367.     close COL;
  1368.     if ($?) {
  1369.     warn "$program: \"$col\" failed!\n" if $debug;
  1370.     $status++;
  1371.     }
  1372.     select(STDOUT);
  1373.     $status == 0;
  1374.  
  1375. # --------------------------------------------------------------------------
  1376. # prompt for <RET> if we're a tty and have a non-stopping pager
  1377. # --------------------------------------------------------------------------
  1378. sub prompt_RTN {
  1379.     local($why) = $_[0] || "to continue";
  1380.     return unless $isatty;
  1381.     unless ($is_less && $ENV{'LESS'} !~ /E/) {
  1382.     print "Hit <RTN> $why: ";
  1383.     $_ = <STDIN>;
  1384.     }
  1385. }
  1386.  
  1387. # --------------------------------------------------------------------------
  1388. # dynamically determine MANPATH (if unset) according to PATH
  1389. # --------------------------------------------------------------------------
  1390. sub config_path {
  1391.     local($_);        # for traversing $PATH
  1392.     local(%seen);    # weed out duplicates
  1393.     local(*manpath);    # eventual return values
  1394.  
  1395.     if (defined $ENV{'MANPATH'}) {
  1396.     $manpath = $ENV{'MANPATH'};
  1397.     } else {
  1398.     for (split(/:/, $ENV{'PATH'})) {
  1399.         next if $_ eq '.';
  1400.         next if $_ eq '..';
  1401.         s![^/+]*$!man! && -d && !$seen{$_}++ && push(@manpath,$_);
  1402.     }
  1403.     $manpath = join(':', @manpath);
  1404.     } 
  1405.     # $manpath;    # last expr is assign to this anyway
  1406.  
  1407. # --------------------------------------------------------------------------
  1408. # grep through MANPATH for a pattern
  1409. # --------------------------------------------------------------------------
  1410. sub grepman {
  1411.     local($code, $_, $dir, $root, $FILE, $found);
  1412.     
  1413.     $code = "while (<FILE>) {\n";
  1414.  
  1415.     for (@ARGV) {
  1416.     s#/#\\/#g;
  1417.     $code .= <<EOCODE;
  1418.         if (/$_/) { 
  1419.         print "\$path: \$_"; 
  1420.         \$found++;
  1421.         next; 
  1422.         }
  1423. EOCODE
  1424.     } 
  1425.  
  1426.     $code .= "}\n";
  1427.  
  1428.     print "grep eval code: $code" if $debug;
  1429.  
  1430.     
  1431.     foreach $root ( split(/:/, $MANPATH)) {
  1432.     unless (chdir($root)) {
  1433.         warn "can't chdir to $root: $!";
  1434.         $status++;
  1435.         next;
  1436.     } 
  1437.     foreach $dir ( <man?*> ) {
  1438.         unless (chdir($dir)) {
  1439.         warn "can't chdir to $root/$dir: $!";
  1440.         $status++;
  1441.         next;
  1442.         } 
  1443.         unless (opendir(DIR, '.')) {
  1444.         warn "can't opendir $root/$dir: $!";
  1445.         $status++;
  1446.         next;
  1447.         } 
  1448.         foreach $FILE ( readdir(DIR) ) {
  1449.         next if $FILE eq '.' || $FILE eq '..';
  1450.         $path = "$root/$dir/$FILE";
  1451.         if ($FILE !~ /\S\.\S/ || !-f $FILE) {
  1452.             print "skipping non-man file: $path\n" if $debug;
  1453.             next;
  1454.         } 
  1455.         if ($FILE =~ /\.Z$/ || $dir =~ /\.Z$/) {
  1456.             $FILE = "$ZCAT $FILE|";
  1457.         } 
  1458.         print STDERR "grepping $path\n" if $debug;
  1459.         unless (open FILE) {
  1460.             warn "can't open $root/$dir/$FILE: $!";
  1461.             $status++;
  1462.             next;
  1463.         } 
  1464.         eval $code;
  1465.         die $@ if $@;
  1466.         } 
  1467.         unless (chdir ($root)) {
  1468.         warn "can't return to $root: $!";
  1469.         $status++;
  1470.         last;
  1471.         } 
  1472.     } 
  1473.     } 
  1474.     exit ($status || !$found);
  1475.