File Coverage

src/xh_x2h.c
Criterion Covered Total %
statement 236 294 80.2
branch 793 3366 23.5
condition n/a
subroutine n/a
pod n/a
total 1029 3660 28.1


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