| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Parse::Win32Registry::Win95::Value; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 13 |  |  | 13 |  | 71 | use strict; | 
|  | 13 |  |  |  |  | 23 |  | 
|  | 13 |  |  |  |  | 1947 |  | 
| 4 | 13 |  |  | 13 |  | 66 | use warnings; | 
|  | 13 |  |  |  |  | 1538 |  | 
|  | 13 |  |  |  |  | 482 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 13 |  |  | 13 |  | 70 | use base qw(Parse::Win32Registry::Value); | 
|  | 13 |  |  |  |  | 21 |  | 
|  | 13 |  |  |  |  | 7863 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 13 |  |  | 13 |  | 84 | use Carp; | 
|  | 13 |  |  |  |  | 22 |  | 
|  | 13 |  |  |  |  | 702 |  | 
| 9 | 13 |  |  | 13 |  | 69 | use Encode; | 
|  | 13 |  |  |  |  | 24 |  | 
|  | 13 |  |  |  |  | 1150 |  | 
| 10 | 13 |  |  | 13 |  | 70 | use Parse::Win32Registry::Base qw(:all); | 
|  | 13 |  |  |  |  | 20 |  | 
|  | 13 |  |  |  |  | 4330 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 13 |  |  | 13 |  | 81 | use constant RGDB_VALUE_HEADER_LENGTH => 0xc; | 
|  | 13 |  |  |  |  | 23 |  | 
|  | 13 |  |  |  |  | 14221 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | sub new { | 
| 15 | 3190 |  |  | 3190 | 0 | 4085 | my $class = shift; | 
| 16 | 3190 |  |  |  |  | 3183 | my $regfile = shift; | 
| 17 | 3190 |  |  |  |  | 3032 | my $offset = shift; # offset to RGDB value entry | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 3190 | 50 |  |  |  | 5790 | croak 'Missing registry file' if !defined $regfile; | 
| 20 | 3190 | 50 |  |  |  | 4863 | croak 'Missing offset' if !defined $offset; | 
| 21 |  |  |  |  |  |  |  | 
| 22 | 3190 |  |  |  |  | 7610 | my $fh = $regfile->get_filehandle; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | # RGDB Value Entry | 
| 25 |  |  |  |  |  |  | # 0x00 dword = value type | 
| 26 |  |  |  |  |  |  | # 0x04 | 
| 27 |  |  |  |  |  |  | # 0x08 word  = value name length | 
| 28 |  |  |  |  |  |  | # 0x0a word  = value data length | 
| 29 |  |  |  |  |  |  | # 0x0c       = value name [for name length bytes] | 
| 30 |  |  |  |  |  |  | #            + value data [for data length bytes] | 
| 31 |  |  |  |  |  |  | # Value type may just be a word, not a dword; | 
| 32 |  |  |  |  |  |  | # following word always appears to be zero. | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 3190 |  |  |  |  | 11496 | sysseek($fh, $offset, 0); | 
| 35 | 3190 |  |  |  |  | 15822 | my $bytes_read = sysread($fh, my $rgdb_value_entry, | 
| 36 |  |  |  |  |  |  | RGDB_VALUE_HEADER_LENGTH); | 
| 37 | 3190 | 100 |  |  |  | 6402 | if ($bytes_read != RGDB_VALUE_HEADER_LENGTH) { | 
| 38 | 1 |  |  |  |  | 4 | warnf('Could not read RGDB value at 0x%x', $offset); | 
| 39 | 1 |  |  |  |  | 12 | return; | 
| 40 |  |  |  |  |  |  | } | 
| 41 |  |  |  |  |  |  |  | 
| 42 | 3189 |  |  |  |  | 7184 | my ($type, | 
| 43 |  |  |  |  |  |  | $name_length, | 
| 44 |  |  |  |  |  |  | $data_length) = unpack('Vx4vv', $rgdb_value_entry); | 
| 45 |  |  |  |  |  |  |  | 
| 46 | 3189 |  |  |  |  | 15083 | $bytes_read = sysread($fh, my $name, $name_length); | 
| 47 | 3189 | 100 |  |  |  | 6443 | if ($bytes_read != $name_length) { | 
| 48 | 1 |  |  |  |  | 4 | warnf('Could not read name for RGDB value at 0x%x', $offset); | 
| 49 | 1 |  |  |  |  | 14 | return; | 
| 50 |  |  |  |  |  |  | } | 
| 51 | 3188 |  |  |  |  | 8090 | $name = decode($Parse::Win32Registry::Base::CODEPAGE, $name); | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 3188 |  |  |  |  | 70402 | $bytes_read = sysread($fh, my $data, $data_length); | 
| 54 | 3188 | 100 |  |  |  | 6587 | if ($bytes_read != $data_length) { | 
| 55 | 1 |  |  |  |  | 4 | warnf('Could not read data for RGDB value at 0x%x', $offset); | 
| 56 | 1 |  |  |  |  | 11 | return; | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 | 3187 |  |  |  |  | 4174 | my $self = {}; | 
| 60 | 3187 |  |  |  |  | 5807 | $self->{_regfile} = $regfile; | 
| 61 | 3187 |  |  |  |  | 4481 | $self->{_offset} = $offset; | 
| 62 | 3187 |  |  |  |  | 4716 | $self->{_length} = RGDB_VALUE_HEADER_LENGTH + $name_length + $data_length; | 
| 63 | 3187 |  |  |  |  | 4310 | $self->{_allocated} = 1; | 
| 64 | 3187 |  |  |  |  | 4308 | $self->{_tag} = 'rgdb value'; | 
| 65 | 3187 |  |  |  |  | 5404 | $self->{_name} = $name; | 
| 66 | 3187 |  |  |  |  | 4022 | $self->{_name_length} = $name_length; | 
| 67 | 3187 |  |  |  |  | 6010 | $self->{_type} = $type; | 
| 68 | 3187 |  |  |  |  | 5048 | $self->{_data} = $data; | 
| 69 | 3187 |  |  |  |  | 4315 | $self->{_data_length} = $data_length; | 
| 70 | 3187 |  |  |  |  | 7273 | bless $self, $class; | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 3187 |  |  |  |  | 12067 | return $self; | 
| 73 |  |  |  |  |  |  | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub get_data { | 
| 76 | 222 |  |  | 222 | 0 | 313 | my $self = shift; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 222 |  |  |  |  | 689 | my $type = $self->get_type; | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 222 |  |  |  |  | 389 | my $data = $self->{_data}; | 
| 81 | 222 | 50 |  |  |  | 540 | return if !defined $data; # actually, Win95 value data is always defined | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | # apply decoding to appropriate data types | 
| 84 | 222 | 100 | 66 |  |  | 1097 | if ($type == REG_DWORD) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 85 | 80 | 100 |  |  |  | 174 | if (length($data) == 4) { | 
| 86 | 65 |  |  |  |  | 168 | $data = unpack('V', $data); | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | else { | 
| 89 |  |  |  |  |  |  | # incorrect length for dword data | 
| 90 | 15 |  |  |  |  | 23 | $data = undef; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  | elsif ($type == REG_DWORD_BIG_ENDIAN) { | 
| 94 | 32 | 100 |  |  |  | 71 | if (length($data) == 4) { | 
| 95 | 20 |  |  |  |  | 46 | $data = unpack('N', $data); | 
| 96 |  |  |  |  |  |  | } | 
| 97 |  |  |  |  |  |  | else { | 
| 98 |  |  |  |  |  |  | # incorrect length for dword data | 
| 99 | 12 |  |  |  |  | 18 | $data = undef; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  | } | 
| 102 |  |  |  |  |  |  | elsif ($type == REG_SZ || $type == REG_EXPAND_SZ) { | 
| 103 |  |  |  |  |  |  | # Snip off any terminating null. | 
| 104 |  |  |  |  |  |  | # Typically, REG_SZ values will not have a terminating null, | 
| 105 |  |  |  |  |  |  | # while REG_EXPAND_SZ values will have a terminating null | 
| 106 | 20 | 100 |  |  |  | 84 | chop $data if substr($data, -1, 1) eq "\0"; | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  | elsif ($type == REG_MULTI_SZ) { | 
| 109 |  |  |  |  |  |  | # Snip off any terminating nulls | 
| 110 | 78 | 100 |  |  |  | 264 | chop $data if substr($data, -1, 1) eq "\0"; | 
| 111 | 78 | 100 |  |  |  | 211 | chop $data if substr($data, -1, 1) eq "\0"; | 
| 112 | 78 |  |  |  |  | 262 | my @multi_sz = split("\0", $data, -1); | 
| 113 |  |  |  |  |  |  | # Make sure there is at least one empty string | 
| 114 | 78 | 100 |  |  |  | 212 | @multi_sz = ('') if @multi_sz == 0; | 
| 115 | 78 | 100 |  |  |  | 542 | return wantarray ? @multi_sz : join($", @multi_sz); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 144 |  |  |  |  | 602 | return $data; | 
| 119 |  |  |  |  |  |  | } | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | sub as_regedit_export { | 
| 122 | 39 |  |  | 39 | 0 | 98 | my $self = shift; | 
| 123 | 39 |  | 50 |  |  | 213 | my $version = shift || 5; | 
| 124 |  |  |  |  |  |  |  | 
| 125 | 39 |  |  |  |  | 226 | my $name = $self->get_name; | 
| 126 | 39 | 100 |  |  |  | 185 | my $export = $name eq '' ? '@=' : '"' . $name . '"='; | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 39 |  |  |  |  | 107 | my $type = $self->get_type; | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | # XXX | 
| 131 |  |  |  |  |  |  | #    if (!defined $self->{_data}) { | 
| 132 |  |  |  |  |  |  | #        $name = $name eq '' ? '@' : qq{"$name"}; | 
| 133 |  |  |  |  |  |  | #        return qq{; $name=(invalid data)\n}; | 
| 134 |  |  |  |  |  |  | #    } | 
| 135 |  |  |  |  |  |  |  | 
| 136 | 39 | 100 | 66 |  |  | 308 | if ($type == REG_SZ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 137 | 4 |  |  |  |  | 12 | $export .= '"' . $self->get_data . '"'; | 
| 138 | 4 |  |  |  |  | 9 | $export .= "\n"; | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  | elsif ($type == REG_BINARY) { | 
| 141 | 2 |  |  |  |  | 7 | $export .= 'hex:'; | 
| 142 | 2 |  |  |  |  | 17 | $export .= format_octets($self->{_data}, length($export)); | 
| 143 |  |  |  |  |  |  | } | 
| 144 |  |  |  |  |  |  | elsif ($type == REG_DWORD) { | 
| 145 | 12 |  |  |  |  | 32 | my $data = $self->get_data; | 
| 146 | 12 | 100 |  |  |  | 58 | $export .= defined($data) | 
| 147 |  |  |  |  |  |  | ? sprintf("dword:%08x", $data) | 
| 148 |  |  |  |  |  |  | : "dword:"; | 
| 149 | 12 |  |  |  |  | 29 | $export .= "\n"; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  | elsif ($type == REG_EXPAND_SZ || $type == REG_MULTI_SZ) { | 
| 152 | 12 | 50 |  |  |  | 128 | my $data = $version == 4 | 
| 153 |  |  |  |  |  |  | ? $self->{_data} # raw data | 
| 154 |  |  |  |  |  |  | : encode("UCS-2LE", $self->{_data}); # ansi->unicode | 
| 155 | 12 |  |  |  |  | 5074 | $export .= sprintf("hex(%x):", $type); | 
| 156 | 12 |  |  |  |  | 95 | $export .= format_octets($data, length($export)); | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | else { | 
| 159 | 9 |  |  |  |  | 38 | $export .= sprintf("hex(%x):", $type); | 
| 160 | 9 |  |  |  |  | 64 | $export .= format_octets($self->{_data}, length($export)); | 
| 161 |  |  |  |  |  |  | } | 
| 162 | 39 |  |  |  |  | 204 | return $export; | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | sub parse_info { | 
| 166 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 167 |  |  |  |  |  |  |  | 
| 168 | 0 |  |  |  |  |  | my $info = sprintf '0x%x rgdb value len=0x%x "%s" type=%d data,len=0x%x', | 
| 169 |  |  |  |  |  |  | $self->{_offset}, | 
| 170 |  |  |  |  |  |  | $self->{_length}, | 
| 171 |  |  |  |  |  |  | $self->{_name}, | 
| 172 |  |  |  |  |  |  | $self->{_type}, | 
| 173 |  |  |  |  |  |  | $self->{_data_length}; | 
| 174 | 0 |  |  |  |  |  | return $info; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | 1; |