#!/usr/bin/perl -I/usr/share/rmagic
###################################################################
# Module: rmagic.pl
#
# Configuration: rmagic.cfg/rmagic.ini
#
# Description: 
# Creates readable, presentable reports from Analog's web site 
# statistics analysis. See the docs at docs/index.html for details.
#
# Recreated: 1999.Feb.19 by Jeremy Wadsack, for Wadsack-Allen Digital Group
# 	Copyright (C) 1999,2002 Wadsack-Allen. All rights reserved. 
#  http://www.wadsack-allen.com/digitalgroup/
#
# Original concept: 1999.Feb.16 by Corey Kaye, for Digital Nova Scotia
# 	Copyright (C) 1999 Digital Nova Scotia. All rights reserved.
#  http://www.digitalns.pair.com/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the Artistic License as described in 
# docs/license.html
###################################################################
# Date        Modification
# -----------------------------------------------------------------
# * SEE What's New AND Changes IN THE DOCUMENTATION *
###################################################################
require 5.004;
use strict;

# Some debugging aids
BEGIN {
	if( 0 ) {
		$SIG{__WARN__} = sub { 
			warn $_[0]; 
			my @fields = caller(0);
			print STDERR "Stack trace: ";
			print STDERR "called from $fields[1] line $fields[2]" unless $_[0] =~ /\n$/;
			print STDERR "\n";
			for( 1 .. 100 ) {
				@fields = caller($_);
				last unless @fields;
				print STDERR "             $fields[3] in $fields[1] line $fields[2]\n";
			} # end for
		};
	} # end if
} # end BEGIN


#-------------------------------#
# Setup which libraries we need #
#-------------------------------#

# Portably handle File routines
use File::Copy;
use File::Basename;
use File::Path;

# If you're running Perl 5.004 you'll need to get this from CPAN, 
# it ships with 5.005 and 5.6
use File::Spec;

# - Find execution directory
#use FindBin qw($RealBin);
my $RealBin = "/usr/share/rmagic"; #Debian inst path

# - Add execution directory to lib path, resolving symbolic refs
#use lib $RealBin; #Debian use -I instead (and has the binary elsewhere)

# - Read INI files
use Config::IniFiles;

# - Our own internal libraries (ship with software)
use wadg::Formatter;
use wadg::Errors;
use wadg::HTMLWriter;
use wadg::rm::Settings;
use wadg::rm::CROParser;
use wadg::rm::Graphs;
use wadg::rm::Report;
	use wadg::rm::Report::GeneralSummary;
	use wadg::rm::Report::TimeReport;
	use wadg::rm::Report::TimeSummary;
	use wadg::rm::Report::HierarchicalReport;
	use wadg::rm::Report::RangeReport;
	use wadg::rm::Report::QuickSummary;

#-------------------------------#
# Declare our global variables  #
#-------------------------------#
my %config;
	$config{_INTERNAL} = ();
	$config{_INTERNAL}{'_TITLE'} = 'Report Magic for Analog';
	$config{_INTERNAL}{'_VERSION'} = '2.13';

my( $rdata, $cdata, $lang );
my $logFile = *STDERR;	
my %statistics;
my %navigation;
my( $HEADER, $PREFIX, $HEADER_USED ) = (
	"$config{_INTERNAL}{_TITLE} $config{_INTERNAL}{_VERSION}\nCopyright (C) 1999,2002 Wadsack-Allen. All rights reserved.\n",
	'rmagic: ',
	0
);

#-------------------------------------#
# Build our objects and configuration #
#-------------------------------------#
my $formatter = new wadg::Formatter();
my $croParser = new wadg::rm::CROParser;
   $croParser->{globals}{'NAME'} = $config{_INTERNAL}{_TITLE};
   $croParser->{globals}{'VERSION'} = $config{_INTERNAL}{_VERSION};

# -- Load the rmagic directory language file or use english 
#    for initial startup messages.
my @language = ($RealBin);
my $fileN = File::Spec->catfile( $RealBin, 'lang.ini' );
if( !-e $fileN ) {
	@language = ($RealBin, 'lang', 'en');
	$fileN = File::Spec->catfile( $RealBin, 'lang', 'en', 'lang.ini' );
} # end if
$lang = new Config::IniFiles( -file => $fileN );
if( !defined $lang ) {
	# Can't use message function if $lang is undefined so just print these notices
	print $HEADER;
	print "\nERROR: Language file $fileN is missing or corrupt.\n\n";
	&finalize( -1 );
} # end if

# - Register our exception handlers now that language is loaded
wadg::Errors::set_warning_handler( \&message );
wadg::Errors::set_error_handler( \&finalize );

# -- Get the rest of the base language files
$fileN = File::Spec->catfile( @language, 'rdata.ini' );
$rdata = new Config::IniFiles( -file => $fileN );
if( !defined $rdata || !$rdata->Sections ) {
	wadg::Errors::error( -1, 'E0011', $fileN );
} # end if

$fileN = File::Spec->catfile( @language, 'cdata.ini' );
$cdata = new Config::IniFiles( -file => $fileN );
if( !defined $cdata || !$cdata->Sections ) {
	wadg::Errors::error( -1, 'E0011', $fileN );
} # end if

# - Get user configuration (without losing data already in %config...)
map( push( @ARGV, "-_INTERNAL_$_=$config{_INTERNAL}{$_}"), keys( %{$config{_INTERNAL}} ) );
tie %config, 'wadg::rm::Settings', ( 
	-argv => \@ARGV,
	-default => 'reports',
);

unless( %config && defined( tied(%config) ) ) {
	# -- Make sure error messages are printed in this case!
	$statistics{Verbose} .= 'E';
	# Get the filenames that should have been tried:
	my $requested = join( ',', grep( !/^-/, @ARGV ) );
	if( $requested ) {
		wadg::Errors::error( -1, 'E0014', $requested );
	} else {
		# Get the default name part.
		my( $base ) = fileparse( $0, '\..*' );
		wadg::Errors::error( -1, 'E0009', $base );
	} # end if
} # end unless

#########################################
# WARNING: DON'T USE CGI MODE!!!
# Yup, you thought you found something 
# cool!. Well, don't do it. It's not safe.
# It's going to disappear shortly for a 
# completely different approach. Don't use
# it. Don't even think about it.
#########################################
#    If CGI is not defined, but the environment contains a 
#    request method AND we're outputing to STDOUT, then assume
#    insecure, buggy IIS server and do CGI anyway!
if( ($config{reports}{File_Out} =~ /^CGI$/i) || 
	( defined $ENV{'REQUEST_METHOD'} && ($config{reports}{File_Out} eq '-') )
) {
	# ** Need to do security check on command-line options in CGI mode
	$config{reports}{File_Out} = '-';
	print STDOUT "Status: 403 Forbidden\n";
	print STDOUT "Content-type:text/html\n";
	print STDOUT q[
		<h1>Forbidden</h1>
		<p>You do not have access to this URL.</p>
	];
	wadg::Errors::error( -1, 'E0000', "DO NOT use CGI mode with Report Magic. It's unsafe." );
} # end if
# -- Separate out the program specific parts of the settings
%statistics = %{$config{statistics}};
tied(%config)->get_nav_styles();
%navigation = %{$config{navigation}};
delete $config{statistics};
delete $config{navigation};
$config{reports}{Meta_Refresh} = $statistics{Meta_Refresh};
$config{reports}{No_Robots} = $statistics{No_Robots};
$config{reports}{Format} = $statistics{Format};

# - Get input report settings and text files
if( defined $statistics{Language} ) {
	@language = ($RealBin, 'lang', $statistics{Language});

	# Do new lang.ini here 'cause english is already loaded
	$fileN = File::Spec->catfile( @language, 'lang.ini' );
	unless( -e $fileN ) {
		wadg::Errors::error( -1, 'E0011', $fileN );
	} # end if
	$lang = new Config::IniFiles( -file => $fileN, -import => $lang );

	# Load the language-specific extensions
	# Can import the object we're writing to 'cause import makes a copy.
	$fileN = File::Spec->catfile( @language, 'rdata.ini' );
	unless( -e $fileN ) {
		wadg::Errors::error( -1, 'E0011', $fileN );
	} # end if
	$rdata = new Config::IniFiles( -file => $fileN, -import => $rdata );

	$fileN = File::Spec->catfile( @language, 'cdata.ini' );
	unless( -e $fileN ) {
		wadg::Errors::error( -1, 'E0011', $fileN );
	} # end if
	$cdata = new Config::IniFiles( -file => $fileN, -import => $cdata );
} # end if

#
# Build next level of inheritance by inserting $rdata into %config
#
delete $config{x};
delete $config{q};
my( $S, $P, %short_names );
# -- Push the rdata under config, gathering a shortname -> letter mapping
foreach $S ( $rdata->Sections() ) {
	my $sn;
	foreach $sn ( $rdata->val( $S, 'ShortName' ) ) {
		$short_names{uc($sn)} = $S if $sn;
	} # end foreach
	$config{$S} = {} unless ref $config{$S} eq 'HASH';
	foreach $P ( $rdata->Parameters( $S ) ) {
		$config{$S}{$P} = $rdata->val( $S, $P ) unless defined $config{$S}{$P};
	} # end foreach
} # end foreach
# -- Now convert shortname sections to letters overriding the letter values
foreach $S (keys %config) {
		next unless defined $short_names{uc($S)};
		$config{$short_names{uc($S)}} = {} unless ref $config{$short_names{uc($S)}} eq 'HASH';
		foreach $P (keys %{$config{$S}}) {
			$config{$short_names{uc($S)}}{$P} = $config{$S}{$P};
		} # end foreach
		delete $config{$S};
} # end foreach

# -- Localize the formatter
$formatter->localize( 'day_abrev', $lang->val( 'Dates', 'shortDays' ) );
$formatter->localize( 'day_names', $lang->val( 'Dates', 'longDays' ) );
$formatter->localize( 'month_abrev', $lang->val( 'Dates', 'shortMonths' ) );
$formatter->localize( 'month_names', $lang->val( 'Dates', 'longMonths' ) );
$formatter->localize( 'number_sep', $lang->val( 'Symbols', 'decimalList' ) );
$formatter->localize( 'decimal_sep', $lang->val( 'Symbols', 'decimalSeparator' ) );
$formatter->localize( 'decimal_digits', $lang->val( 'Symbols', 'decimalDigits' ) );
$formatter->localize( 'percent', $lang->val( 'Symbols', 'percent' ) );


# -- Configure the Report class
# --- Rather than settings globals here, lets create a 'report defaults' hash that sets
#     the rest of the default options ( CDATA => $cdata, LANG => $lang, FORMATTER => $formatter )
#     Then call 'new wadg::rm::Report( token => $reportType, CONFIG => $config{$reportType}, %report_defaults )'
my %report_defaults = ( _CDATA => $cdata, _LANG => $lang, _FORMATTER => $formatter, _CONFIG => \%config );
$wadg::rm::Report::FORMATTER     = $formatter;
$wadg::rm::Report::LANG     = $lang;
$wadg::rm::Report::CONFIG    = \%config;

#---------------------------------------#
# If there's an error log file, open it #
#---------------------------------------#
if( $statistics{Log_File} ) {
	my @now = localtime;
	my $now = (1900 + $now[5]) . '/' . (1 + $now[4]) . '/' . $now[3] . ' ' . $now[2] . ':' . $now[1];
	my $infile;
	($infile, undef, undef) = fileparse( $statistics{File_In}, '\..*' );
	$statistics{Log_File} =~ s/%infile%/$infile/gi;
	$statistics{Log_File} =~ s/\${infile}/$infile/gi;
	$statistics{Log_File} =~ s/%infile%/$infile/gi;
	$statistics{Log_File} =~ s/%([^%]+)%/$formatter->formatDate( $1, $now )/ge;

	if( !open( $logFile, ">>$statistics{Log_File}" ) ) {
		wadg::Errors::error( -1, 'E0012', $statistics{Log_File} );
	} # end if
	# - Tell user of logfile on STDOUT
	print( $formatter->formatMessage( $lang->val( 'Errors', 'N0011' ), $statistics{Log_File} ) . "\n" );
} # end if
# - Send output to the logfile (or STDERR)
#   and auto-flush (so user can 'tail -f')
select( $logFile );
$| = 1;

# - Output the starting notice and date/time.
my $now = localtime;
message( 'N0004', $now );

#-----------------------------------#
# Configure and format our settings #
#-----------------------------------#
# -- Convert any date formatting codes in the input file name using today's date
my @now = localtime;
$now = (1900 + $now[5]) . '/' . (1 + $now[4]) . '/' . $now[3] . ' ' . $now[2] . ':' . $now[1];
$statistics{File_In}	=~ s/%([^%]+)%/$formatter->formatDate( $1, $now )/ge;
my $infile;
($infile, undef, undef) = fileparse( $statistics{File_In}, '\..*' );


# -- Build a quick_summary report object
# We will push max values onto this report's _summaries list
# but it would be better if we could push them on the CROParser
# so we can remove the whole redundant report building at the end!
my $quick_summary = undef;
if( defined $config{q}{Rows} ) {
	# Update QuickSummary ALL list if necessary
	if( $config{q}{Rows} =~ /ALL/i ) {
		my( $rep, @repList );
		foreach $rep ( keys %config ) {
			if( defined $rdata->val( $rep, 'MostActive' ) ) {
				push @repList, $rep;
			} # end if
		} # end if
		$config{q}{Rows} = join( ',', @repList );
	} # end if

	my $reportStyle = $config{q}{ReportType};
	$reportStyle =~ s/^\s*(.+)\s*$/$1/;
	if( "wadg::rm::Report::$reportStyle"->isa( 'wadg::rm::Report::QuickSummary' ) ) {
		$quick_summary = "wadg::rm::Report::$reportStyle"->new( token => 'q', %report_defaults );
	} else {
		# -- Unknown style, so notify the user and dump the report
		wadg::Errors::warning( 'W0003', $reportStyle, 'q' );
	} # end if

	$wadg::rm::Report::QuickSummary::CONFIG = \%config;
} # end if


#-----------------------------------#
# Start processing the data file    #
#-----------------------------------#
# - Output notice of settings file and input file being read
message( 'N0002', tied(%config)->getFilename() );
message( 'N0005', $statistics{File_In} );

# - Get data from Analog's computer readable output file
if( $croParser->getFile( $statistics{File_In} ) ) {
	wadg::Errors::error( -1, 'E0005', $statistics{File_In}, "\n");
} # end if

# Set a default title, if none is given
if( !defined $config{website}{Title} && defined $croParser->{globals}{HN} ) {
	$config{website}{Title} = $formatter->formatMessage( $lang->val( 'Text', 'O0014' ), $croParser->{globals}{HN} );
} # end if


# 
# Convert any outfiles that contain formatting codes or stdout specifier
# use date of last request OR current date if date of last request is undefined
#
if( defined $croParser->{globals}{DataEnd} ) {
	$now = $formatter->getDateString( @{$croParser->{globals}{DataEnd}} );
} else {
	my @now = localtime;
	$now = (1900 + $now[5]) . '/' . (1 + $now[4]) . '/' . $now[3] . ' ' . $now[2] . ':' . $now[1];
} # end if

# - Format the date globals in the CROParser to our specs
if( defined $croParser->{globals}{GenerationTime} ) {
	$croParser->{globals}{GenerationTime} = $formatter->formatDate( $lang->val( 'Text', 'O0008' ), $formatter->getDateString( @{$croParser->{globals}{GenerationTime}} ) );
} # end if
if( defined $croParser->{globals}{DataStart} ) {
	$croParser->{globals}{DataStart} = $formatter->formatDate( $lang->val( 'Text', 'O0008' ), $formatter->getDateString( @{$croParser->{globals}{DataStart}} ) );
} # end if
if( defined $croParser->{globals}{DataEnd} ) {
	$croParser->{globals}{DataEnd} = $formatter->formatDate( $lang->val( 'Text', 'O0008' ), $formatter->getDateString( @{$croParser->{globals}{DataEnd}} ) );
} # end if

foreach ( $config{reports}{File_Out}, 
          $navigation{File_Out}, 
          $statistics{Frame_File_Out}, 
          $statistics{Log_File},
          $statistics{Include},
          $config{website}{Title},
          $croParser->{header},
          $croParser->{footer},
        ) {
	next if !defined;
	s/%infile%/$infile/gi;			# For backward compatibility
	s/\$\{infile\}/$infile/gi;
	s/\$\{([^\:\}]+)\:([^\}]+)\}/$formatter->format_value( $2, $croParser->{globals}{$1}) || "\${$1:$2}"/ge;
	s/\$\{([^\:\}]+)\}/$croParser->{globals}{$1} || "\${$1}"/ge;
	s/%([^%]+)%/$formatter->formatDate( $1, $now )/ge;
} # end foreach
$wadg::rm::Report::GLOBALS = $croParser->{globals};

#---------------------------------#
# Begin creating the reports      #
#---------------------------------#
# - Create initial report files
my $report_writer = &openOutputFiles( $config{reports}{File_Out} );

#
# Start reading input data and process according to report type
#
my( $report, $columns) = 
  ( '',      '',     );

my @lineData;
my $currentReport = new wadg::rm::Report( code => undef );
$currentReport->{token} = '';

#
# Now process the rest of the reports.
#
while( !$croParser->eof ) {

	( $report, $columns, @lineData ) = $croParser->nextLine();
	
	#
	# When report changes, start new report.
	#
	if( $report ne $currentReport->{token} ) {
		# Finish old report
		#   - Push summaries onto quick_summary
		if( defined $quick_summary && defined $config{$currentReport->{token}}{ShortName}) {
			my( $dType ) = split /[\012\015]+/, $config{$currentReport->{token}}{ShortName};
			my $summ = $currentReport->{_summaries}{$dType}{max};
			if( defined $summ ) {
				$quick_summary->{_summaries}{$dType} = () unless defined $quick_summary->{_summaries}{$dType};
				$quick_summary->{_summaries}{$dType}{max} = () unless defined $quick_summary->{_summaries}{$dType}{max};
				foreach ( keys %{$summ} ) {
					$quick_summary->{_summaries}{$dType}{max}{$_} = $summ->{$_};
				} # end foreach
			} # end if
		} # end if
		#   - Outputs summaries and close table, close file if multi-file mode
		if( ($statistics{One_File} != 1) && (defined $currentReport->{writer}) ) {
			undef $currentReport->{REPORTSPAN};		# No duplicate dates
			$currentReport->end_report();
			wadg::rm::Report::closeReportFile( $currentReport->{writer}, $croParser->{footer}, $currentReport->{token} );
		} else {
			$currentReport->end_report();
		} # end if
		
		# -- Skip blank lines
		next unless defined $report;

		# -- Start the new report, extensibly
		my $reportStyle = $config{$report}{ReportType};
		$reportStyle =~ s/^\s*(.+)\s*$/$1/;
		if( $reportStyle eq 'Simple' ) {
			$currentReport = new wadg::rm::Report( token => $report, %report_defaults );
		} elsif( !defined $reportStyle ) {
			# -- Unknown report, so notify the user and dump the report
			wadg::Errors::warning( 'W0001', $report );
			$croParser->nextReport();
			next;
		} elsif( "wadg::rm::Report::$reportStyle"->isa( 'wadg::rm::Report' ) ) {
			$currentReport = "wadg::rm::Report::$reportStyle"->new( token => $report, %report_defaults );
		} elsif( $reportStyle->isa( 'wadg::rm::Report' ) ) {
			$currentReport = new $reportStyle( token => $report, %report_defaults );
		} else {
			# -- Unknown style, so notify the user and dump the report
			wadg::Errors::warning( 'W0003', $reportStyle, $report );
			$croParser->nextReport();
			next;
		} # end if
		
		# -- If multiple report files, then start a new file here
		if( $statistics{One_File} != 1 ) {
			my( $sn ) = split /[\012\015]+/, $config{$report}{ShortName};
			my $fileN = File::Spec->catfile( $config{reports}{File_Out}, "$sn.html" );
			message( 'N0001', $fileN );
			$report_writer = wadg::rm::Report::openReportFile( $fileN, $croParser->{header}, $report );
			unless( defined $report_writer ) {
				wadg::Errors::error( -1, 'E0008', $fileN );
			} # end unless
		} # end if
		$currentReport->{writer} = $report_writer;
		$currentReport->{columns} = $columns;
		$currentReport->{_GLOBALS} = $croParser->{globals};

		# - Output new report title & description
		$currentReport->title();
		$currentReport->description();

		# -- Ouput the first table
		if( $currentReport->isa( 'wadg::rm::Report::GeneralSummary' ) ) {
			next if $currentReport->start_table();
		} else {
			$currentReport->start_table();
			# -- Good report, now set a default for Active_Column if it's not already set
			if( !defined $config{reports}{Active_Column} ) {
				$config{reports}{Active_Column} = substr( $columns, 0, 1 );
				if( $config{reports}{Active_Column} eq uc($config{reports}{Active_Column}) ) {
					$config{reports}{Active_Column} .= '_';
				}  # end if
			} # end if
			
		} # end if
		
	} elsif( $currentReport->{columns} ne $columns ) {
		# When COLUMNS change, close old table and start new one.
		# Except in case of a General Summary
		unless( $currentReport->isa( 'wadg::rm::Report::GeneralSummary' ) ) {
			# - Need to close table
			$currentReport->end_table();

			# - Now, start a new one
			$currentReport->{columns} = $columns;
			while( $currentReport->start_table() ) {
				# If this returns non-zero then the report is not defined,
				# so, just dump the report
				$croParser->nextReport();
				next;
			} # end while
		} # end unless
		$currentReport->{columns} = $columns;
	} # end if


	#
	# Process each report line in the current report context.
	#
	$currentReport->process( @lineData );
	
} # end while

#---------------------------------------#
# Finish up all the processing and quit #
#---------------------------------------#
# - Finish the last report
$currentReport->end_report();

# - Close the output file
&closeOutputFile( $report_writer );

# - Quick summary reopens the output file to insert summary information
$report = 'q';
if( defined $config{$report}{Rows} && defined $quick_summary ) {
	$quick_summary->{_GLOBALS} = $croParser->{globals};
	# -- Use Report to open new file or QuickSummary to insert in current one
	if( $statistics{One_File} == 0 ) {
		my( $sn ) = split /[\012\015]+/, $config{$report}{ShortName};
		my $fileN = File::Spec->catfile( $config{reports}{File_Out}, "$sn.html" );
		message( 'N0001', $fileN );
		$report_writer = wadg::rm::Report::openReportFile( $fileN, $croParser->{header}, $report );
	} else {
		$report_writer = wadg::rm::Report::QuickSummary::openReportFile( $config{reports}{File_Out}, $croParser->{header}, $report );
	} # end if
	unless( defined $report_writer ) {
		wadg::Errors::error( -1, 'E0008', $fileN );
	} # end unless
	$quick_summary->{writer} = $report_writer;
	$quick_summary->{handle} = $report_writer->{file};
	$quick_summary->{columns} = 'THh';

	$quick_summary->title();
	$quick_summary->description();
	$quick_summary->start_table();
	$quick_summary->process();
	$quick_summary->end_report();

	if( $statistics{One_File} == 0 ) {
		wadg::rm::Report::closeReportFile( $quick_summary->{writer}, $croParser->{footer}, $quick_summary->{token} );
	} else {
		wadg::rm::Report::QuickSummary::closeReportFile( $quick_summary->{writer}, $croParser->{footer}, $quick_summary->{token} );
	} # end if
} # end if
# - Output the complete notice and date/time.
$now = localtime;
message( 'N0010', $now );

&finalize( 0 );



###################################################
#                                                 #
#             Functions                           #
#                                                 #
###################################################

# ----------------------------------------------------------
# Sub: openOutputFiles
#
# Args: $filename
#	$filename	The name of the file to store reports in
#
# Description: Opens the output report file, frameset and 
#              navigation file.
# ----------------------------------------------------------
# Date      Modification                              Author
# ----------------------------------------------------------
#           Created by Corey Kaye / DNS                  CK
# 1999Feb19 Modified to support multi-file reports       JW
# 1999Feb24 Added support for background images and 
#           title font                                   JW
# 1999Nov17 Changed NavFile to support non-frames mode   JW
# 2000Mar21 Modified to use $filename parameter          JW
# ----------------------------------------------------------
sub openOutputFiles {
	# - This is the system-style filename
	my $fileName = shift;
	# - These are the URL style HREFs
	my $fileNav = '';
	my $reportFile;
	
	# - Use fileparse() to get $outDir, 'cause dirname() will make 
	#   'reports/' into '.' and we want 'reports/'
	my $outDir;
	( undef, $outDir, undef ) = fileparse( $fileName );

	# - Make outDir if it's not there
	if( !-e($outDir) ) {
		mkpath( $outDir );
	} # end if

	# In multifile report, first page is General Summary
	if( $statistics{One_File} != 1 ) {
		message( 'N0006', $outDir );
		$fileName = File::Spec->catfile( $fileName, 'GENERAL.html' );
	} else {
		message( 'N0008', $fileName );
	} # end if
	$fileName = basename( $fileName );

	my $ln = $lang->val( 'Language', 'Symbol' );

	#
	# Make index File, if doing frameset.
	#
	if( $navigation{File_Out} !~ /^(LEFT|RIGHT|TOP|BOTTOM)$/i ) {
		# - Output notices of file being created
		message( 'N0009', $statistics{Frame_File_Out} );

		local *indexFile;
		$fileNav = $navigation{File_Out};
		$fileNav = basename( $fileNav );

		unless( open(*indexFile,">$statistics{Frame_File_Out}") ) {
			wadg::Errors::error( -1, 'E0006', $statistics{Frame_File_Out} );
		} # end unless
   
   	# Get the HTMLWriter object for this file
   	my $iw = new wadg::HTMLWriter( -file => *indexFile, -output => $statistics{Format} );
		my $norobots = "";
		if( exists $statistics{No_Robots} && defined $statistics{No_Robots} && $statistics{No_Robots} == 1 ) {
			$norobots = $iw->meta( {name => 'ROBOTS', content => 'NOINDEX,NOFOLLOW' } );
		} # end if
   	
		$iw->write( $iw->doctype( {-flavor => 'Frameset'} ) );	# -flavor is ignored by HTML 3.2
		$iw->write( $iw->start_html( {lang => $ln} ) );
		$iw->write( $iw->head( $iw->meta( {name => 'AUTHOR', content => "This page created by $config{_INTERNAL}{'_TITLE'} $config{_INTERNAL}{'_VERSION'}"} ),
		                       $iw->title( $config{website}{Title} ), 
		                       $norobots) );
		my %frameset = ( cols => "22%,*" );
		
		if( defined $statistics{Frame_Border} ) {
			$frameset{border} = $statistics{Frame_Border} if $statistics{Frame_Border} ne '';
			$frameset{frameborder} = 'no' unless $frameset{Frame_Border};
		} # end if
		$iw->write( $iw->start_frameset( \%frameset ) );
		$iw->write( $iw->frame( {src => $fileNav, 
		                         name => 'NAV', 
		                         scrolling => 'AUTO', 
		                         title => $lang->val( 'Text', 'O0010' )
		                        } ) );
		$iw->write( $iw->frame( {src => $fileName, 
		                         name => 'CONTENT', 
		                         scrolling => 'AUTO', 
		                         title => $lang->val( 'Text', 'O0011' )
		                        } ) );
		$iw->write( $iw->noframes( $formatter->formatMessage( $lang->val( 'Text', 'O0001' ), $fileNav ) ) );
		$iw->write( $iw->end_frameset() );
		$iw->write( $iw->end_html() );

		close *indexFile;
	} # end if

	#
	# Make Report File
	# If doing a single report file then create that here, otherwise
	# if doing multiple report files, they will be created at each report
	#
	if( $statistics{One_File} == 1 ) {
		$reportFile = wadg::rm::Report::openReportFile( $config{reports}{File_Out}, $croParser->{header}, 'reports' );
		unless( defined $reportFile ) {
			wadg::Errors::error( -1, 'E0008', $config{reports}{File_Out} );
		} # end unless
	} # end if

	#
	# Make NavMenu
	# Get the content and, if doing frames then store that into a
	# file, otherwise just keep it for each report table
	#

  	# Get the HTMLWriter object for this file
  	my $nw = new wadg::HTMLWriter( -output => $statistics{Format}, -stylesheet => $navigation{_styles} );
  	%{$navigation{website}} = %{$config{website}};
  	$navigation{reports_File_Out} = $config{reports}{File_Out};
  	$navigation{One_File} = $statistics{One_File};
  	$navigation{Summary_Rows} = $config{q}{Rows};
	my $navmenu = wadg::rm::Report::createNavMenu( $croParser, $nw, \%navigation );

	if( $navigation{File_Out} !~ /^(LEFT|RIGHT|TOP|BOTTOM)$/i ) {
		# - Output notices of file being created
		message( 'N0007', $navigation{File_Out} );
		# -- Open the output file and write this all out.
		local *navFile;
		unless( open(*navFile,">$navigation{File_Out}") ) {
			wadg::Errors::error( -1, 'E0007', $navigation{File_Out} );
		} # end unless
		$nw->{file} = *navFile;
		
		my $cs = '';
		if( defined $lang->val( 'Language', 'CharacterSet' ) ) {
			$cs = $nw->meta( {'http-equiv' => 'Content-Type', content => 'text/html; charset=' . $lang->val( 'Language', 'CharacterSet' ) } );
		} # end if
		my $norobots = '';
		if( exists $statistics{No_Robots} && defined $statistics{No_Robots} && $statistics{No_Robots} == 1 ) {
			$norobots = $nw->meta( {name => 'ROBOTS', content => 'NOINDEX,NOFOLLOW' } );
		} # end if

		$nw->write( $nw->doctype( {-flavor => 'Transitional'} ) );		# -flavor is ignored by HTML 3.2
		$nw->write( $nw->start_html( { lang => $ln} ) );

		my $stylesheet = '';
		if( exists $navigation{Stylesheet} && defined $navigation{Stylesheet} && $navigation{Stylesheet} =~ /\w+\s*\{\s*\w+\s*:.*}/ ) {
			$stylesheet = $nw->style( {type => 'text/css'}, $navigation{Stylesheet} );
		} elsif( exists $navigation{Stylesheet} && defined $navigation{Stylesheet} && $navigation{Stylesheet} ) {
			$stylesheet = $nw->link( { type => 'text/css', href => $navigation{Stylesheet}, rel => 'STYLESHEET' } );
		} # end if
		$nw->write( $nw->head( $cs,
		                       $nw->meta( {name => 'AUTHOR', content => "This page created by $config{_INTERNAL}{'_TITLE'} $config{_INTERNAL}{'_VERSION'}"} ),
		                       $norobots,
		                       $nw->title( $lang->val( 'Text', 'O0010' ) ),
		                       $stylesheet,
		                     ) );

		$nw->write( $nw->body( $navmenu ) );
		$nw->write( $nw->end_html() );
		close *navFile;
	} else {
		$config{reports}{Navigation_Writer} = $nw;
		$config{reports}{Navigation_Content} = $navmenu;
		$config{reports}{Navigation_Position} = uc($navigation{File_Out});
		$config{reports}{Navigation_Background} = $navigation{Background};
		$config{reports}{Navigation_BG_Color} = $navigation{BG_Color};
	} # end if
	
	#
	# Copy logos to destination directory, if not using a logo file
	# and not storing logos in a central place.
	#
	if( !$config{website}{Company_Logo} && !$config{reports}{Image_Dir} ) {
		copy( File::Spec->catfile( $RealBin, 'rmlogo.gif' ), File::Spec->catfile( $outDir, 'rmlogo.gif' ) );
		copy( File::Spec->catfile( $RealBin, 'analogo.gif' ), File::Spec->catfile( $outDir, 'analogo.gif' ) );
	} # end if

	return $reportFile
	
} # end openOutputFiles

# ----------------------------------------------------------
# Sub: closeOutputFile
#
# Args: $reportFile
# 	$reportFile	A handle for the file to be closed
#
# Description: Closes the report file at the end of 
#              single file mode.
# ----------------------------------------------------------
# Date      Modification                              Author
# ----------------------------------------------------------
#           Created by Jeremy Wadsack / WADG              JW
# 2000Oct18 Fixed file corruption bug                     JW
# 2001Feb06 Fixed bug in closing of output file           JW
# ----------------------------------------------------------
sub closeOutputFile {
	my $reportFile = shift;
	return wadg::rm::Report::closeReportFile( $reportFile, $croParser->{footer}, 'reports' );
} # end closeOutputFile

# ----------------------------------------------------------
# Sub: message
#
# Args: $code, @args
#	$code	A code to look up in the Errors section of the language file
#	@args	A set of arguments to send into the $code's format
#
# Description: Output's a message to the screen or logfile
# based on the verbosity settings
# ----------------------------------------------------------
# Date      Modification                              Author
# ----------------------------------------------------------
# 1999Apr30 Created messaging function                   JAW
# 1999Sep02 Added support for logfiles                   JAW
# 1999Oct14 Added Verbosity control                      JAW
# 2000Apr25 Made suitable for an exception handler       JAW
# 2000Jul06 Moved header and prefix back here            JAW
# 2001Jan04 Forced scalar context for $lang-val()        JAW
# ----------------------------------------------------------
#
# DANGER WILL ROGERS: This is probably a lot more than an
# error handler should do....
#
sub message {
	my($code, @args) = @_;

	if( index( $statistics{Verbose}, substr($code, 0, 1) ) != -1 ) {
		my $msg = $lang->val( 'Errors', $code );
		$msg = $formatter->formatMessage( $msg, @args );
		$msg = "$PREFIX$msg\n";

		if( defined $HEADER && !$HEADER_USED ) {
			$msg = $HEADER . $msg;
			$HEADER_USED = 1;
		} # end if

		print $msg;
	} # end if
} # end message

# ----------------------------------------------------------
# Sub: finalize
#
# Args: errorlevel, [@message]
#	errorlevel	An error code to return the the shell (if applicable)
#	@message		Optional list to send to &message before dying
#
# Description: A fatal exception exception handler that will 
# clean up the system and exit. Will return an error state 
# to the shell, where applicable. Will print a message before
# exiting if provided. Will exit soft on Macs unless logging 
# or statistics_Always_Quit is set. Otherwise always exists hard.
# ----------------------------------------------------------
# Date      Modification                              Author
# ----------------------------------------------------------
#           Created function and debugged Mac end        JAW
# 2000APR25 Moved messaging inside funtion and made into
#           a fatal exception handler                    JAW
# ----------------------------------------------------------
#
# WARNING: See note under &message above
#
sub finalize {
	my( $arg, $code, @msg ) = @_;

	# - If there's a message parm, do that first
	&message( $code, @msg ) if $code;

	# - If there's a log file, close it
	if( $statistics{Log_File} ) {
		close( $logFile );
	} # end if

	# - If running on a Mac then process hard exit 
	if( $^O eq 'MacOS' ) {
		if( defined $statistics{Log_File} || defined $statistics{Always_Quit} ) {
			MacPerl::Quit( 1 );
		} # end if
	} # end if

	exit( $arg );
} # end finalize
