#    ConfHelper module
#    Copyright (C) 2000 Progeny Linux Systems, Inc.
#    Written by John Goerzen <jgoerzen@progenylinux.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

package ConfHelper;

use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);

# NH : copied and adapted from Debian 4 etch
#require Exporter;
#require AutoLoader;
#
#@ISA = qw(Exporter AutoLoader);
## Items to export into callers namespace by default. Note: do not export
## names by default without a very good reason. Use EXPORT_OK instead.
## Do not simply export all your public functions/methods/constants.
#@EXPORT = qw(
#	
#);
#$VERSION = '0.9';
#
#
## Preloaded methods go here.
#
## Autoload methods go after =cut, and are processed by the autosplit program.
#
#1;
#__END__
# NH : end of adaptation

# Below is the stub of documentation for your module. You better edit it!

=head1 NAME

ConfHelper - Configuration file management

=head1 SYNOPSIS

  use ConfHelper;
  $file = new ConfHelper('myprogram', '/etc/myprogram.conf');

=head1 DESCRIPTION

ConfHelper is a module that is designed to help you edit configuration files
in an automated way without trampling on the toes of users or other programs.
It does this by using "chunking"; that is, your program modifies a certain
chunk of the config file and does not touch everything else.  While this
approach is not appropriate everywhere, it can be remarkably useful anyway.

The methods that return a chunk of data (an entire configuration area)
return an array.  Each element in the array is a single line that has
been run through chomp().  When passed back to set() functions, the newline
character is added back on before being written to disk.

=head1 METHODS

=over 4

=cut

use IO::Handle;
use IO::File;
require Logfile::Rotate;

=item new($package, $filename)

Creates a new object.  The I<package> parameter declares the name of your
program or package, and the I<filename> parameter specifies the name of the
configuration file.

=cut

sub new {
  my ($class, $package, $filename) = @_;
  my $self = {};
  my $fd;
  
  $self->{package} = $package;

  $self->{startline} = 
"### $package DEBCONF AREA. DO NOT EDIT THIS AREA OR INSERT TEXT BEFORE IT.";
  $self->{endline} =
"### END OF DEBCONF AREA.  PLACE YOUR EDITS BELOW; THEY WILL BE PRESERVED.";

  bless $self, $class;

  if ($filename) {
    $self->{filename} = $filename;
    $fd = new IO::File;
    sysopen($fd, $filename, O_CREAT | O_RDWR, 0644) ||
      die "Can't open $filename: $!";
  } else {
    die "Filename not passed."
  }

  $self->{fd} = $fd;
  return $self;
}

=item getversion

Returns the version of the ConfHelper module that created the object.

=cut

# Version

sub getversion {
  return $VERSION;
}

=item config($variable, $value)

Sets a configuration variable.  The following are valid configuration
variables:

=over 4

=item startline

Specifies the complete text of the line that is used to identify the
start of the ConfHelper section of your config file.

=item endline

Specifies the complete text of the line that is used to identify the
end of the ConfHelper section of your config file.

=back

=cut

# Sets a config var.

sub config {
  my ($s, $var, $val) = @_;
  $s->{$var} = $val;
}

=item hasconfarea

Returns 1 if the file has a configuration area, and 0 otherwise.

=cut

sub hasconfarea {
  my $s = shift;

  return ($s->getconfarea() ? 1 : 0);
}


=item getconfarea

Returns your configuration area from the configuration file as a large
array.  If there is none, it returns undef.

=cut

sub getconfarea {
  my $s = shift;
  my $fd = $s->{fd};
  my @retval;

  seek $fd, 0, SEEK_SET;
  
  # Search for the start marker.

  return undef unless $s->findline($s->{startline}, 0);

  # Search for end marker and return the intermediate result.

  return $s->findline($s->{endline}, 1);
}

=item getotherareas

Returns an array that represents all non-ConfHelper areas of your
configuration file.

=cut

sub getotherareas {
  my $s = shift;
  my $fd = $s->{fd};
  my $inline;
  my @retval;

  seek $fd, 0, SEEK_SET;

  # No start marker?  Just slurp in everything.

  unless ($s->findline($s->{startline}, 0)) {
    seek $fd, 0, SEEK_SET;
    while (defined($inline = <$fd>)) {
      chomp $inline;
      push @retval, $inline;
    }
    return @retval;
  }

  seek $fd, 0, SEEK_SET;

  # Find everything before the top.
  
  @retval = $s->findline($s->{startline}, 1);

  # Now, skip to the endline.

  $s->findline($s->{endline}, 0) or die "Missing end line";

  # Finally, slurp in everything else.

  while (defined($inline = <$fd>)) {
    chomp $inline;
    push @retval, $inline;
  }

  return @retval;
}

=item setconfarea(@confarea)

This method will set the configuration area to the value of the string
passed as a parameter.  ConfHelper will automatically rotate the file, saving
backups of it.

=cut

sub setconfarea {
  my ($s, @dcarea) = @_;
  my $fd = $s->{fd};
  my @otherarea = $s->getotherareas();  

  # Close the file, rotate it.

  close $fd;

  my $log = new Logfile::Rotate(File => $s->{filename},
				Count => 9,
				Gzip => 'no');
  $log->rotate();
  undef $log;

  # Reopen it.

  $fd = new IO::File;
  sysopen($fd, $s->{filename}, O_CREAT | O_RDWR, 0644) ||
# NH : fix
      die "Can't open $s->{filename}: $!";
#      die "Can't open $filename: $!";
# NH : /fix
  $s->{fd} = $fd;

  seek $fd, 0, SEEK_SET;

  print $fd $s->{startline}, "\n";
  print $fd join("\n", @dcarea), "\n";
  print $fd $s->{endline}, "\n";
  print $fd join("\n", @otherarea), "\n";
  truncate($fd, tell($fd));
  
  return 1;
}

=item setotherarea_DANGEROUS(@otherarea)

Sets the non-confarea portion of the file to the value specified.
NOTE: THIS FUNCTION IS DANGEROUS AND SHOULD NOT BE USED UNLESS
ABSOLUTELY NECESSARY!  The configuration file is NOT rotated when this
call is used.

=cut

# Sets the other area.

sub setotherarea_DANGEROUS {
  my ($s, @otherarea) = @_;
  my $fd = $s->{fd};
  my @dcarea = $s->getconfarea();

  seek $fd, 0, SEEK_SET;

  print $fd $s->{startline}, "\n";
  print $fd join("\n", @dcarea), "\n";
  print $fd $s->{endline}, "\n";
  print $fd join("\n", @otherarea), "\n";
  truncate($fd, tell($fd));
  
  return 1;
}

#

sub findline {
  my ($s, $line, $r) = @_;
  my $fd = $s->{fd};
  my @retval;
  my $inline;

  while (defined($inline = <$fd>)) {
    chomp $inline;
    if ($inline eq $line) {
      return @retval if $r;
      return 1;
    }
    push @retval, $inline if $r;
  }

  return undef;
}
  

1;

=pod

=back

=head1 AUTHOR

John Goerzen <jgoerzen@progenylinux.com> of Progeny Linux Systems
http://www.progeny.com.

=head1 COPYRIGHT

Copyright (C) 2000 Progeny Linux Systems, Inc.
Written by John Goerzen <jgoerzen@progenylinux.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


=head1 SEE ALSO

debconf(1), perl(1).

=cut


