File Coverage

src/xh_x2h.c
Criterion Covered Total %
statement 171 218 78.4
branch 404 2060 19.6
condition n/a
subroutine n/a
pod n/a
total 575 2278 25.2


line stmt bran cond sub pod time code
1             #include "xh_config.h"
2             #include "xh_core.h"
3              
4             static const char DEF_CONTENT_KEY[] = "content";
5              
6             XH_INLINE void
7 48           xh_x2h_xpath_update(xh_char_t *xpath, xh_char_t *name, size_t name_len)
8             {
9             size_t len;
10              
11 48           len = xh_strlen(xpath);
12 48 100         if (name != NULL) {
13 24 50         if ((len + name_len + 1) > XH_X2H_XPATH_MAX_LEN)
14 0           croak("XPath too long");
15              
16 24           xpath[len++] = '/';
17 138 100         for (;name_len--;) xpath[len++] = *name++;
18             }
19 24 50         else if (len == 0) {
20 0           croak("Can't update xpath, something wrong!");
21             }
22             else {
23 138 100         for (;--len && xpath[len] != '/';) {/* void */}
    100          
24             }
25 48           xpath[len] = '\0';
26              
27             xh_log_trace1("xpath: [%s]", xpath);
28 48           }
29              
30             XH_INLINE xh_bool_t
31 24           xh_x2h_match_node(xh_char_t *name, size_t name_len, SV *expr)
32             {
33             SSize_t i, l;
34             AV *av;
35             SV *fake_str;
36             xh_char_t *expr_str;
37             STRLEN expr_len;
38             REGEXP *re;
39             xh_bool_t matched;
40              
41             xh_log_trace2("match node: [%.*s]", name_len, name);
42              
43 24           fake_str = newSV(0);
44 24           matched = TRUE;
45              
46 24 100         if ( SvRXOK(expr) ) {
47 8           re = (REGEXP *) SvRX(expr);
48 8 50         if (re != NULL && pregexec(re, (char *) name, (char *) (name + name_len),
    100          
49             (char *) name, name_len, fake_str, 0)
50             ) {
51 6           goto MATCHED;
52             }
53             }
54 24 100         else if ( SvROK(expr) && SvTYPE(SvRV(expr)) == SVt_PVAV ) {
    50          
55 12           av = (AV *) SvRV(expr);
56 12           l = av_len(av);
57 26 100         for(i = 0; i <= l; i++) {
58 18           expr = *av_fetch(av, i, 0);
59 18 100         if ( SvRXOK(expr) ) {
60 3           re = (REGEXP *) SvRX(expr);
61 3 50         if (re != NULL && pregexec(re, (char *) name, (char *) (name + name_len),
    100          
62             (char *) name, name_len, fake_str, 0)
63             ) {
64 1           goto MATCHED;
65             }
66             }
67             else {
68 15 100         expr_str = (xh_char_t *) SvPVutf8(expr, expr_len);
69 15 100         if (name_len == expr_len && !xh_strncmp(name, expr_str, name_len)) {
    100          
70 3           goto MATCHED;
71             }
72             }
73             }
74             } else {
75 4 100         expr_str = (xh_char_t *) SvPVutf8(expr, expr_len);
76 4 100         if (name_len == expr_len && !xh_strncmp(name, expr_str, name_len)) {
    100          
77 1           goto MATCHED;
78             }
79             }
80              
81 13           matched = FALSE;
82              
83             MATCHED:
84 24           SvREFCNT_dec(fake_str);
85              
86 24           return matched;
87             }
88              
89             XH_INLINE void
90 3           xh_x2h_pass_matched_node(SV *cb, SV *val)
91             {
92 3           dSP;
93              
94 3           ENTER; SAVETMPS;
95 3 50         PUSHMARK(SP);
96 3 50         XPUSHs(val);
97 3           PUTBACK;
98              
99 3           (void) call_sv(cb, G_DISCARD);
100              
101 3 50         FREETMPS;
102 3           LEAVE;
103 3           }
104              
105             #define NEW_STRING(s, l) \
106             newSVpvn_utf8((const char *) (s), (l), ctx->opts.utf8)
107              
108             #define SET_STRING(v, s, l) \
109             sv_setpvn((v), (const char *) (s), (l)); \
110             if (ctx->opts.utf8) SvUTF8_on(v);
111              
112             #define CAT_STRING(v, s, l) \
113             sv_catpvn((v), (const char *) (s), (l)); \
114             if (ctx->opts.utf8) SvUTF8_on(v);
115              
116             #define SAVE_VALUE(lv, v , s, l) \
117             xh_log_trace2("save value: [%.*s]", l, s); \
118             if ( SvOK(v) ) { \
119             xh_log_trace0("add to array"); \
120             /* get array if value is reference to array */ \
121             if ( SvROK(v) && SvTYPE(SvRV(v)) == SVt_PVAV) { \
122             av = (AV *) SvRV(v); \
123             } \
124             /* create a new array and move value to array */ \
125             else { \
126             av = newAV(); \
127             *(lv) = newRV_noinc((SV *) av); \
128             av_store(av, 0, v); \
129             (v) = *(lv); \
130             } \
131             /* add value to array */ \
132             (lv) = av_store(av, av_len(av) + 1, NEW_STRING((s), (l))); \
133             } \
134             else { \
135             xh_log_trace0("set string"); \
136             SET_STRING((v), (s), (l)); \
137             } \
138              
139             #define _OPEN_TAG(s, l) \
140             val = *lval; \
141             /* if content exists that move to hash with 'content' key */ \
142             if ( !SvROK(val) || SvTYPE(SvRV(val)) == SVt_PVAV ) { \
143             *lval = newRV_noinc((SV *) newHV()); \
144             if (SvROK(val) || (SvOK(val) && SvCUR(val))) { \
145             (void) hv_store((HV *) SvRV(*lval), (const char *) content_key, (I32) content_key_len, val, 0);\
146             } \
147             else { \
148             SvREFCNT_dec(val); \
149             } \
150             val = *lval; \
151             } \
152             /* fetch existen or create empty hash entry */ \
153             lval = hv_fetch((HV *) SvRV(val), (const char *) (s), ctx->opts.utf8 ? -(l) : (l), 1);\
154             /* save as empty string */ \
155             val = *lval; \
156             SAVE_VALUE(lval, val, "", 0) \
157             if (++depth >= ctx->opts.max_depth) goto MAX_DEPTH_EXCEEDED; \
158             nodes[depth].lval = lval; \
159             nodes[depth].flags = XH_X2H_NODE_FLAG_NONE; \
160             if (depth > 1 && ctx->opts.force_array.enable && (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVAV) \
161             && (ctx->opts.force_array.always || xh_x2h_match_node(s, l, ctx->opts.force_array.expr))\
162             ) { \
163             nodes[depth].flags |= XH_X2H_NODE_FLAG_FORCE_ARRAY; \
164             } \
165             (s) = NULL;
166              
167             #define OPEN_TAG(s, l) \
168             xh_log_trace2("new tag: [%.*s]", l, s); \
169             flags |= XH_X2H_TEXT_NODE; \
170             if (real_depth == 0) { \
171             if (flags & XH_X2H_ROOT_FOUND) goto INVALID_XML; \
172             flags |= XH_X2H_ROOT_FOUND; \
173             } \
174             if (XH_X2H_FILTER_SEARCH(flags)) { \
175             xh_x2h_xpath_update(ctx->xpath, s, l); \
176             if (xh_x2h_match_node(ctx->xpath, xh_strlen(ctx->xpath), ctx->opts.filter.expr)) {\
177             xh_log_trace2("match node: [%.*s]", l, s); \
178             ctx->hash = newRV_noinc((SV *) newHV()); \
179             nodes[0].lval = lval = &ctx->hash; \
180             depth = 0; \
181             flags |= XH_X2H_FILTER_MATCHED; \
182             } \
183             } \
184             if (!XH_X2H_FILTER_SEARCH(flags)) { \
185             _OPEN_TAG(s, l) \
186             } \
187             real_depth++;
188              
189             #define _CLOSE_TAG \
190             val = *nodes[depth].lval; \
191             if (ctx->opts.force_content && !SvROK(val)) { \
192             lval = nodes[depth].lval; \
193             *lval = newRV_noinc((SV *) newHV()); \
194             (void) hv_store((HV *) SvRV(*lval), (const char *) content_key, (I32) content_key_len, val, 0);\
195             val = *lval; \
196             } \
197             if ((nodes[depth].flags & XH_X2H_NODE_FLAG_FORCE_ARRAY) \
198             && (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVAV) \
199             ) { \
200             lval = nodes[depth].lval; \
201             av = newAV(); \
202             *lval = newRV_noinc((SV *) av); \
203             av_store(av, 0, val); \
204             } \
205             lval = nodes[--depth].lval;
206              
207             #define CLOSE_TAG \
208             xh_log_trace0("close tag"); \
209             flags &= ~XH_X2H_TEXT_NODE; \
210             if (real_depth == 0) goto INVALID_XML; \
211             if (!XH_X2H_FILTER_SEARCH(flags)) { \
212             _CLOSE_TAG \
213             } \
214             if ((flags & XH_X2H_FILTER_MATCHED) && depth == 0) { \
215             xh_log_trace0("match node finished"); \
216             val = *nodes[0].lval; \
217             if (!ctx->opts.keep_root) { \
218             val = SvRV(val); \
219             hv_iterinit((HV *) val); \
220             val = hv_iterval((HV *) val, hv_iternext((HV *) val)); \
221             SvREFCNT_inc(val); \
222             SvREFCNT_dec(*nodes[0].lval); \
223             } \
224             if (ctx->opts.cb == NULL) { \
225             av_push((AV *) SvRV(ctx->result), val); \
226             } \
227             else { \
228             xh_x2h_pass_matched_node(ctx->opts.cb, val); \
229             SvREFCNT_dec(val); \
230             } \
231             flags ^= XH_X2H_FILTER_MATCHED; \
232             } \
233             if ((flags & (XH_X2H_FILTER_ENABLED | XH_X2H_FILTER_MATCHED)) == XH_X2H_FILTER_ENABLED) {\
234             xh_x2h_xpath_update(ctx->xpath, NULL, 0); \
235             } \
236             real_depth--;
237              
238             #define NEW_NODE_ATTRIBUTE(k, kl, v, vl) \
239             if (!XH_X2H_FILTER_SEARCH(flags)) { \
240             _OPEN_TAG(k, kl) \
241             _NEW_TEXT(v, vl) \
242             _CLOSE_TAG \
243             }
244              
245             #define _NEW_NODE_ATTRIBUTE(k, kl, v, vl) \
246             xh_log_trace4("new attr name: [%.*s] value: [%.*s]", kl, k, vl, v); \
247             /* create hash if not created already */ \
248             if ( !SvROK(*lval) ) { \
249             /* destroy empty old scalar (empty string) */ \
250             SvREFCNT_dec(*lval); \
251             *lval = newRV_noinc((SV *) newHV()); \
252             } \
253             /* save key/value */ \
254             (void) hv_store((HV *) SvRV(*lval), (const char *) (k), ctx->opts.utf8 ? -(kl) : (kl),\
255             NEW_STRING(v, vl), 0); \
256             (k) = (v) = NULL;
257              
258             #define NEW_XML_DECL_ATTRIBUTE(k, kl, v, vl) \
259             xh_log_trace4("new xml decl attr name: [%.*s] value: [%.*s]", kl, k, vl, v);\
260             /* save encoding parameter to converter context if param found */ \
261             if ((kl) == (sizeof("encoding") - 1) && \
262             xh_strncmp((k), XH_CHAR_CAST "encoding", sizeof("encoding") - 1) == 0) {\
263             xh_str_range_copy(ctx->encoding, XH_CHAR_CAST (v), vl, XH_PARAM_LEN);\
264             } \
265             (k) = (v) = NULL;
266              
267             #define NEW_ATTRIBUTE(k, kl, v, vl) NEW_NODE_ATTRIBUTE(k, kl, v, vl)
268              
269             #define _NEW_TEXT(s, l) \
270             val = *lval; \
271             if ( SvROK(val) ) { \
272             xh_log_trace0("add to array"); \
273             /* add content to array*/ \
274             if (SvTYPE(SvRV(val)) == SVt_PVAV) { \
275             av = (AV *) SvRV(val); \
276             av_store(av, av_len(av) + 1, NEW_STRING(s, l)); \
277             } \
278             /* save content to hash with "content" key */ \
279             else { \
280             xh_log_trace0("save to hash"); \
281             lval = hv_fetch((HV *) SvRV(val), (const char *) content_key, (I32) content_key_len, 1);\
282             val = *lval; \
283             SAVE_VALUE(lval, val, s, l) \
284             lval = nodes[depth].lval; \
285             } \
286             } \
287             else if (SvCUR(val) && !ctx->opts.merge_text) { \
288             xh_log_trace0("create a new array"); \
289             xh_log_trace1("create a new array val: %s", SvPV_nolen(val)); \
290             xh_log_trace3("create a new array svrok: %d type: %d rtype: %d", SvROK(val), SvTYPE(val), SvTYPE(SvRV(val)));\
291             /* content already exists, create a new array and move*/ \
292             /* old and new content to array */ \
293             av = newAV(); \
294             *lval = newRV_noinc((SV *) av); \
295             av_store(av, 0, val); \
296             av_store(av, av_len(av) + 1, NEW_STRING(s, l)); \
297             } \
298             else { \
299             xh_log_trace0("concat"); \
300             /* concatenate with previous string */ \
301             CAT_STRING(val, s, l) \
302             } \
303              
304             #define NEW_TEXT(s, l) \
305             xh_log_trace2("new text: [%.*s]", l, s); \
306             if (real_depth == 0) goto INVALID_XML; \
307             if (!XH_X2H_FILTER_SEARCH(flags)) { \
308             _NEW_TEXT(s, l) \
309             }
310              
311             #define NEW_COMMENT(s, l) (s) = NULL;
312              
313             #define NEW_CDATA(s, l) NEW_TEXT(s, l)
314              
315             #define CHECK_EOF_WITH_CHUNK(loop) \
316             if (cur >= eof || *cur == '\0') { \
317             eof = cur; \
318             if (terminate) goto PPCAT(loop, _FINISH); \
319             ctx->state = PPCAT(loop, _START); \
320             goto CHUNK_FINISH; \
321             } \
322              
323             #define CHECK_EOF_WITHOUT_CHUNK(loop) \
324             if (cur >= eof || *cur == '\0') goto PPCAT(loop, _FINISH); \
325              
326             #define CHECK_EOF(loop) CHECK_EOF_WITH_CHUNK(loop)
327              
328             #define DO(loop) \
329             PPCAT(loop, _START): \
330             CHECK_EOF(loop) \
331             c = *cur++; \
332             xh_log_trace3("'%c'=[0x%X] %s start", c, c, STRINGIZE(loop)); \
333             switch (c) {
334              
335             #define _DO(loop) \
336             PPCAT(loop, _START): \
337             CHECK_EOF_WITHOUT_CHUNK(loop) \
338             c = *cur++; \
339             xh_log_trace3("'%c'=[0x%X] %s start", c, c, STRINGIZE(loop)); \
340             switch (c) {
341              
342             #define END(loop) \
343             } \
344             xh_log_trace1(" %s end", STRINGIZE(loop)); \
345             goto PPCAT(loop, _START); \
346             PPCAT(loop, _FINISH):
347              
348             #define EXPECT_ANY(desc) \
349             default: xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
350              
351             #define EXPECT_CHAR(desc, c1) \
352             case c1: xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
353              
354             #define EXPECT_BLANK_WO_CR(desc) \
355             case ' ': case '\t': case '\n': \
356             xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
357              
358             #define EXPECT_BLANK(desc) \
359             case ' ': case '\t': case '\n': case '\r': \
360             xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
361              
362             #define EXPECT_DIGIT(desc) \
363             case '0': case '1': case '2': case '3': case '4': \
364             case '5': case '6': case '7': case '8': case '9': \
365             xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
366              
367             #define EXPECT_HEX_CHAR_LC(desc) \
368             case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': \
369             xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
370              
371             #define EXPECT_HEX_CHAR_UC(desc) \
372             case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': \
373             xh_log_trace3("'%c'=[0x%X] - %s expected", c, c, desc);
374              
375             #define SKIP_BLANK \
376             EXPECT_BLANK("skip blank") break;
377              
378             #define SCAN2(loop, c1, c2) \
379             DO(PPCAT(loop, _1)) EXPECT_CHAR(STRINGIZE(c1), c1) \
380             DO(PPCAT(loop, _2)) EXPECT_CHAR(STRINGIZE(c2), c2)
381              
382             #define END2(loop, stop) \
383             EXPECT_ANY("wrong character") goto stop; \
384             END(PPCAT(loop, _2)) goto stop; \
385             EXPECT_ANY("wrong character") goto stop; \
386             END(PPCAT(loop, _1))
387              
388             #define SCAN3(loop, c1, c2, c3) \
389             DO(PPCAT(loop, _1)) EXPECT_CHAR(STRINGIZE(c1), c1) \
390             DO(PPCAT(loop, _2)) EXPECT_CHAR(STRINGIZE(c2), c2) \
391             DO(PPCAT(loop, _3)) EXPECT_CHAR(STRINGIZE(c3), c3)
392              
393             #define END3(loop, stop) \
394             EXPECT_ANY("wrong character") goto stop; \
395             END(PPCAT(loop, _3)) goto stop; \
396             EXPECT_ANY("wrong character") goto stop; \
397             END(PPCAT(loop, _2)) goto stop; \
398             EXPECT_ANY("wrong character") goto stop; \
399             END(PPCAT(loop, _1))
400              
401             #define SCAN5(loop, c1, c2, c3, c4, c5) \
402             SCAN3(PPCAT(loop, _1), c1, c2, c3) \
403             SCAN2(PPCAT(loop, _2), c4, c5)
404              
405             #define END5(loop, stop) \
406             END2(PPCAT(loop, _2), stop) \
407             END3(PPCAT(loop, _1), stop)
408              
409             #define SCAN6(loop, c1, c2, c3, c4, c5, c6) \
410             SCAN3(PPCAT(loop, _1), c1, c2, c3) \
411             SCAN3(PPCAT(loop, _2), c4, c5, c6)
412              
413             #define END6(loop, stop) \
414             END3(PPCAT(loop, _2), stop) \
415             END3(PPCAT(loop, _1), stop)
416              
417             #define SEARCH_END_TAG \
418             EXPECT_CHAR("end tag", '>') \
419             goto PARSE_CONTENT; \
420             EXPECT_CHAR("self closing tag", '/') \
421             CLOSE_TAG \
422             DO(SEARCH_END_TAG) \
423             EXPECT_CHAR("end tag", '>') \
424             goto PARSE_CONTENT; \
425             EXPECT_ANY("wrong character") \
426             goto INVALID_XML; \
427             END(SEARCH_END_TAG) \
428             goto INVALID_XML;
429              
430             #define SEARCH_NODE_ATTRIBUTE_VALUE(loop, top_loop, quot) \
431             EXPECT_CHAR("start attr value", quot) \
432             content = cur; \
433             flags &= ~XH_X2H_NEED_NORMALIZE; \
434             DO(PPCAT(loop, _END_ATTR_VALUE)) \
435             EXPECT_CHAR("attr value end", quot) \
436             if (flags & XH_X2H_NEED_NORMALIZE) { \
437             NORMALIZE_TEXT(loop, content, cur - content - 1) \
438             NEW_ATTRIBUTE(node, end - node, enc, enc_len) \
439             } \
440             else { \
441             NEW_ATTRIBUTE(node, end - node, content, cur - content - 1)\
442             } \
443             goto top_loop; \
444             EXPECT_CHAR("CR", '\r') \
445             flags |= XH_X2H_NORMALIZE_LINE_FEED; \
446             break; \
447             EXPECT_CHAR("reference", '&') \
448             flags |= XH_X2H_NORMALIZE_REF; \
449             break; \
450             END(PPCAT(loop, _END_ATTR_VALUE)) \
451             goto INVALID_XML;
452              
453             #define SEARCH_XML_DECL_ATTRIBUTE_VALUE(loop, top_loop, quot) \
454             EXPECT_CHAR("start attr value", quot) \
455             content = cur; \
456             DO(PPCAT(loop, _END_ATTR_VALUE)) \
457             EXPECT_CHAR("attr value end", quot) \
458             NEW_ATTRIBUTE(node, end - node, content, cur - content - 1)\
459             goto top_loop; \
460             END(PPCAT(loop, _END_ATTR_VALUE)) \
461             goto INVALID_XML;
462              
463             #define SEARCH_ATTRIBUTE_VALUE(loop, top_loop, quot) SEARCH_NODE_ATTRIBUTE_VALUE(loop, top_loop, quot)
464              
465             #define SEARCH_ATTRIBUTES(loop, search_end_tag) \
466             PPCAT(loop, _SEARCH_ATTRIBUTES_LOOP): \
467             DO(PPCAT(loop, _SEARCH_ATTR)) \
468             search_end_tag \
469             \
470             SKIP_BLANK \
471             \
472             EXPECT_ANY("start attr name") \
473             node = cur - 1; \
474             \
475             DO(PPCAT(loop, _PARSE_ATTR_NAME)) \
476             EXPECT_BLANK("end attr name") \
477             end = cur - 1; \
478             xh_log_trace2("attr name: [%.*s]", end - node, node);\
479             \
480             DO(PPCAT(loop, _ATTR_SKIP_BLANK)) \
481             EXPECT_CHAR("search attr value", '=') \
482             goto PPCAT(loop, _SEARCH_ATTRIBUTE_VALUE); \
483             SKIP_BLANK \
484             EXPECT_ANY("wrong character") \
485             goto INVALID_XML; \
486             END(PPCAT(loop, _ATTR_SKIP_BLANK)) \
487             goto INVALID_XML; \
488             EXPECT_CHAR("end attr name", '=') \
489             end = cur - 1; \
490             xh_log_trace2("attr name: [%.*s]", end - node, node);\
491             \
492             PPCAT(loop, _SEARCH_ATTRIBUTE_VALUE): \
493             DO(PPCAT(loop, _PARSE_ATTR_VALUE)) \
494             SEARCH_ATTRIBUTE_VALUE(PPCAT(loop, _1), PPCAT(loop, _SEARCH_ATTRIBUTES_LOOP), '"')\
495             SEARCH_ATTRIBUTE_VALUE(PPCAT(loop, _2), PPCAT(loop, _SEARCH_ATTRIBUTES_LOOP), '\'')\
496             SKIP_BLANK \
497             EXPECT_ANY("wrong character") \
498             goto INVALID_XML; \
499             END(PPCAT(loop, _PARSE_ATTR_VALUE)) \
500             goto INVALID_XML; \
501             END(PPCAT(loop, _PARSE_ATTR_NAME)) \
502             goto INVALID_XML; \
503             END(PPCAT(loop, _SEARCH_ATTR)) \
504             goto INVALID_XML;
505              
506             #define PARSE_XML_DECLARATION \
507             SCAN3(XML_DECL, 'x', 'm', 'l') \
508             DO(XML_DECL_ATTR) \
509             EXPECT_BLANK("blank") \
510             SEARCH_ATTRIBUTES(XML_DECL_ATTR, SEARCH_END_XML_DECLARATION)\
511             goto INVALID_XML; \
512             EXPECT_ANY("wrong character") \
513             goto INVALID_XML; \
514             END(XML_DECL_ATTR) \
515             goto INVALID_XML; \
516             END3(XML_DECL, INVALID_XML) \
517             goto INVALID_XML;
518              
519             #define SEARCH_END_XML_DECLARATION \
520             EXPECT_CHAR("end tag", '?') \
521             DO(XML_DECL_SEARCH_END_TAG2) \
522             EXPECT_CHAR("end tag", '>') \
523             goto XML_DECL_FOUND; \
524             EXPECT_ANY("wrong character") \
525             goto INVALID_XML; \
526             END(XML_DECL_SEARCH_END_TAG2) \
527             goto INVALID_XML;
528              
529             #define PARSE_DOCTYPE_END \
530             goto PARSE_DOCTYPE_INTSUBSET_START;
531              
532             #define PARSE_DOCTYPE_LITERAL(loop, next, quot) \
533             EXPECT_CHAR("start of literal", quot) \
534             DO(PPCAT(loop, _END_OF_LITERAL)) \
535             EXPECT_CHAR("end of literal", quot) \
536             next \
537             END(PPCAT(loop, _END_OF_LITERAL)) \
538             goto INVALID_XML;
539              
540             #define PARSE_DOCTYPE_LITERALS(prefix, next) \
541             PARSE_DOCTYPE_LITERAL(PPCAT(prefix, _1), next, '"')\
542             PARSE_DOCTYPE_LITERAL(PPCAT(prefix, _2), next, '\'')
543              
544             #define PARSE_DOCTYPE_DELIM(prefix, next) \
545             DO(PPCAT(prefix, _DOCTYPE_DELIM)) \
546             EXPECT_BLANK("delimiter") \
547             DO(PPCAT(prefix, _DOCTYPE_DELIM_SKIP_BLANK)) \
548             SKIP_BLANK \
549             next \
550             EXPECT_ANY("wrong character") \
551             goto INVALID_XML; \
552             END(PPCAT(prefix, _DOCTYPE_DELIM_SKIP_BLANK)) \
553             goto INVALID_XML; \
554             EXPECT_ANY("wrong character") \
555             goto INVALID_XML; \
556             END(PPCAT(prefix, _DOCTYPE_DELIM)) \
557             goto INVALID_XML;
558              
559             #define PARSE_DOCTYPE_SYSTEM \
560             SCAN5(DOCTYPE_SYSTEM, 'Y', 'S', 'T', 'E', 'M') \
561             PARSE_DOCTYPE_DELIM(DOCTYPE_SYSTEM_LOCATION, PARSE_DOCTYPE_LITERALS(DOCTYPE_SYSTEM, PARSE_DOCTYPE_END))\
562             END5(DOCTYPE_SYSTEM, INVALID_XML) \
563             goto INVALID_XML;
564              
565             #define PARSE_DOCTYPE_PUBLIC_ID(prefix) \
566             PARSE_DOCTYPE_LITERAL( \
567             PPCAT(prefix, _1), \
568             PARSE_DOCTYPE_DELIM(DOCTYPE_PUBLIC_LOCATION_1, PARSE_DOCTYPE_LITERALS(DOCTYPE_PUBLIC_LOCATION_1, PARSE_DOCTYPE_END)),\
569             '"' \
570             ) \
571             PARSE_DOCTYPE_LITERAL( \
572             PPCAT(prefix, _2), \
573             PARSE_DOCTYPE_DELIM(DOCTYPE_PUBLIC_LOCATION_2, PARSE_DOCTYPE_LITERALS(DOCTYPE_PUBLIC_LOCATION_2, PARSE_DOCTYPE_END)),\
574             '\'' \
575             )
576              
577             #define PARSE_DOCTYPE_PUBLIC \
578             SCAN5(DOCTYPE_PUBLIC, 'U', 'B', 'L', 'I', 'C') \
579             PARSE_DOCTYPE_DELIM(DOCTYPE_PUBLIC_ID, PARSE_DOCTYPE_PUBLIC_ID(DOCTYPE_PUBLIC))\
580             END5(DOCTYPE_PUBLIC, INVALID_XML) \
581             goto INVALID_XML;
582              
583             #define PARSE_DOCTYPE \
584             SCAN6(DOCTYPE, 'O', 'C', 'T', 'Y', 'P', 'E') \
585             if (flags & (XH_X2H_ROOT_FOUND | XH_X2H_DOCTYPE_FOUND)) goto INVALID_XML;\
586             flags |= XH_X2H_DOCTYPE_FOUND; \
587             DO(DOCTYPE_NAME) \
588             EXPECT_BLANK("delimiter") \
589             DO(DOCTYPE_NAME_START) \
590             SKIP_BLANK \
591             EXPECT_ANY("start name") \
592             DO(DOCTYPE_NAME_END) \
593             EXPECT_BLANK("end name") \
594             DO(DOCTYPE_NAME_BLANK) \
595             SKIP_BLANK \
596             EXPECT_CHAR("end doctype", '>') \
597             goto PARSE_CONTENT; \
598             EXPECT_CHAR("SYSTEM", 'S') \
599             PARSE_DOCTYPE_SYSTEM \
600             EXPECT_CHAR("PUBLIC", 'P') \
601             PARSE_DOCTYPE_PUBLIC \
602             EXPECT_CHAR("internal subset", '[') \
603             goto PARSE_DOCTYPE_INTSUBSET; \
604             EXPECT_ANY("wrong character") \
605             goto INVALID_XML; \
606             END(DOCTYPE_NAME_BLANK) \
607             goto INVALID_XML; \
608             EXPECT_CHAR("end doctype", '>') \
609             goto PARSE_CONTENT; \
610             END(DOCTYPE_NAME_END) \
611             goto INVALID_XML; \
612             END(DOCTYPE_NAME_START) \
613             goto INVALID_XML; \
614             EXPECT_ANY("wrong character") \
615             goto INVALID_XML; \
616             END(DOCTYPE_NAME) \
617             goto INVALID_XML; \
618             END6(DOCTYPE, INVALID_XML) \
619             goto INVALID_XML;
620              
621             #define PARSE_COMMENT \
622             DO(COMMENT1) \
623             EXPECT_CHAR("-", '-') \
624             content = NULL; \
625             DO(END_COMMENT1) \
626             SKIP_BLANK \
627             EXPECT_CHAR("1st -", '-') \
628             if (content == NULL) content = end = cur - 1; \
629             DO(END_COMMENT2) \
630             EXPECT_CHAR("2nd -", '-') \
631             DO(END_COMMENT3) \
632             EXPECT_CHAR(">", '>') \
633             NEW_COMMENT(content, end - content) \
634             goto PARSE_CONTENT; \
635             EXPECT_CHAR("2nd -", '-') \
636             end = cur - 2; \
637             goto END_COMMENT3_START; \
638             EXPECT_ANY("any character") \
639             end = cur - 1; \
640             goto END_COMMENT1_START; \
641             END(END_COMMENT3) \
642             EXPECT_BLANK("skip blank") \
643             end = cur - 1; \
644             goto END_COMMENT1_START; \
645             EXPECT_ANY("any character") \
646             end = cur; \
647             goto END_COMMENT1_START; \
648             END(END_COMMENT2) \
649             EXPECT_ANY("any char") \
650             if (content == NULL) content = cur - 1; \
651             end = cur; \
652             END(END_COMMENT1) \
653             goto INVALID_XML; \
654             \
655             EXPECT_ANY("wrong character") \
656             goto INVALID_XML; \
657             \
658             END(COMMENT1) \
659             goto INVALID_XML;
660              
661             #define PARSE_CDATA \
662             SCAN6(CDATA, 'C', 'D', 'A', 'T', 'A', '[') \
663             content = end = cur; \
664             DO(END_CDATA1) \
665             EXPECT_CHAR("1st ]", ']') \
666             DO(END_CDATA2) \
667             EXPECT_CHAR("2nd ]", ']') \
668             DO(END_CDATA3) \
669             EXPECT_CHAR(">", '>') \
670             end = cur - 3; \
671             NEW_CDATA(content, end - content) \
672             goto PARSE_CONTENT; \
673             EXPECT_CHAR("2nd ]", ']') \
674             goto END_CDATA3_START; \
675             EXPECT_ANY("any character") \
676             goto END_CDATA1_START; \
677             END(END_CDATA3) \
678             EXPECT_ANY("any character") \
679             goto END_CDATA1_START; \
680             END(END_CDATA2) \
681             ; \
682             END(END_CDATA1) \
683             goto INVALID_XML; \
684             END6(CDATA, INVALID_XML)
685              
686             #define PARSE_CDATA_WITH_TRIM \
687             SCAN6(CDATA_WITH_TRIM, 'C', 'D', 'A', 'T', 'A', '[') \
688             content = NULL; \
689             DO(END_CDATA_WITH_TRIM1) \
690             SKIP_BLANK \
691             EXPECT_CHAR("1st ]", ']') \
692             if (content == NULL) content = end = cur - 1; \
693             DO(END_CDATA_WITH_TRIM2) \
694             EXPECT_CHAR("2nd ]", ']') \
695             DO(END_CDATA_WITH_TRIM3) \
696             EXPECT_CHAR(">", '>') \
697             NEW_CDATA(content, end - content) \
698             goto PARSE_CONTENT; \
699             EXPECT_CHAR("2nd ]", ']') \
700             end = cur - 2; \
701             goto END_CDATA_WITH_TRIM3_START; \
702             EXPECT_ANY("any character") \
703             end = cur - 1; \
704             goto END_CDATA_WITH_TRIM1_START; \
705             END(END_CDATA_WITH_TRIM3) \
706             EXPECT_BLANK("skip blank") \
707             end = cur - 1; \
708             goto END_CDATA_WITH_TRIM1_START; \
709             EXPECT_ANY("any character") \
710             end = cur; \
711             goto END_CDATA_WITH_TRIM1_START; \
712             END(END_CDATA_WITH_TRIM2) \
713             EXPECT_ANY("any char") \
714             if (content == NULL) content = cur - 1; \
715             end = cur; \
716             END(END_CDATA_WITH_TRIM1) \
717             goto INVALID_XML; \
718             END6(CDATA_WITH_TRIM, INVALID_XML)
719              
720             #define NORMALIZE_REFERENCE(loop) \
721             _DO(PPCAT(loop, _REFERENCE)) \
722             EXPECT_CHAR("char reference", '#') \
723             _DO(PPCAT(loop, _CHAR_REFERENCE)) \
724             EXPECT_CHAR("hex", 'x') \
725             code = 0; \
726             _DO(PPCAT(loop, _HEX_CHAR_REFERENCE_LOOP)) \
727             EXPECT_DIGIT("hex digit") \
728             code = code * 16 + (c - '0'); \
729             break; \
730             EXPECT_HEX_CHAR_LC("hex a-f") \
731             code = code * 16 + (c - 'a') + 10; \
732             break; \
733             EXPECT_HEX_CHAR_UC("hex A-F") \
734             code = code * 16 + (c - 'A') + 10; \
735             break; \
736             EXPECT_CHAR("reference end", ';') \
737             goto PPCAT(loop, _REFEFENCE_VALUE); \
738             END(PPCAT(loop, _HEX_CHAR_REFERENCE_LOOP)) \
739             goto INVALID_REF; \
740             EXPECT_DIGIT("digit") \
741             code = (c - '0'); \
742             _DO(PPCAT(loop, _CHAR_REFERENCE_LOOP)) \
743             EXPECT_DIGIT("digit") \
744             code = code * 10 + (c - '0'); \
745             break; \
746             EXPECT_CHAR("reference end", ';') \
747             goto PPCAT(loop, _REFEFENCE_VALUE); \
748             END(PPCAT(loop, _CHAR_REFERENCE_LOOP)) \
749             goto INVALID_REF; \
750             EXPECT_ANY("any char") \
751             goto INVALID_REF; \
752             END(PPCAT(loop, _CHAR_REFERENCE)) \
753             goto INVALID_REF; \
754             EXPECT_CHAR("amp or apos", 'a') \
755             if (xh_str_equal3(cur, 'm', 'p', ';')) { \
756             code = '&'; \
757             cur += 3; \
758             goto PPCAT(loop, _REFEFENCE_VALUE); \
759             } \
760             if (xh_str_equal4(cur, 'p', 'o', 's', ';')) { \
761             code = '\''; \
762             cur += 4; \
763             goto PPCAT(loop, _REFEFENCE_VALUE); \
764             } \
765             goto INVALID_REF; \
766             EXPECT_CHAR("lt", 'l') \
767             if (xh_str_equal2(cur, 't', ';')) { \
768             code = '<'; \
769             cur += 2; \
770             goto PPCAT(loop, _REFEFENCE_VALUE); \
771             } \
772             goto INVALID_REF; \
773             EXPECT_CHAR("gt", 'g') \
774             if (xh_str_equal2(cur, 't', ';')) { \
775             code = '>'; \
776             cur += 2; \
777             goto PPCAT(loop, _REFEFENCE_VALUE); \
778             } \
779             goto INVALID_REF; \
780             EXPECT_CHAR("quot", 'q') \
781             if (xh_str_equal4(cur, 'u', 'o', 't', ';')) { \
782             code = '"'; \
783             cur += 4; \
784             goto PPCAT(loop, _REFEFENCE_VALUE); \
785             } \
786             goto INVALID_REF; \
787             EXPECT_ANY("any char") \
788             goto INVALID_REF; \
789             END(PPCAT(loop, _REFERENCE)) \
790             goto INVALID_REF; \
791             PPCAT(loop, _REFEFENCE_VALUE): \
792             xh_log_trace1("parse reference value: %lu", code); \
793             if (code == 0 || code > 0x10FFFF) goto INVALID_REF; \
794             if (code >= 0x80) { \
795             if (code < 0x800) { \
796             *enc_cur++ = (code >> 6) | 0xC0; bits = 0; \
797             } \
798             else if (code < 0x10000) { \
799             *enc_cur++ = (code >> 12) | 0xE0; bits = 6; \
800             } \
801             else if (code < 0x110000) { \
802             *enc_cur++ = (code >> 18) | 0xF0; bits = 12; \
803             } \
804             else { \
805             goto INVALID_REF; \
806             } \
807             for (; bits >= 0; bits-= 6) { \
808             *enc_cur++ = ((code >> bits) & 0x3F) | 0x80; \
809             } \
810             } \
811             else { \
812             *enc_cur++ = (xh_char_t) code; \
813             }
814              
815             #define NORMALIZE_LINE_FEED(loop) \
816             _DO(PPCAT(loop, _NORMALIZE_LINE_FEED)) \
817             EXPECT_CHAR("LF", '\n') \
818             goto PPCAT(loop, _NORMALIZE_LINE_FEED_END); \
819             EXPECT_ANY("any char") \
820             cur--; \
821             goto PPCAT(loop, _NORMALIZE_LINE_FEED_END); \
822             END(PPCAT(loop, _NORMALIZE_LINE_FEED)) \
823             PPCAT(loop, _NORMALIZE_LINE_FEED_END): \
824             *enc_cur++ = '\n';
825              
826             #define NORMALIZE_TEXT(loop, s, l) \
827             enc_len = l; \
828             if (enc_len) { \
829             old_cur = cur; \
830             old_eof = eof; \
831             cur = s; \
832             eof = cur + enc_len; \
833             if (ctx->tmp == NULL) { \
834             xh_log_trace1("malloc() %lu", enc_len); \
835             if ((ctx->tmp = malloc(enc_len)) == NULL) goto MALLOC; \
836             ctx->tmp_size = enc_len; \
837             } \
838             else if (enc_len > ctx->tmp_size) { \
839             xh_log_trace1("realloc() %lu", enc_len); \
840             if ((enc = realloc(ctx->tmp, enc_len)) == NULL) goto MALLOC;\
841             ctx->tmp = enc; \
842             ctx->tmp_size = enc_len; \
843             } \
844             enc = enc_cur = ctx->tmp; \
845             memcpy(enc, cur, enc_len); \
846             _DO(PPCAT(loop, _NORMALIZE_TEXT)) \
847             EXPECT_CHAR("reference", '&') \
848             NORMALIZE_REFERENCE(loop) \
849             break; \
850             EXPECT_CHAR("CR", '\r') \
851             NORMALIZE_LINE_FEED(loop) \
852             break; \
853             EXPECT_ANY("any char") \
854             *enc_cur++ = c; \
855             END(PPCAT(loop, _NORMALIZE_TEXT)) \
856             enc_len = enc_cur - enc; \
857             cur = old_cur; \
858             eof = old_eof; \
859             } \
860             else { \
861             enc = s; \
862             }
863              
864             #define END_OF_TEXT(loop, s, l) \
865             if (s != NULL) { \
866             if (flags & (XH_X2H_IS_NOT_BLANK | XH_X2H_TEXT_NODE)) { \
867             if (flags & XH_X2H_NEED_NORMALIZE) { \
868             NORMALIZE_TEXT(loop, s, (l)) \
869             NEW_TEXT(enc, enc_len) \
870             } \
871             else { \
872             NEW_TEXT(s, (l)) \
873             } \
874             } \
875             s = NULL; \
876             }
877              
878             static void
879 37           xh_x2h_parse_chunk(xh_x2h_ctx_t *ctx, xh_char_t **buf, size_t *bytesleft, xh_bool_t terminate)
880             {
881             xh_char_t c, *cur, *node, *end, *content, *eof, *enc,
882             *enc_cur, *old_cur, *old_eof, *content_key;
883             unsigned int depth, real_depth, code, flags;
884             int bits;
885             SV **lval, *val;
886             xh_x2h_node_t *nodes;
887             AV *av;
888             size_t enc_len, content_key_len;
889              
890 37           cur = *buf;
891 37           eof = cur + *bytesleft;
892 37           nodes = ctx->nodes;
893 37           depth = ctx->depth;
894 37           real_depth = ctx->real_depth;
895 37           flags = ctx->flags;
896 37           node = ctx->node;
897 37           end = ctx->end;
898 37           content = ctx->content;
899 37           code = ctx->code;
900 37           lval = ctx->lval;
901 37           enc = enc_cur = old_eof = old_cur = NULL;
902 37           c = '\0';
903              
904 37 100         if (ctx->opts.content[0] == '\0') {
905 35           content_key = (xh_char_t *) DEF_CONTENT_KEY;
906 35           content_key_len = sizeof(DEF_CONTENT_KEY) - 1;
907             }
908             else {
909 2           content_key = ctx->opts.content;
910 2           content_key_len = xh_strlen(ctx->opts.content);
911             }
912              
913             #define XH_X2H_PROCESS_STATE(st) case st: goto st;
914 37           switch (ctx->state) {
915 16           case PARSER_ST_NONE: break;
916 13           XH_X2H_PARSER_STATE_LIST
917 8           case XML_DECL_FOUND: break;
918 0           case PARSER_ST_DONE: goto DONE;
919             }
920             #undef XH_X2H_PROCESS_STATE
921              
922             PARSE_CONTENT:
923 126           content = NULL;
924 126           flags &= ~(XH_X2H_NEED_NORMALIZE | XH_X2H_IS_NOT_BLANK);
925 703 100         DO(CONTENT)
    50          
    100          
926             EXPECT_CHAR("new element", '<')
927 113 50         DO(PARSE_ELEMENT)
    50          
    0          
928             EXPECT_CHAR("xml declaration", '?')
929 8 50         if (real_depth != 0) goto INVALID_XML;
930 8 50         END_OF_TEXT(TEXT_BEFORE_XML_DECL, content, end - content)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
931             #undef NEW_ATTRIBUTE
932             #define NEW_ATTRIBUTE(k, kl, v, vl) NEW_XML_DECL_ATTRIBUTE(k, kl, v, vl)
933             #undef SEARCH_ATTRIBUTE_VALUE
934             #define SEARCH_ATTRIBUTE_VALUE(loop, top_loop, quot) SEARCH_XML_DECL_ATTRIBUTE_VALUE(loop, top_loop, quot)
935 200 50         PARSE_XML_DECLARATION
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    0          
    50          
    50          
    0          
    100          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
936             #undef NEW_ATTRIBUTE
937             #define NEW_ATTRIBUTE(k, kl, v, vl) NEW_NODE_ATTRIBUTE(k, kl, v, vl)
938             #undef SEARCH_ATTRIBUTE_VALUE
939             #define SEARCH_ATTRIBUTE_VALUE(loop, top_loop, quot) SEARCH_NODE_ATTRIBUTE_VALUE(loop, top_loop, quot)
940             EXPECT_CHAR("comment or cdata or doctype", '!')
941 5           flags &= ~XH_X2H_TEXT_NODE;
942 5 100         END_OF_TEXT(TEXT_BEFORE_COMMENT, content, end - content)
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
943 5 50         DO(XML_COMMENT_NODE_OR_CDATA)
    50          
    0          
944             EXPECT_CHAR("comment", '-')
945 16 50         PARSE_COMMENT
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    100          
946             EXPECT_CHAR("cdata", '[')
947 3 50         if (ctx->opts.trim) {
948 51 50         PARSE_CDATA_WITH_TRIM
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    100          
949             ;
950             }
951             else {
952 0 0         PARSE_CDATA
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
953             ;
954             }
955             EXPECT_CHAR("doctype", 'D')
956 0 0         PARSE_DOCTYPE
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
957             EXPECT_ANY("wrong character")
958 0           goto INVALID_XML;
959             END(XML_COMMENT_NODE_OR_CDATA)
960 0           goto INVALID_XML;
961             EXPECT_CHAR("closing tag", '/')
962 68 100         END_OF_TEXT(TEXT_BEFORE_CLOSING_TAG, content, end - content)
    100          
    100          
    50          
    50          
    50          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    50          
    100          
    100          
    50          
    0          
    100          
    50          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
963             //node = cur;
964 260 50         DO(PARSE_CLOSING_TAG)
    50          
    0          
965             EXPECT_CHAR("end tag name", '>')
966 48 100         CLOSE_TAG
    100          
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    100          
    100          
967 47           goto PARSE_CONTENT;
968             EXPECT_BLANK("end tag name")
969 0 0         DO(SEARCH_CLOSING_END_TAG)
    0          
    0          
970             EXPECT_CHAR("end tag", '>')
971 0 0         CLOSE_TAG
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
972 0           goto PARSE_CONTENT;
973 0           SKIP_BLANK
974             EXPECT_ANY("wrong character")
975 0           goto INVALID_XML;
976 0           END(SEARCH_CLOSING_END_TAG)
977 0           goto INVALID_XML;
978 212           END(PARSE_CLOSING_TAG)
979 0           goto INVALID_XML;
980             EXPECT_ANY("opening tag")
981 52           flags &= ~XH_X2H_TEXT_NODE;
982 52 100         END_OF_TEXT(TEXT_BEFORE_OPENING_TAG, content, end - content)
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    50          
983 52           node = cur - 1;
984 237 50         DO(PARSE_OPENING_TAG)
    50          
    0          
985             EXPECT_CHAR("end tag", '>')
986 41 100         OPEN_TAG(node, cur - node - 1)
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    0          
    0          
    50          
    50          
    100          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
987 39           goto PARSE_CONTENT;
988             EXPECT_CHAR("self closing tag", '/')
989 1 50         OPEN_TAG(node, cur - node - 1)
    0          
    50          
    0          
    50          
    50          
    0          
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
990 1 50         CLOSE_TAG
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    50          
991              
992 1 50         DO(SEARCH_OPENING_END_TAG)
    50          
    0          
    50          
993             EXPECT_CHAR("end tag", '>')
994 1           goto PARSE_CONTENT;
995             EXPECT_ANY("wrong character")
996 0           goto INVALID_XML;
997             END(SEARCH_OPENING_END_TAG)
998 0           goto INVALID_XML;
999             EXPECT_BLANK("end tag name")
1000 10 100         OPEN_TAG(node, cur - node - 1)
    50          
    100          
    100          
    100          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
1001              
1002 109 50         SEARCH_ATTRIBUTES(NODE, SEARCH_END_TAG)
    50          
    0          
    50          
    50          
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    50          
    0          
    50          
    50          
    0          
    100          
    50          
    100          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    0          
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    50          
    0          
    50          
    0          
    0          
    100          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    50          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1003              
1004             goto PARSE_CONTENT;
1005 185           END(PARSE_OPENING_TAG);
1006 0           goto INVALID_XML;
1007             END(PARSE_ELEMENT)
1008              
1009             EXPECT_CHAR("wrong symbol", '>')
1010 0           goto INVALID_XML;
1011             EXPECT_BLANK_WO_CR("blank")
1012 364 100         if (!ctx->opts.trim)
1013 47           goto START_CONTENT;
1014 317           break;
1015             EXPECT_CHAR("CR", '\r')
1016 8 100         if (content != NULL) {
1017 5           flags |= XH_X2H_NORMALIZE_LINE_FEED;
1018             }
1019 8 50         if (!ctx->opts.trim)
1020 0           goto START_CONTENT;
1021 8           break;
1022             EXPECT_CHAR("reference", '&')
1023 0           flags |= (XH_X2H_NORMALIZE_REF | XH_X2H_IS_NOT_BLANK);
1024 0           goto START_CONTENT;
1025             EXPECT_ANY("any char")
1026 192           flags |= XH_X2H_IS_NOT_BLANK;
1027             START_CONTENT:
1028 239 100         if (content == NULL) content = cur - 1;
1029 239           end = cur;
1030 564           END(CONTENT)
1031              
1032 13 100         if (
1033 13 100         ((content != NULL) && (flags & XH_X2H_IS_NOT_BLANK)) ||
    50          
1034 12 50         (real_depth != 0) ||
1035 12           !(flags & XH_X2H_ROOT_FOUND)
1036             ) goto INVALID_XML;
1037              
1038 12           ctx->state = PARSER_ST_DONE;
1039 12           *bytesleft = eof - cur;
1040 12           *buf = cur;
1041 12           return;
1042              
1043             PARSE_DOCTYPE_INTSUBSET:
1044 0 0         DO(DOCTYPE_INTSUBSET)
    0          
    0          
    0          
1045             EXPECT_CHAR("end of internal subset", ']')
1046 0 0         DO(DOCTYPE_END)
    0          
    0          
1047 0           SKIP_BLANK
1048             EXPECT_CHAR("end doctype", '>')
1049 0           goto PARSE_CONTENT;
1050             EXPECT_ANY("wrong character")
1051 0           goto INVALID_XML;
1052 0           END(DOCTYPE_END)
1053 0           goto INVALID_XML;
1054 0           END(DOCTYPE_INTSUBSET)
1055 0           goto INVALID_XML;
1056              
1057             PARSE_DOCTYPE_INTSUBSET_START:
1058 0 0         DO(DOCTYPE_INTSUBSET_START)
    0          
    0          
1059 0           SKIP_BLANK
1060             EXPECT_CHAR("end doctype", '>')
1061 0           goto PARSE_CONTENT;
1062             EXPECT_CHAR("start of internal subset", '[')
1063 0           goto PARSE_DOCTYPE_INTSUBSET;
1064             EXPECT_ANY("wrong character")
1065 0           goto INVALID_XML;
1066 0           END(DOCTYPE_INTSUBSET_START)
1067 0           goto INVALID_XML;
1068              
1069             XML_DECL_FOUND:
1070 8           ctx->state = XML_DECL_FOUND;
1071             CHUNK_FINISH:
1072 21           ctx->content = content;
1073 21           ctx->node = node;
1074 21           ctx->end = end;
1075 21           ctx->depth = depth;
1076 21           ctx->real_depth = real_depth;
1077 21           ctx->flags = flags;
1078 21           ctx->code = code;
1079 21           ctx->lval = lval;
1080 21           *bytesleft = eof - cur;
1081 21           *buf = cur;
1082 21           return;
1083              
1084             MAX_DEPTH_EXCEEDED:
1085 0           croak("Maximum depth exceeded");
1086             INVALID_XML:
1087 4           croak("Invalid XML");
1088             INVALID_REF:
1089 0           croak("Invalid reference");
1090             MALLOC:
1091 0           croak("Memory allocation error");
1092             DONE:
1093 0           croak("Parsing is done");
1094             }
1095              
1096             static void
1097 16           xh_x2h_parse(xh_x2h_ctx_t *ctx, xh_reader_t *reader)
1098             {
1099             xh_char_t *buf, *preserve;
1100             size_t len, off;
1101             xh_bool_t eof;
1102              
1103             do {
1104 29 100         preserve = ctx->node != NULL ? ctx->node : ctx->content;
1105              
1106 29           len = reader->read(reader, &buf, preserve, &off);
1107 29           eof = (len == 0);
1108 29 50         if (off) {
1109 0 0         if (ctx->node != NULL) ctx->node -= off;
1110 0 0         if (ctx->content != NULL) ctx->content -= off;
1111 0 0         if (ctx->end != NULL) ctx->end -= off;
1112             }
1113              
1114             xh_log_trace2("read buf: %.*s", len, buf);
1115              
1116             do {
1117             xh_log_trace2("parse buf: %.*s", len, buf);
1118              
1119 37           xh_x2h_parse_chunk(ctx, &buf, &len, eof);
1120              
1121 33 100         if (ctx->state == XML_DECL_FOUND && ctx->opts.encoding[0] == '\0' && ctx->encoding[0] != '\0') {
    50          
    50          
1122 8           reader->switch_encoding(reader, ctx->encoding, &buf, &len);
1123             }
1124 33 100         } while (len > 0);
1125 25 100         } while (!eof);
1126              
1127 12 50         if (ctx->state != PARSER_ST_DONE)
1128 0           croak("Invalid XML");
1129 12           }
1130              
1131             SV *
1132 20           xh_x2h(xh_x2h_ctx_t *ctx)
1133             {
1134             HV *hv;
1135             HE *he;
1136             SV *result;
1137              
1138 16           dXCPT;
1139 20 100         XCPT_TRY_START
1140             {
1141 16 100         if (ctx->opts.filter.enable) {
1142 6           ctx->flags |= XH_X2H_FILTER_ENABLED;
1143 6 100         if (ctx->opts.cb == NULL)
1144 6           ctx->result = newRV_noinc((SV *) newAV());
1145             }
1146             else {
1147 10           ctx->result = newRV_noinc((SV *) newHV());
1148 10           ctx->nodes[0].lval = ctx->lval = &ctx->result;
1149             }
1150              
1151 16           xh_reader_init(&ctx->reader, ctx->input, ctx->opts.encoding, ctx->opts.buf_size);
1152              
1153 16           xh_x2h_parse(ctx, &ctx->reader);
1154 16           } XCPT_TRY_END
1155              
1156 16 100         XCPT_CATCH
1157             {
1158 4 50         if (ctx->result != NULL) SvREFCNT_dec(ctx->result);
1159 4           xh_reader_destroy(&ctx->reader);
1160 4 50         XCPT_RETHROW;
    0          
1161             }
1162              
1163 12           xh_reader_destroy(&ctx->reader);
1164              
1165 12           result = ctx->result;
1166 12 100         if (ctx->opts.filter.enable) {
1167 6 100         if (ctx->opts.cb != NULL) result = NULL;
1168             }
1169 6 100         else if (!ctx->opts.keep_root) {
1170 5           hv = (HV *) SvRV(result);
1171 5           hv_iterinit(hv);
1172 5 50         if ((he = hv_iternext(hv))) {
1173 5           result = hv_iterval(hv, he);
1174 5           SvREFCNT_inc(result);
1175             }
1176             else {
1177 0           result = NULL;
1178             }
1179 5           SvREFCNT_dec(ctx->result);
1180             }
1181              
1182 12           return result;
1183             }