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 / cfman < prev    next >
Encoding:
Text File  |  1991-03-04  |  9.9 KB  |  403 lines

  1. #!/usr/local/bin/perl
  2. #
  3. # cfman v2.0: man page cross-referencer
  4. # author: Tom Christiansen <tchrist@convex.com>
  5. # date: 15 November 89
  6. #
  7. # usage: cfman [ -d debug-devel ] [ -s sub-sections ] 
  8. #           [ -p manpath ] [ -x xrefpath ] 
  9.  
  10. ($iam = $0) =~ s%.*/%%;
  11.  
  12. $] =~ /(\d+\.\d+).*\nPatch level: (\d+)/;
  13. die "$iam: requires at least perl version 3.0, patchlevel 1 to run correctly\n"
  14.     if $1 < 3.0 || ($1 == 3.0 && $2 < 1);
  15.     
  16.  
  17. require 'getopts.pl';
  18.  
  19. &Getopts('d:s:p:P:x:') || &usage;
  20.  
  21. $manpath = $opt_p if defined $opt_p;
  22. $manpath = $opt_P if defined $opt_P;
  23. $manpath = $ENV{'MANPATH'} unless $manpath;
  24. $manpath = "/usr/man" unless $manpath;
  25. @manpath = split(/:/,$manpath);
  26.  
  27. $opt_x =~ /^:/ && ( $opt_x = $manpath . $opt_x );
  28. @xrefpath = $opt_x ? split(/:/,$opt_x) : @manpath;
  29.  
  30. $debug = $opt_d;
  31.  
  32. @sections = $opt_s ? split(/ */,$opt_s) : 1..8;
  33.  
  34. if ($debug) {
  35.     $" = ':';
  36.     print "manpath is @manpath\n";
  37.     print "xrefpath is @xrefpath\n";
  38.     $" = ' ';
  39.  
  40. file:    foreach $file ( $#ARGV >= $[ ? @ARGV : '*.*' ) {
  41.          printf STDERR "considering %s\n", $file if $debug & 1;
  42.          $bingo = 0;
  43. tree:        foreach $tree ( @manpath ) {
  44.          print "ROOT is $tree\n" if $debug;
  45.          if (!chdir $tree) {
  46.             warn "cannot chdir to $tree: $!";
  47.             next tree;
  48.          } 
  49.          $rootdir = $tree;
  50.          if ( $file =~ m#^/# ) {
  51.             &read_manpages($file); 
  52.             next file;
  53.          } 
  54. section:         foreach $section ( @sections ) {
  55.             &scan_section($tree,$section,$file);
  56.          }
  57.          } 
  58.          print "no man pages matched \"$file\"\n" unless $bingo;
  59.       }
  60.  
  61.  
  62. exit 0;
  63.  
  64. ############################################################################
  65. #
  66. # scan_section()
  67. #
  68. #    checks a given man tree (like /usr/local/man) in a 
  69. #    certain subsection (like '1'), checking for a certain
  70. #    file, like 'tty' (which mean 'tty.*', 'system.3*', or '*.*'.
  71. #
  72. #    will recurse on a subsection name contaning a shell meta-character
  73. #
  74. ############################################################################
  75.  
  76. sub scan_section {
  77.     local ( $manroot, $subsec, $files ) = @_;
  78.     local ( $mandir );
  79.  
  80.     $mandir = "man" . $subsec;
  81.  
  82.  
  83.     # subsec may have been ? or *; if so, recurse!
  84.     if ( &has_meta($mandir) ) {  
  85.     for (<${mandir}>) {
  86.         if (&has_meta($_)) { 
  87.         warn "bad glob of $mandir"; 
  88.         last; 
  89.         } 
  90.         s/^man//;
  91.         &scan_section($manroot,$_,$files);
  92.     } 
  93.     return;
  94.     } 
  95.  
  96.     $files = "$files.*" unless $files =~ /\./;
  97.  
  98.     if (!chdir $mandir) {
  99.     warn "couldn't chdir to $mandir: $!\n" if $debug;
  100.     return;
  101.     } 
  102.  
  103.     printf STDERR "chdir to %s of %s\n", $mandir, $manroot if $debug & 1;
  104.  
  105.     &read_manpages ( &has_meta($files) ? &glob($files) : ($files));
  106.  
  107.     chdir('..');
  108.  
  109. ############################################################################
  110. #
  111. # read_manpages()
  112. #
  113. #    passed a list of filename, which are man pages.  opens each one
  114. #    verifying that the file really is in the place that the .TH line.
  115. #    skips to SEE ALSO section and then verifies existence of each 
  116. #    referenced man page.
  117. ############################################################################
  118.  
  119.  
  120. sub read_manpages {
  121.     local (@pages) = @_;
  122.  
  123.     local ($junk, $sopage, $basename, $line, $page, $pname, $pext, $gotTH);
  124.     local(%seen);
  125.  
  126.  
  127. page:
  128.     foreach $page ( @pages ) {
  129.     next page if $page =~ /\.(BAK|OLD)$/i;
  130.  
  131.     if ($seen{$page}++) {
  132.         print "already saw $page\n" if $debug & 1;
  133.         next page;
  134.     }
  135.  
  136.     if (!open page) {
  137.         warn "couldn't open $page: $!\n";
  138.         next page;
  139.     }
  140.  
  141.     $bingo = 1; # global var
  142.  
  143.     print "checking $page\n" if $debug & 1;
  144.  
  145.     $gotTH = 0;
  146.     $line = 0;
  147.     $sopage = '';
  148.  
  149. line:   while (<page>) {
  150.         print if $debug & 16;
  151.         next line if /^'''/ || /^\.\\"/;
  152.  
  153.         # deal with .so's on the first line.
  154.         # /usr/ucb/man uses this instead of links.
  155.         if (!($line++) && /^\.so\s+(.*)/) {
  156.         $sopage = $1;
  157.         print "$page -> $sopage\n" if $debug & 1;
  158.         ($basename = $sopage) =~ s%.*/%%;
  159.         if ($seen{$basename}++) {
  160.             print "already saw $basename\n" if $debug & 1;
  161.             next page;
  162.         } 
  163.         if (!open(page,"../$sopage")) {
  164.             print "$page: cannot open $sopage: $!\n";
  165.             next page;
  166.         } 
  167.         $page = $basename;
  168.         next line;
  169.         } 
  170.  
  171.         # check for internally consistent .TH line
  172.         if ( /^\.(TH|SC)/ ) { # SC is for mh
  173.          $gotTH++;
  174.          printf STDERR "TH checking %s", $_ if $debug & 4;
  175.          do flush();
  176.          s/"+//g;
  177.          ($junk, $pname, $pext) = split;
  178.          if (¯o($pname)) {
  179.             printf STDERR "%s: can't resolve troff macro in .TH: %s\n",
  180.                 $page, $pname;
  181.             next line;
  182.          } 
  183.          $pext =~ y/A-Z/a-z/;
  184.          $pname =~ s/\\-/-/g;
  185.          $pname =~ y/A-Z/a-z/ if $pname =~ /^[\$0-9A-Z_\055]+$/;
  186.          ($pexpr = $page) =~ s/([.+])/\\$1/g;
  187.          $pexpr =~ s%.*/%%;
  188.          if ( "$pname.$pext" !~ /^$pexpr$/i) {
  189.               printf "%s: thinks it's in %s(%s)\n", 
  190.               $page, $pname, $pext;
  191.          } 
  192.          next line;
  193.         }
  194.  
  195.         next line unless /^\.S[Hh]\s+"*SEE ALSO"*/ 
  196.         || /^\.S[Hh]\s+REFERENCES/    # damn posix
  197.         || /^\.Sa\s*$/;         # damn mh
  198.  
  199.         # finally found the cross-references
  200. xref:       while (<page>) {
  201.         print if $debug & 16;
  202.         last line if /^\.(S[Hh]|Co|Hi|Bu)/; # i really hate mh macros
  203.         next xref unless /\(/;
  204.         next xref if /^.PP/;
  205.         chop;
  206.         s/\\f[RIPB]//g;
  207.         s/\\\|//g;
  208.         s/\\-/-/g;
  209. entry:          foreach $entry ( split(/,/) ) {
  210.             #print "got entry $entry\n";
  211.             next entry unless $entry =~ /\(.*\)/;
  212.             $pname = ''; $pext = '';
  213.             $1 = ''; $2 = '';
  214.             ($pname, $pext) = 
  215.             ($entry =~ /([A-Za-z0-9\$._\-]+)\s*\(([^)]+)\).*$/); 
  216.             if ($debug & 8) {
  217.             printf STDERR "entry was %s, pname is %s, pext is %s\n",
  218.                 $entry, $pname, $pext;
  219.             }     
  220.             if (¯o($pname)) {
  221.             printf "%s: can't resolve troff macro in SEE ALSO: %s\n",
  222.                 $page, $pname;
  223.             next entry;
  224.             } 
  225.             next entry if !$pname || !$pext || $pext !~ /^\w+$/;
  226.             $pext =~ y/A-Z/a-z/;
  227.             $pname =~ y/A-Z/a-z/ if $pname =~ /^[A-Z_0-9\-]+$/;
  228.             #($psect = $pext) =~ s/^(.).*/$1/;
  229.             do check_xref($page,$pname,$pext);
  230.  
  231.         }    # entry: foreach $entry ( split(/,/) ) 
  232.         }        # xref:  while (<page>)
  233.     }        # line:  while (<page>) 
  234.     printf "%s: missing .TH\n", $page if (!$gotTH);
  235.     }              # page:  foreach $page ( @pages )
  236. }                 # sub    read_manapages
  237.  
  238.  
  239. ###########################################################################
  240. #
  241. # check_xref()
  242. #
  243. #    given the name of the page we're looking for, check for a
  244. #    cross reference of a given man page and its assumed subsection
  245. #
  246. ###########################################################################
  247.  
  248. sub check_xref {
  249.     local ($name, $target, $section) = @_;
  250.     local ($basesec, $subsec, $newsec );
  251.  
  252.     printf STDERR " xref of %s(%s)\n", $target, $section if $debug & 2;
  253.  
  254.     return if &pathcheck($target,$section);
  255.  
  256.  
  257.     # if we get this far, something's wrong, so begin notify
  258.     printf "%s: %s(%s)", $name, $target, $section;
  259.  
  260.     ($basesec, $subsec) = ($section =~ /^(\d)(.*)$/);
  261.  
  262.     if ($name =~ /\.\d*([nlp])$/ && ($section == 1 || $section == 8)
  263.         && ($newsec = &pathcheck($target,$1))) { # hack for manl idiocy
  264.     &really($target,$newsec);
  265.     return;
  266.     }
  267.  
  268.     # first check if page.Xn is really in page.X
  269.     if ( $subsec && ($newsec = &pathcheck($target,$basesec))) {
  270.     &really($target,$newsec);
  271.     return;
  272.     } 
  273.  
  274.     if ( $basesec == 1 && &pathcheck($target,8))  {
  275.     &really($target,8);
  276.     return;
  277.     }
  278.  
  279.     if ( $basesec == 8 && &pathcheck($target,1))  {
  280.     &really($target,1);
  281.     return;
  282.     }
  283.  
  284.     # maybe it thinks it's in 8 but got erroneously in 1
  285.     if ( $basesec =~ /[18]/ && ($newsec = &pathcheck($target,'l')))  {
  286.     &really($target,$newsec);
  287.     return;
  288.     } 
  289.  
  290.     # maybe page.X is really in page.Xn; this is expensive
  291.     if ( !$subsec && ($newsec = &pathcheck($target,$basesec.'*'))) {
  292.     &really($target,$newsec);
  293.     return;
  294.     } 
  295.  
  296.     printf " missing\n";
  297.     do flush();
  298. }
  299.  
  300. ###########################################################################
  301. #
  302. # pathcheck()
  303. #
  304. #    takes a name (like 'tty') and a section (like '1d')
  305. #    and looks for 'tty.1d' first in the current root, 
  306. #    then in all other elements of @xrefpath.  the section
  307. #    may have a meta-character in it (like '8*').
  308. #
  309. #    returns the subsection in which we found the page, or
  310. #    null if we failed.
  311. #
  312. ###########################################################################
  313.  
  314. sub pathcheck {
  315.     local ( $name, $section ) = @_;
  316.     local ( $basesec, $metasec, $fullpath, @expansion, $tree, %checked  ); 
  317.     local ( $return ) = 0;
  318.  
  319.     $metasec = &has_meta($section);
  320.  
  321.     ($basesec) = ($section =~ /^(.)/);
  322.  
  323.     foreach $tree ( $rootdir, @xrefpath ) {
  324.     next if !$tree || $checked{$tree}++;  # only check each tree once
  325.  
  326.     $fullpath = "$tree/man$basesec/$name.$section";  
  327.  
  328.     print "   testing $fullpath\n" if $debug & 8;
  329.  
  330.     if (!$metasec) {
  331.         if (-e $fullpath) {
  332.         $return = $section;
  333.         }
  334.     } else {
  335.         open(SAVERR, '>&STDERR');  # csh globbing brain damage
  336.         close STDERR;
  337.         if ((@expansion = <${fullpath}>) && !&has_meta($expansion[0])) {
  338.                     # redundant meta check due to sh brain-damage
  339.         #for (@expansion) { s/.*\.//; } 
  340.         #$section = join(' or ',@expansion);
  341.         ($section) = ($expansion[0] =~ /([^.]+)$/);
  342.         $return = $section;
  343.         }
  344.         open(STDERR, '>&SAVERR');  # csh globbing brain damage
  345.         close SAVERR;
  346.     }
  347.     } 
  348.     printf STDERR "   pathcheck returns $section\n" if $debug & 8;
  349.     $return;
  350.  
  351. #---------------------------------------------------------------------------
  352.  
  353. sub flush {
  354.     $| = 1; 
  355.     print ''; 
  356.     $| = 0;
  357. }
  358.  
  359. sub has_meta {
  360.     $_[0] =~ /[[*?]/;
  361.  
  362. sub macro {
  363.     @_[0] =~ /^\\\*\(/;
  364.  
  365. sub really {
  366.     local($was,$is) = @_;
  367.     print " really in $was($is)\n";
  368. }
  369.  
  370. sub usage {
  371.     die "usage: $iam [-d debug-level] [-s sub-sections] [-p manpath] 
  372.         [-x xrefpath] [pattern ...] \n";
  373. }
  374.  
  375. sub glob {
  376.     local($expr) = @_;
  377.     local(@retlist) = ();
  378.     local(*METADIR);                # paranoia
  379.  
  380.     die "glob: null expr" unless $expr;        # assert
  381.  
  382.     if ($expr =~ /\//) {
  383.     warn "glob: \"$expr\" has slashes, punting...";
  384.     return <${expr}>;
  385.     } 
  386.  
  387.     $expr =~ s/\*/.*/g;
  388.     $expr =~ s/\?/./g;
  389.  
  390.     unless (opendir(METADIR, '.')) {
  391.     warn "glob: can't opendir ".": $!\n";
  392.     } else {
  393.     @retlist = sort grep(/$expr/o, grep(!/^\./, readdir(METADIR)));
  394.     closedir METADIR;
  395.     }
  396.     return @retlist;
  397.