package Psh::PerlEval;

#
# Must be on top of file before any "my" variables!
#
#
# array protected_eval(string EXPR, string FROM) 
#
# Evaluates "$Psh::eval_preamble EXPR", handling trapped signals and
# printing errors properly. The FROM string is passed on to
# handle_message to indicate where errors came from.
# 
# If EXPR ends in an ampersand, it is stripped and the eval is done in
# a forked copy of perl.
#

$Psh::PerlEval::current_package='main';

sub protected_eval
{
	#
	# Local package variables because lexical variables here mask
	# variables of the same name in main!!
	#
 
	local ($Psh::PerlEval::str, $Psh::PerlEval::from) = @_;
	local $Psh::PerlEval::redo_sentinel        = 0;

	# It's not possible to use fork_process for foreground perl
	# as we would lose all variables etc.

	{   #Dummy block to catch loop-control statements at outermost
		#level in EXPR 
		# First, protect against infinite loop
		# caused by redo:
		if ($Psh::PerlEval::redo_sentinel) { last; }
		$Psh::PerlEval::redo_sentinel = 1;
		local $Psh::currently_active= -1;
		$_= $Psh::PerlEval::lastscalar;
		@_= @Psh::PerlEval::lastarray;
		local @Psh::PerlEval::result= eval $Psh::eval_preamble.' package '.$Psh::PerlEval::current_package.'; '.$Psh::PerlEval::str;
		$Psh::PerlEval::lastscalar= $_;
		@Psh::PerlEval::lastarray= @_;

		if ( !$@ && @Psh::PerlEval::result &&
			 $#Psh::PerlEval::result==0 && $Psh::PerlEval::str &&
			 $Psh::PerlEval::result[0] &&
			 $Psh::PerlEval::result[0] eq $Psh::PerlEval::str &&
			 !Psh::is_number($Psh::PerlEval::str) &&
			 $Psh::PerlEval::str=~ /^\s*\S+\s*$/ &&
			 $Psh::PerlEval::str!~ /^\s*(\'|\")\S+(\'|\")\s*$/ ) {
			#
			# Very whacky error handling
			# If you pass one word to perl and it's no function etc
			# it will simply return the word - that's not even a
			# bug actually but in case of psh it's annoying
			# so we try to detect these cases
			#

			Psh::Util::print_error_i18n('no_command',$Psh::PerlEval::str);
			return undef;
		}
		else {
			if ($@) {
				Psh::handle_message($@, $Psh::PerlEval::from);
			}
		}
		return @Psh::PerlEval::result;
	}
	Psh::handle_message("Can't use loop control outside a block",
						$Psh::PerlEval::from);
	return undef;
}


#
# array variable_expansion (arrayref WORDS)
#
# For each element x of the array referred to by WORDS, substitute
# perl variables that appear in x respecting the quoting symbols ' and
# ", and return the array of substituted values. Substitutions inside
# quotes always return a single element in the resulting array;
# outside quotes, the result is split() and pushed on to the
# accumulating array of substituted values
#

sub variable_expansion
{
	local ($Psh::arref) = @_;
	local @Psh::retval  = ();
	local $Psh::word;

	for $Psh::word (@{$Psh::arref}) {
		if    ($Psh::word =~ m/^\'/) { push @Psh::retval, $Psh::word; }
		elsif ($Psh::word =~ m/^\"/) {
			local $Psh::word2= $Psh::word;
			$Psh::word2 =~ s/\\/\\\\/g;
			local $Psh::val = eval("$Psh::eval_preamble $Psh::word2");

			if ($@) { push @Psh::retval, $Psh::word; }
			else    { push @Psh::retval, "\"$Psh::val\""; }
		} else {
			local $Psh::word2= $Psh::word;
			$Psh::word2 =~ s/\\/\\\\/g;
			local $Psh::val = eval("$Psh::eval_preamble \"$Psh::word2\"");

			if ($@) { push @Psh::retval, $Psh::word; }
			else    { push @Psh::retval, $Psh::val; }
			# in former times we used to do a wordsplit here in
			# case of success, but this breaks certain things and
			# don't know exactly why it was here in the first place
		}
	}

	return @Psh::retval;
}

1;


__END__

=head1 NAME

Psh::PerlEval - package containing perl evaluation codes


=head1 SYNOPSIS

	use Psh::PerlEval;

=head1 DESCRIPTION

TBD

=head1 AUTHOR

Glen Whitney I think..

=head1 SEE ALSO

=cut

# The following is for Emacs - I hope it won't annoy anyone
# but this could solve the problems with different tab widths etc
#
# Local Variables:
# tab-width:4
# indent-tabs-mode:t
# c-basic-offset:4
# perl-indent-level:4
# End:
