File Coverage

lib/Devel/Trepan/CmdProcessor/Validate.pm
Criterion Covered Total %
statement 60 125 48.0
branch 11 48 22.9
condition 10 17 58.8
subroutine 17 21 80.9
pod 0 7 0.0
total 98 218 44.9


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011-2012 Rocky Bernstein <rocky@cpan.org>
3             # Trepan command input validation routines. A String type is
4             # usually passed in as the argument to validation routines.
5              
6 12     12   34962 use strict; use warnings;
  12     12   34  
  12         360  
  12         64  
  12         32  
  12         580  
7 12     12   78 use Exporter;
  12         30  
  12         592  
8              
9 12     12   76 use rlib '../../..';
  12         39  
  12         107  
10              
11             package Devel::Trepan::CmdProcessor;
12              
13 12     12   8080 use Cwd 'abs_path';
  12         32  
  12         600  
14 12     12   1357 use Devel::Trepan::DB::Breakpoint;
  12         43  
  12         288  
15 12     12   78 use Devel::Trepan::DB::LineCache;
  12         29  
  12         2268  
16 12     12   83 no warnings 'redefine';
  12         28  
  12         3133  
17              
18             # require_relative '../app/cmd_parse'
19             # require_relative '../app/condition'
20             # require_relative '../app/file'
21             # require_relative '../app/thread'
22              
23             # require_relative 'location' # for resolve_file_with_dir
24              
25             # attr_reader :file_exists_proc # Like File.exists? but checks using
26             # # cached files
27              
28             # Check that arg is an Integer between opts->{min_value} and
29             # opts->{max_value}
30             sub get_an_int($$$)
31             {
32 2     2 0 470 my ($self, $arg, $opts) = @_;
33 2   100     16 $opts ||= {};
34 2         14 my $ret_value = $self->get_int_noerr($arg);
35 2 50       13 if (! defined $ret_value) {
36 0 0       0 if ($opts->{msg_on_error}) {
37 0         0 $self->errmsg($opts->{msg_on_error});
38             } else {
39 0         0 $self->errmsg("Expecting an integer, got: ${arg}.");
40             }
41 0         0 return undef;
42             }
43 2 50 66     28 if (defined($opts->{min_value}) and $ret_value < $opts->{min_value}) {
    50 33        
44             my $msg = sprintf("Expecting integer value to be at least %d; got %d.",
45 0         0 $opts->{min_value}, $ret_value);
46 0         0 $self->errmsg($msg);
47 0         0 return undef;
48             } elsif (defined($opts->{max_value}) and $ret_value > $opts->{max_value}) {
49             my $msg = sprintf("Expecting integer value to be at most %d; got %d.",
50 0         0 $opts->{max_value}, $ret_value);
51 0         0 $self->errmsg($msg);
52 0         0 return undef;
53             }
54 2         11 return $ret_value;
55             }
56              
57 12         808 use constant DEFAULT_GET_INT_OPTS => {
58             min_value => 0, default => 1, cmdname => undef, max_value => undef
59 12     12   94 };
  12         30  
60 12     12   617 use Devel::Trepan::Util qw(hash_merge);
  12         34  
  12         1942  
61              
62             # # If argument parameter 'arg' is not given, then use what is in
63             # # $opts->{default}. If String 'arg' evaluates to an integer between
64             # # least min_value and at_most, use that. Otherwise report an
65             # # error. If there's a stack frame use that for bindings in
66             # # evaluation.
67             # sub get_int($$;$)
68             # {
69             # my ($self, $arg, $opts)= @_;
70             # $opts ||={};
71              
72             # return $opts->{default} unless $arg;
73             # $opts = hash_merge($opts, DEFAULT_GET_INT_OPTS);
74             # my $val = $arg ? $self->get_int_noerr($arg) : $opts->{default};
75             # unless ($val) {
76             # if ($opts->{cmdname}) {
77             # my $msg = sprintf("Command '%s' expects an integer; " +
78             # "got: %s.", $opts->{cmdname}, $arg);
79             # $self->errmsg($msg);
80             # } else {
81             # $self->errmsg('Expecting a positive integer, got: ${arg}');
82             # }
83             # return undef;
84             # }
85              
86             # if ($val < $opts->{min_value}) {
87             # if ($opts->{cmdname}) {
88             # my $msg = sprintf("Command '%s' expects an integer at least" .
89             # ' %d; got: %d.',
90             # $opts->{cmdname}, $opts->{min_value},
91             # $opts->{default});
92             # $self->errmsg($msg);
93             # } else {
94             # my $msg = sprintf("Expecting a positive integer at least" .
95             # ' %d; got: %d',
96             # $opts->{min_value}, $opts->{default});
97             # $self->errmsg($msg);
98             # }
99             # return undef;
100             # elsif ($self->opts{max_value} and $val > $self->opts{max_value}) {
101             # if ($self->opts{cmdname}) {
102             # my $msg = sprintf("Command '%s' expects an integer at most" .
103             # ' %d; got: %d', $opts->{cmdname},
104             # $opts->{max_value}, $val);
105             # $self->errmsg($msg);
106             # }
107             # } else {
108             # my $msg = sprintf("Expecting an integer at most %d; got: %d",
109             # $opts->{:max_value}, $val);
110             # $self->errmsg($msg);
111             # }
112             # return undef;
113             # }
114             # return $val
115             # }
116              
117             sub get_int_list($$;$)
118             {
119 0     0 0 0 my ($self, $args, $opts) = @_;
120 0 0       0 $opts = {} unless defined $opts;
121 0         0 map {$self->get_an_int($_, $opts)} @{$args}; # .compact
  0         0  
  0         0  
122             }
123              
124             # Eval arg and it is an integer return the value. Otherwise
125             # return undef;
126             sub get_int_noerr($$)
127             {
128 9     9 0 4074 my ($self, $arg) = @_;
129 9         25 my $val = eval {
130 12     12   104 no warnings 'all';
  12         2310  
  12         4686  
131 9         440 eval($arg);
132             };
133 9 100       38 if (defined $val) {
134 7 50       73 return $val =~ /^[+-]?\d+$/ ? $val : undef;
135             } else {
136 2         12 return undef;
137             }
138             }
139              
140             # sub get_thread_from_string(id_or_num_str)
141             # if id_or_num_str == '.'
142             # Thread.current
143             # elsif id_or_num_str.downcase == 'm'
144             # Thread.main
145             # else
146             # num = get_int_noerr(id_or_num_str)
147             # if num
148             # get_thread(num)
149             # else
150             # nil
151             # }
152             # }
153             # }
154              
155             # # Return the instruction sequence associated with string
156             # # OBJECT_STRING or nil if no instruction sequence
157             # sub object_iseq(object_string)
158             # iseqs = find_iseqs(ISEQS__, object_string)
159             # # FIXME: do something if there is more than one.
160             # if iseqs.size == 1
161             # iseqs[-1]
162             # elsif meth = method?(object_string)
163             # meth.iseq
164             # else
165             # nil
166             # }
167             # rescue
168             # nil
169             # }
170              
171             # sub position_to_line_and_offset(iseq, filename, position, offset_type)
172             # case offset_type
173             # when :line
174             # if ary = iseq.lineoffsets[position]
175             # # Normally the first offset is a trace instruction and doesn't
176             # # register as the given line, so we need to take the next instruction
177             # # after the first one, when available.
178             # vm_offset = ary.size > 1 ? ary[1] : ary[0]
179             # line_no = position
180             # elsif found_iseq = find_iseqs_with_lineno(filename, position)
181             # return position_to_line_and_offset(found_iseq, filename, position,
182             # offset_type)
183             # elsif found_iseq = find_iseq_with_line_from_iseq(iseq, position)
184             # return position_to_line_and_offset(found_iseq, filename, position,
185             # offset_type)
186             # else
187             # errmsg("Unable to find offset for line #{position}\n\t" +
188             # "in #{iseq.name} of file #{filename}")
189             # return [nil, nil]
190             # }
191             # when :offset
192             # position = position.position unless position.kind_of?(Fixnum)
193             # if ary=iseq.offset2lines(position)
194             # line_no = ary.first
195             # vm_offset = position
196             # else
197             # errmsg "Unable to find line for offset #{position} in #{iseq}"
198             # return [nil, nil]
199             # }
200             # when nil
201             # vm_offset = 0
202             # line_no = iseq.offset2lines(vm_offset).first
203             # else
204             # errmsg "Bad parse offset_type: #{offset_type.inspect}"
205             # return [nil, nil]
206             # }
207             # return [iseq, line_no, vm_offset]
208             # }
209              
210             # # Parse a breakpoint position. On success return:
211             # # - the instruction sequence to use
212             # # - the line number - a Fixnum
213             # # - vm_offset - a Fixnum
214             # # - the condition (by default 'true') to use for this breakpoint
215             # # - true condition should be negated. Used in *condition* if/unless
216             # sub breakpoint_position(position_str, allow_condition)
217             # break_cmd_parse = if allow_condition
218             # parse_breakpoint(position_str)
219             # else
220             # parse_breakpoint_no_condition(position_str)
221             # }
222             # return [nil] * 5 unless break_cmd_parse
223             # tail = [break_cmd_parse.condition, break_cmd_parse.negate]
224             # meth_or_frame, file, position, offset_type =
225             # parse_position(break_cmd_parse.position)
226             # if meth_or_frame
227             # if iseq = meth_or_frame.iseq
228             # iseq, line_no, vm_offset =
229             # position_to_line_and_offset(iseq, file, position, offset_type)
230             # if vm_offset && line_no
231             # return [iseq, line_no, vm_offset] + tail
232             # }
233             # else
234             # errmsg("Unable to set breakpoint in #{meth_or_frame}")
235             # }
236             # elsif file && position
237             # if :line == offset_type
238             # iseq = find_iseqs_with_lineno(file, position)
239             # if iseq
240             # junk, line_no, vm_offset =
241             # position_to_line_and_offset(iseq, file, position, offset_type)
242             # return [@frame.iseq, line_no, vm_offset] + tail
243             # else
244             # errmsg("Unable to find instruction sequence for" +
245             # " position #{position} in #{file}")
246             # }
247             # else
248             # errmsg "Come back later..."
249             # }
250             # elsif @frame.file == file
251             # line_no, vm_offset = position_to_line_and_offset(@frame.iseq, position,
252             # offset_type)
253             # return [@frame.iseq, line_no, vm_offset] + tail
254             # else
255             # errmsg("Unable to parse breakpoint position #{position_str}")
256             # }
257             # return [nil] * 5
258             # }
259              
260             # Return true if arg is 'on' or 1 and false arg is 'off' or 0.
261             # Any other value is returns undef.
262             sub get_onoff($$;$$)
263             {
264 4     4 0 1973 my ($self, $arg, $default, $print_error) = @_;
265 4 50       16 $print_error = 1 unless defined $print_error;
266 4 50       16 unless (defined $arg) {
267 0 0       0 unless (defined $default) {
268 0 0       0 if ($print_error) {
269 0         0 $self->errmsg("Expecting 'on', 1, 'off', or 0. Got nothing.");
270 0         0 return undef;
271             }
272             }
273 0         0 return $default
274             }
275 4         12 my $darg = lc $arg;
276 4 100 100     35 return 1 if ($arg eq '1') || ($darg eq 'on');
277 2 50 66     20 return 0 if ($arg eq '0') || ($darg eq'off');
278              
279 0 0         $self->errmsg("Expecting 'on', 1, 'off', or 0. Got: ${arg}.") if
280             $print_error;
281 0           return undef;
282             }
283              
284             sub is_method($$)
285             {
286 0     0 0   my ($self, $method_name) = @_;
287 0           my ($filename, $fn, $line_num) = DB::find_subline($method_name) ;
288 0           return !!$line_num;
289             }
290              
291             # # FIXME: this is a ? method but we return
292             # # the method value.
293             # sub method?(meth)
294             # get_method(meth)
295             # }
296              
297             # parse_position
298             # parse: file line [rest...]
299             # line [rest..]
300             # fn [rest..]
301             # returns (filename, line_num, fn, rest)
302             # NOTE: Test for failure should only be on $line_num
303             sub parse_position($$;$)
304             {
305 0     0 0   my ($self, $args, $validate_line_num) = @_;
306 0           my @args = @$args;
307 0           my $size = scalar @args;
308 0           my $gobble_count = 0;
309 0 0         $validate_line_num = 0 unless defined $validate_line_num;
310              
311 0 0         if (0 == $size) {
312 12     12   104 no warnings 'once';
  12         27  
  12         3986  
313 0           return ($DB::filename, $DB::line, undef, 0, ());
314             }
315 0           my ($filename, $line_num, $fn);
316 0           my $first_arg = shift @args;
317 0 0         if ($first_arg =~ /^\d+$/) {
318 0           $line_num = $first_arg;
319 0           $filename = $DB::filename;
320 0           $gobble_count = 1;
321 0           $fn = undef;
322             } else {
323 0           ($filename, $fn, $line_num) = DB::find_subline($first_arg) ;
324 0 0         unless ($line_num) {
325 0           $filename = $first_arg;
326 0           my $mapped_filename = map_file($filename);
327 0 0         if (-r $mapped_filename) {
328 0 0         if (scalar @args == 0) {
329 0           $line_num = 1;
330             } else {
331 0           $line_num = shift @args;
332             }
333 0 0         unless ($line_num =~ /\d+/) {
334 0           $self->errmsg("Got filename $first_arg, " .
335             "expecting $line_num to a line number");
336 0           return ($filename, undef, undef, 0, @args);
337             }
338             } else {
339 0           $self->errmsg("Expecting $first_arg to be a file " .
340             "or function name");
341 0           return ($filename, undef, $fn, 0, @args);
342             }
343             }
344 0           $gobble_count = 1;
345             }
346 0 0         if ($validate_line_num) {
347 0           local(*DB::dbline) = "::_<'$filename" ;
348 0 0 0       if (!defined($DB::dbline[$line_num]) || $DB::dbline[$line_num] == 0) {
349 0           $self->errmsg("Line $line_num of file $filename not a stopping line");
350 0           return ($filename, undef, $fn, 0, @args);
351             }
352             }
353 0           return ($filename, $line_num, $fn, $gobble_count, @args);
354             }
355              
356              
357             # sub validate_initialize
358             # ## top_srcdir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
359             # ## @dbgr_script_iseqs, @dbgr_iseqs = filter_scripts(top_srcdir)
360             # @file_exists_proc = Proc.new {|filename|
361             # if LineCache.cached?(filename) || LineCache.cached_script?(filename) ||
362             # (File.readable?(filename) && !File.directory?(filename))
363             # true
364             # else
365             # matches = find_scripts(filename)
366             # if matches.size == 1
367             # LineCache.remap_file(filename, matches[0])
368             # true
369             # else
370             # false
371             # }
372             # }
373             # }
374             # }
375             # }
376             # }
377              
378             unless (caller) {
379 12     12   88 no strict;
  12         30  
  12         1394  
380             require Devel::Trepan::DB;
381             my @onoff = qw(1 0 on off);
382             for my $val (@onoff) {
383             printf "onoff(${val}) = %s\n", get_onoff('bogus', $val);
384             }
385              
386             for my $val (qw(1 1E bad 1+1 -5)) {
387             my $result = get_int_noerr('bogus', $val);
388             $result = '<undef>' unless defined $result;
389             print "get_int_noerr(${val}) = $result\n";
390             }
391              
392 12     12   78 no warnings 'redefine';
  12         34  
  12         3377  
393             require Devel::Trepan::CmdProcessor;
394             my $proc = Devel::Trepan::CmdProcessor::new(__PACKAGE__);
395             my @aref = $proc->get_int_list(['1+0', '3-1', '3']);
396             print join(', ', @aref), "\n";
397              
398             @aref = $proc->get_int_list(['a', '2', '3']);
399             print join(', ', @aref[1..2]), "\n";
400              
401             local @position = ();
402             sub print_position() {
403 0     0 0   my @call_values = caller(0);
404 0           for my $arg (@position) {
405 0 0         print defined($arg) ? $arg : 'undef';
406 0           print "\n";
407             }
408 0           print "\n";
409 0           return @call_values;
410             }
411             my @call_values = foo();
412              
413             $DB::package = 'main';
414             @position = $proc->parse_position([__FILE__, __LINE__], 0);
415             print_position;
416             @position = $proc->parse_position([__LINE__], 0);
417             print_position;
418             # @position = $proc->parse_position(['print_position'], 0);
419             # print cmdproc.parse_position('@8').inspect
420             # print cmdproc.parse_position('8').inspect
421             # print cmdproc.parse_position("#{__FILE__} #{__LINE__}").inspect
422              
423             # print '=' * 40
424             # ['Array.map', 'Trepan::CmdProcessor.new',
425             # 'foo', 'cmdproc.errmsg'].each do |str|
426             # print "#{str} should be method: #{!!cmdproc.method?(str)}"
427             # }
428             # print '=' * 40
429              
430             # # FIXME:
431             # print "Trepan::CmdProcessor.allocate is: #{cmdproc.get_method('Trepan::CmdProcessor.allocate')}"
432              
433             # ['food', '.errmsg'].each do |str|
434             # print "#{str} should be false: #{cmdproc.method?(str).to_s}"
435             # }
436             # print '-' * 20
437             # p cmdproc.breakpoint_position('foo', true)
438             # p cmdproc.breakpoint_position('@0', true)
439             # p cmdproc.breakpoint_position("#{__LINE__}", true)
440             # p cmdproc.breakpoint_position("#{__FILE__} @0", false)
441             # p cmdproc.breakpoint_position("#{__FILE__}:#{__LINE__}", true)
442             # p cmdproc.breakpoint_position("#{__FILE__} #{__LINE__} if 1 == a", true)
443             # p cmdproc.breakpoint_position("cmdproc.errmsg", false)
444             # p cmdproc.breakpoint_position("cmdproc.errmsg:@0", false)
445             # ### p cmdproc.breakpoint_position(%w(2 if a > b))
446             }
447              
448             1;