File Coverage

blib/lib/Parse/WBXML.pm
Criterion Covered Total %
statement 244 378 64.5
branch 89 156 57.0
condition 6 11 54.5
subroutine 44 56 78.5
pod 33 43 76.7
total 416 644 64.6


line stmt bran cond sub pod time code
1             package Parse::WBXML;
2             # ABSTRACT: Support for WBXML as defined by the Open Mobile Alliance specs
3 4     4   88746 use strict;
  4         9  
  4         154  
4 4     4   21 use warnings;
  4         6  
  4         162  
5 4     4   3193 use parent qw(Mixin::Event::Dispatch);
  4         1268  
  4         22  
6 4     4   35470 use Try::Tiny;
  4         6762  
  4         255  
7              
8 4     4   6736 use I18N::Charset qw(mib_to_charset_name);
  4         972961  
  4         596  
9 4     4   52 use Encode ();
  4         8  
  4         357  
10              
11             our $VERSION = '0.005';
12              
13             =head1 NAME
14              
15             Parse::WBXML - event-driven support for the generation and parsing of WBXML documents
16              
17             =head1 VERSION
18              
19             version 0.005
20              
21             =head1 SYNOPSIS
22              
23             use Parse::WBXML;
24             my $wbxml = Parse::WBXML->new;
25             $wbxml->add_handler_for_event(
26             start_element => sub {
27             my ($self, $el) = @_;
28             $self;
29             },
30             characters => sub {
31             my ($self, $data) = @_;
32             $self;
33             },
34             end_element => sub {
35             my ($self, $el) = @_;
36             $self;
37             },
38             );
39             $wbxml->parse("wbxml data");
40              
41             =head1 DESCRIPTION
42              
43             WARNING: this is an early alpha release, if you want WBXML support then please try
44             the other modules in L first. The current API may change before the 1.0
45             release.
46              
47             Provides a pure-Perl implementation for the WBXML compressed XML format.
48             Slower and less efficient than the libwbxml2-based alternatives (L),
49             but supports streaming SAX-like parsing.
50              
51             This may be of some use in low-bandwidth situations where you want data as soon
52             as available from the stream, or in cases where the document is damaged and you
53             want to recover as much data as possible, or if you just don't have libwbxml2
54             available.
55              
56             =head1 METHODS
57              
58             =cut
59              
60             # From WAP-192-WBXML-20010725-a table 4, "Global tokens"
61             use constant {
62 4         22190 TOKEN_SWITCH_PAGE => 0x00,
63             TOKEN_END => 0x01,
64             TOKEN_ENTITY => 0x02,
65             TOKEN_STR_I => 0x03,
66             TOKEN_LITERAL => 0x04,
67             TOKEN_EXT_I_0 => 0x40,
68             TOKEN_EXT_I_1 => 0x41,
69             TOKEN_EXT_I_2 => 0x42,
70             TOKEN_PI => 0x43,
71             TOKEN_LITERAL_C => 0x44,
72             TOKEN_EXT_T_0 => 0x80,
73             TOKEN_EXT_T_1 => 0x81,
74             TOKEN_EXT_T_2 => 0x82,
75             TOKEN_STR_T => 0x83,
76             TOKEN_LITERAL_A => 0x84,
77             TOKEN_EXT_0 => 0xC0,
78             TOKEN_EXT_1 => 0xC1,
79             TOKEN_EXT_2 => 0xC2,
80             TOKEN_OPAQUE => 0xC3,
81             TOKEN_LITERAL_AC => 0xC4,
82 4     4   23 };
  4         6  
83              
84             # From WAP-192-WBXML-20010725-a table 5 ("Public Identifiers") and
85             # http://www.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.aspx
86             my %public_id = (
87             0 => 'String table index',
88             1 => 'Unknown',
89             2 => "-//WAPFORUM//DTD WML 1.0//EN",
90             3 => "-//WAPFORUM//DTD WTA 1.0//EN",
91             4 => "-//WAPFORUM//DTD WML 1.1//EN",
92             5 => "-//WAPFORUM//DTD SI 1.0//EN",
93             6 => "-//WAPFORUM//DTD SL 1.0//EN",
94             7 => "-//WAPFORUM//DTD CO 1.0//EN",
95             8 => "-//WAPFORUM//DTD CHANNEL 1.1//EN",
96             9 => "-//WAPFORUM//DTD WML 1.2//EN",
97             10 => "-//WAPFORUM//DTD WML 1.3//EN",
98             11 => "-//WAPFORUM//DTD PROV 1.0//EN",
99             12 => "-//WAPFORUM//DTD WTA-WML 1.2//EN",
100             13 => "-//WAPFORUM//DTD EMN 1.0//EN",
101             14 => "-//OMA//DTD DRMREL 1.0//EN",
102             15 => "-//WIRELESSVILLAGE//DTD CSP 1.0//EN",
103             16 => "-//WIRELESSVILLAGE//DTD CSP 1.1//EN",
104             17 => "-//OMA//DTD WV-CSP 1.2//EN",
105             18 => "-//OMA//DTD IMPS-CSP 1.3//EN",
106             19 => "-//OMA//DRM 2.1//EN",
107             20 => "-//OMA//SRM 1.0//EN",
108             21 => "-//OMA//DCD 1.0//EN",
109             22 => "-//OMA//DTD DS-DataObjectEmail 1.2//EN",
110             23 => "-//OMA//DTD DS-DataObjectFolder 1.2//EN",
111             24 => "-//OMA//DTD DS-DataObjectFile 1.2//EN",
112             0x0FD3 => "-//SYNCML//DTD SyncML 1.1//EN",
113             0x0FD4 => "-//SYNCML//DTD DevInf 1.1//EN",
114             );
115              
116             # From a myriad of OMA specs, and the wbrules.xml file in WAP-wbxml, haven't
117             # found a single comprehensive list (although the latter comes close).
118             my %ns = (
119             "-//WAPFORUM//DTD SI 1.0//EN" => {
120             tag => {
121             0 => {
122             5 => q{si},
123             6 => q{indication},
124             7 => q{info},
125             8 => q{item},
126             },
127             },
128             attrstart => {
129             0 => {
130             5 => { name => q{action}, prefix => q{signal-none} },
131             6 => { name => q{action}, prefix => q{signal-low} },
132             7 => { name => q{action}, prefix => q{signal-medium} },
133             8 => { name => q{action}, prefix => q{signal-high} },
134             9 => { name => q{action}, prefix => q{delete} },
135             10 => { name => q{created}, prefix => q{} },
136             11 => { name => q{href}, prefix => q{} },
137             12 => { name => q{href}, prefix => q{http://} },
138             13 => { name => q{href}, prefix => q{http://www.} },
139             14 => { name => q{href}, prefix => q{https://} },
140             15 => { name => q{href}, prefix => q{https://www.} },
141             16 => { name => q{si-expires}, prefix => q{} },
142             17 => { name => q{si-id}, prefix => q{} },
143             18 => { name => q{class}, prefix => q{} },
144             },
145             },
146             attrvalue => {
147             0 => {
148             133 => q{.com/},
149             134 => q{.edu/},
150             135 => q{.net/},
151             136 => q{.org/},
152             },
153             },
154             },
155             "-//WAPFORUM//DTD SL 1.0//EN" => {
156             tag => {
157             0 => {
158             5 => q{sl},
159             },
160             },
161             attrstart => {
162             0 => {
163             5 => { name => q{action}, prefix => q{execute-low} },
164             6 => { name => q{action}, prefix => q{execute-high} },
165             7 => { name => q{action}, prefix => q{cache} },
166             8 => { name => q{href}, prefix => q{} },
167             9 => { name => q{href}, prefix => q{http://} },
168             10 => { name => q{href}, prefix => q{http://www.} },
169             11 => { name => q{href}, prefix => q{https://} },
170             12 => { name => q{href}, prefix => q{https://www.} },
171             },
172             },
173             attrvalue => {
174             0 => {
175             133 => q{.com/},
176             134 => q{.edu/},
177             135 => q{.net/},
178             136 => q{.org/},
179             },
180             },
181             },
182             "-//WAPFORUM//DTD CO 1.0//EN" => {
183             tag => {
184             0 => {
185             5 => q{co},
186             6 => q{invalidate-object},
187             7 => q{invalidate-service},
188             },
189             },
190             attrstart => {
191             0 => {
192             5 => { name => q{uri}, prefix => q{} },
193             6 => { name => q{uri}, prefix => q{http://} },
194             7 => { name => q{uri}, prefix => q{http://www.} },
195             8 => { name => q{uri}, prefix => q{https://} },
196             9 => { name => q{uri}, prefix => q{https://www.} },
197             },
198             },
199             attrvalue => {
200             0 => {
201             133 => q{.com/},
202             134 => q{.edu/},
203             135 => q{.net/},
204             136 => q{.org/},
205             },
206             },
207             },
208             "-//WAPFORUM//DTD CHANNEL 1.2//EN" => {
209             tag => {
210             0 => {
211             5 => q{channel},
212             6 => q{title},
213             7 => q{abstract},
214             8 => q{resource},
215             },
216             },
217             attrstart => {
218             0 => {
219             5 => { name => q{maxspace}, prefix => q{} },
220             6 => { name => q{base}, prefix => q{} },
221             7 => { name => q{href}, prefix => q{} },
222             8 => { name => q{href}, prefix => q{http://} },
223             9 => { name => q{href}, prefix => q{https://} },
224             10 => { name => q{lastmod}, prefix => q{} },
225             11 => { name => q{etag}, prefix => q{} },
226             12 => { name => q{md5}, prefix => q{} },
227             13 => { name => q{success}, prefix => q{} },
228             14 => { name => q{success}, prefix => q{http://} },
229             15 => { name => q{success}, prefix => q{https://} },
230             16 => { name => q{failure}, prefix => q{} },
231             17 => { name => q{failure}, prefix => q{http://} },
232             18 => { name => q{failure}, prefix => q{https://} },
233             19 => { name => q{eventid}, prefix => q{} },
234             20 => { name => q{eventid}, prefix => q{wtaev-} },
235             21 => { name => q{channelid}, prefix => q{} },
236             22 => { name => q{useraccessible}, prefix => q{} },
237             },
238             },
239             attrvalue => {
240             },
241             },
242             "-//WAPFORUM//DTD CHANNEL 1.1//EN" => {
243             tag => {
244             0 => {
245             5 => q{channel},
246             6 => q{title},
247             7 => q{abstract},
248             8 => q{resource},
249             },
250             },
251             attrstart => {
252             0 => {
253             5 => { name => q{maxspace}, prefix => q{} },
254             6 => { name => q{base}, prefix => q{} },
255             7 => { name => q{href}, prefix => q{} },
256             8 => { name => q{href}, prefix => q{http://} },
257             9 => { name => q{href}, prefix => q{https://} },
258             10 => { name => q{lastmod}, prefix => q{} },
259             11 => { name => q{etag}, prefix => q{} },
260             12 => { name => q{md5}, prefix => q{} },
261             13 => { name => q{success}, prefix => q{} },
262             14 => { name => q{success}, prefix => q{http://} },
263             15 => { name => q{success}, prefix => q{https://} },
264             16 => { name => q{failure}, prefix => q{} },
265             17 => { name => q{failure}, prefix => q{http://} },
266             18 => { name => q{failure}, prefix => q{https://} },
267             19 => { name => q{EventId}, prefix => q{} },
268             },
269             },
270             attrvalue => {
271             },
272             },
273             "-//WAPFORUM//DTD WML 1.3//EN" => {
274             tag => {
275             0 => {
276             27 => q{pre},
277             28 => q{a},
278             29 => q{td},
279             30 => q{tr},
280             31 => q{table},
281             32 => q{p},
282             33 => q{postfield},
283             34 => q{anchor},
284             35 => q{access},
285             36 => q{b},
286             37 => q{big},
287             38 => q{br},
288             39 => q{card},
289             40 => q{do},
290             41 => q{em},
291             42 => q{fieldset},
292             43 => q{go},
293             44 => q{head},
294             45 => q{i},
295             46 => q{img},
296             47 => q{input},
297             48 => q{meta},
298             49 => q{noop},
299             50 => q{prev},
300             51 => q{onevent},
301             52 => q{optgroup},
302             53 => q{option},
303             54 => q{refresh},
304             55 => q{select},
305             56 => q{small},
306             57 => q{strong},
307             59 => q{template},
308             60 => q{timer},
309             61 => q{u},
310             62 => q{setvar},
311             63 => q{wml},
312             },
313             },
314             attrstart => {
315             0 => {
316             5 => { name => q{accept-charset}, prefix => q{} },
317             6 => { name => q{align}, prefix => q{bottom} },
318             7 => { name => q{align}, prefix => q{center} },
319             8 => { name => q{align}, prefix => q{left} },
320             9 => { name => q{align}, prefix => q{middle} },
321             10 => { name => q{align}, prefix => q{right} },
322             11 => { name => q{align}, prefix => q{top} },
323             12 => { name => q{alt}, prefix => q{} },
324             13 => { name => q{content}, prefix => q{} },
325             15 => { name => q{domain}, prefix => q{} },
326             16 => { name => q{emptyok}, prefix => q{false} },
327             17 => { name => q{emptyok}, prefix => q{true} },
328             18 => { name => q{format}, prefix => q{} },
329             19 => { name => q{height}, prefix => q{} },
330             20 => { name => q{hspace}, prefix => q{} },
331             21 => { name => q{ivalue}, prefix => q{} },
332             22 => { name => q{iname}, prefix => q{} },
333             24 => { name => q{label}, prefix => q{} },
334             25 => { name => q{localsrc}, prefix => q{} },
335             26 => { name => q{maxlength}, prefix => q{} },
336             27 => { name => q{method}, prefix => q{get} },
337             28 => { name => q{method}, prefix => q{post} },
338             29 => { name => q{mode}, prefix => q{nowrap} },
339             30 => { name => q{mode}, prefix => q{wrap} },
340             31 => { name => q{multiple}, prefix => q{false} },
341             32 => { name => q{multiple}, prefix => q{true} },
342             33 => { name => q{name}, prefix => q{} },
343             34 => { name => q{newcontext}, prefix => q{false} },
344             35 => { name => q{newcontext}, prefix => q{true} },
345             36 => { name => q{onpick}, prefix => q{} },
346             37 => { name => q{onenterbackward}, prefix => q{} },
347             38 => { name => q{onenterforward}, prefix => q{} },
348             39 => { name => q{ontimer}, prefix => q{} },
349             40 => { name => q{optional}, prefix => q{false} },
350             41 => { name => q{optional}, prefix => q{true} },
351             42 => { name => q{path}, prefix => q{} },
352             46 => { name => q{scheme}, prefix => q{} },
353             47 => { name => q{sendreferer}, prefix => q{false} },
354             48 => { name => q{sendreferer}, prefix => q{true} },
355             49 => { name => q{size}, prefix => q{} },
356             50 => { name => q{src}, prefix => q{} },
357             51 => { name => q{ordered}, prefix => q{true} },
358             52 => { name => q{ordered}, prefix => q{false} },
359             53 => { name => q{tabindex}, prefix => q{} },
360             54 => { name => q{title}, prefix => q{} },
361             55 => { name => q{type}, prefix => q{} },
362             56 => { name => q{type}, prefix => q{accept} },
363             57 => { name => q{type}, prefix => q{delete} },
364             58 => { name => q{type}, prefix => q{help} },
365             59 => { name => q{type}, prefix => q{password} },
366             60 => { name => q{type}, prefix => q{onpick} },
367             61 => { name => q{type}, prefix => q{onenterbackward} },
368             62 => { name => q{type}, prefix => q{onenterforward} },
369             63 => { name => q{type}, prefix => q{ontimer} },
370             69 => { name => q{type}, prefix => q{options} },
371             70 => { name => q{type}, prefix => q{prev} },
372             71 => { name => q{type}, prefix => q{reset} },
373             72 => { name => q{type}, prefix => q{text} },
374             73 => { name => q{type}, prefix => q{vnd.} },
375             74 => { name => q{href}, prefix => q{} },
376             75 => { name => q{href}, prefix => q{http://} },
377             76 => { name => q{href}, prefix => q{https://} },
378             77 => { name => q{value}, prefix => q{} },
379             78 => { name => q{vspace}, prefix => q{} },
380             79 => { name => q{width}, prefix => q{} },
381             80 => { name => q{xml:lang}, prefix => q{} },
382             82 => { name => q{align}, prefix => q{} },
383             83 => { name => q{columns}, prefix => q{} },
384             84 => { name => q{class}, prefix => q{} },
385             85 => { name => q{id}, prefix => q{} },
386             86 => { name => q{forua}, prefix => q{false} },
387             87 => { name => q{forua}, prefix => q{true} },
388             88 => { name => q{src}, prefix => q{http://} },
389             89 => { name => q{src}, prefix => q{https://} },
390             90 => { name => q{http-equiv}, prefix => q{} },
391             91 => { name => q{http-equiv}, prefix => q{Content-Type} },
392             92 => { name => q{content}, prefix => q{application/vnd.wap.wmlc;charset=} },
393             93 => { name => q{http-equiv}, prefix => q{Expires} },
394             94 => { name => q{accesskey}, prefix => q{} },
395             95 => { name => q{enctype}, prefix => q{} },
396             96 => { name => q{enctype}, prefix => q{application/x-www-form-urlencoded} },
397             97 => { name => q{enctype}, prefix => q{multipart/form-data} },
398             98 => { name => q{xml:space}, prefix => q{preserve} },
399             99 => { name => q{xml:space}, prefix => q{default} },
400             100 => { name => q{cache-control}, prefix => q{no-cache} },
401             },
402             },
403             attrvalue => {
404             0 => {
405             133 => q{.com/},
406             134 => q{.edu/},
407             135 => q{.net/},
408             136 => q{.org/},
409             137 => q{accept},
410             138 => q{bottom},
411             139 => q{clear},
412             140 => q{delete},
413             141 => q{help},
414             142 => q{http://},
415             143 => q{http://www.},
416             144 => q{https://},
417             145 => q{https://www.},
418             147 => q{middle},
419             148 => q{nowrap},
420             149 => q{onpick},
421             150 => q{onenterbackward},
422             151 => q{onenterforward},
423             152 => q{ontimer},
424             153 => q{options},
425             154 => q{password},
426             155 => q{reset},
427             157 => q{text},
428             158 => q{top},
429             159 => q{unknown},
430             160 => q{wrap},
431             161 => q{www.},
432             },
433             },
434             },
435             "-//WAPFORUM//DTD WML 1.2//EN" => {
436             tag => {
437             0 => {
438             27 => q{pre},
439             28 => q{a},
440             29 => q{td},
441             30 => q{tr},
442             31 => q{table},
443             32 => q{p},
444             33 => q{postfield},
445             34 => q{anchor},
446             35 => q{access},
447             36 => q{b},
448             37 => q{big},
449             38 => q{br},
450             39 => q{card},
451             40 => q{do},
452             41 => q{em},
453             42 => q{fieldset},
454             43 => q{go},
455             44 => q{head},
456             45 => q{i},
457             46 => q{img},
458             47 => q{input},
459             48 => q{meta},
460             49 => q{noop},
461             50 => q{prev},
462             51 => q{onevent},
463             52 => q{optgroup},
464             53 => q{option},
465             54 => q{refresh},
466             55 => q{select},
467             56 => q{small},
468             57 => q{strong},
469             59 => q{template},
470             60 => q{timer},
471             61 => q{u},
472             62 => q{setvar},
473             63 => q{wml},
474             },
475             },
476             attrstart => {
477             0 => {
478             0 => { name => q{xml:space}, prefix => q{preserve} },
479             5 => { name => q{accept-charset}, prefix => q{} },
480             6 => { name => q{align}, prefix => q{bottom} },
481             7 => { name => q{align}, prefix => q{center} },
482             8 => { name => q{align}, prefix => q{left} },
483             9 => { name => q{align}, prefix => q{middle} },
484             10 => { name => q{align}, prefix => q{right} },
485             11 => { name => q{align}, prefix => q{top} },
486             12 => { name => q{alt}, prefix => q{} },
487             13 => { name => q{content}, prefix => q{} },
488             15 => { name => q{domain}, prefix => q{} },
489             16 => { name => q{emptyok}, prefix => q{false} },
490             17 => { name => q{emptyok}, prefix => q{true} },
491             18 => { name => q{format}, prefix => q{} },
492             19 => { name => q{height}, prefix => q{} },
493             20 => { name => q{hspace}, prefix => q{} },
494             21 => { name => q{ivalue}, prefix => q{} },
495             22 => { name => q{iname}, prefix => q{} },
496             24 => { name => q{label}, prefix => q{} },
497             25 => { name => q{localsrc}, prefix => q{} },
498             26 => { name => q{maxlength}, prefix => q{} },
499             27 => { name => q{method}, prefix => q{get} },
500             28 => { name => q{method}, prefix => q{post} },
501             29 => { name => q{mode}, prefix => q{nowrap} },
502             30 => { name => q{mode}, prefix => q{wrap} },
503             31 => { name => q{multiple}, prefix => q{false} },
504             32 => { name => q{multiple}, prefix => q{true} },
505             33 => { name => q{name}, prefix => q{} },
506             34 => { name => q{newcontext}, prefix => q{false} },
507             35 => { name => q{newcontext}, prefix => q{true} },
508             36 => { name => q{onpick}, prefix => q{} },
509             37 => { name => q{onenterbackward}, prefix => q{} },
510             38 => { name => q{onenterforward}, prefix => q{} },
511             39 => { name => q{ontimer}, prefix => q{} },
512             40 => { name => q{optional}, prefix => q{false} },
513             41 => { name => q{optional}, prefix => q{true} },
514             42 => { name => q{path}, prefix => q{} },
515             46 => { name => q{scheme}, prefix => q{} },
516             47 => { name => q{sendreferer}, prefix => q{false} },
517             48 => { name => q{sendreferer}, prefix => q{true} },
518             49 => { name => q{size}, prefix => q{} },
519             50 => { name => q{src}, prefix => q{} },
520             51 => { name => q{ordered}, prefix => q{true} },
521             52 => { name => q{ordered}, prefix => q{false} },
522             53 => { name => q{tabindex}, prefix => q{} },
523             54 => { name => q{title}, prefix => q{} },
524             55 => { name => q{type}, prefix => q{} },
525             56 => { name => q{type}, prefix => q{accept} },
526             57 => { name => q{type}, prefix => q{delete} },
527             58 => { name => q{type}, prefix => q{help} },
528             59 => { name => q{type}, prefix => q{password} },
529             60 => { name => q{type}, prefix => q{onpick} },
530             61 => { name => q{type}, prefix => q{onenterbackward} },
531             62 => { name => q{type}, prefix => q{onenterforward} },
532             63 => { name => q{type}, prefix => q{ontimer} },
533             69 => { name => q{type}, prefix => q{options} },
534             70 => { name => q{type}, prefix => q{prev} },
535             71 => { name => q{type}, prefix => q{reset} },
536             72 => { name => q{type}, prefix => q{text} },
537             73 => { name => q{type}, prefix => q{vnd.} },
538             74 => { name => q{href}, prefix => q{} },
539             75 => { name => q{href}, prefix => q{http://} },
540             76 => { name => q{href}, prefix => q{https://} },
541             77 => { name => q{value}, prefix => q{} },
542             78 => { name => q{vspace}, prefix => q{} },
543             79 => { name => q{width}, prefix => q{} },
544             80 => { name => q{xml:lang}, prefix => q{} },
545             82 => { name => q{align}, prefix => q{} },
546             83 => { name => q{columns}, prefix => q{} },
547             84 => { name => q{class}, prefix => q{} },
548             85 => { name => q{id}, prefix => q{} },
549             86 => { name => q{forua}, prefix => q{false} },
550             87 => { name => q{forua}, prefix => q{true} },
551             88 => { name => q{src}, prefix => q{http://} },
552             89 => { name => q{src}, prefix => q{https://} },
553             90 => { name => q{http-equiv}, prefix => q{} },
554             91 => { name => q{http-equiv}, prefix => q{Content-Type} },
555             92 => { name => q{content}, prefix => q{application/vnd.wap.wmlc;charset=} },
556             93 => { name => q{http-equiv}, prefix => q{Expires} },
557             94 => { name => q{accesskey}, prefix => q{} },
558             95 => { name => q{enctype}, prefix => q{} },
559             96 => { name => q{enctype}, prefix => q{application/x-www-form-urlencoded} },
560             97 => { name => q{enctype}, prefix => q{multipart/form-data} },
561             },
562             },
563             attrvalue => {
564             0 => {
565             133 => q{.com/},
566             134 => q{.edu/},
567             135 => q{.net/},
568             136 => q{.org/},
569             137 => q{accept},
570             138 => q{bottom},
571             139 => q{clear},
572             140 => q{delete},
573             141 => q{help},
574             142 => q{http://},
575             143 => q{http://www.},
576             144 => q{https://},
577             145 => q{https://www.},
578             147 => q{middle},
579             148 => q{nowrap},
580             149 => q{onpick},
581             150 => q{onenterbackward},
582             151 => q{onenterforward},
583             152 => q{ontimer},
584             153 => q{options},
585             154 => q{password},
586             155 => q{reset},
587             157 => q{text},
588             158 => q{top},
589             159 => q{unknown},
590             160 => q{wrap},
591             161 => q{www.},
592             },
593             },
594             },
595             "-//WAPFORUM//DTD WML 1.1//EN" => {
596             tag => {
597             0 => {
598             28 => q{a},
599             29 => q{td},
600             30 => q{tr},
601             31 => q{table},
602             32 => q{p},
603             33 => q{postfield},
604             34 => q{anchor},
605             35 => q{access},
606             36 => q{b},
607             37 => q{big},
608             38 => q{br},
609             39 => q{card},
610             40 => q{do},
611             41 => q{em},
612             42 => q{fieldset},
613             43 => q{go},
614             44 => q{head},
615             45 => q{i},
616             46 => q{img},
617             47 => q{input},
618             48 => q{meta},
619             49 => q{noop},
620             50 => q{prev},
621             51 => q{onevent},
622             52 => q{optgroup},
623             53 => q{option},
624             54 => q{refresh},
625             55 => q{select},
626             56 => q{small},
627             57 => q{strong},
628             59 => q{template},
629             60 => q{timer},
630             61 => q{u},
631             62 => q{setvar},
632             63 => q{wml},
633             },
634             },
635             attrstart => {
636             0 => {
637             5 => { name => q{accept-charset}, prefix => q{} },
638             6 => { name => q{align}, prefix => q{bottom} },
639             7 => { name => q{align}, prefix => q{center} },
640             8 => { name => q{align}, prefix => q{left} },
641             9 => { name => q{align}, prefix => q{middle} },
642             10 => { name => q{align}, prefix => q{right} },
643             11 => { name => q{align}, prefix => q{top} },
644             12 => { name => q{alt}, prefix => q{} },
645             13 => { name => q{content}, prefix => q{} },
646             15 => { name => q{domain}, prefix => q{} },
647             16 => { name => q{emptyok}, prefix => q{false} },
648             17 => { name => q{emptyok}, prefix => q{true} },
649             18 => { name => q{format}, prefix => q{} },
650             19 => { name => q{height}, prefix => q{} },
651             20 => { name => q{hspace}, prefix => q{} },
652             21 => { name => q{ivalue}, prefix => q{} },
653             22 => { name => q{iname}, prefix => q{} },
654             24 => { name => q{label}, prefix => q{} },
655             25 => { name => q{localsrc}, prefix => q{} },
656             26 => { name => q{maxlength}, prefix => q{} },
657             27 => { name => q{method}, prefix => q{get} },
658             28 => { name => q{method}, prefix => q{post} },
659             29 => { name => q{mode}, prefix => q{nowrap} },
660             30 => { name => q{mode}, prefix => q{wrap} },
661             31 => { name => q{multiple}, prefix => q{false} },
662             32 => { name => q{multiple}, prefix => q{true} },
663             33 => { name => q{name}, prefix => q{} },
664             34 => { name => q{newcontext}, prefix => q{false} },
665             35 => { name => q{newcontext}, prefix => q{true} },
666             36 => { name => q{onpick}, prefix => q{} },
667             37 => { name => q{onenterbackward}, prefix => q{} },
668             38 => { name => q{onenterforward}, prefix => q{} },
669             39 => { name => q{ontimer}, prefix => q{} },
670             40 => { name => q{optional}, prefix => q{false} },
671             41 => { name => q{optional}, prefix => q{true} },
672             42 => { name => q{path}, prefix => q{} },
673             46 => { name => q{scheme}, prefix => q{} },
674             47 => { name => q{sendreferer}, prefix => q{false} },
675             48 => { name => q{sendreferer}, prefix => q{true} },
676             49 => { name => q{size}, prefix => q{} },
677             50 => { name => q{src}, prefix => q{} },
678             51 => { name => q{ordered}, prefix => q{true} },
679             52 => { name => q{ordered}, prefix => q{false} },
680             53 => { name => q{tabindex}, prefix => q{} },
681             54 => { name => q{title}, prefix => q{} },
682             55 => { name => q{type}, prefix => q{} },
683             56 => { name => q{type}, prefix => q{accept} },
684             57 => { name => q{type}, prefix => q{delete} },
685             58 => { name => q{type}, prefix => q{help} },
686             59 => { name => q{type}, prefix => q{password} },
687             60 => { name => q{type}, prefix => q{onpick} },
688             61 => { name => q{type}, prefix => q{onenterbackward} },
689             62 => { name => q{type}, prefix => q{onenterforward} },
690             63 => { name => q{type}, prefix => q{ontimer} },
691             69 => { name => q{type}, prefix => q{options} },
692             70 => { name => q{type}, prefix => q{prev} },
693             71 => { name => q{type}, prefix => q{reset} },
694             72 => { name => q{type}, prefix => q{text} },
695             73 => { name => q{type}, prefix => q{vnd.} },
696             74 => { name => q{href}, prefix => q{} },
697             75 => { name => q{href}, prefix => q{http://} },
698             76 => { name => q{href}, prefix => q{https://} },
699             77 => { name => q{value}, prefix => q{} },
700             78 => { name => q{vspace}, prefix => q{} },
701             79 => { name => q{width}, prefix => q{} },
702             80 => { name => q{xml:lang}, prefix => q{} },
703             82 => { name => q{align}, prefix => q{} },
704             83 => { name => q{columns}, prefix => q{} },
705             84 => { name => q{class}, prefix => q{} },
706             85 => { name => q{id}, prefix => q{} },
707             86 => { name => q{forua}, prefix => q{false} },
708             87 => { name => q{forua}, prefix => q{true} },
709             88 => { name => q{src}, prefix => q{http://} },
710             89 => { name => q{src}, prefix => q{https://} },
711             90 => { name => q{http-equiv}, prefix => q{} },
712             91 => { name => q{http-equiv}, prefix => q{Content-Type} },
713             92 => { name => q{content}, prefix => q{application/vnd.wap.wmlc;charset=} },
714             93 => { name => q{http-equiv}, prefix => q{Expires} },
715             },
716             },
717             attrvalue => {
718             0 => {
719             133 => q{.com/},
720             134 => q{.edu/},
721             135 => q{.net/},
722             136 => q{.org/},
723             137 => q{accept},
724             138 => q{bottom},
725             139 => q{clear},
726             140 => q{delete},
727             141 => q{help},
728             142 => q{http://},
729             143 => q{http://www.},
730             144 => q{https://},
731             145 => q{https://www.},
732             147 => q{middle},
733             148 => q{nowrap},
734             149 => q{onpick},
735             150 => q{onenterbackward},
736             151 => q{onenterforward},
737             152 => q{ontimer},
738             153 => q{options},
739             154 => q{password},
740             155 => q{reset},
741             157 => q{text},
742             158 => q{top},
743             159 => q{unknown},
744             160 => q{wrap},
745             161 => q{www.},
746             },
747             },
748             },
749             "-//WAPFORUM//DTD WML 1.0//EN" => {
750             tag => {
751             0 => {
752             34 => q{A},
753             35 => q{ACCESS},
754             36 => q{B},
755             37 => q{BIG},
756             38 => q{BR},
757             39 => q{CARD},
758             40 => q{DO},
759             41 => q{EM},
760             42 => q{FIELSET},
761             43 => q{GO},
762             44 => q{HEAD},
763             45 => q{I},
764             46 => q{IMG},
765             47 => q{INPUT},
766             48 => q{META},
767             49 => q{NOOP},
768             50 => q{PREV},
769             51 => q{ONEVENT},
770             52 => q{OPTGROUP},
771             53 => q{OPTION},
772             54 => q{REFRESH},
773             55 => q{SELECT},
774             56 => q{SMALL},
775             57 => q{STRONG},
776             58 => q{TAB},
777             59 => q{TEMPLATE},
778             60 => q{TIMER},
779             61 => q{U},
780             62 => q{VAR},
781             63 => q{WML},
782             },
783             },
784             attrstart => {
785             0 => {
786             5 => { name => q{ACCEPT-CHARSET}, prefix => q{} },
787             6 => { name => q{ALIGN}, prefix => q{BOTTOM} },
788             7 => { name => q{ALIGN}, prefix => q{CENTER} },
789             8 => { name => q{ALIGN}, prefix => q{LEFT} },
790             9 => { name => q{ALIGN}, prefix => q{MIDDLE} },
791             10 => { name => q{ALIGN}, prefix => q{RIGHT} },
792             11 => { name => q{ALIGN}, prefix => q{TOP} },
793             12 => { name => q{ALT}, prefix => q{} },
794             13 => { name => q{CONTENT}, prefix => q{} },
795             14 => { name => q{DEFAULT}, prefix => q{} },
796             15 => { name => q{DOMAIN}, prefix => q{} },
797             16 => { name => q{EMPTYOK}, prefix => q{FALSE} },
798             17 => { name => q{EMPTYOK}, prefix => q{TRUE} },
799             18 => { name => q{FORMAT}, prefix => q{} },
800             19 => { name => q{HEIGHT}, prefix => q{} },
801             20 => { name => q{HSPACE}, prefix => q{} },
802             21 => { name => q{IDEFAULT}, prefix => q{} },
803             22 => { name => q{IKEY}, prefix => q{} },
804             23 => { name => q{KEY}, prefix => q{} },
805             24 => { name => q{LABEL}, prefix => q{} },
806             25 => { name => q{LOCALSRC}, prefix => q{} },
807             26 => { name => q{MAXLENGTH}, prefix => q{} },
808             27 => { name => q{METHOD}, prefix => q{GET} },
809             28 => { name => q{METHOD}, prefix => q{POST} },
810             29 => { name => q{MODE}, prefix => q{NOWRAP} },
811             30 => { name => q{MODE}, prefix => q{WRAP} },
812             31 => { name => q{MULTIPLE}, prefix => q{FALSE} },
813             32 => { name => q{MULTIPLE}, prefix => q{TRUE} },
814             33 => { name => q{NAME}, prefix => q{} },
815             34 => { name => q{NEWCONTEXT}, prefix => q{FALSE} },
816             35 => { name => q{NEWCONTEXT}, prefix => q{TRUE} },
817             36 => { name => q{ONCLICK}, prefix => q{} },
818             37 => { name => q{ONENTERBACKWARD}, prefix => q{} },
819             38 => { name => q{ONENTERFORWARD}, prefix => q{} },
820             39 => { name => q{ONTIMER}, prefix => q{} },
821             40 => { name => q{OPTIONAL}, prefix => q{FALSE} },
822             41 => { name => q{OPTIONAL}, prefix => q{TRUE} },
823             42 => { name => q{PATH}, prefix => q{} },
824             43 => { name => q{POSTDATA}, prefix => q{} },
825             44 => { name => q{PUBLIC}, prefix => q{FALSE} },
826             45 => { name => q{PUBLIC}, prefix => q{TRUE} },
827             46 => { name => q{SCHEME}, prefix => q{} },
828             47 => { name => q{SENDREFERER}, prefix => q{FALSE} },
829             48 => { name => q{SENDREFERER}, prefix => q{TRUE} },
830             49 => { name => q{SIZE}, prefix => q{} },
831             50 => { name => q{SRC}, prefix => q{} },
832             51 => { name => q{STYLE}, prefix => q{LIST} },
833             52 => { name => q{STYLE}, prefix => q{SET} },
834             53 => { name => q{TABINDEX}, prefix => q{} },
835             54 => { name => q{TITLE}, prefix => q{} },
836             55 => { name => q{TYPE}, prefix => q{} },
837             56 => { name => q{TYPE}, prefix => q{ACCEPT} },
838             57 => { name => q{TYPE}, prefix => q{DELETE} },
839             58 => { name => q{TYPE}, prefix => q{HELP} },
840             59 => { name => q{TYPE}, prefix => q{PASSWORD} },
841             60 => { name => q{TYPE}, prefix => q{ONCLICK} },
842             61 => { name => q{TYPE}, prefix => q{ONENTERBACKWARD} },
843             62 => { name => q{TYPE}, prefix => q{ONENTERFORWARD} },
844             63 => { name => q{TYPE}, prefix => q{ONTIMER} },
845             69 => { name => q{TYPE}, prefix => q{OPTIONS} },
846             70 => { name => q{TYPE}, prefix => q{PREV} },
847             71 => { name => q{TYPE}, prefix => q{RESET} },
848             72 => { name => q{TYPE}, prefix => q{TEXT} },
849             73 => { name => q{TYPE}, prefix => q{vnd.} },
850             74 => { name => q{URL}, prefix => q{} },
851             75 => { name => q{URL}, prefix => q{http://} },
852             76 => { name => q{URL}, prefix => q{https://} },
853             77 => { name => q{USER-AGENT}, prefix => q{} },
854             78 => { name => q{VALUE}, prefix => q{} },
855             79 => { name => q{VSPACE}, prefix => q{} },
856             80 => { name => q{WIDTH}, prefix => q{} },
857             81 => { name => q{xml:lang}, prefix => q{} },
858             },
859             },
860             attrvalue => {
861             0 => {
862             133 => q{.com/},
863             134 => q{.edu/},
864             135 => q{.net/},
865             136 => q{.org/},
866             137 => q{ACCEPT},
867             138 => q{BOTTOM},
868             139 => q{CLEAR},
869             140 => q{DELETE},
870             141 => q{HELP},
871             142 => q{http://},
872             143 => q{http://www.},
873             144 => q{https://},
874             145 => q{https://www.},
875             146 => q{LIST},
876             147 => q{MIDDLE},
877             148 => q{NOWRAP},
878             149 => q{ONCLICK},
879             150 => q{ONENTERBACKWARD},
880             151 => q{ONENTERFORWARD},
881             152 => q{ONTIMER},
882             153 => q{OPTIONS},
883             154 => q{PASSWORD},
884             155 => q{RESET},
885             156 => q{SET},
886             157 => q{TEXT},
887             158 => q{TOP},
888             159 => q{UNKNOWN},
889             160 => q{WRAP},
890             161 => q{www.},
891             },
892             },
893             },
894             # From http://www.openmobilealliance.org/tech/affiliates/wap/wap-183-provcont-20010724-a.pdf
895             # and libwbxml2
896             "-//WAPFORUM//DTD PROV 1.0//EN" => {
897             tag => {
898             0 => {
899             5 => q{wap-provisioningdoc},
900             6 => q{characteristic},
901             7 => q{parm},
902             },
903             1 => {
904             6 => q{characteristic},
905             7 => q{parm},
906             },
907             },
908             attrstart => {
909             0 => {
910             5 => { name => q{name}, prefix => q{} },
911             6 => { name => q{value}, prefix => q{} },
912             7 => { name => q{name}, prefix => q{NAME} },
913             8 => { name => q{name}, prefix => q{NAP-ADDRESS} },
914             9 => { name => q{name}, prefix => q{NAP-ADDRTYPE} },
915             10 => { name => q{name}, prefix => q{CALLTYPE} },
916             11 => { name => q{name}, prefix => q{VALIDUNTIL} },
917             12 => { name => q{name}, prefix => q{AUTHTYPE} },
918             13 => { name => q{name}, prefix => q{AUTHNAME} },
919             14 => { name => q{name}, prefix => q{AUTHSECRET} },
920             15 => { name => q{name}, prefix => q{LINGER} },
921             16 => { name => q{name}, prefix => q{BEARER} },
922             17 => { name => q{name}, prefix => q{NAPID} },
923             18 => { name => q{name}, prefix => q{COUNTRY} },
924             19 => { name => q{name}, prefix => q{NETWORK} },
925             20 => { name => q{name}, prefix => q{INTERNET} },
926             21 => { name => q{name}, prefix => q{PROXY-ID} },
927             22 => { name => q{name}, prefix => q{PROXY-PROVIDER-ID} },
928             23 => { name => q{name}, prefix => q{DOMAIN} },
929             24 => { name => q{name}, prefix => q{PROVURL} },
930             25 => { name => q{name}, prefix => q{PXAUTH-TYPE} },
931             26 => { name => q{name}, prefix => q{PXAUTH-ID} },
932             27 => { name => q{name}, prefix => q{PXAUTH-PW} },
933             28 => { name => q{name}, prefix => q{STARTPAGE} },
934             29 => { name => q{name}, prefix => q{BASAUTH-ID} },
935             30 => { name => q{name}, prefix => q{BASAUTH-PW} },
936             31 => { name => q{name}, prefix => q{PUSHENABLED} },
937             32 => { name => q{name}, prefix => q{PXADDR} },
938             33 => { name => q{name}, prefix => q{PXADDRTYPE} },
939             34 => { name => q{name}, prefix => q{TO-NAPID} },
940             35 => { name => q{name}, prefix => q{PORTNBR} },
941             36 => { name => q{name}, prefix => q{SERVICE} },
942             37 => { name => q{name}, prefix => q{LINKSPEED} },
943             38 => { name => q{name}, prefix => q{DNLINKSPEED} },
944             39 => { name => q{name}, prefix => q{LOCAL-ADDR} },
945             40 => { name => q{name}, prefix => q{LOCAL-ADDRTYPE} },
946             41 => { name => q{name}, prefix => q{CONTEXT-ALLOW} },
947             42 => { name => q{name}, prefix => q{TRUST} },
948             43 => { name => q{name}, prefix => q{MASTER} },
949             44 => { name => q{name}, prefix => q{SID} },
950             45 => { name => q{name}, prefix => q{SOC} },
951             46 => { name => q{name}, prefix => q{WSP-VERSION} },
952             47 => { name => q{name}, prefix => q{PHYSICAL-PROXY-ID} },
953             48 => { name => q{name}, prefix => q{CLIENT-ID} },
954             49 => { name => q{name}, prefix => q{DELIVERY-ERR-SDU} },
955             50 => { name => q{name}, prefix => q{DELIVERY-ORDER} },
956             51 => { name => q{name}, prefix => q{TRAFFIC-CLASS} },
957             52 => { name => q{name}, prefix => q{MAX-SDU-SIZE} },
958             53 => { name => q{name}, prefix => q{MAX-BITRATE-UPLINK} },
959             54 => { name => q{name}, prefix => q{MAX-BITRATE-DNLINK} },
960             55 => { name => q{name}, prefix => q{RESIDUAL-BER} },
961             56 => { name => q{name}, prefix => q{SDU-ERROR-RATIO} },
962             57 => { name => q{name}, prefix => q{TRAFFIC-HANDL-PRIO} },
963             58 => { name => q{name}, prefix => q{TRANSFER-DELAY} },
964             59 => { name => q{name}, prefix => q{GUARANTEED-BITRATE-UPLINK} },
965             60 => { name => q{name}, prefix => q{GUARANTEED-BITRATE-DNLINK} },
966             61 => { name => q{name}, prefix => q{PXADDR-FQDN} },
967             62 => { name => q{name}, prefix => q{PPGAUTH-TYPE} },
968             69 => { name => q{version}, prefix => q{} },
969             70 => { name => q{version}, prefix => q{1.0} },
970             71 => { name => q{name}, prefix => q{PULLENABLED} },
971             72 => { name => q{name}, prefix => q{DNS-ADDR} },
972             73 => { name => q{name}, prefix => q{MAX-NUM-RETRY} },
973             74 => { name => q{name}, prefix => q{FIRST-RETRY-TIMEOUT} },
974             75 => { name => q{name}, prefix => q{REREG-THRESHOLD} },
975             76 => { name => q{name}, prefix => q{T-BIT} },
976             78 => { name => q{name}, prefix => q{AUTH-ENTITY} },
977             79 => { name => q{name}, prefix => q{SPI} },
978             80 => { name => q{type}, prefix => q{} },
979             81 => { name => q{type}, prefix => q{PXLOGICAL} },
980             82 => { name => q{type}, prefix => q{PXPHYSICAL} },
981             83 => { name => q{type}, prefix => q{PORT} },
982             84 => { name => q{type}, prefix => q{VALIDITY} },
983             85 => { name => q{type}, prefix => q{NAPDEF} },
984             86 => { name => q{type}, prefix => q{BOOTSTRAP} },
985             87 => { name => q{type}, prefix => q{VENDORCONFIG} },
986             88 => { name => q{type}, prefix => q{CLIENTIDENTITY} },
987             89 => { name => q{type}, prefix => q{PXAUTHINFO} },
988             90 => { name => q{type}, prefix => q{NAPAUTHINFO} },
989             91 => { name => q{type}, prefix => q{ACCESS} },
990             },
991             1 => {
992             5 => { name => q{name}, prefix => q{} },
993             6 => { name => q{value}, prefix => q{} },
994             7 => { name => q{name}, prefix => q{NAME} },
995             20 => { name => q{name}, prefix => q{INTERNET} },
996             28 => { name => q{name}, prefix => q{STARTPAGE} },
997             34 => { name => q{name}, prefix => q{TO-NAPID} },
998             35 => { name => q{name}, prefix => q{PORTNBR} },
999             36 => { name => q{name}, prefix => q{SERVICE} },
1000             80 => { name => q{type}, prefix => q{} },
1001             83 => { name => q{type}, prefix => q{PORT} },
1002             85 => { name => q{type}, prefix => q{APPLICATION} },
1003             86 => { name => q{type}, prefix => q{APPADDR} },
1004             87 => { name => q{type}, prefix => q{APPAUTH} },
1005             88 => { name => q{type}, prefix => q{CLIENTIDENTITY} },
1006             89 => { name => q{type}, prefix => q{RESOURCE} },
1007             },
1008             },
1009             attrvalue => {
1010             0 => {
1011             133 => q{IPV4},
1012             134 => q{IPV6},
1013             135 => q{E164},
1014             136 => q{ALPHA},
1015             137 => q{APN},
1016             138 => q{SCODE},
1017             139 => q{TETRA-ITSI},
1018             140 => q{MAN},
1019             144 => q{ANALOG-MODEM},
1020             145 => q{V.120},
1021             146 => q{V.110},
1022             147 => q{X.31},
1023             148 => q{BIT-TRANSPARENT},
1024             149 => q{DIRECT-ASYNCHRONOUS-DATA-SERVICE},
1025             154 => q{PAP},
1026             155 => q{CHAP},
1027             156 => q{HTTP-BASIC},
1028             157 => q{HTTP-DIGEST},
1029             158 => q{WTLS-SS},
1030             162 => q{GSM-USSD},
1031             163 => q{GSM-SMS},
1032             164 => q{ANSI-136-GUTS},
1033             165 => q{IS-95-CDMA-SMS},
1034             166 => q{IS-95-CDMA-CSD},
1035             167 => q{IS-95-CDMA-PACKET},
1036             168 => q{ANSI-136-CSD},
1037             169 => q{ANSI-136-GPRS},
1038             170 => q{GSM-CSD},
1039             171 => q{GSM-GPRS},
1040             172 => q{AMPS-CDPD},
1041             173 => q{PDC-CSD},
1042             174 => q{PDC-PACKET},
1043             175 => q{IDEN-SMS},
1044             176 => q{IDEN-CSD},
1045             177 => q{IDEN-PACKET},
1046             178 => q{FLEX/REFLEX},
1047             179 => q{PHS-SMS},
1048             180 => q{PHS-CSD},
1049             181 => q{TETRA-SDS},
1050             182 => q{TETRA-PACKET},
1051             183 => q{ANSI-136-GHOST},
1052             184 => q{MOBITEX-MPAK},
1053             185 => q{CDMA2000-1X-SIMPLE-IP},
1054             186 => q{CDMA2000-1X-MOBILE-IP},
1055             197 => q{AUTOBAUDING},
1056             202 => q{CL-WSP},
1057             203 => q{CO-WSP},
1058             204 => q{CL-SEC-WSP},
1059             205 => q{CO-SEC-WSP},
1060             206 => q{CL-SEC-WTA},
1061             207 => q{CO-SEC-WTA},
1062             },
1063             },
1064             },
1065             "-//WAPFORUM//DTD WTA-WML 1.2//EN" => {
1066             tag => {
1067             0 => {
1068             27 => q{pre},
1069             28 => q{a},
1070             29 => q{td},
1071             30 => q{tr},
1072             31 => q{table},
1073             32 => q{p},
1074             33 => q{postfield},
1075             34 => q{anchor},
1076             35 => q{access},
1077             36 => q{b},
1078             37 => q{big},
1079             38 => q{br},
1080             39 => q{card},
1081             40 => q{do},
1082             41 => q{em},
1083             42 => q{fieldset},
1084             43 => q{go},
1085             44 => q{head},
1086             45 => q{i},
1087             46 => q{img},
1088             47 => q{input},
1089             48 => q{meta},
1090             49 => q{noop},
1091             50 => q{prev},
1092             51 => q{onevent},
1093             52 => q{optgroup},
1094             53 => q{option},
1095             54 => q{refresh},
1096             55 => q{select},
1097             56 => q{small},
1098             57 => q{strong},
1099             59 => q{template},
1100             60 => q{timer},
1101             61 => q{u},
1102             62 => q{setvar},
1103             },
1104             1 => {
1105             63 => q{wta-wml},
1106             },
1107             },
1108             attrstart => {
1109             0 => {
1110             0 => { name => q{xml:space}, prefix => q{preserve} },
1111             10 => { name => q{align}, prefix => q{right} },
1112             11 => { name => q{align}, prefix => q{top} },
1113             12 => { name => q{alt}, prefix => q{} },
1114             13 => { name => q{content}, prefix => q{} },
1115             15 => { name => q{domain}, prefix => q{} },
1116             24 => { name => q{label}, prefix => q{} },
1117             25 => { name => q{localsrc}, prefix => q{} },
1118             26 => { name => q{maxlength}, prefix => q{} },
1119             27 => { name => q{method}, prefix => q{get} },
1120             28 => { name => q{method}, prefix => q{post} },
1121             29 => { name => q{mode}, prefix => q{nowrap} },
1122             30 => { name => q{mode}, prefix => q{wrap} },
1123             31 => { name => q{multiple}, prefix => q{false} },
1124             35 => { name => q{newcontext}, prefix => q{true} },
1125             36 => { name => q{onpick}, prefix => q{} },
1126             37 => { name => q{onenterbackward}, prefix => q{} },
1127             38 => { name => q{onenterforward}, prefix => q{} },
1128             39 => { name => q{ontimer}, prefix => q{} },
1129             40 => { name => q{optional}, prefix => q{false} },
1130             41 => { name => q{optional}, prefix => q{true} },
1131             42 => { name => q{path}, prefix => q{} },
1132             46 => { name => q{scheme}, prefix => q{} },
1133             47 => { name => q{sendreferer}, prefix => q{false} },
1134             49 => { name => q{size}, prefix => q{} },
1135             50 => { name => q{src}, prefix => q{} },
1136             51 => { name => q{ordered}, prefix => q{true} },
1137             52 => { name => q{ordered}, prefix => q{false} },
1138             53 => { name => q{tabindex}, prefix => q{} },
1139             54 => { name => q{title}, prefix => q{} },
1140             55 => { name => q{type}, prefix => q{} },
1141             57 => { name => q{type}, prefix => q{delete} },
1142             58 => { name => q{type}, prefix => q{help} },
1143             59 => { name => q{type}, prefix => q{password} },
1144             60 => { name => q{type}, prefix => q{onpick} },
1145             61 => { name => q{type}, prefix => q{onenterbackward} },
1146             62 => { name => q{type}, prefix => q{onenterforward} },
1147             63 => { name => q{type}, prefix => q{ontimer} },
1148             69 => { name => q{type}, prefix => q{options} },
1149             70 => { name => q{type}, prefix => q{prev} },
1150             71 => { name => q{type}, prefix => q{reset} },
1151             72 => { name => q{type}, prefix => q{text} },
1152             73 => { name => q{type}, prefix => q{vnd.} },
1153             74 => { name => q{href}, prefix => q{} },
1154             75 => { name => q{href}, prefix => q{http://} },
1155             76 => { name => q{href}, prefix => q{https://} },
1156             77 => { name => q{value}, prefix => q{} },
1157             78 => { name => q{vspace}, prefix => q{} },
1158             79 => { name => q{width}, prefix => q{} },
1159             82 => { name => q{align}, prefix => q{} },
1160             83 => { name => q{columns}, prefix => q{} },
1161             84 => { name => q{class}, prefix => q{} },
1162             85 => { name => q{id}, prefix => q{} },
1163             86 => { name => q{forua}, prefix => q{false} },
1164             87 => { name => q{forua}, prefix => q{true} },
1165             92 => { name => q{content}, prefix => q{application/vnd.wap.wmlc;charset=} },
1166             93 => { name => q{http-equiv}, prefix => q{Expires} },
1167             94 => { name => q{accesskey}, prefix => q{} },
1168             95 => { name => q{enctype}, prefix => q{} },
1169             97 => { name => q{enctype}, prefix => q{multipart/form-data} },
1170             },
1171             1 => {
1172             5 => { name => q{href}, prefix => q{wtai://} },
1173             6 => { name => q{href}, prefix => q{wtai://wp/mc;} },
1174             7 => { name => q{href}, prefix => q{wtai://wp/sd;} },
1175             8 => { name => q{href}, prefix => q{wtai://wp/ap;} },
1176             9 => { name => q{href}, prefix => q{wtai://ms/ec} },
1177             16 => { name => q{type}, prefix => q{wtaev-} },
1178             17 => { name => q{type}, prefix => q{wtaev-cc/} },
1179             18 => { name => q{type}, prefix => q{wtaev-cc/ic} },
1180             19 => { name => q{type}, prefix => q{wtaev-cc/cl} },
1181             20 => { name => q{type}, prefix => q{wtaev-cc/co} },
1182             21 => { name => q{type}, prefix => q{wtaev-cc/oc} },
1183             22 => { name => q{type}, prefix => q{wtaev-cc/cc} },
1184             23 => { name => q{type}, prefix => q{wtaev-cc/dtmf} },
1185             32 => { name => q{type}, prefix => q{wtaev-nt/} },
1186             33 => { name => q{type}, prefix => q{wtaev-nt/it} },
1187             34 => { name => q{type}, prefix => q{wtaev-nt/st} },
1188             48 => { name => q{type}, prefix => q{wtaev-pb/} },
1189             56 => { name => q{type}, prefix => q{wtaev-lg/} },
1190             80 => { name => q{type}, prefix => q{wtaev-ms/} },
1191             81 => { name => q{type}, prefix => q{wtaev-ms/ns} },
1192             88 => { name => q{type}, prefix => q{wtaev-gsm/} },
1193             89 => { name => q{type}, prefix => q{wtaev-gsm/ru} },
1194             90 => { name => q{type}, prefix => q{wtaev-gsm/ch} },
1195             91 => { name => q{type}, prefix => q{wtaev-gsm/ca} },
1196             96 => { name => q{type}, prefix => q{wtaev-pdc/} },
1197             104 => { name => q{type}, prefix => q{wtaev-ansi136/} },
1198             105 => { name => q{type}, prefix => q{wtaev-ansi136/ia} },
1199             106 => { name => q{type}, prefix => q{wtaev-ansi136/if} },
1200             112 => { name => q{type}, prefix => q{wtaev-cdma/} },
1201             },
1202             },
1203             attrvalue => {
1204             0 => {
1205             133 => q{.com/},
1206             134 => q{.edu/},
1207             135 => q{.net/},
1208             136 => q{.org/},
1209             137 => q{accept},
1210             138 => q{bottom},
1211             139 => q{clear},
1212             140 => q{delete},
1213             141 => q{help},
1214             142 => q{http://},
1215             143 => q{http://www.},
1216             144 => q{https://},
1217             145 => q{https://www.},
1218             147 => q{middle},
1219             148 => q{nowrap},
1220             149 => q{onpick},
1221             150 => q{onenterbackward},
1222             151 => q{onenterforward},
1223             152 => q{ontimer},
1224             153 => q{options},
1225             154 => q{password},
1226             155 => q{reset},
1227             157 => q{text},
1228             158 => q{top},
1229             159 => q{unknown},
1230             160 => q{wrap},
1231             161 => q{www.},
1232             },
1233             },
1234             },
1235             "-//W3C//DTD XHTML 1.0 Strict//EN" => {
1236             tag => {
1237             },
1238             attrstart => {
1239             },
1240             attrvalue => {
1241             },
1242             },
1243             "-//W3C//DTD XHTML 1.0 Transitional//EN" => {
1244             tag => {
1245             },
1246             attrstart => {
1247             },
1248             attrvalue => {
1249             },
1250             },
1251             "-//W3C//DTD XHTML 1.0 Frameset//EN" => {
1252             tag => {
1253             },
1254             attrstart => {
1255             },
1256             attrvalue => {
1257             },
1258             },
1259             "-//W3C//DTD XHTML 1.1//EN" => {
1260             tag => {
1261             },
1262             attrstart => {
1263             },
1264             attrvalue => {
1265             },
1266             },
1267             "-//W3C//DTD XHTML Basic 1.0//EN" => {
1268             tag => {
1269             },
1270             attrstart => {
1271             },
1272             attrvalue => {
1273             },
1274             },
1275             # From http://www.openmobilealliance.org/technical/release_program/docs/DRM/V1_0-20040625-A/OMA-Download-DRMREL-V1_0-20040615-A.pdf
1276             "-//OMA//DTD DRMREL 1.0//EN" => {
1277             tag => {
1278             0 => {
1279             5 => q{o-ex:rights},
1280             6 => q{o-ex:context},
1281             7 => q{o-dd:version},
1282             8 => q{o-dd:uid},
1283             9 => q{o-ex:agreement},
1284             10 => q{o-ex:asset},
1285             11 => q{ds:KeyInfo},
1286             12 => q{ds:KeyValue},
1287             13 => q{o-ex:permission},
1288             14 => q{o-dd:play},
1289             15 => q{o-dd:display},
1290             16 => q{o-dd:execute},
1291             17 => q{o-dd:print},
1292             18 => q{o-ex:constraint},
1293             19 => q{o-dd:count},
1294             20 => q{o-dd:datetime},
1295             21 => q{o-dd:start},
1296             22 => q{o-dd:end},
1297             23 => q{o-dd:interval},
1298             },
1299             },
1300             attrstart => {
1301             0 => {
1302             5 => { name => q{xmlns:o-ex}, prefix => q{} },
1303             6 => { name => q{xmlns:o-dd}, prefix => q{} },
1304             7 => { name => q{xmlns:ds}, prefix => q{} },
1305             },
1306             },
1307             attrvalue => {
1308             0 => {
1309             133 => q{http://odrl.net/1.1/ODRL-EX},
1310             134 => q{http://odrl.net/1.1/ODRL-DD},
1311             135 => q{http://www.w3.org/2000/09/xmldsig#/},
1312             },
1313             },
1314             },
1315             "-//WAPFORUM//DTD EMN 1.0//EN" => {
1316             tag => {
1317             0 => {
1318             5 => q{emn},
1319             },
1320             },
1321             attrstart => {
1322             0 => {
1323             5 => { name => q{timestamp}, prefix => q{} },
1324             6 => { name => q{mailbox}, prefix => q{} },
1325             7 => { name => q{mailbox}, prefix => q{mailat:} },
1326             8 => { name => q{mailbox}, prefix => q{pop://} },
1327             9 => { name => q{mailbox}, prefix => q{imap://} },
1328             10 => { name => q{mailbox}, prefix => q{http://} },
1329             11 => { name => q{mailbox}, prefix => q{http://www.} },
1330             12 => { name => q{mailbox}, prefix => q{https://} },
1331             13 => { name => q{mailbox}, prefix => q{https://www.} },
1332             },
1333             },
1334             attrvalue => {
1335             0 => {
1336             133 => q{.com},
1337             134 => q{.edu},
1338             135 => q{.net},
1339             136 => q{.org},
1340             },
1341             },
1342             },
1343             # From http://www.syncml.org/docs/syncml_represent_v101_20010615.pdf (describes
1344             # 1.0.1 which is mostly the same as 1.1)
1345             "-//SYNCML//DTD SyncML 1.1//EN" => {
1346             tag => {
1347             0 => {
1348             5 => q{Add},
1349             6 => q{Alert},
1350             7 => q{Archive},
1351             8 => q{Atomic},
1352             9 => q{Chal},
1353             10 => q{Cmd},
1354             11 => q{CmdID},
1355             12 => q{CmdRef},
1356             13 => q{Copy},
1357             14 => q{Cred},
1358             15 => q{Data},
1359             16 => q{Delete},
1360             17 => q{Exec},
1361             18 => q{Final},
1362             19 => q{Get},
1363             20 => q{Item},
1364             21 => q{Lang},
1365             22 => q{LocName},
1366             23 => q{LocURI},
1367             24 => q{Map},
1368             25 => q{MapItem},
1369             26 => q{Meta},
1370             27 => q{MsgID},
1371             28 => q{MsgRef},
1372             29 => q{NoResp},
1373             30 => q{NoResults},
1374             31 => q{Put},
1375             32 => q{Replace},
1376             33 => q{RespURI},
1377             34 => q{Results},
1378             35 => q{Search},
1379             36 => q{Sequence},
1380             37 => q{SessionID},
1381             38 => q{SftDel},
1382             39 => q{Source},
1383             40 => q{SourceRef},
1384             41 => q{Status},
1385             42 => q{Sync},
1386             43 => q{SyncBody},
1387             44 => q{SyncHdr},
1388             45 => q{SyncML},
1389             46 => q{Target},
1390             47 => q{TargetRef},
1391             # 48 => q{rsrvd}, # marked as reserved for future expansion in the spec
1392             49 => q{VerDTD},
1393             50 => q{VerProto},
1394             },
1395             },
1396             attrstart => {
1397             0 => {
1398             },
1399             },
1400             attrvalue => {
1401             0 => {
1402             },
1403             },
1404             },
1405             # From http://www.openmobilealliance.org/technical/release_program/docs/DS/V1_1_2-20040721-A/OMA-SyncML-DevInfo-V1_1_2-20040721-A.pdf
1406             "-//SYNCML//DTD DevInf 1.1//EN" => {
1407             tag => {
1408             0 => {
1409             5 => q{CTCap},
1410             6 => q{CTType},
1411             7 => q{DataStore},
1412             8 => q{DataType},
1413             9 => q{DevID},
1414             10 => q{DevInf},
1415             11 => q{DevTyp},
1416             12 => q{DisplayName},
1417             13 => q{DSMem},
1418             14 => q{Ext},
1419             15 => q{FwV},
1420             16 => q{HwV},
1421             17 => q{Man},
1422             18 => q{MaxGUIDSize},
1423             19 => q{MaxID},
1424             20 => q{MaxMem},
1425             21 => q{Mod},
1426             22 => q{OEM},
1427             23 => q{ParamName},
1428             24 => q{PropName},
1429             25 => q{Rx},
1430             26 => q{Rx-Pref},
1431             27 => q{SharedMem},
1432             28 => q{Size},
1433             29 => q{SourceRef},
1434             30 => q{SwV},
1435             31 => q{SyncCap},
1436             32 => q{SyncType},
1437             33 => q{Tx},
1438             34 => q{Tx-Pref},
1439             35 => q{ValEnum},
1440             36 => q{VerCT},
1441             37 => q{VerDTD},
1442             38 => q{Xnam},
1443             39 => q{Xval},
1444             40 => q{UTC},
1445             41 => q{SupportNumberOfChanges},
1446             42 => q{SupportLargeObjs},
1447             },
1448             },
1449             attrstart => {
1450             0 => {
1451             },
1452             },
1453             attrvalue => {
1454             0 => {
1455             },
1456             },
1457             },
1458             );
1459              
1460             =head1 ACCESSOR METHODS
1461              
1462             =head2 charset
1463              
1464             Returns the current charset, such as 'UTF-8'.
1465              
1466             =cut
1467              
1468 400     400 1 2023 sub charset { $_[0]->{charset} }
1469              
1470             =head2 publicid
1471              
1472             Returns the current public ID, which is the XML DTD identifier for this
1473             document, e.g. "-//WAPFORUM//DTD SI 1.0//EN".
1474              
1475             =cut
1476              
1477 295     295 1 5034 sub publicid { $_[0]->{publicid} }
1478              
1479             =head2 version
1480              
1481             Returns current version as a string, e.g. "1.3".
1482              
1483             =cut
1484              
1485 182     182 1 4998 sub version { shift->{version} }
1486              
1487             =head2 attribute_codepage
1488              
1489             Returns current attribute codepage. This is the table used for all
1490             document-specific attribute tag lookups, and defaults to 0.
1491              
1492             =cut
1493              
1494 352   50 352 1 2480 sub attribute_codepage { shift->{attribute_codepage} ||= 0 }
1495              
1496             =head2 tag_codepage
1497              
1498             Returns current tag codepage. This is the table used for all
1499             document-specific element tag lookups, and defaults to 0.
1500              
1501             =cut
1502              
1503 802   100 802 1 7012 sub tag_codepage { shift->{tag_codepage} ||= 0 }
1504              
1505             =head1 METHODS
1506              
1507             =head2 new
1508              
1509             Constructor. Ignores everything you give it.
1510              
1511             =cut
1512              
1513             sub new {
1514 182     182 1 418691 my $class = shift;
1515 182         1215 my $self = bless { queue => [], element => [] }, $class;
1516              
1517 182         805 $self->queue_start;
1518              
1519             # We apply these via handlers since we always want them to run first.
1520             $self->add_handler_for_event(
1521             version => sub {
1522 182     182   7312 my ($self, $version) = @_;
1523 182         421 my $major = 1 + (($version & 0xF0) >> 4);
1524 182         287 my $minor = ($version & 0x0F);
1525 182         826 $self->{version} = "${major}.$minor";
1526 182         419 $self;
1527             },
1528             publicid => sub {
1529 182     182   6185 my ($self, $publicid) = @_;
1530 182         577 my $type = $public_id{$publicid};
1531 182 50       440 die "Unknown type for $publicid" unless defined $type;
1532 182         506 $self->{ns} = $ns{$type};
1533 182         326 $self->{publicid} = $type;
1534 182         444 $self;
1535             },
1536             charset => sub {
1537 182     182   5756 my ($self, $charset) = @_;
1538 182         709 $self->{charset} = mib_to_charset_name($charset);
1539 182         2404 $self;
1540             },
1541 182         2423 );
1542 182         9388 $self;
1543             }
1544              
1545             =head2 mb_to_int
1546              
1547             Convert multi-byte sequence to an integer value.
1548              
1549             =cut
1550              
1551             sub mb_to_int {
1552 649     649 1 11634 my $self = shift;
1553 649         771 my $buffref = shift;
1554 649   100     2282 my $count = shift || 0;
1555 649 100       6646 return unless $$buffref =~ s/^.{$count}([\x80-\xFF]*[\x00-\x7F])//s;
1556              
1557 648         875 my $v = 0;
1558 648         3290 $v = ($v << 7) + (ord($_) & 0x7F) for split //, $1;
1559 648         1800 return $v;
1560             }
1561              
1562             =head2 decode_string
1563              
1564             Decodes the given string using the current L.
1565              
1566             =cut
1567              
1568             sub decode_string {
1569 400     400 1 3350 my $self = shift;
1570 400         841 return Encode::decode($self->charset, $_[0]);
1571             }
1572              
1573             =head2 encode_string
1574              
1575             Encodes the given string using the current L.
1576              
1577             =cut
1578              
1579             sub encode_string {
1580 0     0 1 0 my $self = shift;
1581 0         0 return Encode::encode($self->charset, $_[0]);
1582             }
1583              
1584             =head2 parse
1585              
1586             Parse as much as we can from the given buffer.
1587              
1588             Takes a single scalar ref as parameter, this should point to the buffer
1589             to be processed.
1590              
1591             =cut
1592              
1593             sub parse {
1594 196     196 1 14244 my $self = shift;
1595 196         437 my $buffref = shift;
1596 196         533 $self->process_queue($buffref);
1597             }
1598              
1599             =head2 queue_start
1600              
1601             Queue the initial items for parsing.
1602              
1603             =cut
1604              
1605             sub queue_start {
1606 182     182 1 276 my $self = shift;
1607 182         849 $self->push_queued(qw(version publicid charset strtbl body));
1608 182         256 return $self;
1609             }
1610              
1611             =head2 process_queue
1612              
1613             Process everything we can in the queue.
1614              
1615             Takes a single scalar ref as parameter, this should point to the buffer
1616             to be processed.
1617              
1618             =cut
1619              
1620 4     4   5282 use Data::Dumper;
  4         30585  
  4         17425  
1621             sub process_queue {
1622 196     196 1 250 my $self = shift;
1623 196         304 my $buffref = shift;
1624              
1625             ITEM:
1626 196         561 while(my $next = $self->next_queued) {
1627             # warn "next = $next, top element is " . Dumper $self->{element};
1628             # $self->as_hex($$buffref);
1629 3734 100       7427 last ITEM unless $self->parse_item($next, $buffref);
1630             }
1631             }
1632              
1633             =head2 next_queued
1634              
1635             Return current item in the queue (without removing it).
1636              
1637             =cut
1638              
1639             sub next_queued {
1640 3734     3734 1 5375 my $self = shift;
1641 3734         12545 $self->{queue}[0];
1642             }
1643              
1644             =head2 mark_item_complete
1645              
1646             Remove the current item from the queue.
1647              
1648             =cut
1649              
1650             sub mark_item_complete {
1651 2097     2097 1 2748 my $self = shift;
1652 2097         2329 shift @{$self->{queue}};
  2097         3814  
1653 2097         3211 $self;
1654             }
1655              
1656             =head2 push_queued
1657              
1658             Queue some more items. More of a shift than a push.
1659              
1660             =cut
1661              
1662             sub push_queued {
1663 1795     1795 1 2160 my $self = shift;
1664 1795         2028 unshift @{$self->{queue}}, @_;
  1795         5411  
1665 1795         2853 $self;
1666             }
1667              
1668             =head2 parse_item
1669              
1670             Parse the given item if we have a method for it.
1671              
1672             =cut
1673              
1674             sub parse_item {
1675 3734     3734 1 4379 my $self = shift;
1676 3734         5831 my $method = 'parse_' . shift;
1677 3734         10282 $self->$method(@_);
1678             }
1679              
1680             =head2 parse_version
1681              
1682             Deconstruct a version - single byte containing major in the high nybble, minor in
1683             the lower nybble.
1684              
1685             =cut
1686              
1687             sub parse_version {
1688 182     182 1 319 my ($self, $buffref) = @_;
1689 182 50       524 return unless length $$buffref;
1690              
1691 182         1489 my ($version) = unpack 'C1', substr $$buffref, 0, 1, '';
1692 182         453 $self->mark_item_complete;
1693 182         685 $self->invoke_event(version => $version);
1694 182         3424 return $self;
1695             }
1696              
1697             =head2 parse_publicid
1698              
1699             Look up the given public ID, which is either a token for a preset value or
1700             a reference to the string table.
1701              
1702             =cut
1703              
1704             sub parse_publicid {
1705 183     183 1 370 my ($self, $buffref) = @_;
1706 183 100       466 return unless length $$buffref;
1707 182         471 my $rslt = $self->mb_to_int($buffref);
1708 182 50       539 return unless defined $rslt;
1709              
1710 182         430 $self->mark_item_complete;
1711 182         572 $self->invoke_event(publicid => $rslt);
1712 182         3356 return $self;
1713             }
1714              
1715             =head2 parse_charset
1716              
1717             =cut
1718              
1719             sub parse_charset {
1720 183     183 1 329 my ($self, $buffref) = @_;
1721 183 100       421 return unless length $$buffref;
1722 182         461 my $rslt = $self->mb_to_int($buffref);
1723 182 50       458 return unless defined $rslt;
1724              
1725 182         416 $self->mark_item_complete;
1726 182         561 $self->invoke_event(charset => $rslt);
1727 182         2940 return $self;
1728             }
1729              
1730             =head2 parse_strtbl
1731              
1732             =cut
1733              
1734             sub parse_strtbl {
1735 182     182 1 262 my ($self, $buffref) = @_;
1736 182         469 $self->mark_item_complete;
1737 182         375 $self->push_queued(qw(strtbl_length));
1738 182         709 return $self;
1739             }
1740              
1741             =head2 parse_strtbl_length
1742              
1743             =cut
1744              
1745             sub parse_strtbl_length {
1746 183     183 1 309 my ($self, $buffref) = @_;
1747 183 100       411 return unless length $$buffref;
1748 182         387 my $rslt = $self->mb_to_int($buffref);
1749 182 50       441 return unless defined $rslt;
1750              
1751 182         387 $self->mark_item_complete;
1752 182         855 $self->{strtbl_length} = $rslt;
1753 182         355 $self->{strtbl} = '';
1754 182         577 $self->invoke_event(strtbl_length => $rslt);
1755 182 100       3070 $self->push_queued(qw(strtbl_data)) if $rslt;
1756 182         764 return $self;
1757             }
1758              
1759             =head2 parse_strtbl_data
1760              
1761             =cut
1762              
1763             sub parse_strtbl_data {
1764 20     20 1 42 my ($self, $buffref) = @_;
1765 20 50       76 return unless length $$buffref >= $self->{strtbl_length};
1766 20         95 my ($bytes) = substr $$buffref, 0, $self->{strtbl_length}, '';
1767              
1768             # don't forget to decode...
1769 20         130 $self->{strtbl} = join("\0", map $self->decode_string($_), split /\0/, $bytes) . "\0";
1770 20         1039 $self->mark_item_complete;
1771 20         75 $self->invoke_event(strtbl => $self->{strtbl});
1772 20         395 return $self;
1773             }
1774              
1775             =head2 parse_body
1776              
1777             =cut
1778              
1779             sub parse_body {
1780 183     183 1 355 my ($self, $buffref) = @_;
1781 183 100       422 return unless length $$buffref;
1782 182         385 my $v = ord substr $$buffref, 0, 1;
1783              
1784 182 50       458 die 'PI' if $v == TOKEN_PI;
1785              
1786 182         384 $self->push_queued(qw(element pi));
1787 182         708 return $self;
1788             }
1789              
1790             =head2 parse_pi
1791              
1792             =cut
1793              
1794             sub parse_pi {
1795 22     22 1 35 my ($self, $buffref) = @_;
1796 22 50       167 return unless length $$buffref;
1797 0         0 my $v = ord substr $$buffref, 0, 1;
1798              
1799 0 0       0 if($v == TOKEN_PI) {
1800 0         0 die 'PI';
1801 0         0 substr $$buffref, 0, 1, '';
1802 0         0 return $self;
1803             } else {
1804 0         0 $self->mark_item_complete;
1805 0         0 return $self;
1806             }
1807             }
1808              
1809             =head2 parse_attribute
1810              
1811             =cut
1812              
1813             sub parse_attribute {
1814 707     707 1 982 my ($self, $buffref) = @_;
1815 707 100       1756 return unless length $$buffref;
1816              
1817 703         1148 my $v = ord substr $$buffref, 0, 1;
1818              
1819             # No more attributes, move on to content if we have it
1820 703 100       1445 if($v == TOKEN_END) {
1821 193         319 substr $$buffref, 0, 1, '';
1822 193         415 $self->finish_attribute;
1823              
1824 193         376 $self->mark_item_complete;
1825 193         534 $self->invoke_event(end_attributes => );
1826 193         3824 $self->invoke_event(start_element => $self->{element}[-1]);
1827 193 100       3094 if($self->{has_content}) {
1828 76         182 $self->push_queued(qw(content));
1829             } else {
1830 117         157 $self->invoke_event(end_element => pop @{ $self->{element} });
  117         416  
1831             }
1832 193         2697 return $self;
1833             }
1834              
1835             # Start a new literal attribute
1836 510 50       972 if($v == TOKEN_LITERAL) {
1837 0         0 my $idx = $self->mb_to_int($buffref, 1);
1838 0 0       0 return unless defined $idx;
1839              
1840             # No prefix, literal attribute name
1841 0 0       0 die "undef literal?" unless defined(my $name = $self->tag_code_from_literal($idx));
1842 0         0 return $self->start_new_attribute(
1843             name => $name,
1844             value => '',
1845             );
1846             }
1847              
1848             # Try some attribute value parsing
1849 510 100       926 if($v == TOKEN_STR_I) {
1850 128 100       297 return unless defined(my $str = $self->termstr($buffref, 1));
1851 122         7111 $self->{attribute_value} .= $str;
1852 122         645 return $self;
1853             }
1854              
1855 382 100       709 if($v == TOKEN_STR_T) {
1856 17 50       51 return unless defined (my $idx = $self->mb_to_int($buffref, 1));
1857              
1858 17         109 my $txt = substr $self->{strtbl}, $idx, index($self->{strtbl}, "\0", $idx) - $idx;
1859 17         33 $self->{attribute_value} .= $txt;
1860 17         90 return $self;
1861             }
1862              
1863 365 100       660 if($v == TOKEN_OPAQUE) {
1864 13 50       44 return unless defined(my $len = $self->mb_to_int($buffref, 1));
1865              
1866 13         34 my $str = substr $$buffref, 0, $len, '';
1867 13         41 $self->{attribute_value} .= $self->decode_string($str);
1868 13         654 return $self;
1869             }
1870              
1871             # We're down to either a start or a value tag from our document namespace, check
1872             # for codepage switch request then look 'em up
1873 352 50       722 $self->switch_attribute_codepage($buffref) if $self->should_switch_codepage($buffref);
1874              
1875 352         771 my $code = $self->next_attribute_item_from_buffer($buffref);
1876 352 100       940 return unless defined $code;
1877              
1878 348 100       804 if(ref $code) {
1879 305         1033 return $self->start_new_attribute(
1880             name => $code->{name},
1881             value => $code->{prefix},
1882             );
1883             } else {
1884 43         92 $self->{attribute_value} .= $code;
1885 43         233 return $self;
1886             }
1887             }
1888              
1889             sub as_hex {
1890 0     0 0 0 my $self = shift;
1891 0         0 my $data = shift;
1892 0         0 warn " hex: " . join ' ', map sprintf('%02x', ord($_)), split //, $data;
1893 0         0 $self;
1894             }
1895              
1896              
1897             sub finish_attribute {
1898 304     304 0 603 my $self = shift;
1899 304         1174 $self->{element}[-1]{Attributes}{$self->{attribute_name}} = $self->{attribute_value};
1900 304         1043 $self->invoke_event(attribute => $self->{attribute_name}, $self->{attribute_value});
1901 304         6298 undef $self->{attribute_name};
1902 304         437 return $self;
1903             }
1904              
1905             sub start_new_attribute {
1906 305     305 0 444 my $self = shift;
1907 305         1610 my %args = @_;
1908 305 100       882 $self->finish_attribute if defined $self->{attribute_name};
1909 305 50       816 $self->{attribute_name} = exists $args{name} ? $args{name} : '';
1910 305 50       965 $self->{attribute_value} = exists $args{value} ? $args{value} : '';
1911 305         1655 return $self;
1912             }
1913              
1914             =head2 parse_attrvalue
1915              
1916             =cut
1917              
1918             sub parse_attrvalue {
1919 0     0 1 0 my $self = shift;
1920 0         0 my $buffref = shift;
1921 0 0       0 return unless length $$buffref;
1922              
1923 0         0 my $v = substr $$buffref, 0, 1;
1924 0 0       0 $self->switch_attribute_codepage($buffref) if $self->should_switch_codepage($buffref);
1925              
1926 0 0       0 if(ord($v) == TOKEN_STR_I) {
    0          
    0          
1927 0 0       0 return unless defined(my $str = $self->termstr($buffref, 1));
1928 0         0 $self->{attribute_value} .= $str;
1929 0         0 return $self;
1930             } elsif(ord($v) == TOKEN_STR_T) {
1931 0         0 die "Table ref!";
1932             } elsif(ord($v) == TOKEN_OPAQUE) {
1933 0 0       0 return unless defined(my $len = $self->mb_to_int($buffref, 1));
1934              
1935 0         0 my $str = substr $$buffref, 0, $len, '';
1936 0         0 $self->{attribute_value} .= $self->decode_string($str);
1937 0         0 return $self;
1938             }
1939 0 0       0 if(defined(my $start = $self->attrvalue_from_id(ord($v)))) {
1940 0         0 $self->{attribute_value} .= $start;
1941 0         0 substr $$buffref, 0, 1, '';
1942 0         0 return $self;
1943             }
1944 0         0 $self->invoke_event(attribute => $self->{attribute_name}, $self->{attribute_value});
1945 0         0 $self->{element}[-1]{Attributes}{$self->{attribute_name}} = $self->{attribute_value};
1946 0         0 $self->mark_item_complete;
1947 0         0 $self->push_queued(qw(attribute));
1948 0         0 $self;
1949             }
1950              
1951             =head2 parse_element
1952              
1953             =cut
1954              
1955             sub parse_element {
1956 689     689 1 948 my ($self, $buffref) = @_;
1957 689 50       1434 return unless length $$buffref;
1958              
1959 689         2286 my $v = ord substr $$buffref, 0, 1;
1960              
1961             # ([switchPage] stag) - since the former is optional, doesn't matter if we have no stag
1962             # to match because we'll hit it next time around
1963 689 100       1396 $self->switch_tag_codepage($buffref) if $self->should_switch_codepage($buffref);
1964              
1965 689 50       2756 if($v == TOKEN_LITERAL) {
    50          
    50          
    50          
1966             # Some literal thing from the string table
1967 0         0 die "Have LITERAL\n";
1968             } elsif($v == TOKEN_LITERAL_A) {
1969             # Unknown tag, with attributes and no content
1970 0         0 die "Have LITERAL attrib\n";
1971             } elsif($v == TOKEN_LITERAL_C) {
1972             # Unknown tag, with content and no attributes
1973             # Defer until we get the full index
1974 0         0 my $idx = $self->mb_to_int($buffref, 1);
1975 0 0       0 return unless defined $idx;
1976              
1977 0         0 my $tag = $self->tag_code_from_literal($idx);
1978             # substr $self->{strtbl}, $idx, index $self->{strtbl}, "\0", $idx;
1979 0         0 $self->{has_content} = 1;
1980 0         0 $self->{has_attributes} = 0;
1981 0 0 0     0 die "No tag??" unless defined $tag && length $tag;
1982 0         0 push @{ $self->{element} }, {
  0         0  
1983             Name => $tag,
1984             LocalName => $tag,
1985             Attributes => { },
1986             };
1987             # we're all done, time for the content handler to deal with the rest
1988 0         0 $self->mark_item_complete;
1989 0         0 $self->push_queued(qw(content));
1990 0         0 $self->invoke_event(start_element => $self->{element}[-1]);
1991 0         0 return $self;
1992             } elsif($v == TOKEN_LITERAL_AC) {
1993 0         0 die "Have LITERAL attrib+content\n";
1994             }
1995              
1996 689         857 my $tag_id = $v & 0x3F;
1997              
1998             # Not literal, must be a tag
1999 689         1157 substr $$buffref, 0, 1, '';
2000 689         2097 my $tag = $self->{ns}{tag}{$self->tag_codepage}{$tag_id};
2001 689 100       1951 die "Tag $tag_id is not defined for " . $self->publicid . " in codepage " . $self->tag_codepage unless defined $tag;
2002              
2003             # Drop us from the stack...
2004 576         1219 $self->mark_item_complete;
2005              
2006 576         629 push @{ $self->{element} }, {
  576         2502  
2007             Name => $tag,
2008             LocalName => $tag,
2009             Attributes => { },
2010             };
2011              
2012 576         1166 $self->{has_attributes} = $v & 0x80;
2013 576         934 $self->{has_content} = $v & 0x40;
2014              
2015             # Content queued first because we're in LIFO order
2016 576 100       1058 if($self->{has_content}) {
2017 449         841 $self->push_queued(qw(content));
2018             } else {
2019 127 100       449 $self->invoke_event(end_element => pop @{ $self->{element} }) unless $self->{has_attributes};
  6         19  
2020             }
2021              
2022             # Attributes?
2023 576 100       1234 if($self->{has_attributes}) {
2024 197         412 $self->push_queued(qw(attribute));
2025             } else {
2026 379         1227 $self->invoke_event(start_element => $self->{element}[-1]);
2027             }
2028              
2029 576         6554 $self->invoke_event(element => $tag);
2030 576         10748 return $self;
2031             }
2032              
2033             sub termstr {
2034 319     319 0 469 my ($self, $buffref, $idx) = @_;
2035 319   50     559 $idx ||= 0;
2036 319 100       867 return unless (my $end = index $$buffref, "\0", $idx) >= 0;
2037              
2038             # Account for initial offset
2039 313         365 $end -= $idx;
2040              
2041             # Drop prefix
2042 313         434 substr $$buffref, 0, $idx, '';
2043              
2044             # Extract our string without including the terminator
2045 313         588 my $str = substr $$buffref, 0, $end, '';
2046              
2047             # then drop the terminator as well
2048 313         411 substr $$buffref, 0, length("\0"), '';
2049              
2050             # make sure we decode from whatever the original charset was
2051 313         624 return $self->decode_string($str);
2052             }
2053              
2054             sub parse_content {
2055 1200     1200 0 1704 my ($self, $buffref) = @_;
2056 1200 100       2644 return unless length $$buffref;
2057              
2058 1157         1751 my $v = ord substr $$buffref, 0, 1;
2059              
2060 1157 100       8735 if($v == TOKEN_STR_I) {
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
2061 191 50       353 return unless defined(my $txt = $self->termstr($buffref, 1));
2062 191         7770 $self->invoke_event(characters => { Data => $txt });
2063 191         3220 return $self;
2064             } elsif($v == TOKEN_STR_T) {
2065 61 50       123 return unless defined (my $idx = $self->mb_to_int($buffref, 1));
2066              
2067 61         301 my $txt = substr $self->{strtbl}, $idx, index($self->{strtbl}, "\0", $idx) - $idx;
2068 61         243 $self->invoke_event(characters => { Data => $txt });
2069 61         1034 return $self;
2070             } elsif(grep $v == $_, TOKEN_EXT_I_0, TOKEN_EXT_I_1, TOKEN_EXT_I_2) {
2071 0         0 die "Inline extension";
2072             } elsif(grep $v == $_, TOKEN_EXT_T_0, TOKEN_EXT_T_1, TOKEN_EXT_T_2) {
2073 0         0 die "strtbl extension";
2074             } elsif(grep $v == $_, TOKEN_EXT_0, TOKEN_EXT_1, TOKEN_EXT_2) {
2075 0         0 die "extension";
2076             } elsif($v == TOKEN_ENTITY) {
2077 0         0 die "entity";
2078             } elsif($v == TOKEN_PI) {
2079 0         0 die "PI";
2080             } elsif($v == TOKEN_OPAQUE) {
2081 0         0 die "opaque";
2082             } elsif($v == TOKEN_END) {
2083 398         537 substr $$buffref, 0, 1, '';
2084 398         928 $self->mark_item_complete;
2085 398         511 $self->invoke_event(end_element => pop @{ $self->{element} });
  398         1254  
2086 398         6921 return $self;
2087             }
2088              
2089 507         1062 $self->push_queued(qw(element));
2090 507         2067 return $self;
2091             }
2092              
2093             =head2 tag_from_id
2094              
2095             =cut
2096              
2097             sub tag_from_id {
2098 0     0 1 0 my $self = shift;
2099 0         0 my $id = shift;
2100 0         0 $self->{ns}{tag}{$self->tag_codepage}{$id}
2101             }
2102              
2103             =head2 attrstart_from_id
2104              
2105             =cut
2106              
2107             sub attrstart_from_id {
2108 0     0 1 0 my $self = shift;
2109 0         0 my $id = shift;
2110 0         0 $self->{ns}{attrstart}{$self->attribute_codepage}{$id}
2111             }
2112              
2113             =head2 attrvalue_from_id
2114              
2115             =cut
2116              
2117             sub attrvalue_from_id {
2118 0     0 1 0 my $self = shift;
2119 0         0 my $id = shift;
2120 0         0 $self->{ns}{attrvalue}{$self->attribute_codepage}{$id}
2121             }
2122              
2123             sub should_switch_codepage {
2124 1050     1050 0 1553 my $self = shift;
2125 1050         1201 my $buffref = shift;
2126              
2127             # Need at least 2 bytes
2128 1050 100       2037 return unless length $$buffref >= 2;
2129 1047 100       4014 return unless ord(substr $$buffref, 0, 1) == TOKEN_SWITCH_PAGE;
2130 18         79 1;
2131             }
2132              
2133             sub codepage_index_from_buffer {
2134 9     9 0 15 my $self = shift;
2135 9         11 my $buffref = shift;
2136 9 50       19 return unless $self->should_switch_codepage($buffref);
2137              
2138             # We want to take both bytes out of the buffer, and use the second
2139             # as our new codepage index.
2140 9         48 my ($v) = reverse split //, substr $$buffref, 0, 2, '';
2141 9         33 return ord $v;
2142             }
2143              
2144             sub switch_attribute_codepage {
2145 0     0 0 0 my $self = shift;
2146 0 0       0 return unless defined(my $v = $self->codepage_index_from_buffer(@_));
2147              
2148 0         0 $self->{attribute_codepage} = $v;
2149 0         0 return 1;
2150             }
2151              
2152             sub switch_tag_codepage {
2153 9     9 0 16 my $self = shift;
2154 9 50       38 return unless defined(my $v = $self->codepage_index_from_buffer(@_));
2155              
2156 9         15 $self->{tag_codepage} = $v;
2157 9         15 return 1;
2158             }
2159              
2160             =head2 dump_from_buffer
2161              
2162             Given a buffer of data, will report as much information as we can extract.
2163              
2164             =cut
2165              
2166             sub dump_from_buffer {
2167 0     0 1 0 my $self = shift;
2168 0         0 my $buffer = shift;
2169 0         0 my $out = '';
2170             try {
2171 0 0   0   0 die "Did not have header" unless length $buffer >= 4;
2172             $self->add_handler_for_event(
2173             version => sub {
2174 0         0 my ($self, $version) = @_;
2175 0         0 $out .= "Version raw [$version] parsed as [" . $self->version . "]\n";
2176 0         0 0;
2177             },
2178             publicid => sub {
2179 0         0 my ($self, $publicid) = @_;
2180 0         0 $out .= "Public ID raw [$publicid] parsed as [" . $self->publicid . "]\n";
2181 0         0 0;
2182             },
2183             charset => sub {
2184 0         0 my ($self, $charset) = @_;
2185 0         0 $out .= "Charset raw [$charset] parsed as [" . $self->charset . "]\n";
2186 0         0 0;
2187             },
2188             strtbl_length => sub {
2189 0         0 my ($self, $len) = @_;
2190 0         0 $out .= "String table contains [$len] entries\n";
2191 0         0 0;
2192             },
2193             element => sub {
2194 0         0 my ($self, $tag) = @_;
2195 0         0 $out .= "Element [$tag]\n";
2196 0         0 $self;
2197             },
2198             strtbl_data => sub {
2199 0         0 my ($self, $str) = @_;
2200 0         0 $out .= " string [$_]\n" for split /\0/, $str;
2201 0         0 0;
2202             },
2203             start_element => sub {
2204 0         0 my ($self, $el) = @_;
2205 0         0 $out .= "Element start: [" . $el->{Name} . "], " . @{$self->{element}} . " deep\n";
  0         0  
2206 0         0 for my $k (sort keys %{$el->{Attributes}}) {
  0         0  
2207 0         0 my $v = $el->{Attributes}{$k};
2208 0         0 $out .= " attrib $k => $v\n";
2209             }
2210 0         0 $self;
2211             },
2212             end_element => sub {
2213 0         0 my ($self, $el) = @_;
2214 0         0 $out .= "Element end: [" . $el->{Name} . "], " . @{$self->{element}} . " deep\n";
  0         0  
2215 0         0 $self;
2216             },
2217             characters => sub {
2218 0         0 my ($self, $data) = @_;
2219 0         0 $out .= " character data: [" . $data->{Data} . "]\n";
2220 0         0 $self;
2221             },
2222 0         0 );
2223 0         0 $self->parse(\(my $data = substr $buffer, 0, 4, ''));
2224 0 0       0 die "Header parse did not extract everything" if length $data;
2225 0         0 $self->parse(\$buffer);
2226             } catch {
2227 0     0   0 die "Had error [$_] while parsing data, result so far is\n$out";
2228 0         0 };
2229 0         0 $out;
2230             }
2231              
2232             =head2 next_tag_code_from_buffer
2233              
2234             Extracts the next tag code from the buffer, returning an
2235             empty list if none found.
2236              
2237             =cut
2238              
2239             sub next_tag_code_from_buffer {
2240 0     0 1 0 my $self = shift;
2241 0         0 my $buffref = shift;
2242 0 0       0 return unless length $$buffref;
2243              
2244 0         0 my $byte = substr $$buffref, 0, 1;
2245 0         0 my $has_attributes = $byte & 0x80;
2246 0         0 my $has_content = $byte & 0x40;
2247 0         0 $byte &= 0x3F;
2248              
2249 0 0       0 if($byte == TOKEN_LITERAL) {
2250 0 0       0 return unless defined (my $idx = $self->mb_to_int($buffref, 1));
2251 0         0 return $self->tag_code_from_literal($idx);
2252             }
2253              
2254 0         0 substr $$buffref, 0, 1, '';
2255 0         0 return $byte;
2256             }
2257              
2258             =head2 next_attribute_item_from_buffer
2259              
2260             =cut
2261              
2262             sub next_attribute_item_from_buffer {
2263 352     352 1 448 my $self = shift;
2264 352         381 my $buffref = shift;
2265              
2266 352         609 my $byte = ord substr $$buffref, 0, 1, '';
2267              
2268             # This is an attribute value, see section 5.8.3 in WAP-192-WBXML-20010725-a
2269             # warn "CP = " . $self->attribute_codepage;
2270             # warn "byte = $byte\n";
2271             # warn Dumper $self->{ns};
2272 352 100       844 return $self->{ns}{attrvalue}{$self->attribute_codepage}{$byte} if $byte & 0x80;
2273             # ... otherwise a start hashref
2274 308         982 return $self->{ns}{attrstart}{$self->attribute_codepage}{$byte};
2275             }
2276              
2277             =pod
2278              
2279             Get tag
2280             Process attibutes if any
2281             Process content if any
2282              
2283              
2284             =cut
2285              
2286             sub tag_code_from_literal {
2287 0     0 0   my $self = shift;
2288 0           my $idx = shift;
2289 0           return substr $self->{strtbl}, $idx, index($self->{strtbl}, "\0", $idx) - $idx;
2290             # old version
2291 0           return substr $self->{strtbl}, $idx, index $self->{strtbl}, "\0", $idx;
2292             }
2293              
2294             1;
2295              
2296             __END__