#!/usr/local/bin/perl

# Log::File.pm
#   Implements a simple log file.
#
#   01/13/2000 Version 1.0.5
#   Use and distribute this script as per the Artistic License
#   Copyright (C) 2001 Igor S. Livshits <mailto:ispy@ayradyss.org>

package Log::File;
use Exporter;
use FileHandle;

@ISA= qw(Exporter);
@EXPORT=(MakeEntry, SeparateLogEntries, Path, Close);

#
# Methods
#

# Constructor
#
sub new
{
  my($type)= shift;		# skim off the class reference
  my($this)= {};		# create this object and set defaults

  bless $this, $type;		# connect the hash to the class
  $this->Initialize(@_);	# initialize it
  $this->PrepareLogFile();	# prepare the file for writing
  return $this;			# and return a reference to it
}


# Initialize
#   Set up and sanity check all instance variables
#
sub Initialize
{
  my($this)= shift;		# skim off the class reference
  my(%parameters)= @_;		# the rest of the passed parameters
  my($terse)= ( 1 == 1 );	# the terse time tag flag

  undef $this->{'fileHandle'};	# not a valid file yet

  foreach $parameter (keys(%parameters))
  {				# instantiate this object's variables
    $this->{$parameter}= $parameters{$parameter};
  }

  # Confirm our default path
  $this->{'defaultPath'}= "/var/log/"
    unless defined($this->{'defaultPath'});

  # Confirm our new line delimiter
  unless (defined($this->{'newLineDelimiter'}))
  {
    $this->{'newLineDelimiter'}= "\n";
  }
  
  # Confirm our log entry separator
  unless (defined($this->{'separator'}))
  {
    $this->{'separator'}= $this->{'newLineDelimiter'} . "-"
      . $this->{'newLineDelimiter'}. $this->{'newLineDelimiter'};
  }

  # Confirm our directory path delimiter
  unless (defined($this->{'directoryPathDelimiter'}))
  {
    $this->{'directoryPathDelimiter'}= "/";
  }

  # Confirm our name
  unless (defined($this->{'name'}))
  {
    $this->{'name'}= "log";
  }

  # Confirm our log file name
  unless (defined($this->{'logFileName'}))
  {
    my($timeTag)= $this->TimeTag($terse);
    while (-e $this->{'name'}.".".$timeTag)
    {				# make sure our time stamp is unique
      $timeTag++;		# add a second...
    }
    $this->{'logFileName'}= $this->{'name'}.".".$timeTag;
  }

  # Confirm our directory
  if (defined($this->{'directory'}))
  {
    # Make sure the directory name ends with a directory delimiter 
    $this->{'directory'}.= $this->{'directoryPathDelimiter'}
      unless ($this->{'directory'}=~ /$this->{'directoryPathDelimiter'}$/);
    mkdir($this->{'directory'}, 0755) unless -d $this->{'directory'};
  }
  unless (-d $this->{'directory'})
  {				# our directory does not exist
    unless (mkdir($this->{'directory'}, 0755))
    {				# an attempt to create it failed
      print "Unable to create the requested log directory <",
        $this->{'directory'}, ">: $!", $this->{'newLineDelimiter'};
      print "Will try present working directory for logs.",
        $this->{'newLineDelimiter'};
				# try for a default directory
      $this->{'directory'}= $this->{'defaultPath'} . $this->{'name'}
        . $this->{'directoryPathDelimiter'};
      unless (-d $this->{'directory'})
      {				# our default directory is missing
	unless (mkdir($this->{'directory'}, 0755))
	{			# report the error and blank the directory
	  print "Unable to create the default log directory <",
	    $this->{'directory'}, ">: $!", $this->{'newLineDelimiter'};
	  print "Will try present working directory for logs.",
	    $this->{'newLineDelimiter'};
	  $this->{'directory'}= "";
	}
      }
    }
  }
}


# PrepareLogFile
#
sub PrepareLogFile
{
  my($this)= shift;		# skim off the class reference
  my($fh)= $this->{'fileHandle'}; # the log file handle

  if ($fh)
  {				# catch attempts to prepare an active log
    print $fh "Ignored an attempt to prepare the log file again",
      $this->{'newLineDelimiter'};
  }
  else
  {				# attempt to open a file with write privs
    $fh= $this->{'fileHandle'}=
      new FileHandle ">".$this->{'directory'}.$this->{'logFileName'};
  }
    
  if ($fh)
  {				# make sure we can write
    $fh->autoflush(1);		# unbuffer output
    print $fh "Starting log: ", TimeTag(), ".", $this->{'newLineDelimiter'};
  }
  else
  {
    print "Could not write in my log; will ignore further log entries."
      . $this->{'newLineDelimiter'};
  }
}


# MakeEntry
#
sub MakeEntry
{
  my($this)= shift;		# skim off the class reference
  my($fh)= $this->{'fileHandle'}; # the log file handle

  print $fh join("", @_, $this->{'newLineDelimiter'})
    if $fh;
}


# SeparateLogEntries
#
sub SeparateLogEntries
{
  my($this)= shift;		# skim off the class reference
  my($fh)= $this->{'fileHandle'}; # the log file handle

  print $fh $this->{'separator'}
    if $fh;
}


# Path
#
sub Path
{
  my($this)= shift;		# skim off the class reference

  return $this->{'directory'} . $this->{'logFileName'};
}



# Close
#
sub Close
{
  my($this)= shift;		# skim off the class reference
  my($fh)= $this->{'fileHandle'}; # the log file handle

  if ($fh)
  {				# make sure we can write
    $this->SeparateLogEntries();
    print $fh "Closing log: ", TimeTag(), ".", $this->{'newLineDelimiter'};

    undef($this->{'fileHandle'}); # destroy the file handle
  }
}


# TimeTag
#
sub TimeTag()
{
  my($terse)= shift;		# should I return a terse tag?
  my($second, $minute, $hour, $day, $month, $year)=
    localtime(time());		# set up the time tag
  
  $month++;			# correct for 0 based count
				# and pad single digits
  $minute= "0".$minute if ($minute < 10);
  $second= "0".$second if ($second < 10);
  $month= "0".$month if ($month < 10);
  $day= "0".$day if ($day < 10);
  $year+= 1900;			# add missing centuries

  return "$year$month$day$hour$minute$second"
    if $terse;			# return a terse time stamp per request

  return			# otherwise, a long version
    "$hour:$minute:$second $month/$day/$year";
}


#
# Required termination
#
1;