Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | =head1 NAME |
2 | ||
3 | Term::ReadLine - Perl interface to various C<readline> packages. | |
4 | If no real package is found, substitutes stubs instead of basic functions. | |
5 | ||
6 | =head1 SYNOPSIS | |
7 | ||
8 | use Term::ReadLine; | |
9 | my $term = new Term::ReadLine 'Simple Perl calc'; | |
10 | my $prompt = "Enter your arithmetic expression: "; | |
11 | my $OUT = $term->OUT || \*STDOUT; | |
12 | while ( defined ($_ = $term->readline($prompt)) ) { | |
13 | my $res = eval($_); | |
14 | warn $@ if $@; | |
15 | print $OUT $res, "\n" unless $@; | |
16 | $term->addhistory($_) if /\S/; | |
17 | } | |
18 | ||
19 | =head1 DESCRIPTION | |
20 | ||
21 | This package is just a front end to some other packages. It's a stub to | |
22 | set up a common interface to the various ReadLine implementations found on | |
23 | CPAN (under the C<Term::ReadLine::*> namespace). | |
24 | ||
25 | =head1 Minimal set of supported functions | |
26 | ||
27 | All the supported functions should be called as methods, i.e., either as | |
28 | ||
29 | $term = new Term::ReadLine 'name'; | |
30 | ||
31 | or as | |
32 | ||
33 | $term->addhistory('row'); | |
34 | ||
35 | where $term is a return value of Term::ReadLine-E<gt>new(). | |
36 | ||
37 | =over 12 | |
38 | ||
39 | =item C<ReadLine> | |
40 | ||
41 | returns the actual package that executes the commands. Among possible | |
42 | values are C<Term::ReadLine::Gnu>, C<Term::ReadLine::Perl>, | |
43 | C<Term::ReadLine::Stub>. | |
44 | ||
45 | =item C<new> | |
46 | ||
47 | returns the handle for subsequent calls to following | |
48 | functions. Argument is the name of the application. Optionally can be | |
49 | followed by two arguments for C<IN> and C<OUT> filehandles. These | |
50 | arguments should be globs. | |
51 | ||
52 | =item C<readline> | |
53 | ||
54 | gets an input line, I<possibly> with actual C<readline> | |
55 | support. Trailing newline is removed. Returns C<undef> on C<EOF>. | |
56 | ||
57 | =item C<addhistory> | |
58 | ||
59 | adds the line to the history of input, from where it can be used if | |
60 | the actual C<readline> is present. | |
61 | ||
62 | =item C<IN>, C<OUT> | |
63 | ||
64 | return the filehandles for input and output or C<undef> if C<readline> | |
65 | input and output cannot be used for Perl. | |
66 | ||
67 | =item C<MinLine> | |
68 | ||
69 | If argument is specified, it is an advice on minimal size of line to | |
70 | be included into history. C<undef> means do not include anything into | |
71 | history. Returns the old value. | |
72 | ||
73 | =item C<findConsole> | |
74 | ||
75 | returns an array with two strings that give most appropriate names for | |
76 | files for input and output using conventions C<"E<lt>$in">, C<"E<gt>out">. | |
77 | ||
78 | =item Attribs | |
79 | ||
80 | returns a reference to a hash which describes internal configuration | |
81 | of the package. Names of keys in this hash conform to standard | |
82 | conventions with the leading C<rl_> stripped. | |
83 | ||
84 | =item C<Features> | |
85 | ||
86 | Returns a reference to a hash with keys being features present in | |
87 | current implementation. Several optional features are used in the | |
88 | minimal interface: C<appname> should be present if the first argument | |
89 | to C<new> is recognized, and C<minline> should be present if | |
90 | C<MinLine> method is not dummy. C<autohistory> should be present if | |
91 | lines are put into history automatically (maybe subject to | |
92 | C<MinLine>), and C<addhistory> if C<addhistory> method is not dummy. | |
93 | ||
94 | If C<Features> method reports a feature C<attribs> as present, the | |
95 | method C<Attribs> is not dummy. | |
96 | ||
97 | =back | |
98 | ||
99 | =head1 Additional supported functions | |
100 | ||
101 | Actually C<Term::ReadLine> can use some other package, that will | |
102 | support a richer set of commands. | |
103 | ||
104 | All these commands are callable via method interface and have names | |
105 | which conform to standard conventions with the leading C<rl_> stripped. | |
106 | ||
107 | The stub package included with the perl distribution allows some | |
108 | additional methods: | |
109 | ||
110 | =over 12 | |
111 | ||
112 | =item C<tkRunning> | |
113 | ||
114 | makes Tk event loop run when waiting for user input (i.e., during | |
115 | C<readline> method). | |
116 | ||
117 | =item C<ornaments> | |
118 | ||
119 | makes the command line stand out by using termcap data. The argument | |
120 | to C<ornaments> should be 0, 1, or a string of a form | |
121 | C<"aa,bb,cc,dd">. Four components of this string should be names of | |
122 | I<terminal capacities>, first two will be issued to make the prompt | |
123 | standout, last two to make the input line standout. | |
124 | ||
125 | =item C<newTTY> | |
126 | ||
127 | takes two arguments which are input filehandle and output filehandle. | |
128 | Switches to use these filehandles. | |
129 | ||
130 | =back | |
131 | ||
132 | One can check whether the currently loaded ReadLine package supports | |
133 | these methods by checking for corresponding C<Features>. | |
134 | ||
135 | =head1 EXPORTS | |
136 | ||
137 | None | |
138 | ||
139 | =head1 ENVIRONMENT | |
140 | ||
141 | The environment variable C<PERL_RL> governs which ReadLine clone is | |
142 | loaded. If the value is false, a dummy interface is used. If the value | |
143 | is true, it should be tail of the name of the package to use, such as | |
144 | C<Perl> or C<Gnu>. | |
145 | ||
146 | As a special case, if the value of this variable is space-separated, | |
147 | the tail might be used to disable the ornaments by setting the tail to | |
148 | be C<o=0> or C<ornaments=0>. The head should be as described above, say | |
149 | ||
150 | If the variable is not set, or if the head of space-separated list is | |
151 | empty, the best available package is loaded. | |
152 | ||
153 | export "PERL_RL=Perl o=0" # Use Perl ReadLine without ornaments | |
154 | export "PERL_RL= o=0" # Use best available ReadLine without ornaments | |
155 | ||
156 | (Note that processing of C<PERL_RL> for ornaments is in the discretion of the | |
157 | particular used C<Term::ReadLine::*> package). | |
158 | ||
159 | =head1 CAVEATS | |
160 | ||
161 | It seems that using Term::ReadLine from Emacs minibuffer doesn't work | |
162 | quite right and one will get an error message like | |
163 | ||
164 | Cannot open /dev/tty for read at ... | |
165 | ||
166 | One possible workaround for this is to explicitly open /dev/tty like this | |
167 | ||
168 | open (FH, "/dev/tty" ) | |
169 | or eval 'sub Term::ReadLine::findConsole { ("&STDIN", "&STDERR") }'; | |
170 | die $@ if $@; | |
171 | close (FH); | |
172 | ||
173 | or you can try using the 4-argument form of Term::ReadLine->new(). | |
174 | ||
175 | =cut | |
176 | ||
177 | use strict; | |
178 | ||
179 | package Term::ReadLine::Stub; | |
180 | our @ISA = qw'Term::ReadLine::Tk Term::ReadLine::TermCap'; | |
181 | ||
182 | $DB::emacs = $DB::emacs; # To peacify -w | |
183 | our @rl_term_set; | |
184 | *rl_term_set = \@Term::ReadLine::TermCap::rl_term_set; | |
185 | ||
186 | sub PERL_UNICODE_STDIN () { 0x0001 } | |
187 | ||
188 | sub ReadLine {'Term::ReadLine::Stub'} | |
189 | sub readline { | |
190 | my $self = shift; | |
191 | my ($in,$out,$str) = @$self; | |
192 | my $prompt = shift; | |
193 | print $out $rl_term_set[0], $prompt, $rl_term_set[1], $rl_term_set[2]; | |
194 | $self->register_Tk | |
195 | if not $Term::ReadLine::registered and $Term::ReadLine::toloop | |
196 | and defined &Tk::DoOneEvent; | |
197 | #$str = scalar <$in>; | |
198 | $str = $self->get_line; | |
199 | $str =~ s/^\s*\Q$prompt\E// if ($^O eq 'MacOS'); | |
200 | utf8::upgrade($str) | |
201 | if (${^UNICODE} & PERL_UNICODE_STDIN || defined ${^ENCODING}) && | |
202 | utf8::valid($str); | |
203 | print $out $rl_term_set[3]; | |
204 | # bug in 5.000: chomping empty string creats length -1: | |
205 | chomp $str if defined $str; | |
206 | $str; | |
207 | } | |
208 | sub addhistory {} | |
209 | ||
210 | sub findConsole { | |
211 | my $console; | |
212 | ||
213 | if ($^O eq 'MacOS') { | |
214 | $console = "Dev:Console"; | |
215 | } elsif (-e "/dev/tty") { | |
216 | $console = "/dev/tty"; | |
217 | } elsif (-e "con" or $^O eq 'MSWin32') { | |
218 | $console = "con"; | |
219 | } else { | |
220 | $console = "sys\$command"; | |
221 | } | |
222 | ||
223 | if (($^O eq 'amigaos') || ($^O eq 'beos') || ($^O eq 'epoc')) { | |
224 | $console = undef; | |
225 | } | |
226 | elsif ($^O eq 'os2') { | |
227 | if ($DB::emacs) { | |
228 | $console = undef; | |
229 | } else { | |
230 | $console = "/dev/con"; | |
231 | } | |
232 | } | |
233 | ||
234 | my $consoleOUT = $console; | |
235 | $console = "&STDIN" unless defined $console; | |
236 | if (!defined $consoleOUT) { | |
237 | $consoleOUT = defined fileno(STDERR) ? "&STDERR" : "&STDOUT"; | |
238 | } | |
239 | ($console,$consoleOUT); | |
240 | } | |
241 | ||
242 | sub new { | |
243 | die "method new called with wrong number of arguments" | |
244 | unless @_==2 or @_==4; | |
245 | #local (*FIN, *FOUT); | |
246 | my ($FIN, $FOUT, $ret); | |
247 | if (@_==2) { | |
248 | my($console, $consoleOUT) = $_[0]->findConsole; | |
249 | ||
250 | open(FIN, "<$console"); | |
251 | open(FOUT,">$consoleOUT"); | |
252 | #OUT->autoflush(1); # Conflicts with debugger? | |
253 | my $sel = select(FOUT); | |
254 | $| = 1; # for DB::OUT | |
255 | select($sel); | |
256 | $ret = bless [\*FIN, \*FOUT]; | |
257 | } else { # Filehandles supplied | |
258 | $FIN = $_[2]; $FOUT = $_[3]; | |
259 | #OUT->autoflush(1); # Conflicts with debugger? | |
260 | my $sel = select($FOUT); | |
261 | $| = 1; # for DB::OUT | |
262 | select($sel); | |
263 | $ret = bless [$FIN, $FOUT]; | |
264 | } | |
265 | if ($ret->Features->{ornaments} | |
266 | and not ($ENV{PERL_RL} and $ENV{PERL_RL} =~ /\bo\w*=0/)) { | |
267 | local $Term::ReadLine::termcap_nowarn = 1; | |
268 | $ret->ornaments(1); | |
269 | } | |
270 | return $ret; | |
271 | } | |
272 | ||
273 | sub newTTY { | |
274 | my ($self, $in, $out) = @_; | |
275 | $self->[0] = $in; | |
276 | $self->[1] = $out; | |
277 | my $sel = select($out); | |
278 | $| = 1; # for DB::OUT | |
279 | select($sel); | |
280 | } | |
281 | ||
282 | sub IN { shift->[0] } | |
283 | sub OUT { shift->[1] } | |
284 | sub MinLine { undef } | |
285 | sub Attribs { {} } | |
286 | ||
287 | my %features = (tkRunning => 1, ornaments => 1, 'newTTY' => 1); | |
288 | sub Features { \%features } | |
289 | ||
290 | package Term::ReadLine; # So late to allow the above code be defined? | |
291 | ||
292 | our $VERSION = '1.02'; | |
293 | ||
294 | my ($which) = exists $ENV{PERL_RL} ? split /\s+/, $ENV{PERL_RL} : undef; | |
295 | if ($which) { | |
296 | if ($which =~ /\bgnu\b/i){ | |
297 | eval "use Term::ReadLine::Gnu;"; | |
298 | } elsif ($which =~ /\bperl\b/i) { | |
299 | eval "use Term::ReadLine::Perl;"; | |
300 | } else { | |
301 | eval "use Term::ReadLine::$which;"; | |
302 | } | |
303 | } elsif (defined $which and $which ne '') { # Defined but false | |
304 | # Do nothing fancy | |
305 | } else { | |
306 | eval "use Term::ReadLine::Gnu; 1" or eval "use Term::ReadLine::Perl; 1"; | |
307 | } | |
308 | ||
309 | #require FileHandle; | |
310 | ||
311 | # To make possible switch off RL in debugger: (Not needed, work done | |
312 | # in debugger). | |
313 | our @ISA; | |
314 | if (defined &Term::ReadLine::Gnu::readline) { | |
315 | @ISA = qw(Term::ReadLine::Gnu Term::ReadLine::Stub); | |
316 | } elsif (defined &Term::ReadLine::Perl::readline) { | |
317 | @ISA = qw(Term::ReadLine::Perl Term::ReadLine::Stub); | |
318 | } elsif (defined $which && defined &{"Term::ReadLine::$which\::readline"}) { | |
319 | @ISA = "Term::ReadLine::$which"; | |
320 | } else { | |
321 | @ISA = qw(Term::ReadLine::Stub); | |
322 | } | |
323 | ||
324 | package Term::ReadLine::TermCap; | |
325 | ||
326 | # Prompt-start, prompt-end, command-line-start, command-line-end | |
327 | # -- zero-width beautifies to emit around prompt and the command line. | |
328 | our @rl_term_set = ("","","",""); | |
329 | # string encoded: | |
330 | our $rl_term_set = ',,,'; | |
331 | ||
332 | our $terminal; | |
333 | sub LoadTermCap { | |
334 | return if defined $terminal; | |
335 | ||
336 | require Term::Cap; | |
337 | $terminal = Tgetent Term::Cap ({OSPEED => 9600}); # Avoid warning. | |
338 | } | |
339 | ||
340 | sub ornaments { | |
341 | shift; | |
342 | return $rl_term_set unless @_; | |
343 | $rl_term_set = shift; | |
344 | $rl_term_set ||= ',,,'; | |
345 | $rl_term_set = 'us,ue,md,me' if $rl_term_set eq '1'; | |
346 | my @ts = split /,/, $rl_term_set, 4; | |
347 | eval { LoadTermCap }; | |
348 | unless (defined $terminal) { | |
349 | warn("Cannot find termcap: $@\n") unless $Term::ReadLine::termcap_nowarn; | |
350 | $rl_term_set = ',,,'; | |
351 | return; | |
352 | } | |
353 | @rl_term_set = map {$_ ? $terminal->Tputs($_,1) || '' : ''} @ts; | |
354 | return $rl_term_set; | |
355 | } | |
356 | ||
357 | ||
358 | package Term::ReadLine::Tk; | |
359 | ||
360 | our($count_handle, $count_DoOne, $count_loop); | |
361 | $count_handle = $count_DoOne = $count_loop = 0; | |
362 | ||
363 | our($giveup); | |
364 | sub handle {$giveup = 1; $count_handle++} | |
365 | ||
366 | sub Tk_loop { | |
367 | # Tk->tkwait('variable',\$giveup); # needs Widget | |
368 | $count_DoOne++, Tk::DoOneEvent(0) until $giveup; | |
369 | $count_loop++; | |
370 | $giveup = 0; | |
371 | } | |
372 | ||
373 | sub register_Tk { | |
374 | my $self = shift; | |
375 | $Term::ReadLine::registered++ | |
376 | or Tk->fileevent($self->IN,'readable',\&handle); | |
377 | } | |
378 | ||
379 | sub tkRunning { | |
380 | $Term::ReadLine::toloop = $_[1] if @_ > 1; | |
381 | $Term::ReadLine::toloop; | |
382 | } | |
383 | ||
384 | sub get_c { | |
385 | my $self = shift; | |
386 | $self->Tk_loop if $Term::ReadLine::toloop && defined &Tk::DoOneEvent; | |
387 | return getc $self->IN; | |
388 | } | |
389 | ||
390 | sub get_line { | |
391 | my $self = shift; | |
392 | $self->Tk_loop if $Term::ReadLine::toloop && defined &Tk::DoOneEvent; | |
393 | my $in = $self->IN; | |
394 | local ($/) = "\n"; | |
395 | return scalar <$in>; | |
396 | } | |
397 | ||
398 | 1; | |
399 |