File Coverage

blib/lib/WAP/SAXDriver/wbxml.pm
Criterion Covered Total %
statement 96 566 16.9
branch 0 308 0.0
condition 0 87 0.0
subroutine 32 58 55.1
pod 3 19 15.7
total 131 1038 12.6


line stmt bran cond sub pod time code
1             #
2             # WAP::SAXDriver::wbxml
3             #
4            
5             package WAP::SAXDriver::wbxml;
6            
7 1     1   658 use strict;
  1         2  
  1         26  
8 1     1   4 use warnings;
  1         1  
  1         23  
9            
10 1     1   4 use base qw(XML::SAX::Base);
  1         8  
  1         1260  
11 1     1   28022 use IO::File;
  1         11334  
  1         132  
12 1     1   957 use IO::String;
  1         2544  
  1         719  
13            
14             our $VERSION = '2.07';
15            
16             sub _parse_characterstream {
17 0     0     my $p = shift;
18 0           my $xml = shift;
19 0           my $opt = $p->{ParseOptions};
20            
21 0           $p->_init_parser($opt);
22 0 0         die __PACKAGE__,": Not an IO::Handle\n"
23             unless ($xml->isa('IO::Handle'));
24 0           $p->{io_handle} = $xml;
25 0           my $result = $p->_parse($opt);
26 0           $p->_cleanup;
27 0           return $result;
28             }
29            
30             sub _parse_bytestream {
31 0     0     my $p = shift;
32 0           my $xml = shift;
33 0           my $opt = $p->{ParseOptions};
34            
35 0           $p->_init_parser($opt);
36 0 0         die __PACKAGE__,": Not an IO::Handle\n"
37             unless ($xml->isa('IO::Handle'));
38 0           $p->{io_handle} = $xml;
39 0           my $result = $p->_parse($opt);
40 0           $p->_cleanup;
41 0           return $result;
42             }
43            
44             sub _parse_string {
45 0     0     my $p = shift;
46 0           my $xml = shift;
47 0           my $opt = $p->{ParseOptions};
48            
49 0           $p->_init_parser($opt);
50 0           $p->{io_handle} = new IO::String($xml);
51 0           my $result = $p->_parse($opt);
52 0           $p->_cleanup;
53 0           return $result;
54             }
55            
56             sub _parse_systemid {
57 0     0     my $p = shift;
58 0           my $xml = shift;
59 0           my $opt = $p->{ParseOptions};
60            
61 0           $p->_init_parser($opt);
62 0           $p->{io_handle} = new IO::File($xml, 'r');
63 0 0         die "Can't open $xml ($!)\n"
64             unless (defined $p->{io_handle});
65 0           binmode $p->{io_handle}, ':raw';
66 0           my $result = $p->_parse($opt);
67 0           $p->_cleanup;
68 0           return $result;
69             }
70            
71             our ($default_rules, $rules);
72            
73             sub _init_parser {
74 0     0     my $self = shift;
75 0           my $opt = shift;
76            
77 0 0         die __PACKAGE__,": parser instance ($self) already parsing\n"
78             if defined $self->{_InParse};
79            
80 0           $self->{_InParse} = 1;
81            
82 0 0         if ($opt->{UseOnlyDefaultRules}) {
83 0           $self->{Rules} = undef;
84             }
85             else {
86 0 0         unless (defined $rules) {
87 0           my $infile;
88 0 0         if ($opt->{RulesPath}) {
89 0           $infile = $opt->{RulesPath};
90             }
91             else {
92 0           my $path = $INC{'WAP/SAXDriver/wbxml.pm'};
93 0           $path =~ s/\.pm$//i;
94 0           $infile = $path . '/wap.wbrules2.pl';
95             }
96 0           require $infile;
97             }
98 0           $self->{Rules} = $rules;
99             }
100             }
101            
102            
103             sub _cleanup {
104 0     0     my $self = shift;
105            
106 0           $self->{_InParse} = 0;
107 0           delete $self->{PublicId};
108 0           delete $self->{Encoding};
109 0           delete $self->{App};
110 0           delete $self->{publicid_idx};
111 0           delete $self->{root_name};
112 0 0         delete $self->{io_strtbl} if (exists $self->{io_strtbl});
113 0 0         delete $self->{strtbl} if (exists $self->{strtbl});
114 0           delete $self->{io_handle};
115             }
116            
117             sub location {
118 0     0 1   my $self = shift;
119            
120 0           my $pos = $self->{io_handle}->tell();
121            
122 0           my @properties = (
123             ColumnNumber => $pos,
124             LineNumber => 1,
125             BytePosition => $pos,
126             );
127            
128 0 0         push (@properties, PublicId => $self->{PublicId})
129             if (defined $self->{PublicId});
130            
131 0           return { @properties };
132             }
133            
134             ################################# W B X M L ##################################
135            
136 1     1   861 use integer;
  1         8  
  1         5  
137            
138             # Global tokens
139 1     1   29 use constant SWITCH_PAGE => 0x00;
  1         2  
  1         83  
140 1     1   5 use constant _END => 0x01;
  1         2  
  1         46  
141 1     1   5 use constant ENTITY => 0x02;
  1         2  
  1         41  
142 1     1   6 use constant STR_I => 0x03;
  1         2  
  1         37  
143 1     1   6 use constant LITERAL => 0x04;
  1         2  
  1         40  
144 1     1   5 use constant EXT_I_0 => 0x40;
  1         2  
  1         47  
145 1     1   5 use constant EXT_I_1 => 0x41;
  1         2  
  1         38  
146 1     1   16 use constant EXT_I_2 => 0x42;
  1         2  
  1         44  
147 1     1   17 use constant PI => 0x43;
  1         2  
  1         49  
148 1     1   5 use constant LITERAL_C => 0x44;
  1         2  
  1         61  
149 1     1   4 use constant EXT_T_0 => 0x80;
  1         2  
  1         146  
150 1     1   4 use constant EXT_T_1 => 0x81;
  1         2  
  1         37  
151 1     1   5 use constant EXT_T_2 => 0x82;
  1         1  
  1         29  
152 1     1   4 use constant STR_T => 0x83;
  1         1  
  1         34  
153 1     1   4 use constant LITERAL_A => 0x84;
  1         2  
  1         33  
154 1     1   4 use constant EXT_0 => 0xC0;
  1         2  
  1         34  
155 1     1   5 use constant EXT_1 => 0xC1;
  1         2  
  1         61  
156 1     1   5 use constant EXT_2 => 0xC2;
  1         1  
  1         33  
157 1     1   5 use constant OPAQUE => 0xC3;
  1         1  
  1         46  
158 1     1   4 use constant LITERAL_AC => 0xC4;
  1         1  
  1         38  
159             # Global token masks
160 1     1   4 use constant NULL => 0x00;
  1         1  
  1         39  
161 1     1   5 use constant HAS_CHILD => 0x40;
  1         2  
  1         51  
162 1     1   4 use constant HAS_ATTR => 0x80;
  1         2  
  1         37  
163 1     1   5 use constant TAG_MASK => 0x3F;
  1         1  
  1         67  
164 1     1   6 use constant ATTR_MASK => 0x7F;
  1         2  
  1         3048  
165            
166             sub _parse {
167 0     0     my $self = shift;
168 0           my ($opt) = @_;
169            
170 0           $self->{PublicId} = undef;
171 0           $self->{Encoding} = undef;
172 0           $self->{App} = undef;
173            
174 0           my $version = $self->get_version();
175 0           $self->get_publicid();
176 0           $self->get_charset();
177 0 0 0       if ( !defined $self->{Encoding}
178             and exists $opt->{Source}{Encoding} ) {
179 0           $self->{Encoding} = $self->{Source}{Encoding};
180             }
181 0           $self->get_strtbl();
182 0 0         $self->{PublicId} = $self->get_str_t($self->{publicid_idx})
183             if (exists $self->{publicid_idx});
184 0 0         if ($self->{PublicId} eq 'PublicId-Unknown') {
185 0           my ($val) = values %{$self->{Rules}->{App}};
  0            
186 0           $self->{App} = $val;
187             }
188             else {
189 0           $self->{App} = $self->{Rules}->{App}{$self->{PublicId}};
190             }
191            
192 0           $self->SUPER::start_document( {
193             Version => '1.0',
194             Encoding => $self->{Encoding},
195             Standalone => undef,
196             VersionWBXML => $version,
197             } );
198 0           $self->SUPER::xml_decl( {
199             Version => '1.0',
200             Encoding => $self->{Encoding},
201             Standalone => undef,
202             VersionWBXML => $version,
203             } );
204            
205 0           my $rc = $self->body();
206 0           my $end = $self->SUPER::end_document( { } );
207            
208 0 0         unless (defined $rc) {
209 0           my $pos = $self->{io_handle}->tell();
210 0           $self->SUPER::fatal_error( {
211             Message => q{},
212             PublicId => $self->{PublicId},
213             ColumnNumber => $pos,
214             LineNumber => 1,
215             BytePosition => $pos
216             } );
217 0           warn __PACKAGE__,": Fatal error at position $pos\n";
218             }
219            
220 0           return $end;
221             }
222            
223             sub getmb32 {
224 0     0 0   my $self = shift;
225 0           my $byte;
226 0           my $val = 0;
227 0           my $nb = 0;
228 0           do {
229 0           $nb ++;
230 0 0         return undef unless ($nb < 6);
231 0           my $ch = $self->{io_handle}->getc();
232 0 0         return undef unless (defined $ch);
233 0           $byte = ord $ch;
234 0           $val <<= 7;
235 0           $val += ($byte & 0x7f);
236             }
237             while (0 != ($byte & 0x80));
238 0           return $val
239             }
240            
241             sub get_version {
242 0     0 0   my $self = shift;
243 0           my $ch = $self->{io_handle}->getc();
244 0 0         return undef unless (defined $ch);
245 0           my $v = ord $ch;
246 0           return (1 + $v / 16) . '.' . ($v % 16);
247             }
248            
249             sub get_publicid {
250 0     0 0   my $self = shift;
251 0           my $publicid = $self->getmb32();
252 0 0         return undef unless (defined $publicid);
253 0 0         if ($publicid == 1) {
    0          
254 0           $self->{PublicId} = "PublicId-Unknown";
255             }
256             elsif ($publicid) {
257 0 0         if (exists $self->{Rules}->{PublicIdentifier}{$publicid}) {
258 0           $self->{PublicId} = $self->{Rules}->{PublicIdentifier}{$publicid};
259             }
260             else {
261 0           $self->warning("PublicId-$publicid unreferenced");
262 0           $self->{PublicId} = "PublicId-$publicid";
263             }
264             }
265             else {
266 0           $self->{publicid_idx} = $self->getmb32();
267             }
268             }
269            
270             sub get_charset {
271 0     0 0   my $self = shift;
272 0           my $charset = $self->getmb32();
273 0 0         return unless (defined $charset);
274 0 0         if ($charset != 0) {
275 0           my $default_charset = {
276             # here, only built-in encodings of Expat.
277             # MIBenum => iana name
278             3 => 'ANSI_X3.4-1968', # US-ASCII
279             4 => 'ISO_8859-1:1987',
280             106 => 'UTF-8',
281             };
282 0 0         if (exists $default_charset->{$charset}) {
283 0           $self->{Encoding} = $default_charset->{$charset};
284 0           return;
285             }
286 0           eval "use I18N::Charset";
287 0 0         unless ($@) {
288 0 0         if (defined I18N::Charset::mib_to_charset_name($charset)) {
289 0           $self->{Encoding} = I18N::Charset::mib_to_charset_name($charset);
290 0           return;
291             }
292             }
293 0           $self->{Encoding} = "MIBenum-$charset";
294 0           $self->warning("$self->{Encoding} unreferenced");
295             }
296             }
297            
298             sub get_strtbl {
299 0     0 0   my $self = shift;
300 0           my $len = $self->getmb32();
301 0 0         if ($len) {
302 0           my $str = q{};
303 0           $self->{io_handle}->read($str,$len);
304 0           $self->{strtbl} = $str . chr 0;
305 0           $self->{io_strtbl} = new IO::String($self->{strtbl});
306             }
307             }
308            
309             sub get_str_t {
310 0     0 0   my $self = shift;
311 0           my ($idx) = @_;
312 0 0         return undef unless (defined $idx);
313 0 0         return undef unless (exists $self->{io_strtbl});
314 0           $self->{io_strtbl}->setpos($idx);
315 0           my $str = q{};
316 0           my $ch = $self->{io_strtbl}->getc();
317 0 0         return undef unless (defined $ch);
318 0           while (ord $ch != 0) {
319 0           $str .= $ch;
320 0           $ch = $self->{io_strtbl}->getc();
321 0 0         return undef unless (defined $ch);
322             }
323 0           return $str;
324             }
325            
326             sub body {
327 0     0 0   my $self = shift;
328 0           my $rc;
329 0           $self->{codepage_tag} = 0;
330 0           $self->{codepage_attr} = 0;
331 0           my $tag = $self->get_tag();
332 0           while ($tag == PI) {
333 0           $rc = $self->pi();
334 0 0         return undef unless (defined $rc);
335 0           $tag = $self->get_tag();
336             }
337 0           $rc = $self->element($tag);
338 0 0         return undef unless (defined $rc);
339 0           $tag = $self->get_tag();
340 0 0         if (defined $tag) {
341 0           while ($tag == PI) {
342 0           $rc = $self->pi();
343 0 0         return undef unless (defined $rc);
344 0           $tag = $self->get_tag();
345             }
346             }
347 0           return 1;
348             }
349            
350             sub pi {
351 0     0 0   my $self = shift;
352 0           my $attr = $self->get_attr();
353 0           my $rc = $self->attribute($attr);
354 0 0         return undef unless (defined $rc);
355 0           my $target = $self->{attrs};
356 0           $attr = $self->get_attr();
357 0           my $data = q{};
358 0           while ($attr != _END) {
359 0           $rc = $self->attribute($attr);
360 0 0         return undef unless (defined $rc);
361 0           $data .= $self->{attrv};
362 0           $attr = $self->get_attr();
363             }
364 0           delete $self->{attrs};
365 0           delete $self->{attrv};
366 0           $self->SUPER::processing_instruction( {
367             Target => $target,
368             Data => $data
369             } );
370 0           return 1;
371             }
372            
373             sub element {
374 0     0 0   my $self = shift;
375 0           my ($tag) = @_;
376            
377 0 0         return undef unless (defined $tag);
378 0           my $token = $tag & TAG_MASK;
379 0           my $name;
380 0 0         if ($token == LITERAL) {
381 0           my $idx = $self->getmb32();
382 0           $name = $self->get_str_t($idx);
383 0 0         return undef unless (defined $name);
384             }
385             else {
386 0           $token += 256 * $self->{codepage_tag};
387 0 0 0       if ( defined $self->{App}
388             and exists $self->{App}{TAG}{$token}) {
389 0           $name = $self->{App}{TAG}{$token};
390             }
391             else {
392 0           $name = "TAG-$token";
393 0           $self->warning("$name unreferenced");
394             }
395             }
396 0 0         unless (exists $self->{root_name}) {
397 0 0         if ($self->{PublicId} ne 'PublicId-Unknown') {
398 0   0       my $system_id = $self->{App}->{systemid} || $name . '.dtd';
399 0           $self->SUPER::start_dtd( {
400             Name => $name,
401             PublicId => $self->{PublicId},
402             SystemId => $system_id
403             } );
404 0           $self->SUPER::end_dtd( { } );
405             }
406 0           $self->{root_name} = $name;
407             }
408 0           my %saxattr;
409 0 0         if ($tag & HAS_ATTR) {
410 0           my $attr = $self->get_attr();
411 0           while ($attr != _END) {
412 0           my $rc = $self->attribute($attr);
413 0 0         return undef unless (defined $rc);
414 0 0         if (exists $self->{attrs}) {
415 0           my $lname = $self->{attrs};
416 0           my $at = {
417             Name => $lname,
418             Value => $self->{attrv}
419             };
420 0           $saxattr{"{}$lname"} = $at;
421             }
422 0           $attr = $self->get_attr();
423             }
424 0           delete $self->{attrs};
425 0           delete $self->{attrv};
426             }
427             $self->SUPER::start_element( {
428 0           Name => $name,
429             Attributes => \%saxattr
430             } );
431 0 0         if ($tag & HAS_CHILD) {
432 0           while ((my $child = $self->get_tag()) != _END) {
433 0           my $rc = $self->content($child,$token);
434 0 0         return undef unless (defined $rc);
435             }
436             }
437             $self->SUPER::end_element( {
438 0           Name => $name
439             } );
440 0           return 1;
441             }
442            
443             sub content {
444 0     0 0   my $self = shift;
445 0           my ($tag,$parent) = @_;
446            
447 0 0         return undef unless (defined $tag);
448 0 0         if ($tag == ENTITY) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
449 0           my $entcode = $self->getmb32();
450 0 0         return undef unless (defined $entcode);
451 0           $self->SUPER::characters( {
452             Data => chr $entcode
453             } );
454             }
455             elsif ($tag == STR_I) {
456 0           my $string = $self->get_str_i();
457 0 0         return undef unless (defined $string);
458 0 0 0       if ( defined $self->{App}
459             and exists $self->{App}{variable_subs} ) {
460 0           $string =~ s/\$/\$\$/g;
461             }
462             $self->SUPER::characters( {
463 0           Data => $string
464             } );
465             }
466             elsif ($tag == EXT_I_0) {
467 0           my $string = $self->get_str_i();
468 0 0         return undef unless (defined $string);
469 0 0 0       if ( defined $self->{App}
470             and exists $self->{App}{variable_subs} ) {
471 0           $self->SUPER::characters( {
472             Data => "\$($string:escape)"
473             } );
474             }
475             else {
476 0           $self->error("EXT_I_0 unexpected");
477             }
478             }
479             elsif ($tag == EXT_I_1) {
480 0           my $string = $self->get_str_i();
481 0 0         return undef unless (defined $string);
482 0 0 0       if ( defined $self->{App}
483             and exists $self->{App}{variable_subs} ) {
484 0           $self->SUPER::characters( {
485             Data => "\$($string:unesc)"
486             } );
487             }
488             else {
489 0           $self->error("EXT_I_1 unexpected");
490             }
491             }
492             elsif ($tag == EXT_I_2) {
493 0           my $string = $self->get_str_i();
494 0 0         return undef unless (defined $string);
495 0 0 0       if ( defined $self->{App}
496             and exists $self->{App}{variable_subs} ) {
497 0           $self->SUPER::characters( {
498             Data => "\$($string)"
499             } );
500             }
501             else {
502 0           $self->error("EXT_I_2 unexpected");
503             }
504             }
505             elsif ($tag == PI) {
506 0           my $rc = $self->pi();
507 0 0         return undef unless (defined $rc);
508             }
509             elsif ($tag == EXT_T_0) {
510 0           my $idx = $self->getmb32();
511 0 0 0       if ( defined $self->{App}
    0 0        
512             and exists $self->{App}{variable_subs} ) {
513 0           my $string = $self->get_str_t($idx);
514 0 0         return undef unless (defined $string);
515 0           $self->SUPER::characters( {
516             Data => "\$($string:escape)"
517             } );
518             }
519             elsif ( defined $self->{App}
520             and exists $self->{App}{EXT0VALUE}) {
521 0 0         if (exists $self->{App}{EXT0VALUE}{$idx}) {
522 0           $self->SUPER::characters( {
523             Data => $self->{App}{EXT0VALUE}{$idx}
524             } );
525             }
526             else {
527 0           $self->error("EXT_T_0 $idx unknown");
528             }
529             }
530             else {
531 0           $self->error("EXT_T_0 unexpected");
532             }
533             }
534             elsif ($tag == EXT_T_1) {
535 0           my $idx = $self->getmb32();
536 0 0 0       if ( defined $self->{App}
    0 0        
537             and exists $self->{App}{variable_subs} ) {
538 0           my $string = $self->get_str_t($idx);
539 0 0         return undef unless (defined $string);
540 0           $self->SUPER::characters( {
541             Data => "\$($string:unesc)"
542             } );
543             }
544             elsif ( defined $self->{App}
545             and exists $self->{App}{EXT1VALUE}) {
546 0 0         if (exists $self->{App}{EXT1VALUE}{$idx}) {
547 0           $self->SUPER::characters( {
548             Data => $self->{App}{EXT1VALUE}{$idx}
549             } );
550             }
551             else {
552 0           $self->error("EXT_T_1 $idx unknown");
553             }
554             }
555             else {
556 0           $self->error("EXT_T_1 unexpected");
557             }
558             }
559             elsif ($tag == EXT_T_2) {
560 0           my $idx = $self->getmb32();
561 0 0 0       if ( defined $self->{App}
    0 0        
562             and exists $self->{App}{variable_subs} ) {
563 0           my $string = $self->get_str_t($idx);
564 0 0         return undef unless (defined $string);
565 0           $self->SUPER::characters( {
566             Data => "\$($string)"
567             } );
568             }
569             elsif ( defined $self->{App}
570             and exists $self->{App}{EXT2VALUE}) {
571 0 0         if (exists $self->{App}{EXT2VALUE}{$idx}) {
572 0           $self->SUPER::characters( {
573             Data => $self->{App}{EXT2VALUE}{$idx}
574             } );
575             }
576             else {
577 0           $self->error("EXT_T_2 $idx unknown");
578             }
579             }
580             else {
581 0           $self->error("EXT_T_2 unexpected");
582             }
583             }
584             elsif ($tag == STR_T) {
585 0           my $idx = $self->getmb32();
586 0           my $string = $self->get_str_t($idx);
587 0 0         return undef unless (defined $string);
588 0 0 0       if ( defined $self->{App}
589             and exists $self->{App}{variable_subs} ) {
590 0           $string =~ s/\$/\$\$/g;
591             }
592             $self->SUPER::characters( {
593 0           Data => $string
594             } );
595             }
596             elsif ($tag == EXT_0) {
597 0           $self->error("EXT_0 unexpected");
598             }
599             elsif ($tag == EXT_1) {
600 0           $self->error("EXT_1 unexpected");
601             }
602             elsif ($tag == EXT_2) {
603 0           $self->error("EXT_2 unexpected");
604             }
605             elsif ($tag == OPAQUE) {
606 0           my $data = $self->get_opaque();
607 0 0         return undef unless (defined $data);
608 0 0 0       my $encoding = (defined $self->{App} and exists $self->{App}{TagEncoding}{$parent})
609             ? $self->{App}{TagEncoding}{$parent} : q{};
610 0 0         if ($encoding eq 'base64') {
    0          
    0          
611 1     1   1015 use MIME::Base64;
  1         848  
  1         2512  
612 0           my $encoded = encode_base64($data);
613 0           $self->SUPER::characters( {
614             Data => $encoded
615             } );
616             }
617             elsif ($encoding eq 'datetime') {
618 0           my $len = length $data;
619 0           my $value = q{};
620 0 0         if ($len == 6) {
621 0           my @byte = unpack 'C*', $data;
622 0           my $year = ($byte[0] << 6) | ($byte[1] >> 2);
623 0           my $month = (($byte[1] & 0x3) << 2) | ($byte[2] >> 6);
624 0           my $day = (($byte[2] >> 1) & 0x1F);
625 0           my $hour = (($byte[2] & 0x1) << 4) | ($byte[3] >> 4);
626 0           my $min = (($byte[3] & 0xF) << 2) | ($byte[4] >> 6);
627 0           my $sec = ($byte[4] & 0x3F);
628 0           my $tz = $byte[5];
629 0           $value = sprintf('%04d%02d%02dT%02d%02d%02d%c',$year,$month,$day,$hour,$min,$sec,$tz);
630             }
631             else {
632 0           $self->error("OPAQUE : invalid 'datetime'");
633             }
634 0           $self->SUPER::characters( {
635             Data => $value
636             } );
637             }
638             elsif ($encoding eq 'integer') {
639 0           my $len = length $data;
640 0           my $value = 0;
641 0 0         if ($len == 1) {
    0          
    0          
642 0           $value = unpack 'C', $data;
643             }
644             elsif ($len == 2) {
645 0           $value = unpack 'n', $data;
646             }
647             elsif ($len == 4) {
648 0           $value = unpack 'N', $data;
649             }
650             else {
651 0           $self->error("OPAQUE : invalid 'integer'");
652             }
653 0           $self->SUPER::characters( {
654             Data => "$value"
655             } );
656             }
657             else {
658 0           $self->SUPER::characters( {
659             Data => $data
660             } );
661             }
662             }
663             else {
664 0           my $rc = $self->element($tag); # LITERAL and all TAG
665 0 0         return undef unless (defined $rc);
666             }
667 0           return 1;
668             }
669            
670             sub attribute {
671 0     0 0   my $self = shift;
672 0           my ($attr) = @_;
673            
674 0 0         return undef unless (defined $attr);
675 0 0         if ($attr == ENTITY) { # ATTRV
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
676 0           my $entcode = $self->getmb32();
677 0 0         return undef unless (defined $entcode);
678 0           $self->{attrv} .= chr $entcode;
679             }
680             elsif ($attr == STR_I) { # ATTRV
681 0           my $string = $self->get_str_i();
682 0 0         return undef unless (defined $string);
683 0 0 0       if ( exists $self->{ATTRSTART}{validate}
684             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
685 0           $string =~ s/\$/\$\$/g;
686             }
687 0           $self->{attrv} .= $string;
688             }
689             elsif ($attr == LITERAL) { # ATTRS
690 0           my $idx = $self->getmb32();
691 0           my $string = $self->get_str_t($idx);
692 0 0         return undef unless (defined $string);
693 0           $self->{attrs} = $string;
694 0           $self->{attrv} = q{};
695 0           $self->{ATTRSTART} = undef;
696             }
697             elsif ($attr == EXT_I_0) { # ATTRV
698 0           my $string = $self->get_str_i();
699 0 0         return undef unless (defined $string);
700 0 0 0       if ( defined $self->{ATTRSTART}
701             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
702 0           $self->{attrv} .= "\$($string:escape)";
703             }
704             else {
705 0           $self->error("EXT_I_0 unexpected");
706             }
707             }
708             elsif ($attr == EXT_I_1) { # ATTRV
709 0           my $string = $self->get_str_i();
710 0 0         return undef unless (defined $string);
711 0 0 0       if ( defined $self->{ATTRSTART}
712             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
713 0           $self->{attrv} .= "\$($string:unesc)";
714             }
715             else {
716 0           $self->error("EXT_I_1 unexpected");
717             }
718             }
719             elsif ($attr == EXT_I_2) { # ATTRV
720 0           my $string = $self->get_str_i();
721 0 0         return undef unless (defined $string);
722 0 0 0       if ( defined $self->{ATTRSTART}
723             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
724 0           $self->{attrv} .= "\$($string)";
725             }
726             else {
727 0           $self->error("EXT_I_2 unexpected");
728             }
729             }
730             elsif ($attr == EXT_T_0) { # ATTRV
731 0           my $idx = $self->getmb32();
732 0 0 0       if ( defined $self->{ATTRSTART}
    0 0        
733             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
734 0           my $string = $self->get_str_t($idx);
735 0 0         return undef unless (defined $string);
736 0           $self->{attrv} .= "\$($string:escape)";
737             }
738             elsif ( defined $self->{App}
739             and exists $self->{App}{EXT0VALUE}) {
740 0 0         if (exists $self->{App}{EXT0VALUE}{$idx}) {
741 0           $self->{attrv} .= $self->{App}{EXT0VALUE}{$idx}
742             }
743             else {
744 0           $self->error("EXT_T_0 $idx unknown");
745             }
746             }
747             else {
748 0           $self->error("EXT_T_0 unexpected");
749             }
750             }
751             elsif ($attr == EXT_T_1) { # ATTRV
752 0           my $idx = $self->getmb32();
753 0 0 0       if ( defined $self->{ATTRSTART}
    0 0        
754             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
755 0           my $string = $self->get_str_t($idx);
756 0 0         return undef unless (defined $string);
757 0           $self->{attrv} .= "\$($string:unesc)";
758             }
759             elsif ( defined $self->{App}
760             and exists $self->{App}{EXT1VALUE}) {
761 0 0         if (exists $self->{App}{EXT1VALUE}{$idx}) {
762 0           $self->{attrv} .= $self->{App}{EXT1VALUE}{$idx}
763             }
764             else {
765 0           $self->error("EXT_T_1 $idx unknown");
766             }
767             }
768             else {
769 0           $self->error("EXT_T_1 unexpected");
770             }
771             }
772             elsif ($attr == EXT_T_2) { # ATTRV
773 0           my $idx = $self->getmb32();
774 0 0 0       if ( defined $self->{ATTRSTART}
    0 0        
775             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
776 0           my $string = $self->get_str_t($idx);
777 0 0         return undef unless (defined $string);
778 0           $self->{attrv} .= "\$($string)";
779             }
780             elsif ( defined $self->{App}
781             and exists $self->{App}{EXT2VALUE}) {
782 0 0         if (exists $self->{App}{EXT2VALUE}{$idx}) {
783 0           $self->{attrv} .= $self->{App}{EXT2VALUE}{$idx}
784             }
785             else {
786 0           $self->error("EXT_T_2 $idx unknown");
787             }
788             }
789             else {
790 0           $self->error("EXT_T_2 unexpected");
791             }
792             }
793             elsif ($attr == STR_T) { # ATTRV
794 0           my $idx = $self->getmb32();
795 0           my $string = $self->get_str_t($idx);
796 0 0         return undef unless (defined $string);
797 0 0 0       if ( exists $self->{ATTRSTART}{validate}
798             and $self->{ATTRSTART}{validate} eq 'vdata' ) {
799 0           $string =~ s/\$/\$\$/g;
800             }
801 0           $self->{attrv} .= $string;
802             }
803             elsif ($attr == EXT_0) { # ATTRV
804 0           $self->error("EXT_0 unexpected");
805             }
806             elsif ($attr == EXT_1) { # ATTRV
807 0           $self->error("EXT_1 unexpected");
808             }
809             elsif ($attr == EXT_2) { # ATTRV
810 0           $self->error("EXT_2 unexpected");
811             }
812             elsif ($attr == OPAQUE) { # ATTRV
813 0           my $data = $self->get_opaque();
814 0 0         return undef unless (defined $data);
815 0 0 0       if ( exists $self->{ATTRSTART}{encoding}
816             and $self->{ATTRSTART}{encoding} eq 'iso-8601' ) {
817 0           foreach (split //, $data) {
818 0           $self->{attrv} .= sprintf('%02X', ord $_);
819             }
820             }
821             else {
822 0           $self->error("OPAQUE unexpected");
823             }
824             }
825             else {
826 0           my $token = $attr; # & ATTR_MASK;
827 0           $token += 256 * $self->{codepage_attr};
828 0 0         if ($attr & 0x80) {
829 0 0 0       if ( defined $self->{App}
830             and exists $self->{App}{ATTRVALUE}{$token}) {
831 0           $self->{attrv} .= $self->{App}{ATTRVALUE}{$token};
832             }
833             else {
834 0           $self->{attrv} .= "ATTRV-$token";
835 0           $self->warning("ATTRV-$token unreferenced");
836             }
837             }
838             else {
839 0           $self->{attrv} = q{};
840 0           $self->{ATTRSTART} = undef;
841 0 0 0       if ( defined $self->{App}
842             and exists $self->{App}{ATTRSTART}{$token} ) {
843 0           $self->{ATTRSTART} = $self->{App}{ATTRSTART}{$token};
844 0           $self->{attrs} = $self->{ATTRSTART}{name};
845 0 0         $self->{attrv} = $self->{ATTRSTART}{value}
846             if (exists $self->{ATTRSTART}{value});
847             }
848             else {
849 0           $self->{attrs} = "ATTRS-$token";
850 0           $self->warning("ATTRS-$token unreferenced");
851             }
852             }
853             }
854 0           return 1;
855             }
856            
857             sub get_tag {
858 0     0 0   my $self = shift;
859 0           my $ch = $self->{io_handle}->getc();
860 0 0         return undef unless (defined $ch);
861 0           my $tag = ord $ch;
862 0 0         if ($tag == SWITCH_PAGE) {
863 0           $ch = $self->{io_handle}->getc();
864 0 0         return undef unless (defined $ch);
865 0           $self->{codepage_tag} = ord $ch;
866 0           $ch = $self->{io_handle}->getc();
867 0 0         return undef unless (defined $ch);
868 0           $tag = ord $ch;
869             }
870 0           return $tag;
871             }
872            
873             sub get_attr {
874 0     0 0   my $self = shift;
875 0           my $ch = $self->{io_handle}->getc();
876 0 0         return undef unless (defined $ch);
877 0           my $attr = ord $ch;
878 0 0         if ($attr == SWITCH_PAGE) {
879 0           $ch = $self->{io_handle}->getc();
880 0 0         return undef unless (defined $ch);
881 0           $self->{codepage_attr} = ord $ch;
882 0           $ch = $self->{io_handle}->getc();
883 0 0         return undef unless (defined $ch);
884 0           $attr = ord $ch;
885             }
886 0           return $attr;
887             }
888            
889             sub get_str_i {
890 0     0 0   my $self = shift;
891 0           my $str = q{};
892 0           my $ch = $self->{io_handle}->getc();
893 0 0         return undef unless (defined $ch);
894 0           while (ord $ch != 0) {
895 0           $str .= $ch;
896 0           $ch = $self->{io_handle}->getc();
897 0 0         return undef unless (defined $ch);
898             }
899 0           return $str;
900             }
901            
902             sub get_opaque {
903 0     0 0   my $self = shift;
904 0           my $data;
905 0           my $len = $self->getmb32();
906 0 0         return undef unless (defined $len);
907 0           $self->{io_handle}->read($data,$len);
908 0           return $data;
909             }
910            
911             sub warning {
912 0     0 1   my $self = shift;
913 0           my ($msg) = @_;
914 0           my $pos = $self->{io_handle}->tell();
915 0           $self->{message_no_op} = __PACKAGE__ . ": Warning: $msg\n\tat position $pos\n";
916 0           $self->SUPER::warning( {
917             Message => $msg,
918             PublicId => $self->{PublicId},
919             ColumnNumber => $pos,
920             LineNumber => 1,
921             BytePosition => $pos
922             } );
923             }
924            
925             sub error {
926 0     0 1   my $self = shift;
927 0           my ($msg) = @_;
928 0           my $pos = $self->{io_handle}->tell();
929 0           $self->{message_no_op} = __PACKAGE__ . ": Error: $msg\n\tat position $pos\n";
930 0           $self->SUPER::error( {
931             Message => $msg,
932             PublicId => $self->{PublicId},
933             ColumnNumber => $pos,
934             LineNumber => 1,
935             BytePosition => $pos
936             } );
937             }
938            
939             sub no_op {
940 0     0 0   my $self = shift;
941 0 0         if (exists $self->{message_no_op}) {
942 0           warn $self->{message_no_op};
943 0           delete $self->{message_no_op};
944             }
945             }
946            
947             1;
948            
949             __END__