#!/usr/bin/perl -w
# batch-active-update
# Author: David Lawrence <tale@isc.org>

# Reads a series of ctlinnd newgroup/rmgroup/changegroup commands, such as
# is output by checkgroups and actsync, and efficiently handles them all at
# once.  Input can come from command line files or stdin, a la awk/sed.

require '/usr/lib/news/lib/innshellvars.pl';

$oldact = $inn::active;         # active file location
$oldact = $inn::active;         # active file location (same; shut up, perl -w)
$newact = "$oldact.new$$";      # temporary name for new active file
$actime = "$oldact.times";      # active.times file
$pausemsg = 'batch active update, ok'; # message to be used for pausing?
$diff_flags = '';		# Flags for diff(1); default chosen if null.

$0 =~ s#^.*/##;

die "$0: must run as $inn::newsuser user"
  unless $> == (getpwnam($inn::newsuser))[2];

$debug = -t STDOUT ? 1 : 0;

$| = 1;                # show output as it happens (for an rsh/ssh pipe)

# Guess at best flags for a condensed diff listing.  The
# checks for alternative operating systems is incomplete.
unless ($diff_flags) {
  if (`diff -v 2>&1` =~ /GNU/) {
    $diff_flags = '-u0';
  } elsif ($^O =~ /^(dec_osf|solaris)$/) {
    $diff_flags = '-C0';
  } elsif ($^O eq 'nextstep') {
    $diff_flags = '-c0';
  } else {
    $diff_flags = '-c';
  }
}

print "reading list of groups to update\n" if $debug;

$eval  = "while (<OLDACT>) {\n";
$eval .= "  \$group = (split)[0];\n";

while (<>) {
  if (/^\s*\S*ctlinnd newgroup (\S+) (\S)/) {
    $toadd{$1} = $2;
  } elsif (/^\s*\S*ctlinnd rmgroup (\S+)/) {
    $eval .= "  next if \$group eq '$1';\n";
  } elsif (/^\s*\S*ctlinnd changegroup (\S+) (\S)/) {
    $eval .= "  s/ \\S+\$/ $2/ if \$group eq '$1';\n";
  }
}

$eval .= "  delete \$toadd{\$group};\n";
$eval .= "  if (!print(NEWACT \$_)) {\n";
$eval .= "    die \"\$0: writing \$newact failed (\$!), aborting\\n\";\n";
$eval .= "  }\n";
$eval .= "}\n";

&ctlinnd("pause $pausemsg");
&ctlinnd("flush ''");

open(OLDACT, "< $oldact") || die "$0: open $oldact: $!\n";
open(NEWACT, "> $newact") || die "$0: open $newact: $!\n";

print "rewriting active file\n" if $debug;
eval $eval;
for (sort keys %toadd) {
  $add = "$_ 0000000000 0000000001 $toadd{$_}\n";
  if (!print( NEWACT $add)) {
    &ctlinnd("go $pausemsg");
    die "$0: writing $newact failed ($!), aborting\n";
  }
}

close(OLDACT) || warn "$0: close $oldact: $!\n";
close(NEWACT) || warn "$0: close $newact: $!\n";

if (!rename("$oldact", "$oldact.old")) {
  warn "$0: rename $oldact $oldact.old: $!\n";
}

if (!rename("$newact", "$oldact")) {
  die "$0: rename $newact $oldact: $!\n";
}

&ctlinnd("reload active 'updated from checkgroups'");
system("diff $diff_flags $oldact.old $oldact");
&ctlinnd("go $pausemsg");

print "updating $actime\n" if $debug;
if (open(TIMES, ">> $actime")) {
  $time = time;
  for (sort keys %toadd) {
    print TIMES "$_ $time checkgroups-update\n" || last;
  }
  close(TIMES) || warn "$0: close $actime: $!\n";
} else {
  warn "$0: $actime not updated: $!\n";
}

exit 0;

sub
ctlinnd

{
  local($command) = @_;

  print "ctlinnd $command\n" if $debug;
  if (system("$inn::newsbin/ctlinnd -s $command")) {
    die "$0: \"$command\" failed, aborting\n";
  }
}
