File Coverage

blib/lib/Term/TUI.pm
Criterion Covered Total %
statement 18 175 10.2
branch 0 94 0.0
condition 0 23 0.0
subroutine 7 21 33.3
pod 4 4 100.0
total 29 317 9.1


line stmt bran cond sub pod time code
1             package Term::TUI;
2             # Copyright (c) 1999-2008 Sullivan Beck. All rights reserved.
3             # This program is free software; you can redistribute it and/or modify it
4             # under the same terms as Perl itself.
5              
6             ########################################################################
7             # TODO
8             ########################################################################
9              
10             # improve completion:
11             # /math
12             # ad
13             # completes correctly to add but
14             # /math/ad
15             # doesn't autocomplete.
16              
17             # add abbreviation
18              
19             # case insensitivity
20              
21             # add .. and . to valid mode strings
22              
23             # "Hr. Jochen Stenzel"
24             # alias command
25             # history file (stored last commands)
26              
27             # config file (store commands to execute)
28              
29             ########################################################################
30              
31 1     1   13121 use warnings;
  1         3  
  1         54  
32 1     1   6 use vars qw($VERSION);
  1         2  
  1         71  
33             $VERSION="1.23";
34              
35             require 5.000;
36             require Exporter;
37              
38 1     1   1089 use Term::ReadLine;
  1         3941  
  1         41  
39 1     1   1014 use Text::ParseWords;
  1         1809  
  1         134  
40             #use Text::Abbrev;
41              
42             @ISA = qw(Exporter);
43             @EXPORT = qw(TUI_Run);
44             @EXPORT_OK = qw(TUI_Script TUI_Out TUI_Version);
45             %EXPORT_TAGS = (all => [ @EXPORT, @EXPORT_OK ]);
46              
47 1     1   6 use strict "vars";
  1         2  
  1         831  
48              
49             sub TUI_Version {
50 0     0 1   return $VERSION;
51             }
52              
53             BEGIN {
54 1     1   1544 my($term,$out);
55              
56             #
57             # Takes a program name (to be used in the prompt) and an interface
58             # description, and runs with it.
59             #
60              
61             #
62             # Interactive version.
63             #
64             sub TUI_Run {
65 0     0 1   my($program,$hashref)=@_;
66 0           my(@mode,$line,$err);
67 0           my($prompt)="$program> ";
68 0           $term=new Term::ReadLine $program;
69 0           $term->ornaments(0);
70              
71             # Command line completion
72 0           $term->Attribs->{'do_expand'}=1;
73 0           $term->Attribs->{'completion_entry_function'} =
74             $term->Attribs->{'list_completion_function'};
75              
76 0   0       $out=$term->OUT || STDOUT;
77              
78 0           my($ret)=0;
79              
80             # Command line completion
81             # The strings for completion
82 0           my(@completions) = _GetStrings(\@mode,$hashref);
83 0           $term->Attribs->{'completion_word'} = \@completions;
84              
85 0           while (defined ($line=$term->readline($prompt)) ) {
86 0           $err=_Line(\@mode,$hashref,$line);
87              
88             # Command line completion
89 0           @completions = _GetStrings(\@mode,$hashref);
90 0           $term->Attribs->{'completion_word'} = \@completions;
91              
92 0 0         if ($err =~ /^exit\[(\d+)\]$/) {
93 0           $ret=$1;
94 0           last;
95             }
96 0 0 0       print $out $err if ($err && $err !~ /^\d+$/);
97              
98 0 0         if (@mode) {
99 0           $prompt=$program . ":" . join("/",@mode) . "> ";
100             } else {
101 0           $prompt="$program> ";
102             }
103             }
104 0           return $ret;
105             }
106              
107             #
108             # Non-interactive version.
109             #
110             sub TUI_Script {
111 0     0 1   my($hashref,$script,$sep)=@_;
112 0           $out=STDOUT;
113              
114 0 0         $sep=";" if (! $sep);
115 0           my(@cmd)=split(/$sep/,$script);
116              
117 0           my($err,$cmd,@mode);
118 0           my($ret)=0;
119 0           foreach $cmd (@cmd) {
120 0           $err=_Line(\@mode,$hashref,$cmd);
121 0 0         if ($err =~ /^exit\[(\d+)\]$/) {
122 0           $ret=$1;
123 0           last;
124             }
125 0 0         print $out $err if ($err);
126             }
127 0           return $ret;
128             }
129              
130             #
131             # Prints a message.
132             #
133             sub TUI_Out {
134 0     0 1   my($mess)=@_;
135 0           print $out $mess;
136             }
137             }
138              
139              
140             ########################################################################
141             # NOT FOR EXPORT
142             ########################################################################
143              
144             {
145             # Stuff for doing completion.
146              
147             my $i;
148             my @matches;
149              
150             sub _TUI_completion_function {
151 0     0     my($text,$state)=@_;
152 0 0         $i = ($state ? $i : 0);
153              
154 0 0         if (! $i) {
155 0 0         if ($text =~ /^\s*(\S+)\s+(\S+)$/) {
    0          
    0          
156             # MODE CMD^
157             # completes CMD
158             # MODE/CMD OPTION^
159             # no matches
160              
161             } elsif ($text =~ /^\s*(\S+)\s+$/) {
162             # MODE ^
163             # completes CMD
164             # MODE/CMD ^
165             # no matches
166              
167             } elsif ($text =~ /^\s*(\S+)$/) {
168             # MODE^
169             # MODE/CMD^
170              
171             } else {
172 0           @matches=();
173             }
174             }
175             }
176             }
177              
178             #
179             # Takes the current mode (as a list), the interface description, and
180             # the current line and acts on the line.
181             #
182             sub _Line {
183 0     0     my($moderef,$cmdref,$line)=@_;
184              
185 0           $line =~ s/\s+$//;
186 0           $line =~ s/^\s+//;
187 0 0         return if (! $line);
188              
189 0           my(@cmd)=shellwords($line);
190 0           return _Cmd($moderef,$cmdref,@cmd);
191             }
192              
193             BEGIN {
194 1     1   15 my(%Cmds) =
195             (
196             ".." => [ "Go up one level", "_Mode",0 ],
197             "/" => [ "Go to top level", "_Mode",1 ],
198             "help" => [ "Online help", "_Help" ],
199             "exit" => [ "Exit", "_Exit",0 ],
200             "quit" => [ "An alias for exit", "_Exit",0 ],
201             "abort" => [ "Exit without saving", "_Exit",1 ]
202             );
203 1         105 my($Moderef,$Cmdref);
204              
205             #
206             # Returns an array of strings (commands or modes) that can be
207             # entered given a mode
208             #
209             sub _GetStrings {
210 0     0     my ($moderef,$cmdref) = @_;
211 0           my @strings;
212              
213 0 0 0       if (!defined $Cmdref || ref $Cmdref ne "HASH") {
214 0           $Cmdref = $cmdref;
215             }
216 0           my $desc = _GetMode(@{$moderef});
  0            
217 0 0         if ( ref $desc eq "HASH" ) {
218 0           @strings = grep !/^\./, sort keys %$desc;
219             }
220 0           push @strings,keys %Cmds;
221 0           return @strings;
222             }
223              
224             #
225             # Takes the current mode (as a list), the interface description, and the
226             # current command (as a list) and executes the command.
227             #
228             sub _Cmd {
229 0     0     my($moderef,$cmdref,@args)=@_;
230 0           my($cmd)=shift(@args);
231 0           $Moderef=$moderef;
232 0           $Cmdref=$cmdref;
233 0           my(@mode,$desc,$mode,$help);
234              
235 0 0         if (exists $Cmds{lc $cmd}) {
236 0           $desc=$Cmds{lc $cmd};
237              
238             } else {
239 0           ($mode,@mode)=_CheckMode(\$cmd);
240              
241 0 0 0       if ($mode && $cmd) {
    0 0        
    0          
242             #
243             # MODE/CMD [ARGS]
244             # CMD [ARGS]
245             #
246 0           $desc=_CheckCmd($mode,$cmd);
247              
248             } elsif ($mode && @args) {
249             #
250             # MODE CMD [ARGS]
251             #
252 0           $cmd=shift(@args);
253 0           $desc=_CheckCmd($mode,$cmd);
254              
255             } elsif ($mode) {
256             #
257             # MODE
258             #
259 0           $desc=[ "","_Mode",2,@mode ]
260             }
261             }
262              
263 0           my(@args0);
264 0 0         if (ref $desc eq "ARRAY") {
265 0           ($help,$cmd,@args0)=@$desc;
266 0 0         if (! defined &$cmd) {
267 0           $cmd="::$cmd";
268 0 0         if (! defined &$cmd) {
269 0           return "ERROR: invalid subroutine\n";
270             }
271             }
272 0           return &$cmd(@args0,@args);
273             } else {
274 0           return "ERROR: unknown command\n";
275             }
276             }
277              
278             #
279             # Takes a mode and/or command (as a list) and determines the mode
280             # to use. Returns a description of that mode.
281             #
282             sub _CheckMode {
283 0     0     my($cmdref)=@_;
284 0           my($cmd)=$$cmdref;
285 0           my(@mode,$tmp2);
286              
287 0 0         if ($cmd =~ s,^/,,) {
288 0           @mode=split(m|/|,$cmd);
289             } else {
290 0           @mode=(@$Moderef,split(m|/|,$cmd));
291             }
292              
293 0           my($tmp)=_GetMode(@mode);
294 0 0         if ($tmp) {
295 0           $$cmdref="";
296             } else {
297 0           $tmp2=pop(@mode);
298 0           $tmp=_GetMode(@mode);
299 0 0         $$cmdref=$tmp2 if ($tmp);
300             }
301              
302 0 0         @mode=() if (! $tmp);
303 0           return ($tmp,@mode);
304             }
305              
306             #
307             # Takes a mode (as a list) and returns it's description (or "" if it's
308             # not a mode).
309             #
310             sub _GetMode {
311 0     0     my(@mode)=@_;
312 0           my($tmp)=$Cmdref;
313 0           my($mode);
314              
315 0           foreach $mode (@mode) {
316 0 0 0       if (exists $$tmp{$mode} &&
317             ref $$tmp{$mode} eq "HASH") {
318 0           $tmp=$$tmp{$mode};
319             } else {
320 0           $tmp="";
321 0           last;
322             }
323             }
324 0           $tmp;
325             }
326              
327             ##############################################
328              
329             #
330             # A command to change the mode.
331             # .. op=0
332             # / op=1
333             # MODE op=2
334             #
335             sub _Mode {
336 0     0     my($op,@mode)=@_;
337              
338 0 0         if ($op==0) {
    0          
    0          
339             # Up one level
340 0 0         if ($#$Moderef>=0) {
341 0           pop(@$Moderef);
342             } else {
343 0           return "WARNING: Invalid operation\n";
344             }
345              
346             } elsif ($op==1) {
347             # Top
348 0           @$Moderef=();
349              
350             } elsif ($op==2) {
351             # Change modes
352 0           @$Moderef=@mode;
353              
354             } else {
355 0           return "ERROR: Invalid mode operation: $op\n";
356             }
357 0           return "";
358             }
359              
360             sub _Help {
361 0     0     my($cmd,@args)=@_;
362              
363 0           my($tmp,$mode,@mode);
364              
365 0 0         ($tmp,@mode)=_CheckMode(\$cmd) if ($cmd);
366 0 0         if (! $tmp) {
367 0           @mode=@$Moderef;
368 0 0         if (@mode) {
369 0           $tmp=_GetMode(@mode);
370             } else {
371 0           $tmp=$Cmdref;
372             }
373             }
374              
375 0 0         return "IMPOSSIBLE: invalid mode\n" if (! $tmp);
376              
377 0           my($mess);
378 0 0 0       $cmd=shift(@args) if (! $cmd && @args);
379 0 0         if ($cmd) {
380             #
381             # Help on a command
382             #
383 0 0         if (exists $Cmds{$cmd}) {
    0          
384 0           $tmp=$Cmds{$cmd};
385 0           $mess=$$tmp[0];
386              
387             } elsif (exists $$tmp{$cmd}) {
388 0           $tmp=$$tmp{$cmd};
389 0 0         if (ref $tmp ne "ARRAY") {
390 0           $mess="Invalid command $cmd";
391             } else {
392 0           $mess=$$tmp[0];
393 0 0         $mess="No help available" if (! $mess);
394             }
395             } else {
396 0           $mess="Invalid command: $cmd";
397             }
398              
399             } else {
400             #
401             # Help on a mode
402             #
403 0 0         if (exists $$tmp{".HELP"}) {
404 0           $mess=$$tmp{".HELP"};
405 0           my(@gc)=sort grep /^([^.]|\.\.)/i,keys %Cmds;
406 0           my(@cmd)=sort grep /^[^.]/,keys %{ $tmp };
  0            
407 0           my(@m,@c)=();
408 0           foreach $cmd (@cmd) {
409 0 0         if (ref $$tmp{$cmd} eq "ARRAY") {
    0          
410 0           push(@c,$cmd);
411             } elsif (ref $$tmp{$cmd} eq "HASH") {
412 0           push(@m,$cmd);
413             }
414             }
415 0           $mess .= "\n\nAdditional help:\n\n";
416 0 0         $mess .= " Modes: @m\n" if (@m);
417 0           $mess .= " Cmds : @gc";
418 0 0         $mess .= "\n" if (@c);
419 0 0         $mess .= " @c" if (@c);
420              
421             } else {
422 0           $mess="No help available";
423             }
424             }
425              
426 0           return "\n$mess\n\n";
427             }
428             }
429              
430             #
431             # Takes a mode and command and return a description of the command.
432             #
433             sub _CheckCmd {
434 0     0     my($moderef,$cmd)=@_;
435 0 0 0       return $$moderef{$cmd}
436             if (exists $$moderef{$cmd} &&
437             ref $$moderef{$cmd} eq "ARRAY");
438 0           return ();
439             }
440              
441             sub _Exit {
442 0     0     my($flag)=@_;
443 0           return "exit[$flag]";
444             }
445              
446             # sub {
447             # map {lc($_)} (keys %commands, keys %aliases)
448             # };
449              
450             # $term->Attribs->{'do_expand'}=1;
451             # $term->Attribs->{'completion_entry_function'} =
452             # sub {
453             # $term->Attribs->{'line_buffer'} =~ /\s/ ?
454             # &{$term->Attribs->{'filename_completion_function'}}(@_) :
455             # &{$term->Attribs->{'list_completion_function'}}(@_)
456             # };
457             # $term->Attribs->{'completion_word'}=[(map {lc($_)} (keys %commands))];
458              
459             1;
460             # Local Variables:
461             # mode: cperl
462             # indent-tabs-mode: nil
463             # cperl-indent-level: 3
464             # cperl-continued-statement-offset: 2
465             # cperl-continued-brace-offset: 0
466             # cperl-brace-offset: 0
467             # cperl-brace-imaginary-offset: 0
468             # cperl-label-offset: -2
469             # End: