File Coverage

blib/lib/WAP/wbxml.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1            
2             package WAP::wbxml;
3            
4 1     1   721 use strict;
  1         3  
  1         33  
5 1     1   4 use warnings;
  1         2  
  1         48  
6            
7             our $VERSION = '1.13';
8            
9             =head1 NAME
10            
11             WAP::wbxml - Binarization of XML file
12            
13             =head1 SYNOPSIS
14            
15             use XML::DOM;
16             use WAP::wbxml;
17            
18             $parser = new XML::DOM::Parser;
19             $doc_xml = $parser->parsefile($infile);
20            
21             $rules = WAP::wbxml::WbRules::Load();
22             $wbxml = new WAP::wbxml($rules, $publicid);
23             $output = $wbxml->compile($doc_xml, $encoding);
24            
25             =head1 DESCRIPTION
26            
27             This module implements binarisation of XML file according the specification :
28            
29             WAP - Wireless Application Protocol /
30             Binary XML Content Format Specification /
31             Version 1.3 WBXML (15th May 2000 Approved)
32            
33             The XML input file must refere to a DTD with a public identifier.
34            
35             The file WAP/wap.wbrules.xml configures this tool for all known DTD.
36            
37             This module needs I18N::Charset and XML::DOM modules.
38            
39             WAP Specifications, including Binary XML Content Format (WBXML)
40             are available on Ehttp://www.wapforum.org/E.
41            
42             =over 4
43            
44             =cut
45            
46 1     1   890 use integer;
  1         13  
  1         5  
47 1     1   955 use bytes;
  1         9  
  1         5  
48            
49 1     1   882 use MIME::Base64;
  1         859  
  1         79  
50 1     1   585 use WAP::wbxml::WbRules;
  0            
  0            
51             use XML::DOM;
52            
53             # Global tokens
54             use constant SWITCH_PAGE => 0x00;
55             use constant _END => 0x01;
56             use constant ENTITY => 0x02;
57             use constant STR_I => 0x03;
58             use constant LITERAL => 0x04;
59             use constant EXT_I_0 => 0x40;
60             use constant EXT_I_1 => 0x41;
61             use constant EXT_I_2 => 0x42;
62             use constant PI => 0x43;
63             use constant LITERAL_C => 0x44;
64             use constant EXT_T_0 => 0x80;
65             use constant EXT_T_1 => 0x81;
66             use constant EXT_T_2 => 0x82;
67             use constant STR_T => 0x83;
68             use constant LITERAL_A => 0x84;
69             use constant EXT_0 => 0xC0;
70             use constant EXT_1 => 0xC1;
71             use constant EXT_2 => 0xC2;
72             use constant OPAQUE => 0xC3;
73             use constant LITERAL_AC => 0xC4;
74             # Global token masks
75             use constant NULL => 0x00;
76             use constant HAS_CHILD => 0x40;
77             use constant HAS_ATTR => 0x80;
78            
79             =item new
80            
81             $wbxml = new WAP::wbxml($rules, $publicid);
82            
83             Create a instance of WBinarizer for a specified kind of DTD.
84            
85             If PublicId is undefined, the first found rules are used.
86            
87             If the DTD is not known in the rules, default rules are used.
88            
89             =cut
90            
91             sub new {
92             my $proto = shift;
93             my $class = ref($proto) || $proto;
94             my $self = {};
95             bless $self, $class;
96             my ($rules, $publicid) = @_;
97             $self->{publicid} = $publicid;
98             $self->{rules} = $rules;
99             if ($publicid) {
100             $self->{rulesApp} = $rules->{App}->{$publicid};
101             unless ($self->{rulesApp}) {
102             $self->{rulesApp} = $rules->{DefaultApp};
103             warn "Using default rules.\n";
104             }
105             }
106             else {
107             my ($val) = values %{$rules->{App}};
108             $self->{rulesApp} = $val;
109             }
110             $self->{skipDefault} = $self->{rulesApp}->{skipDefault};
111             $self->{variableSubs} = $self->{rulesApp}->{variableSubs};
112             $self->{tagCodepage} = 0;
113             $self->{attrCodepage} = 0;
114             return $self;
115             }
116            
117             sub compileDatetime {
118             # WAP / WML
119             my $self = shift;
120             my ($content) = @_;
121             my $str;
122             if ($content =~ /(\d+)-(\d+)-(\d+)T(\d+)\.(\d+)\.(\d+)Z/) {
123             my $year = chr (16 * ($1 / 1000) + (($1 / 100) % 10))
124             . chr (16 * (($1 / 10) % 10) + ($1 % 10));
125             my $month = chr (16 * ($2 / 10) + ($2 % 10));
126             my $day = chr (16 * ($3 / 10) + ($3 % 10));
127             my $hour = chr (16 * ($4 / 10) + ($4 % 10));
128             my $min = chr (16 * ($5 / 10) + ($5 % 10));
129             my $sec = chr (16 * ($6 / 10) + ($6 % 10));
130             $str = $year . $month . $day;
131             $str .= $hour if (ord $hour or ord $min or ord $sec);
132             $str .= $min if (ord $min or ord $sec);
133             $str .= $sec if (ord $sec);
134             }
135             else {
136             warn "Validate 'Datetime' error : $content.\n";
137             $str = "\x19\x70\x01\x01";
138             }
139             $self->putb('body', OPAQUE);
140             $self->putmb('body', length $str);
141             $self->putstr('body', $str);
142             }
143            
144             sub compileBinaryWV {
145             # WV
146             my $self = shift;
147             my ($value) = @_;
148             $value =~ s/\s+//g;
149             my $data = decode_base64($value);
150             if (length $data) {
151             $self->putb('body', OPAQUE);
152             $self->putmb('body', length $data);
153             $self->putstr('body', $data);
154             }
155             }
156            
157             sub compileIntegerWV {
158             # WV
159             my $self = shift;
160             my ($value) = @_;
161             $value =~ s/\s+/ /g;
162             unless ($value =~ /^\s*$/) {
163             if ($value < 0 and $value > 4294967295) {
164             warn "'Integer' error : $value.\n";
165             $self->compilePreserveStringI($value);
166             }
167             else {
168             $self->putb('body', OPAQUE);
169             if ($value < 256) {
170             $self->putmb('body', 1);
171             $self->putb('body', $value);
172             }
173             elsif ($value < 65536) {
174             $self->putmb('body', 2);
175             $self->putstr('body', pack("n", $value));
176             }
177             else {
178             $self->putmb('body', 4);
179             $self->putstr('body', pack("N", $value));
180             }
181             }
182             }
183             }
184            
185             sub compileDatetimeWV {
186             # WV
187             my $self = shift;
188             my ($content) = @_;
189             my $str;
190             if ($content =~ /^\s*(\d\d\d\d)(\d\d)(\d\d)T(\d\d)(\d\d)(\d\d)(Z)?\s*$/) {
191             my $year = $1;
192             my $month = $2;
193             my $day = $3;
194             my $hour = $4;
195             my $min = $5;
196             my $sec = $6;
197             my $tz = $7 || "\0";
198             $self->putb('body', OPAQUE);
199             $self->putmb('body', 6);
200             $self->putb('body', $year >> 6);
201             $self->putb('body', (($year & 0x03F) << 2) | ($month >> 2));
202             $self->putb('body', (($month & 0x3) << 6) | ($day << 1) | ($hour >> 4));
203             $self->putb('body', (($hour & 0xF) << 4) | ($min >> 2));
204             $self->putb('body', (($min & 0x3) << 6) | $sec);
205             $self->putb('body', ord $tz);
206             }
207             else {
208             warn "'Datetime' error : $content.\n";
209             $self->compilePreserveStringI($content);
210             }
211             }
212            
213             sub compilePreserveStringT {
214             my $self = shift;
215             my ($str) = @_;
216             if (exists $self->{h_str}->{$str}) {
217             $self->putmb('body', $self->{h_str}->{$str});
218             }
219             else {
220             my $pos = length $self->{strtbl};
221             $self->{h_str}->{$str} = $pos;
222             # print $pos," ",$str,"\n";
223             $self->putmb('body', $pos);
224             $self->putstr('strtbl', $str);
225             $self->putb('strtbl', NULL);
226             }
227             }
228            
229             sub compilePreserveStringI {
230             my $self = shift;
231             my ($str) = @_;
232             my $idx = $self->{rulesApp}->getExtValue($str, 'Ext0Values');
233             if (defined $idx) {
234             $self->putb('body', EXT_T_0);
235             $self->putmb('body', $idx);
236             return;
237             }
238             $idx = $self->{rulesApp}->getExtValue($str, 'Ext1Values');
239             if (defined $idx) {
240             $self->putb('body', EXT_T_1);
241             $self->putmb('body', $idx);
242             return;
243             }
244             $idx = $self->{rulesApp}->getExtValue($str, 'Ext2Values');
245             if (defined $idx) {
246             $self->putb('body', EXT_T_2);
247             $self->putmb('body', $idx);
248             return;
249             }
250             $self->putb('body', STR_I);
251             $self->putstr('body', $str);
252             $self->putb('body', NULL);
253             }
254            
255             sub compileStringI {
256             my $self = shift;
257             my ($str) = @_;
258             $str =~ s/\s+/ /g;
259             $self->compilePreserveStringI($str) unless ($str =~ /^\s*$/);
260             }
261            
262             sub compileStringIwithVariables {
263             # WAP / WML
264             my $self = shift;
265             my ($str) = @_;
266             my $text = '';
267             while ($str) {
268             for ($str) {
269             s/^([^\$]+)//
270             and $text .= $1,
271             last;
272            
273             s/^\$\$//
274             and $text .= '$',
275             last;
276            
277             s/^\$([A-Z_a-z][0-9A-Z_a-z]*)//
278             and $self->compileStringI($text),
279             $text = q{},
280             $self->putb('body', EXT_T_2),
281             $self->compilePreserveStringT($1),
282             last;
283            
284             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*\)//
285             and $self->compileStringI($text),
286             $text = q{},
287             $self->putb('body', EXT_T_2),
288             $self->compilePreserveStringT($1),
289             last;
290            
291             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*:\s*escape\s*\)//
292             and $self->compileStringI($text),
293             $text = q{},
294             $self->putb('body', EXT_T_0),
295             $self->compilePreserveStringT($1),
296             last;
297            
298             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*:\s*unesc\s*\)//
299             and $self->compileStringI($text),
300             $text = q{},
301             $self->putb('body', EXT_T_1),
302             $self->compilePreserveStringT($1),
303             last;
304            
305             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*:\s*noesc\s*\)//
306             and $self->compileStringI($text),
307             $text = q{},
308             $self->putb('body', EXT_T_2),
309             $self->compilePreserveStringT($1),
310             last;
311            
312             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*:\s*([Ee]([Ss][Cc][Aa][Pp][Ee])?)\s*\)//
313             and $self->compileStringI($text),
314             $text = q{},
315             $self->putb('body', EXT_T_0),
316             $self->compilePreserveStringT($1),
317             warn "deprecated-var : $1:$2\n",
318             last;
319            
320             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*:\s*([Uu][Nn]([Ee][Ss][Cc])?)\s*\)//
321             and $self->compileStringI($text),
322             $text = q{},
323             $self->putb('body', EXT_T_1),
324             $self->compilePreserveStringT($1),
325             warn "deprecated-var : $1:$2\n",
326             last;
327            
328             s/^\$\(\s*([A-Z_a-z][0-9A-Z_a-z]*)\s*:\s*([Nn][Oo]([Ee][Ss][Cc])?)\s*\)//
329             and $self->compileStringI($text),
330             $text = q{},
331             $self->putb('body', EXT_T_2),
332             $self->compilePreserveStringT($1),
333             warn "deprecated-var : $1:$2\n",
334             last;
335            
336             warn "Pb with: $str \n";
337             return;
338             }
339             }
340             $self->compileStringI($text);
341             }
342            
343             sub compileEntity {
344             my $self = shift;
345             my ($entity) = @_;
346             if (exists $self->{rulesApp}->{CharacterEntity}{$entity}) {
347             my $code = $self->{rulesApp}->{CharacterEntity}{$entity};
348             $self->putb('body', ENTITY);
349             $self->putmb('body', $code);
350             }
351             else {
352             warn "entity reference : $entity";
353             }
354             }
355            
356             sub compileAttributeExtToken {
357             my $self = shift;
358             my ($ext_token) = @_;
359             my $codepage = $ext_token / 256;
360             my $token = $ext_token % 256;
361             if ($codepage != $self->{attrCodepage}) {
362             $self->putb('body', SWITCH_PAGE);
363             $self->putb('body', $codepage);
364             $self->{attrCodepage} = $codepage;
365             }
366             $self->putb('body', $token);
367             }
368            
369             sub compileTagExtToken {
370             my $self = shift;
371             my ($ext_token) = @_;
372             my $codepage = $ext_token / 256;
373             my $token = $ext_token % 256;
374             if ($codepage != $self->{tagCodepage}) {
375             $self->putb('body', SWITCH_PAGE);
376             $self->putb('body', $codepage);
377             $self->{tagCodepage} = $codepage;
378             }
379             $self->putb('body', $token);
380             }
381            
382             sub compileAttributeValues {
383             my $self = shift;
384             my ($value) = @_;
385             my $attr;
386             my $start;
387             my $end;
388             while (1) {
389             ($attr, $start, $end) = $self->{rulesApp}->getAttrValue($value, $self->{attrCodepage});
390             last unless ($attr);
391             $self->compilePreserveStringI($start) if (defined $start);
392             $self->compileAttributeExtToken($attr->{ext_token});
393             $value = $end;
394             }
395             $self->compilePreserveStringI($start) if (defined $start);
396             }
397            
398             sub compileProcessingInstruction {
399             my $self = shift;
400             my ($target, $data) = @_;
401             $self->putb('body', PI);
402             my ($attr_start, $dummy) = $self->{rulesApp}->getAttrStart($target, q{}, $self->{attrCodepage});
403             if ($attr_start) {
404             # well-known attribute name
405             $self->compileAttributeExtToken($attr_start->{ext_token});
406             }
407             else {
408             # unknown attribute name
409             $self->putb('body', LITERAL);
410             $self->compilePreserveStringT($target);
411             }
412             if (defined $data) {
413             $self->compileAttributeValues($data);
414             }
415             $self->putb('body', _END);
416             }
417            
418             sub prepareAttribute {
419             my $self = shift;
420             my ($tagname, $attr) = @_;
421             my $attr_name = $attr->getName();
422             my $attr_value = $attr->getValue();
423             my ($attr_start, $remain) = $self->{rulesApp}->getAttrStart($attr_name, $attr_value, $self->{attrCodepage});
424             if ($attr_start) {
425             # well-known attribute name
426             my $default_list = $attr_start->{default} || q{};
427             my $fixed_list = $attr_start->{fixed} || q{};
428             if (! $remain) {
429             return 0 if (index($fixed_list, $tagname) >= 0);
430             return 0 if ($self->{skipDefault} and index($default_list, $tagname) >= 0);
431             }
432             }
433             return 1;
434             }
435            
436             sub compileAttribute {
437             my $self = shift;
438             my ($tagname, $attr) = @_;
439             my $attr_name = $attr->getName();
440             my $attr_value = $attr->getValue();
441             my ($attr_start, $remain) = $self->{rulesApp}->getAttrStart($attr_name, $attr_value, $self->{attrCodepage});
442             if ($attr_start) {
443             # well-known attribute name
444             my $default_list = $attr_start->{default} || q{};
445             my $fixed_list = $attr_start->{fixed} || q{};
446             my $validate = $attr_start->{validate} || q{};
447             my $encoding = $attr_start->{encoding} || q{};
448             unless ($remain) {
449             return if (index($fixed_list, $tagname) >= 0);
450             return if ($self->{skipDefault} and index($default_list, $tagname) >= 0);
451             }
452             $self->compileAttributeExtToken($attr_start->{ext_token});
453            
454             if ($encoding eq 'iso-8601') {
455             $self->compileDatetime($attr_value);
456             }
457             else {
458             if ($remain ne q{}) {
459             if ($validate eq 'length') {
460             warn "Validate 'length' error : $remain.\n"
461             unless ($remain =~ /^[0-9]+%?$/);
462             $self->compilePreserveStringI($remain);
463             }
464             else {
465             if ($self->{variableSubs} and $validate eq 'vdata') {
466             if (index($remain, "\$") >= 0) {
467             $self->compileStringIwithVariables($remain);
468             }
469             else {
470             $self->compileAttributeValues($remain);
471             }
472             }
473             else {
474             $self->compileAttributeValues($remain);
475             }
476             }
477             }
478             }
479             }
480             else {
481             # unknown attribute name
482             $self->putb('body', LITERAL);
483             $self->compilePreserveStringT($attr_name);
484             $self->putb('body', STR_T);
485             $self->compilePreserveStringT($attr_value);
486             }
487             }
488            
489             sub compileElement {
490             my $self = shift;
491             my ($elt, $xml_lang, $xml_space) = @_;
492             my $cpl_token = NULL;
493             my $tagname = $elt->getNodeName();
494             my $attrs = $elt->getAttributes();
495             if ($attrs->getLength()) {
496             my $attr;
497             $attr = $elt->getAttribute('xml:lang');
498             $xml_lang = $attr if ($attr);
499             $attr = $elt->getAttribute('xml:space');
500             $xml_space = $attr if ($attr);
501             my $nb = 0;
502             for (my $i = 0; $i < $attrs->getLength(); $i ++) {
503             my $attr = $attrs->item($i);
504             if ($attr->getNodeType() == ATTRIBUTE_NODE) {
505             $nb += $self->prepareAttribute($tagname, $attr);
506             }
507             }
508             $cpl_token |= HAS_ATTR if ($nb);
509             }
510             if ($elt->hasChildNodes()) {
511             $cpl_token |= HAS_CHILD;
512             }
513             my $tag_token = $self->{rulesApp}->getTag($tagname, $self->{tagCodepage});
514             if ($tag_token) {
515             # well-known tag name
516             $self->compileTagExtToken($cpl_token | $tag_token->{ext_token});
517             }
518             else {
519             # unknown tag name
520             $self->putb('body', $cpl_token | LITERAL);
521             $self->compilePreserveStringT($tagname);
522             }
523             if ($cpl_token & HAS_ATTR) {
524             for (my $i = 0; $i < $attrs->getLength(); $i ++) {
525             my $attr = $attrs->item($i);
526             if ($attr->getNodeType() == ATTRIBUTE_NODE) {
527             $self->compileAttribute($tagname, $attr);
528             }
529             }
530             $self->putb('body', _END);
531             }
532             if ($cpl_token & HAS_CHILD) {
533             $self->compileContent($elt->getFirstChild(), $tag_token, $xml_lang, $xml_space);
534             $self->putb('body', _END);
535             }
536             }
537            
538             sub compileContent {
539             my $self = shift;
540             my ($tag, $parent, $xml_lang, $xml_space) = @_;
541             for (my $node = $tag;
542             $node;
543             $node = $node->getNextSibling() ) {
544             my $type = $node->getNodeType();
545             if ($type == ELEMENT_NODE) {
546             $self->compileElement($node, $xml_lang, $xml_space);
547             }
548             elsif ($type == TEXT_NODE) {
549             my $value = $node->getNodeValue();
550             if ($self->{variableSubs}) {
551             $self->compileStringIwithVariables($value);
552             }
553             else {
554             if ($xml_space eq 'preserve') {
555             $self->compilePreserveStringI($value) unless ($value =~ /^\s*$/);
556             }
557             else {
558             my $encoding = ($parent and exists $parent->{encoding}) ? $parent->{encoding} : "";
559             if ($encoding eq 'base64') {
560             $self->compileBinaryWV($value);
561             }
562             elsif ($encoding eq 'datetime') {
563             $self->compileDatetimeWV($value);
564             }
565             elsif ($encoding eq 'integer') {
566             $self->compileIntegerWV($value);
567             }
568             else {
569             $self->compileStringI($value);
570             }
571             }
572             }
573             }
574             elsif ($type == CDATA_SECTION_NODE) {
575             my $value = $node->getNodeValue();
576             $self->compilePreserveStringI($value);
577             }
578             elsif ($type == COMMENT_NODE) {
579             # do nothing
580             }
581             elsif ($type == ENTITY_REFERENCE_NODE) {
582             $self->compileEntity($node->getNodeName());
583             }
584             elsif ($type == PROCESSING_INSTRUCTION_NODE) {
585             my $target = $node->getTarget();
586             my $data = $node->getData();
587             $self->compileProcessingInstruction($target, $data);
588             }
589             else {
590             die "unexcepted ElementType in compileContent : $type\n";
591             }
592             }
593             }
594            
595             sub compileBody {
596             my $self = shift;
597             my ($doc) = @_;
598             my $xml_lang = q{};
599             my $xml_space = $self->{rulesApp}->{xmlSpace};
600             for (my $node = $doc->getFirstChild();
601             $node;
602             $node = $node->getNextSibling() ) {
603             my $type = $node->getNodeType();
604             if ($type == ELEMENT_NODE) {
605             $self->compileElement($node, $xml_lang, $xml_space);
606             }
607             elsif ($type == PROCESSING_INSTRUCTION_NODE) {
608             my $target = $node->getTarget();
609             my $data = $node->getData();
610             $self->compileProcessingInstruction($target, $data);
611             }
612             }
613             }
614            
615             sub compileCharSet {
616             my $self = shift;
617             my ($encoding) = @_;
618             if (defined $encoding) {
619             eval "use I18N::Charset";
620             die $@ if ($@);
621             my $mib = charset_name_to_mib($encoding);
622             if (defined $mib) {
623             $self->putmb('header', $mib);
624             }
625             else {
626             warn "unknown encoding.\n";
627             $self->putmb('header', 0); # unknown encoding
628             }
629             }
630             else {
631             $self->putmb('header', 106); # UTF-8 : default XML encoding
632             }
633             }
634            
635             sub compilePublicId {
636             my $self = shift;
637             if (! $self->{publicid}) {
638             $self->putmb('header', 1);
639             }
640             elsif (exists $self->{rules}->{PublicIdentifiers}->{$self->{publicid}}) {
641             my $publicid = $self->{rules}->{PublicIdentifiers}->{$self->{publicid}};
642             $self->putmb('header', $publicid);
643             }
644             else {
645             $self->putb('header', NULL);
646             my $pos = length $self->{strtbl}; # 0
647             $self->{h_str}->{$self->{publicid}} = $pos;
648             $self->putmb('header', $pos);
649             $self->putstr('strtbl', $self->{publicid});
650             $self->putb('strtbl', NULL);
651             }
652             }
653            
654             sub compileVersion {
655             my $self = shift;
656             $self->putb('header', $self->{rules}->{version});
657             }
658            
659             =item compile
660            
661             $output = $wbxml->compile($doc_xml, $encoding);
662            
663             Compiles a XML document.
664            
665             =cut
666            
667             sub compile {
668             my $self = shift;
669             my ($doc, $encoding) = @_;
670             $self->{header} = q{};
671             $self->{body} = q{};
672             $self->{strtbl} = q{};
673             $self->{h_str} = {};
674             $self->{tagCodepage} = 0;
675             $self->{attrCodepage} = 0;
676             $self->compileVersion();
677             $self->compilePublicId();
678             $self->compileCharSet($encoding);
679             $self->compileBody($doc);
680             $self->putmb('header', length $self->{strtbl});
681             my $out = $self->{header} . $self->{strtbl} . $self->{body};
682             return $out;
683             }
684            
685             =item outfile
686            
687             $filename = $wbxml->outfile($infile);
688            
689             Builds output filename with the good extension.
690            
691             =cut
692            
693             sub outfile {
694             my $self = shift;
695             my ($infile) = @_;
696             my $filename = $infile;
697             if ($filename =~ /\.[^\.]+$/) {
698             $filename =~ s/\.[^\.]+$/\./;
699             }
700             else {
701             $filename .= '.';
702             }
703             $filename .= $self->{rulesApp}->{tokenisedExt};
704             return $filename;
705             }
706            
707             sub putb {
708             my $self = shift;
709             my ($str, $val) = @_;
710             $self->{$str} = $self->{$str} . chr $val;
711             }
712            
713             sub putmb {
714             my $self = shift;
715             my ($str, $val) = @_;
716             my $tmp = chr ($val & 0x7f);
717             for ($val >>= 7; $val != 0; $val >>= 7) {
718             $tmp = chr (0x80 | ($val & 0x7f)) . $tmp;
719             }
720             $self->{$str} = $self->{$str} . $tmp;
721             }
722            
723             sub putstr {
724             my $self = shift;
725             my ($str, $val) = @_;
726             $self->{$str} = $self->{$str} . $val;
727             }
728            
729             =back
730            
731             =head1 SEE ALSO
732            
733             wbxmlc, WAP::SAXDriver::wbxml
734            
735             =head1 COPYRIGHT
736            
737             (c) 2001-2011 Francois PERRAD, France.
738            
739             This program (WAP::wbxml.pm and the internal DTD of wbrules.xml) is distributed
740             under the terms of the Artistic Licence.
741            
742             The WAP Specification are copyrighted by the Wireless Application Protocol Forum Ltd.
743             See Ehttp://www.wapforum.org/what/copyright.htmE.
744            
745             =head1 AUTHOR
746            
747             Francois PERRAD, francois.perrad@gadz.org
748            
749             =cut
750            
751             1;
752