File Coverage

src/xh_x2h.c
Criterion Covered Total %
statement 174 222 78.3
branch 383 1796 21.3
condition n/a
subroutine n/a
pod n/a
total 557 2018 27.6


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