#!/usr/bin/perl -w

use strict;
use English;
#use File::stat;
use Errno;
use Fcntl ':mode';
use Getopt::Long;

my $FALSE = 0;
my $TRUE = !$FALSE;

sub readConfFile( $ );
sub findUrl($@);
sub subUrl($@);
sub giveHelp();
sub executablePathUrl($);

my ($optConfig, $optHelp);

my $validOptions = GetOptions("config=s" => \$optConfig,
                              "help" => \$optHelp,
                              );

if (!$validOptions) { printf(STDERR "Invalid syntax.\n"); exit(1); }

if ($optHelp) { 
    giveHelp(); 
    exit(0);
}
    
my ($webdir, $browser) = readConfFile($optConfig);

my $url;

my $directUrl = findUrl($webdir, @ARGV);

if (defined($directUrl)) {
    $url = $directUrl;
} else {
    if (@ARGV == 1) {
        $url = executablePathUrl($ARGV[0]);
    }
}

if (defined($url)) {
    print("Browsing URL '$url'...\n");
    system($browser, $url);
} else {
    if (@ARGV == 1) {
        my $mantopic = $ARGV[0];
        print("No web doc.  Running 'man' on topic '$mantopic'...\n");
        system("man", $mantopic);
    } elsif (@ARGV == 2 && $ARGV[0] =~ m{\d+}) {
        my ($mansection, $mantopic) = @ARGV;
        print("No web doc.  Running 'man ' on Section $mansection, " .
              "Topic '$mantopic'...\n");
        system("man", $mansection, $mantopic);
    } else {
        print(STDERR "No web documentation found for topic chain @ARGV " .
              "and it isn't in the right form to try a man page\n");
        exit(1);
    }
}



sub giveHelp() {

    print("Manweb is a replacement for Man.  It gets reference \n");
    print("documentation from the Worldwide Web or a private web. \n");
    print("Manweb is distributed with the Netpbm package \n");
    print("(http://netpbm.sourceforge.net).\n");
    print("\n");
    print("Documentation of Manweb is at \n");
    print("\n");
    print("        http://netpbm.sourceforge.net/doc/manweb.html\n");
    print("\n");
    print("Or if you have it properly installed, just use the command \n");
    print("\n");
    print("        manweb manweb \n");
}



sub findUrl($@) {
    my ($webdir, @topicList) = @_;
#-----------------------------------------------------------------------------
#  Starting in the directory $webdir, find the URL for the documentation
#  of the topic identified by @topicList.  @topicList is a main topic
#  followed by a subtopic of that topic, and so on.
#
#  If @topicList is an empty list, return the url that refers to the 
#  directory $webdir itself.
#-----------------------------------------------------------------------------
    if (@topicList == 0) {
        $url = directoryUrl($webdir);
    } else {
        my $topic0 = shift(@topicList);

        # First look for a .url file 

        $url = doturl($webdir, $topic0, @topicList);
        if (!defined($url)) {
            # No .url file.  Look for directory.
            
            my $subwebdir = "$webdir/$topic0";
            my ($dev, $ino, $mode, $rest) = stat($subwebdir);
            if (defined($mode) && S_ISDIR($mode)) {
                $url = findUrl($subwebdir, @topicList);
            } else {
                # No directory.  Look for html file.
                my $htmlfilename = "$webdir/$topic0.html";
            
                my ($dev, $ino, $mode, $rest) = stat($htmlfilename);
                if (defined($mode) && S_ISREG($mode)) {
                    if (@topicList > 0) {
                        print("Ignoring subtopic chain '@topicList' because " .
                              "There is an html file named " .
                              "'$htmlfilename'.\n");
                    } 
                    $url = "file://$htmlfilename";
                }
            }
        }
    }
    return($url);
}



sub directoryUrl($$) {
    # If this directory has an index file, that's the URL.  Otherwise
    # it's just the directory itself.  Too bad the browser doesn't do
    # this for us, like it does for HTTP URLs.

    my ($webdir) = @_;
    my ($dev, $ino, $mode, $rest) = stat("$webdir/index.html");
    if (defined($mode) && S_ISREG($mode)) {
        $url = "file://$webdir/index.html";
    } else {
        my ($dev, $ino, $mode, $rest) = stat("$webdir/index.htm");
        if (defined($mode) && S_ISREG($mode)) {
            $url = "file://$webdir/index.htm";
        } else {
            $url = "file://$webdir";
        }
    }
    return($url);
}




sub doturl($$) {
    my ($webdir, $topic0, @topicList) = @_;
#-----------------------------------------------------------------------------
#  Handle a .url file.
#
#  If there is a file named "$topic0.url" in the directory $webdir,
#  return the URL that gets to the proper web page for subtopic list
#  @topiclist with respect to the URL in that .url file.
#
#  If there's no such .url file, though, return an undefined value.
#-----------------------------------------------------------------------------
    my $url;

    my $urlfilename = "$webdir/$topic0.url";

    my $openworked = open(URLFILE, "<$urlfilename");
        
    if ($openworked) {
        my @url = <URLFILE>;
        if (@url == 0) {
            die("URL file '$urlfilename' is empty.");
        } elsif (@url > 1) {
            die("URL file '$urlfilename' contains more than one line.");
        } else {
            my $topUrl = $url[0];
            chomp($topUrl);
            if (@topicList > 0) {
                if ($topUrl =~ m|.*[^/]$|) {
                    print("Ignoring subtopic chain '@topicList' because " .
                          "URL '$topUrl' is not a directory URL.\n");
                }
                $url = $topUrl . join("/", @topicList) . ".html";
            } else {
                $url = $topUrl;
            }
        }
    }
    return($url);
}



sub executablePathUrl($) {
    my ($progName) = @_;
#-----------------------------------------------------------------------------
#  If $progName is the name of a program that would be found in the
#  program search path (as defined by the PATH environment variable),
#  and the directory in which the program resides contains a file
#  .docurl, return the first line of that file, appended with
#  "$progName.html" as the URL.  If the line from the file doesn't end
#  with a slash, though, just return the line itself.
#
#  If $progName is not such a program name, or there is no .docurl,
#  return undefined. 
#-----------------------------------------------------------------------------
    my $url;

    my @path = split(/:/,$ENV{"PATH"});
    
    my $i;
    my $progDir;
    for ($i = 0; $i < @path && !$progDir; ++$i) {
        if (-x($path[$i] . "/" . $progName)) {
            $progDir = $path[$i];
        }
    }

    if ($progDir) {
        my $urlfilename = "$progDir/doc.url";
        if (-f($urlfilename)) {
            my $openworked = open(URLFILE, "<$urlfilename");
        
            if ($openworked) {
                my @url = <URLFILE>;
                if (@url == 0) {
                    die("URL file '$urlfilename' is empty.");
                } elsif (@url > 1) {
                    die("URL file '$urlfilename' contains more " .
                        "than one line.");
                } else {
                    my $topUrl = $url[0];
                    chomp($topUrl);
                    if ($topUrl =~ m|.*[^/]$|) {
                        $url = $topUrl;
                    } else {
                        $url = "$topUrl/$progName.html";
                    }
                }
            } else {
                die("Unable to open file '$urlfilename'.");
            }
        }
    }

    return($url);
}



sub readConfFile($) {
#-----------------------------------------------------------------------------
#  Read the configuration file (/etc/manweb.conf or value of
#  MANWEB_CONF_FILE or named by our argument).  Return values set in
#  it, or defaults.
#-----------------------------------------------------------------------------
    my ($fileArg) = @_;
    
    my $confFile;

    if (defined($fileArg)) {
        $confFile = $fileArg;
    } else {
        my $envVblValue = $ENV{"MANWEB_CONF_FILE"};
        if (defined($envVblValue)) {
            $confFile = $envVblValue;
        } else {
            $confFile = "/etc/manweb.conf";
        }
    }

    open(CONF, "<$confFile") or die("Can't open configuration file " .
                                    "'$confFile'.  $ERRNO");
    
    my $webdir;

    while(<CONF>) {
        chomp();
        if (/^\s*#/) {
            #It's comment - ignore
        } elsif (/^\s*$/) {
            #It's a blank line - ignore
        } elsif (/\s*(\S+)\s*=\s*(\S+)/) {
            #It looks like "keyword=value"
            my ($keyword, $value) = ($1, $2);
            if ($keyword eq "webdir") {
                $webdir = $value;
                if ($webdir =~ m{^[^/]}) {
                    die("webdir value '$value' " .
                        "in configuration file '$confFile' " .
                        "is not valid.  It must be an absolute path, and " .
                        "therefore start with a slash.");
                } elsif ($webdir =~ m{^//}) {
                    # Two slashes would cause a unique problem when we try
                    # to make a file: URL out of it.
                    die("webdir value '$value' " .
                        "in configuration file '$confFile' " .
                        "is not valid.  It starts with two slashes.");
                }
            } elsif ($keyword eq "browser") {
                $browser = $value;
            } else {
                die("Unrecognized keyword in configuration file '$confFile': " 
                    . "'$keyword'");
            }
        } else {
            die("Invalid syntax in configuration file line '$_'.  " .
                "Must be keyword=value, #comment, or blank line");
            }
    }              
    close(CONF);

    if (!defined($webdir)) {
        $webdir = "/usr/man/web";
    }
    if (!defined($browser)) {
        $browser = $ENV{"BROWSER"} ? $ENV{"BROWSER"} : "lynx";
    }
    
    return($webdir, $browser);
}





