package Modules::System;

BEGIN {
	use Exporter ();
	@ISA = qw(Exporter);
	@EXPORT = qw(&exec_sys_util &list_sys_util &escape_shell_arg 
							 &get_temp_file &get_exec_res &callSystem &callSystemOrDie );
}

use IO::File;
use POSIX;

use strict "vars";

use Modules::ErrLog qw(&soft_error &hard_error &error);

my $stdout = '';
my $stderr = '';
my $ret_code = 0;

my $err_msg  = '';
my $err_code = 0;
## 0 - no error
## 1 - unable to execute the utility
## 2 - error while executing the utility

sub escape_shell_arg ($){
    my ($arg) = @_;

    $arg =~ s/\'/\'\\\'\'/g;    # (' => '\'')
    return "'$arg'";
}

## This utility executes external utility and returns its status. All its parameters
## will be quoted inside the utility.
##
## @param 	$mode		'exec' | 'list'
## @param 	$util		name of utility (with/without path)
## @param	$arg_ref	reference to an array of arguments
## @param	$env_ref	reference to a hash of environment variables 
##
## @global	$sys_msg

sub call_util ($$;$$){
	my ($mode, $util, $arg_ref, $env_ref) = @_;
	my @arguments = (defined $arg_ref) ? @{$arg_ref} : ();
	my %environ   = (defined $env_ref) ? %{$env_ref} : ();

	&clean_results_;
	
    # quote arguments
	$_ = &escape_shell_arg($_) foreach (@arguments);

    # form environment variables
	my $env_str = '';
	foreach my $var (keys %environ) {
		$env_str .= $var.'='.&escape_shell_arg($environ{$var}).' ';
	}
    # form arguments
	my $arguments = join (' ', @arguments);
	# form output
	my $output = ($mode eq 'list') ? '2>/dev/null' : '1>/dev/null';
	my $cline  = "$env_str $util $arguments $output";
    
	# todo: read both stdout & stderr output
	unless (open(EU, "$cline |")) {
		&set_error_(1, "Unable to execute command: $cline : $!");
		return 0;
	}

    # gather output
	$output = '';
	while(<EU>) {
		$output .= $_;
	}
	close(EU);
    
	if ($mode eq 'list') {
		$stdout = $output;
	} else {
		$stderr = $output;
	}

	$ret_code = $? >> 8;  # get return code
	return 1 if (($ret_code == 0) || ($mode eq 'list' && $util eq 'crontab' && $output eq ''));

	&set_error_(2, "Error while executing an utility: $cline: $!");
	return 0;
}

sub exec_sys_util ($;$$) {
	return &call_util('exec', @_);
}

sub list_sys_util ($;$$) {
	return &call_util('list', @_);
}

sub system2($;@) {
	my (@args) = @_;
	system @args;
	if ($? == -1) {
	    warn $!;
	    return 0;
	};
	my $exit_value = $? >> 8;
	return 1 if ($exit_value == 0);
	warn($!);
	return 0;
}

## returns result of the last execution
sub get_exec_res ()
{
	return ($ret_code, $stdout, $stderr);
}

sub set_error_ ($$) {
	($err_code, $err_msg) = @_;
	warn $err_msg;
}

sub get_error() {
#	return 
}

sub clean_results_ {
	$stdout = '';
	$stderr = '';
	
	$ret_code = 0;
	
	$err_msg  = '';
	$err_code = 0;
}

## get descriptor for temporary file
sub get_temp_file {
	my $name = '';
	my $fh;
	my $count = 10;
	
	do { 
		use POSIX qw(tmpnam);
		$name = tmpnam(); 
		return (undef, undef) if ($count-- == 0);
	} until $fh = IO::File->new($name, O_RDWR|O_CREAT|O_EXCL);
	
	return ($fh, $name);
}


sub callSystem {
	my ( $cmd, $errMsg, $exitCode ) = @_;
	$ret_code = system( $cmd );

	$ret_code >>= 8;  # get return code

	if ( $ret_code ) {

		my $msg = "error execute command '$cmd'\n$!\n";

		if ( $exitCode ){
			&hard_error( $errMsg, $exitCode, $msg );

		} elsif( $errMsg ) {
			if ( $errMsg =~ /#DEFAULT#/ ){
				$errMsg =~ s/#DEFAULT#/$msg/;
				print STDERR $errMsg;

			} else {
				soft_error( $errMsg );
			}
		}
		if ( wantarray() ){
			return (0, $ret_code);
		} else {
			return 0;
		}
	}
	return 1;
}

sub callSystemOrDie {
	my ( $cmd, $errMsg, $exitCode ) = @_;
	$ret_code = system( $cmd );

	$ret_code >>= 8;  # get return code

	if ( $ret_code ) {

		my $msg = "error execute command '$cmd'\n$!\n";

		unless ( $exitCode ) {
			if ( $errMsg =~ /#(\d+)(?:\|\||$)/ ) {
				$exitCode = $1;
			}
		}

		&hard_error( $errMsg, $exitCode || $ret_code, $msg );

	}
	return 1;
}


return 1;

END {}
