/usr/local/CPAN/NNML/NNML/Group.pm


#                              -*- Mode: Perl -*- 
# Group.pm -- 
# ITIID           : $ITI$ $Header $__Header$
# Author          : Ulrich Pfeifer
# Created On      : Sat Sep 28 16:33:51 1996
# Last Modified By: Ulrich Pfeifer
# Last Modified On: Sat Mar 15 13:44:15 1997
# Language        : CPerl
# Update Count    : 58
# Status          : Unknown, Use with caution!
# 
# (C) Copyright 1996, Universität Dortmund, all rights reserved.
# 

package NNML::Group;
use IO::File;
use strict;

sub new {
  my $type = shift;
  my %parm = @_;
  my $self = {};

  for (qw(name dir min max post ctime)) {
    $self->{'_'.$_} = $parm{$_} if exists $parm{$_};
  }
  $self->{_time} = 0;
  bless $self, $type;
}

sub max   { $_[0]->{_max}};
sub min   { $_[0]->{_min}};
sub name  { $_[0]->{_name}};
sub post  { $_[0]->{_post}};
sub ctime { $_[0]->{_ctime}};
sub dir   { $_[0]->{_dir}};

sub add   {
  my ($self, $id) = @_;
  my $ano = ++$self->{_max};

  if ($id) {
    $self->{_byid}->{$id}  = $ano;
    $self->{_byno}->{$ano} = $id;
  }
  $ano;
}

sub article_by_id {
  my ($self, $msgid) = @_;

  $self->_update;
  $self->{_byid}->{$msgid};
}

sub article_by_no {
  my ($self, $ano) = @_;

  $self->_update;
  $self->{_byno}->{$ano};
}

sub overview {$_[0]->{_dir}. '/.overview'}

sub _update {
  my $self = shift;

  # Assume '.overview' has not changed if 'active' was not
  # modified. The implementation is not correct since a stat() for
  # active is not forced - but it saves many stat() calls. A stat()
  # call for 'active' is forced when message id's are used. Therfore
  # this is quite good.

  if (NNML::Active::last_change() > $self->{_time}) {
    my $mtime = (stat($self->overview))[9];

    $self->_read_overview if $mtime > $self->{_time};
  }
}

sub _read_overview {
  my $self = shift;
  $self->{_time}  = time;
  $self->{_byid}  = {};
  $self->{_byno}  = {};
  $self->{_ctime} = (stat($self->overview))[9];
  my $fh = new IO::File "<" . $self->overview;
  die "Could not read overview file" unless defined $fh;
  my $line;
  while (defined ($line = <$fh>)) {
    chomp($line);
    my($ano, $subject, $from, $date, $id, $references, $chars, $lines, $xref)
      = split /\t/, $line;
    $id =~ s/^\s+//; $id =~ s/\s+$//;
    $self->{_byid}->{$id}  = $ano;
    $self->{_byno}->{$ano} = $id;
  }
  $fh->close;
}

# This assumes that articles are stored in increasing order.
# It deserves tuning (binary search).
sub newnews {
  my ($self, $time) = @_;
  my %result;
  
  $self->_update;
  return () if $self->{_ctime} < $time;
  my $ano;
  my $dir = $self->{_dir}.'/';
  for ($ano=$self->max;$ano>=$self->min;$ano--) {
    my $file = $dir.$ano;
    if (-e $file) {
      my $ctime = (stat($file))[9];
      if ($ctime >= $time) {
        $result{$self->{_byno}->{$ano}} = $ctime;
      } else {
        last;
      }
    }
  }
  %result;
}

sub xover {
  my ($self,$min,$max) = @_;
  my $result;

  $min ||= $self->min;
  $max ||= $self->max;
  my $fh = new IO::File "<" . $self->overview;
  die "Could not read overview file" unless defined $fh;
  my $line;
  while (defined ($line = <$fh>)) {
    if ($line =~ /^(\d+)/) {
      if ($1 >= $min and $1 <= $max) {
        $result .= $line;
      }
    }
  }
  $fh->close;
  $result;
}

sub get {
  my ($self, $ano) = @_;
  my $file = $self->{_dir} . "/$ano";
  if (-e $file) {
    my $date = ((stat($file))[10]);
    my $fh = new IO::File "<" . $file;
    return unless $fh;
    local($/);

    my ($head, $body) = split /^\r?\n/m, <$fh>, 2;
    return $head, $body, $date;
  }
}

sub delete {
  my ($self, $ano) = @_;
  my $file = $self->{_dir} . "/$ano";
  my ($result, $line);
  
  if (-e $file) {
    unlink $file or return;
  }
  my $overview = $self->overview;
  my $backup   = $self->overview . '~';
  rename ($overview, $backup) or return;
  my $in  = new IO::File "<" . $backup;
  my $out = new IO::File ">" . $overview;
  return unless $in and $out;
  while (defined ($line = <$in>)) {
    if ($line =~ /^(\d+)/) {
      if ($1 == $ano) {
        $result++;
        next;
      }
    }
    $out->print($line);
  }
  $in->close;
  $out->close;
  return($result);
}

1;