File Coverage

blib/lib/Parse/DMIDecode/Handle.pm
Criterion Covered Total %
statement 119 161 73.9
branch 49 90 54.4
condition 18 58 31.0
subroutine 15 24 62.5
pod 11 13 84.6
total 212 346 61.2


line stmt bran cond sub pod time code
1             ############################################################
2             #
3             # $Id: Handle.pm 976 2007-03-04 20:47:36Z nicolaw $
4             # Parse::DMIDecode::Handle - SMBIOS Structure Handle Object Class
5             #
6             # Copyright 2006 Nicola Worthington
7             #
8             # Licensed under the Apache License, Version 2.0 (the "License");
9             # you may not use this file except in compliance with the License.
10             # You may obtain a copy of the License at
11             #
12             # http://www.apache.org/licenses/LICENSE-2.0
13             #
14             # Unless required by applicable law or agreed to in writing, software
15             # distributed under the License is distributed on an "AS IS" BASIS,
16             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17             # See the License for the specific language governing permissions and
18             # limitations under the License.
19             #
20             ############################################################
21              
22             package Parse::DMIDecode::Handle;
23             # vim:ts=4:sw=4:tw=78
24              
25 4     4   21 use strict;
  4         7  
  4         154  
26 4     4   1963 use Parse::DMIDecode::Constants qw(@TYPES %GROUPS %TYPE2GROUP);
  4         11  
  4         725  
27             #use Scalar::Util qw(refaddr);
28             #use Storable qw(dclone);
29 4     4   33 use Carp qw(croak cluck confess carp);
  4         8  
  4         343  
30 4     4   20 use vars qw($VERSION $DEBUG);
  4         8  
  4         3197  
31              
32             $VERSION = '0.03' || sprintf('%d', q$Revision: 976 $ =~ /(\d+)/g);
33             $DEBUG ||= $ENV{DEBUG} ? 1 : 0;
34              
35             my $objstore = {};
36              
37              
38             #
39             # Methods
40             #
41              
42             sub new {
43 346 50   346 1 894 ref(my $class = shift) && croak 'Class name required';
44 346 50       750 croak 'Odd number of elements passed when even was expected' if @_ % 2;
45              
46 346         1234 my $stor = {@_};
47 346         767 my @validkeys = qw(raw nowarnings);
48 346         704 my $validkeys = join('|',@validkeys);
49 346         452 my @invalidkeys = grep(!/^$validkeys$/,keys %{$stor});
  346         3018  
50 346         788 delete $stor->{$_} for @invalidkeys;
51 346 0 33     871 cluck('Unrecognised parameters passed: '.join(', ',@invalidkeys))
52             if @invalidkeys && $^W;
53              
54 346 50       903 return unless defined defined $stor->{raw};
55 346         1926 for (split(/\n/,$stor->{raw})) {
56 3099 100 66     14734 if (/^Handle ([0-9A-Fx]+)(?:, DMI type (\d+), (\d+) bytes?\.?)?\s*$/) {
    100          
57 346         1227 $stor->{handle} = $1;
58 346 100       975 $stor->{dmitype} = $2 if defined $2;
59 346 100       1079 $stor->{bytes} = $3 if defined $3;
60             } elsif (defined $stor->{handle} &&
61             /^\s*DMI type (\d+), (\d+) bytes?\.?\s*$/) {
62 206 50       792 $stor->{dmitype} = $1 if defined $1;
63 206 50       755 $stor->{bytes} = $2 if defined $2;
64             } else {
65 2547 100       5300 $stor->{raw_entries} = [] unless defined $stor->{raw_entries};
66 2547         2733 push @{$stor->{raw_entries}}, $_;
  2547         5996  
67             }
68             }
69              
70 346 50       1513 my ($data,$keywords) = _parse($stor)
71             if $stor->{raw_entries};
72              
73 346         723 my @objects;
74 346         517 for my $name (keys(%{$data})) {
  346         1021  
75 350         1014 my $self = bless \(my $dummy), $class;
76 350         1250 push @objects, $self;
77              
78 350         692 $objstore->{_refaddr($self)} = _deepcopy($stor);
79 350         1216 my $stor = $objstore->{_refaddr($self)};
80              
81 350         996 $stor->{description} = substr($name,4);
82 350   50     2121 $stor->{data} = $data->{$name} || {};
83 350   100     1351 $stor->{keywords} = $keywords->{$name} || {};
84              
85 350         696 DUMP('$self',$self);
86 350         577 DUMP('$stor',$stor);
87             }
88              
89 346         886 DUMP('\@objects',\@objects);
90 346         2457 return @objects;
91             }
92              
93              
94 4     4   27 no warnings 'redefine';
  4         8  
  4         284  
95             sub UNIVERSAL::a_sub_not_likely_to_be_here { ref($_[0]) }
96 4     4   24 use warnings 'redefine';
  4         6  
  4         7673  
97              
98              
99             sub _blessed ($) {
100 723     723   3747 local($@, $SIG{__DIE__}, $SIG{__WARN__});
101             return length(ref($_[0]))
102 723 50       2398 ? eval { $_[0]->a_sub_not_likely_to_be_here }
  723         2252  
103             : undef
104             }
105              
106              
107             sub _refaddr($) {
108 723 50   723   1935 my $pkg = ref($_[0]) or return undef;
109 723 50       1358 if (_blessed($_[0])) {
110 723         1846 bless $_[0], 'Scalar::Util::Fake';
111             } else {
112 0         0 $pkg = undef;
113             }
114 723         4217 "$_[0]" =~ /0x(\w+)/;
115 723         764 my $i = do { local $^W; hex $1 };
  723         1629  
  723         2398  
116 723 50       2137 bless $_[0], $pkg if defined $pkg;
117 723         2584 return $i;
118             }
119              
120              
121             sub _deepcopy{
122 5045     5045   6287 my $this = shift;
123 5045 100       9688 if (!ref($this)) {
    100          
    50          
124 4345         13317 $this;
125             } elsif (ref($this) eq 'ARRAY') {
126 350         380 [ map _deepcopy($_), @{$this} ];
  350         900  
127             } elsif (ref($this) eq 'HASH'){
128 350         415 scalar { map { $_ => _deepcopy($this->{$_}) } keys %{$this} };
  2100         4845  
  350         1250  
129             } else {
130 0         0 confess "What type is $_?";
131             }
132             }
133              
134              
135             sub parsed_structures {
136 0     0 1 0 my $self = shift;
137 0 0 0     0 croak 'Not called as a method by parent object'
138             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
139 0         0 return _deepcopy($objstore->{_refaddr($self)}->{data});
140             }
141              
142              
143             sub keyword {
144 6     6 1 8 my $self = shift;
145 6 50 33     56 croak 'Not called as a method by parent object'
146             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
147 6 0       18 croak sprintf('%s elements passed when one was expected',
    50          
148             (@_ > 1 ? 'Multiple' : 'No')) if @_ != 1;
149 6         10 return $objstore->{_refaddr($self)}->{keywords}->{$_[0]};
150             }
151              
152              
153             sub keywords {
154 17     17 1 18 my $self = shift;
155 17 50 33     95 croak 'Not called as a method by parent object'
156             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
157 17         18 return sort(keys(%{$objstore->{_refaddr($self)}->{keywords}}));
  17         79  
158             }
159              
160              
161             sub raw {
162 0     0 1 0 my $self = shift;
163 0 0 0     0 croak 'Not called as a method by parent object'
164             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
165 0         0 return $objstore->{_refaddr($self)}->{raw};
166             }
167              
168              
169             sub description {
170 0     0 1 0 my $self = shift;
171 0 0 0     0 croak 'Not called as a method by parent object'
172             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
173 0         0 return $objstore->{_refaddr($self)}->{description};
174             }
175              
176              
177             sub bytes {
178 0     0 1 0 my $self = shift;
179 0 0 0     0 croak 'Not called as a method by parent object'
180             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
181 0         0 return $objstore->{_refaddr($self)}->{bytes};
182             }
183              
184              
185 0     0 1 0 sub type { &dmitype; }
186             sub dmitype {
187 0     0 1 0 my $self = shift;
188 0 0 0     0 croak 'Not called as a method by parent object'
189             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
190 0         0 return $objstore->{_refaddr($self)}->{dmitype};
191             }
192              
193              
194 0     0 1 0 sub address { &handle; }
195             sub handle {
196 0     0 1 0 my $self = shift;
197 0 0 0     0 croak 'Not called as a method by parent object'
198             unless ref $self && UNIVERSAL::isa($self, __PACKAGE__);
199 0         0 return $objstore->{_refaddr($self)}->{handle};
200             }
201              
202              
203             sub _parse {
204 346     346   434 my $stor = shift;
205 346 50       692 return unless defined $stor->{raw_entries};
206              
207 346         575 my $name_indent = 0;
208 346         358 my $key_indent = 0;
209 346         512 my $name = '';
210 346         376 my $name_cnt = 0;
211 346         409 my $key = '';
212 346         339 my $legacy_dmidecode_binary_data = 0;
213              
214 346         406 my @errors;
215             my %data;
216 0         0 my %keywords;
217              
218             # DUMP('$stor->{raw_entries}',$stor->{raw_entries});
219              
220 346         490 for (my $l = 0; $l < @{$stor->{raw_entries}}; $l++) {
  2893         8213  
221 2547         4462 local $_ = $stor->{raw_entries}->[$l];
222 2547         7679 my ($indent) = $_ =~ /^(\s+)/;
223 2547 100       5899 $indent = '' unless defined $indent;
224 2547         3519 $indent = length($indent);
225              
226             # Old version of dmidecode - we don't support this very well
227             #Handle 0xD402
228             #| DMI type 212, 47 bytes.
229             #70 00 71 00 03 40 59 6d d8 00 55 7f 80 d9 00 55 | p.q..@Ym..U....U
230             #7f 00 00 c0 5c 00 0a 03 c0 67 00 05 83 00 76 00 | ....\....g....v.
231             #00 84 00 77 00 00 ff ff 00 00 00 | ...w.......
232 2547 50 66     10974 if ((!$name || $legacy_dmidecode_binary_data) && /^(([a-f0-9]{2} )+)\s*\t[[:print:]]{1,16}\s*$/) {
      66        
233 0         0 my $data = $1;
234 0         0 chop $data;
235 0   0     0 $name ||= sprintf('%03d|%s',++$name_cnt,'OEM-specific Type');
236 0         0 $key = 'Header and Data';
237 0         0 $legacy_dmidecode_binary_data = 1;
238 0 0       0 $data{$name}->{$key}->[1] = [] unless defined $data{$name}->{$key}->[1];
239 0         0 push @{$data{$name}->{$key}->[1]}, $data;
  0         0  
240 0         0 next;
241             }
242              
243 2547 100       4949 $name_indent = $indent if $l == 0;
244 2547 100       5116 if ($l == 1) {
245 305 50       497 if ($indent > $name_indent) { $key_indent = $indent; }
  305         432  
246 0         0 else { push @errors, "Parser warning: key_indent ($indent) <= name_indent ($name_indent): $_"; }
247             }
248              
249             # data
250 2547 100 66     40244 if (/^\s{$name_indent}(\S+.*?)\s*$/) {
    100 33        
    50 33        
      33        
251 350         1393 $name = sprintf('%03d|%s',++$name_cnt,$1);
252 350         911 $data{$name} = {};
253 350         923 $key = '';
254              
255             } elsif ($name && /^\s{$key_indent}(\S.*?)(?::|:\s+(\S+.*?))?\s*$/) {
256 1617         3302 $key = $1;
257 1617         8741 $data{$name}->{$key}->[0] = $2;
258 1617 50       6693 $data{$name}->{$key}->[1] = [] unless defined $data{$name}->{$key}->[1];
259 1617 100       7526 $keywords{$name}->{_keyword($stor,$key)} = $data{$name}->{$key}->[0]
260             if defined $TYPE2GROUP{$stor->{dmitype}}
261              
262             } elsif ($name && $key && $indent > $key_indent && /^\s*(\S+.*?)\s*$/) {
263 580         717 push @{$data{$name}->{$key}->[1]}, $1;
  580         8130  
264 580 100       2538 $keywords{$name}->{_keyword($stor,$key)} = $data{$name}->{$key}->[1]
265             if defined $TYPE2GROUP{$stor->{dmitype}};# && !defined $data{$name}->{$key}->[0];
266              
267             # unknown
268             } else {
269 0         0 push @errors, "Parser warning: $_";
270             }
271             }
272              
273             sub _keyword {
274 1409     1409   2173 my ($stor,$key) = @_;
275 1409         5596 (my $keyword = $key) =~ s/[^a-z0-9]/-/gi;
276 1409         4323 $keyword = lc("$TYPE2GROUP{$stor->{dmitype}}-$keyword");
277 1409         6912 return $keyword;
278             }
279              
280             #if ($^W && @errors) {
281 346 50 33     970 if (@errors && !$stor->{nowarnings}) {
282 0         0 carp $_ for @errors;
283             }
284              
285 346         1254 return (\%data,\%keywords);
286             }
287              
288              
289             sub TRACE {
290 0 0   0 0 0 return unless $DEBUG;
291 0         0 carp(shift());
292             }
293              
294              
295             sub DUMP {
296 1046 50   1046 0 2821 return unless $DEBUG;
297 0           eval {
298 0           require Data::Dumper;
299 0           local $Data::Dumper::Indent = 2;
300 0           local $Data::Dumper::Terse = 1;
301 0           carp(shift().': '.Data::Dumper::Dumper(shift()));
302             }
303             }
304              
305             1;
306              
307              
308              
309             =pod
310              
311             =head1 NAME
312              
313             Parse::DMIDecode::Handle - SMBIOS Structure Handle Object Class
314              
315             =head1 SYNOPSIS
316              
317             use Parse::DMIDecode qw();
318             my $decoder = new Parse::DMIDecode;
319             $decoder->probe;
320            
321             for my $handle ($decoder->get_handles) {
322             printf("Handle %s of type %s is %s bytes long (minus strings).\n".
323             " > Contians the following keyword data entries:\n",
324             $handle->handle,
325             $handle->dmitype,
326             $handle->bytes
327             );
328            
329             for my $keyword ($handle->keywords) {
330             my $value = $handle->keyword($keyword);
331             printf("Keyword \"%s\" => \"%s\"\n",
332             $keyword,
333             (ref($value) eq 'ARRAY' ?
334             join(', ',@{$value}) : ($value||''))
335             );
336             }
337             }
338              
339             =head1 DESCRIPTION
340              
341             =head1 METHODS
342              
343             =head2 new
344              
345             Create a new struture handle object. This is called by L's
346             I (and indirectly by I) methods.
347              
348             =head2 raw
349              
350             my $raw_data = $handle->raw;
351              
352             Returns the raw data as generated by I that was
353             parsed to create this handle object.
354              
355             =head2 bytes
356              
357             my $bytes = $handle->bytes;
358              
359             =head2 address
360              
361             my $address = $handle->address;
362              
363             Returns the address handle of the structure.
364              
365             =head2 handle
366              
367             Alias for address.
368              
369             =head2 dmitype
370              
371             my $dmitype = $handle->dmitype;
372              
373             =head2 type
374              
375             Alias for dmitype.
376              
377             =head2 description
378              
379             my $description = $handle->description;
380              
381             =head2 keywords
382              
383             my @keywords = $handle->keywords;
384              
385             Returns a list of keyword data pairs available for retreival from
386             this handle object.
387              
388             =head2 keyword
389              
390             for my $keyword ($handle->keywords) {
391             printf("Keyword \"%s\" => \"%s\"\n",
392             $keyword,
393             $handle->keyword($keyword)
394             );
395             }
396              
397             =head2 parsed_structures
398              
399             use Data::Dumper;
400             my $ref = $handle->parsed_structures;
401             print Dumper($ref);
402              
403             Returns a copy of the parsed structures. This should be used with care
404             as this is a cloned copy of the parsed data structures that the
405             I object uses internally, and as such may
406             change format in later releases without notice.
407              
408             =head1 SEE ALSO
409              
410             L
411              
412             =head1 VERSION
413              
414             $Id: Handle.pm 976 2007-03-04 20:47:36Z nicolaw $
415              
416             =head1 AUTHOR
417              
418             Nicola Worthington
419              
420             L
421              
422             If you like this software, why not show your appreciation by sending the
423             author something nice from her
424             L?
425             ( http://www.amazon.co.uk/gp/registry/1VZXC59ESWYK0?sort=priority )
426              
427             =head1 COPYRIGHT
428              
429             Copyright 2006 Nicola Worthington.
430              
431             This software is licensed under The Apache Software License, Version 2.0.
432              
433             L
434              
435             =cut
436              
437              
438             __END__