home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / perl / scripts-convex / man-selfload < prev    next >
Encoding:
Text File  |  1992-02-12  |  48.1 KB  |  1,830 lines

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