The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Psh::Prompt;

use strict;
require Psh::OS;
require Psh::Util;
require Psh::Options;

#
# string prompt_string(TEMPLATE)
#
# Construct a prompt string from TEMPLATE.
#

my $default_prompt = '\s% ';
my %color_table=
  (
   bold => '\e[01m',
   faint => '\e[02m',
   standout => '\e[03m',
   underline => '\e[04m',
   blink => '\e[05m',
   reverse => '\e[07m',
   conceal => '\e[08m',
   nobold => '\e[0m',
   noattr => '\e[00m',
   black => '\e[30m',
   red => '\e[31m',
   green => '\e[32m',
   yellow => '\e[33m',
   blue => '\e[34m',
   magenta => '\e[35m',
   cyan => '\e[36m',
   white => '\e[37m',
   default => '\e[39m',
   none => '\e[00m',
   bgblack => '\e[40m',
   bgred => '\e[41m',
   bggreen => '\e[42m',
   bgyellow => '\e[43m',
   bgblue => '\e[44m',
   bgmagenta => '\e[45m',
   bgcyan => '\e[46m',
   bgwhite => '\e[47m',
   bgdefault => '\e[49m',
  );
my %sign_table=
  (
   'iso8859-1' => {
				   332 => '/',
				   304 => '-',
				   300 => "\\",
				   333 => '+',
				   262 => '=',
				   261 => '|',
				   260 => '#',
				  },
   'default'   => {
				   332 => "\332",
				   304 => "\304",
				   300 => "\300",
				   333 => "\333",
				   262 => "\262",
				   261 => "\261",
				   260 => "\260",
				 },
  );


%Psh::Prompt::prompt_vars = (
	'd' => sub {
			my ($wday, $mon, $mday) = (localtime)[6, 4, 3];
			$wday = (Psh::Locale::weekdays())[$wday];
			$mon  = (Psh::Locale::months())[$mon];
			return "$wday $mon $mday";
		},
	'e' => sub { return "\e"} ,
	'E' => sub { return "\e"} ,
	'h' => sub { return $Psh::host; },
	'H' => sub { return $Psh::longhost || $Psh::host; },
	's' => sub {
			my $shell = $Psh::bin;
			$shell =~ s/^.*\///;
			return $shell;
		},
	'S' => sub { return "\0" }, # extends to \
	'n' => sub { return "\n" },
	't' => sub {
			my ($hour, $min, $sec) = (localtime)[2, 1, 0];
			return sprintf("%02d:%02d:%02d", $hour, $min, $sec);
		},
	'u' => sub {
			# Camel, 2e, p. 172: 'getlogin'.
			return getlogin || (getpwuid($>))[0] || "uid$>";
		},
	'w' => sub {
			my $dir = $ENV{PWD};
			my $home = Psh::OS::get_home_dir();
			return $dir unless (length($home) > length($Psh::OS::FILE_SEPARATOR));	# in case the home dir is the root dir
            $dir =~ s/\\/\\\\/g;
			$dir =~ s/^\Q$home\E/\~/ if $home;
			return $dir;
		},
	'W' => sub {
			my $dir = $ENV{PWD};
			$dir =~ s/\\/\\\\/g;
			my ($newdir)= $dir=~ m:/([^/]+)$:;
			return $newdir||$dir||'/';
		},
	'#' => sub { return $Psh::cmd; },
	'!' => sub { return scalar(@Psh::history); },
	'$' => sub { return ($> ? '$' : '#'); },
	'[' => sub { return $Psh::term->ReadLine() eq 'Term::ReadLine::Gnu'?"\001":''},
	']' => sub { return $Psh::term->ReadLine() eq 'Term::ReadLine::Gnu'?"\002":''},
);


sub _prompt_helper {
	my $code= shift;
	my $var = $Psh::Prompt::prompt_vars{$code};
	my $sub;

	if (ref $var eq 'CODE') {
		$sub = &$var();
	} elsif($code =~ /^[0-9]+$/) {
		$sub= chr(oct($code));
	} elsif($code =~ /^\:[0-9]+$/) {
		$sub= chr($code);
	} elsif($code =~ /^0x/) {
		$sub= chr(hex($code));
	} else {
		Psh::Util::print_warning_i18n('prompt_unknown_escape',$code,$Psh::bin);
		$sub = ''
	}
	
	{
		local $1;
		if ($sub =~ m/\\([^\\])/) {
			Psh::Util::print_warning_i18n('prompt_expansion_error',$code,
							   $1, $Psh::bin);
			$sub =~ s/\\[^\\]//g;
		}
	}
	return $sub;
}

sub _color_helper {
	my $name= shift;
	my $result= '\[';
	my @tmp= split /\s+/, $name;
	foreach (@tmp) {
		if ($color_table{$_}) {
			$result.=$color_table{$_};
		} else {
			Psh::Util::print_debug_class('o',"Unknown prompt color $_\n");
		}
	}
	$result.='\]';
	return $result;
}

sub prompt_string
{
	my $prompt_templ = shift;
	my $temp;

	#
	# First, get the prompt string from a subroutine or from the default:
	#

	if (ref($prompt_templ) eq 'CODE') { # If it is a subroutine,
		$temp = &$prompt_templ();
	} elsif (ref($prompt_templ)) {      # If it isn't a scalar
		Psh::Util::print_warning_i18n('prompt_wrong_type',$Psh::bin);
		$temp = $default_prompt;
	} else {
		$temp = $prompt_templ;
	}

	#
	# Now, subject it to substitutions:
    #
	# Substitution is in x steps:
	# 1) \\ is substituted by \0 to be able to restore them later on
	# 2) The special construct \$( ... ) or $(...) is interpreted
	# 3) \char and \digits are interpreted
	# 4) \0 is restored to \
	#

	$temp=~ s/\\\\/\0/g; # save double backslash

	# Substitute program execution (for bash compatibility)
	$temp=~ s/\\\$\(/\$\(/g;
	while ($temp =~ m/^(.*)\$\((.+?)\)(.*)$/s) {
		my $sub='';
		my ($save1, $code, $save2) = ($1, $2, $3);
		eval {
			$sub=Psh::OS::backtick($code);
			chomp $sub;
		};
		Psh::Util::print_debug_class('e',"Error: $@") if $@;
		$sub='' if( $@);
		$sub=~ s/\\/\0/g;
		$temp=$save1 . $sub . $save2;
	}

	my $encoding= Psh::Options::get_option('encoding');
	my $my_sign= $sign_table{'default'};
	if ($encoding and exists $sign_table{$encoding}) {
		$my_sign= $sign_table{'iso8859-1'};
	}
	# Color substitution
	$temp=~ s/\\C\{(.+?)\}/&_color_helper($1)/ge;
	# Graphics sign conversion
	$temp=~ s/\\S\{(\d+?)\}/$my_sign->{$1}/g;

	# Standard prompt_var substitution
	$temp=~ s/\\([0-9]x?[0-9a-fA-F]*|[^0-9\\])/&_prompt_helper($1)/ge;

	$temp=~ s/\0/\\/g; # restore former double backslash

	return $temp;
}

sub normal_prompt {
	my $prompt= Psh::Options::get_option('ps1');
	$prompt= $default_prompt unless defined $prompt;
	return $prompt;
}

sub continue_prompt {
	my $prompt= Psh::Options::get_option('ps2');
	$prompt= '> ' unless defined $prompt;
	return $prompt;
}

sub pre_prompt_hook {
	my $tmp= Psh::Options::get_option('prompt_command');
	if ($tmp) {
		if (ref $tmp and ref $tmp eq 'CODE') {
			eval {
				&$tmp;
			};
			if ($@) {
				Psh::handle_message($@,'promp_command');
			}
		} else {
			Psh::evl($tmp);
		}
	}
	change_title();
}

sub change_title {
	my $title= Psh::Options::get_option('window_title');
	return if !$title;
	$title= prompt_string($title);
	Psh::OS::set_window_title($title);
}

1;