Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | package Psh::Prompt; |
2 | ||
3 | use strict; | |
4 | require Psh::OS; | |
5 | require Psh::Util; | |
6 | require Psh::Options; | |
7 | ||
8 | # | |
9 | # string prompt_string(TEMPLATE) | |
10 | # | |
11 | # Construct a prompt string from TEMPLATE. | |
12 | # | |
13 | ||
14 | my $default_prompt = '\s% '; | |
15 | my %color_table= | |
16 | ( | |
17 | bold => '\e[01m', | |
18 | faint => '\e[02m', | |
19 | standout => '\e[03m', | |
20 | underline => '\e[04m', | |
21 | blink => '\e[05m', | |
22 | reverse => '\e[07m', | |
23 | conceal => '\e[08m', | |
24 | nobold => '\e[0m', | |
25 | noattr => '\e[00m', | |
26 | black => '\e[30m', | |
27 | red => '\e[31m', | |
28 | green => '\e[32m', | |
29 | yellow => '\e[33m', | |
30 | blue => '\e[34m', | |
31 | magenta => '\e[35m', | |
32 | cyan => '\e[36m', | |
33 | white => '\e[37m', | |
34 | default => '\e[39m', | |
35 | none => '\e[00m', | |
36 | bgblack => '\e[40m', | |
37 | bgred => '\e[41m', | |
38 | bggreen => '\e[42m', | |
39 | bgyellow => '\e[43m', | |
40 | bgblue => '\e[44m', | |
41 | bgmagenta => '\e[45m', | |
42 | bgcyan => '\e[46m', | |
43 | bgwhite => '\e[47m', | |
44 | bgdefault => '\e[49m', | |
45 | ); | |
46 | my %sign_table= | |
47 | ( | |
48 | 'iso8859-1' => { | |
49 | 332 => '/', | |
50 | 304 => '-', | |
51 | 300 => "\\", | |
52 | 333 => '+', | |
53 | 262 => '=', | |
54 | 261 => '|', | |
55 | 260 => '#', | |
56 | }, | |
57 | 'default' => { | |
58 | 332 => "\332", | |
59 | 304 => "\304", | |
60 | 300 => "\300", | |
61 | 333 => "\333", | |
62 | 262 => "\262", | |
63 | 261 => "\261", | |
64 | 260 => "\260", | |
65 | }, | |
66 | ); | |
67 | ||
68 | ||
69 | %Psh::Prompt::prompt_vars = ( | |
70 | 'd' => sub { | |
71 | my ($wday, $mon, $mday) = (localtime)[6, 4, 3]; | |
72 | $wday = (Psh::Locale::weekdays())[$wday]; | |
73 | $mon = (Psh::Locale::months())[$mon]; | |
74 | return "$wday $mon $mday"; | |
75 | }, | |
76 | 'e' => sub { return "\e"} , | |
77 | 'E' => sub { return "\e"} , | |
78 | 'h' => sub { return $Psh::host; }, | |
79 | 'H' => sub { return $Psh::longhost || $Psh::host; }, | |
80 | 's' => sub { | |
81 | my $shell = $Psh::bin; | |
82 | $shell =~ s/^.*\///; | |
83 | return $shell; | |
84 | }, | |
85 | 'S' => sub { return "\0" }, # extends to \ | |
86 | 'n' => sub { return "\n" }, | |
87 | 't' => sub { | |
88 | my ($hour, $min, $sec) = (localtime)[2, 1, 0]; | |
89 | return sprintf("%02d:%02d:%02d", $hour, $min, $sec); | |
90 | }, | |
91 | 'u' => sub { | |
92 | # Camel, 2e, p. 172: 'getlogin'. | |
93 | return getlogin || (getpwuid($>))[0] || "uid$>"; | |
94 | }, | |
95 | 'w' => sub { | |
96 | my $dir = $ENV{PWD}; | |
97 | my $home = Psh::OS::get_home_dir(); | |
98 | return $dir unless (length($home) > length($Psh::OS::FILE_SEPARATOR)); # in case the home dir is the root dir | |
99 | $dir =~ s/\\/\\\\/g; | |
100 | $dir =~ s/^\Q$home\E/\~/ if $home; | |
101 | return $dir; | |
102 | }, | |
103 | 'W' => sub { | |
104 | my $dir = $ENV{PWD}; | |
105 | $dir =~ s/\\/\\\\/g; | |
106 | my ($newdir)= $dir=~ m:/([^/]+)$:; | |
107 | return $newdir||$dir||'/'; | |
108 | }, | |
109 | '#' => sub { return $Psh::cmd; }, | |
110 | '!' => sub { return scalar(@Psh::history); }, | |
111 | '$' => sub { return ($> ? '$' : '#'); }, | |
112 | '[' => sub { return $Psh::term->ReadLine() eq 'Term::ReadLine::Gnu'?"\001":''}, | |
113 | ']' => sub { return $Psh::term->ReadLine() eq 'Term::ReadLine::Gnu'?"\002":''}, | |
114 | ); | |
115 | ||
116 | ||
117 | sub _prompt_helper { | |
118 | my $code= shift; | |
119 | my $var = $Psh::Prompt::prompt_vars{$code}; | |
120 | my $sub; | |
121 | ||
122 | if (ref $var eq 'CODE') { | |
123 | $sub = &$var(); | |
124 | } elsif($code =~ /^[0-9]+$/) { | |
125 | $sub= chr(oct($code)); | |
126 | } elsif($code =~ /^\:[0-9]+$/) { | |
127 | $sub= chr($code); | |
128 | } elsif($code =~ /^0x/) { | |
129 | $sub= chr(hex($code)); | |
130 | } else { | |
131 | Psh::Util::print_warning_i18n('prompt_unknown_escape',$code,$Psh::bin); | |
132 | $sub = '' | |
133 | } | |
134 | ||
135 | { | |
136 | local $1; | |
137 | if ($sub =~ m/\\([^\\])/) { | |
138 | Psh::Util::print_warning_i18n('prompt_expansion_error',$code, | |
139 | $1, $Psh::bin); | |
140 | $sub =~ s/\\[^\\]//g; | |
141 | } | |
142 | } | |
143 | return $sub; | |
144 | } | |
145 | ||
146 | sub _color_helper { | |
147 | my $name= shift; | |
148 | my $result= '\['; | |
149 | my @tmp= split /\s+/, $name; | |
150 | foreach (@tmp) { | |
151 | if ($color_table{$_}) { | |
152 | $result.=$color_table{$_}; | |
153 | } else { | |
154 | Psh::Util::print_debug_class('o',"Unknown prompt color $_\n"); | |
155 | } | |
156 | } | |
157 | $result.='\]'; | |
158 | return $result; | |
159 | } | |
160 | ||
161 | sub prompt_string | |
162 | { | |
163 | my $prompt_templ = shift; | |
164 | my $temp; | |
165 | ||
166 | # | |
167 | # First, get the prompt string from a subroutine or from the default: | |
168 | # | |
169 | ||
170 | if (ref($prompt_templ) eq 'CODE') { # If it is a subroutine, | |
171 | $temp = &$prompt_templ(); | |
172 | } elsif (ref($prompt_templ)) { # If it isn't a scalar | |
173 | Psh::Util::print_warning_i18n('prompt_wrong_type',$Psh::bin); | |
174 | $temp = $default_prompt; | |
175 | } else { | |
176 | $temp = $prompt_templ; | |
177 | } | |
178 | ||
179 | # | |
180 | # Now, subject it to substitutions: | |
181 | # | |
182 | # Substitution is in x steps: | |
183 | # 1) \\ is substituted by \0 to be able to restore them later on | |
184 | # 2) The special construct \$( ... ) or $(...) is interpreted | |
185 | # 3) \char and \digits are interpreted | |
186 | # 4) \0 is restored to \ | |
187 | # | |
188 | ||
189 | $temp=~ s/\\\\/\0/g; # save double backslash | |
190 | ||
191 | # Substitute program execution (for bash compatibility) | |
192 | $temp=~ s/\\\$\(/\$\(/g; | |
193 | while ($temp =~ m/^(.*)\$\((.+?)\)(.*)$/s) { | |
194 | my $sub=''; | |
195 | my ($save1, $code, $save2) = ($1, $2, $3); | |
196 | eval { | |
197 | $sub=Psh::OS::backtick($code); | |
198 | chomp $sub; | |
199 | }; | |
200 | Psh::Util::print_debug_class('e',"Error: $@") if $@; | |
201 | $sub='' if( $@); | |
202 | $sub=~ s/\\/\0/g; | |
203 | $temp=$save1 . $sub . $save2; | |
204 | } | |
205 | ||
206 | my $encoding= Psh::Options::get_option('encoding'); | |
207 | my $my_sign= $sign_table{'default'}; | |
208 | if ($encoding and exists $sign_table{$encoding}) { | |
209 | $my_sign= $sign_table{'iso8859-1'}; | |
210 | } | |
211 | # Color substitution | |
212 | $temp=~ s/\\C\{(.+?)\}/&_color_helper($1)/ge; | |
213 | # Graphics sign conversion | |
214 | $temp=~ s/\\S\{(\d+?)\}/$my_sign->{$1}/g; | |
215 | ||
216 | # Standard prompt_var substitution | |
217 | $temp=~ s/\\([0-9]x?[0-9a-fA-F]*|[^0-9\\])/&_prompt_helper($1)/ge; | |
218 | ||
219 | $temp=~ s/\0/\\/g; # restore former double backslash | |
220 | ||
221 | return $temp; | |
222 | } | |
223 | ||
224 | sub normal_prompt { | |
225 | my $prompt= Psh::Options::get_option('ps1'); | |
226 | $prompt= $default_prompt unless defined $prompt; | |
227 | return $prompt; | |
228 | } | |
229 | ||
230 | sub continue_prompt { | |
231 | my $prompt= Psh::Options::get_option('ps2'); | |
232 | $prompt= '> ' unless defined $prompt; | |
233 | return $prompt; | |
234 | } | |
235 | ||
236 | sub pre_prompt_hook { | |
237 | my $tmp= Psh::Options::get_option('prompt_command'); | |
238 | if ($tmp) { | |
239 | if (ref $tmp and ref $tmp eq 'CODE') { | |
240 | eval { | |
241 | &$tmp; | |
242 | }; | |
243 | if ($@) { | |
244 | Psh::handle_message($@,'promp_command'); | |
245 | } | |
246 | } else { | |
247 | Psh::evl($tmp); | |
248 | } | |
249 | } | |
250 | change_title(); | |
251 | } | |
252 | ||
253 | sub change_title { | |
254 | my $title= Psh::Options::get_option('window_title'); | |
255 | return if !$title; | |
256 | $title= prompt_string($title); | |
257 | Psh::OS::set_window_title($title); | |
258 | } | |
259 | ||
260 | 1; |