#!/usr/bin/perl
# ucampas-navtest -- test if a list of *-b.html files are in the same NavTree
#
# https://www.cl.cam.ac.uk/local/sys/web/ucampas/
#
# Markus Kuhn -- https://www.cl.cam.ac.uk/~mgk25/

# this software requires Perl 5.14 or newer
use 5.014;  # implies use strict;

my $libdir;
BEGIN {
    use FindBin qw($RealBin); # find directory where this file is located ...
    $libdir= $RealBin;        #              ... derive library directory ...
    $libdir =~ s|/bin\z|/share/ucampas|;
}
use lib $libdir, "$libdir/perl-PlexTree";            # ... and add it to @INC
use NavTree;
use PlexTree;

# Parse the command-line options

my $usage = <<'EOT';
ucampas-navtest -- test if a list of *-b.html files are all in the same NavTree

Usage:

  ucampas-navtest [options] {files}

Examples:

  ucampas-navtest *-b.{html,php}
  find -name '*-b.html' -print0 | ucampas-navtest -0
  find -name .svn -prune -false -o -iregex '.*-b\.\(html?\|php\)' -print0 | \
    ucampas-navtest -0

Normal options:

  --dump Print the entire navigation tree, as ucampas reads it, in the
         format of a uconfig.txt file, and check nothing. The tree is the
         one that the file named as an argument belongs to, or that
         "index" belongs to. Diffing two such dumps shows what a change to
         the uconfig.txt files did to the site, if anything.

  -s     Read a list of files to be checked as linefeed-terminated strings
         from stdin

  -0     Read a list of files to be checked as zero-terminated strings
         from stdin (e.g. as written by "find -print0")

If neither any file nor option -0 or -s are specified, then this command
performs the equivalent of

  ucampas-find -print0 | ucampas-navtest -0

EOT

# parse the global uconfig.txt file
NavTree::set_global_uconfig("$libdir/uconfig.txt");

my @fn = ();  # list of files to be checked

my $parse_options = 1;
my $filelist_read;
my $dump;
while (@ARGV) {
    $_ = shift @ARGV;
    if ($parse_options && /^-/) {
	if (/^--dump\z/) {
	    $dump = 1;
	} elsif (/^-s\z/) {
	    while (defined($_ = <>)) {
		chomp;
		push @fn, $_;
	    }
	    $filelist_read = 1;
	} elsif (/^-0\z/) {
	    local $/ = "\0";
	    while (defined($_ = <>)) {
		chomp;
		push @fn, $_;
	    }
	    $filelist_read = 1;
	} elsif (/^-h/ || /^--help/) {
	    print $usage;
	    exit 0;
	} elsif (/^--\z/) {
	    $parse_options = 0;
	} else {
	    die("Unknown command line option '$_'!\n\n" . $usage);
	}
    } else {
	push @fn, $_;
    }
}

# --dump needs no list of files to check, only somewhere to start: printing
# the tree is itself what reads it, as the printer walks the children through
# the accessor that reads a uconfig.txt file where it has not been read yet.
# So one file anywhere in the tree brings out all of it.
if ($dump) {
    my $fn = @fn ? $fn[0] : 'index';
    $fn =~ s/^\.\///;
    print NavTree::find_file(NavTree::preprocess_filename($fn, 'output'))
	->top->print_uconfig;
    exit 0;
}

# if no file list was specified, call ucampas-find
unless (@fn || $filelist_read) {
    local $/ = "\0";
    my $find;
    open($find, "$RealBin/ucampas-find -print0 |")
	or die("Cannot execute '$RealBin/ucampas-find -print0': $!\n");
    while (defined($_ = <$find>)) {
	chomp;
	push @fn, $_;
    }
    close($find)
	or die("Cannot close '$RealBin/ucampas-find -print0': $!\n");
}

my $cur;
my $top;
my @missing = ();
foreach my $fn (@fn) {
    s/^\.\///;  # remove the redundant ./ prefix found in "find" output
    #print $fn . "\n";
    $fn = NavTree::preprocess_filename($fn, 'output');
    $cur = NavTree::find_file($fn);
    if (defined $top) {
	if ($top->cid != $cur->cid || $cur->get('unreachable')) {
	    push @missing, $fn;
	}
    } else {
	$top = $cur->top;
    }
}

if (@missing) {
    print STDERR "\nWarning: these files were not found in the Ucampas " .
	"navigation tree starting at\n" . $top->fpath . "uconfig.txt:\n\n" .
	join("\n", @missing) . "\n\nPlease update uconfig.txt appropriately.\n"
}

exit(@missing);
