1 /*
2 * Functions converting HTSMSGs to/from XML
3 * Copyright (C) 2008 Andreas Öman
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * XML parser, written according to this spec:
21 *
22 * http://www.w3.org/TR/2006/REC-xml-20060816/
23 *
24 * Parses of UTF-8 and ISO-8859-1 (Latin 1) encoded XML and output as
25 * htsmsg's with UTF-8 encoded payloads
26 *
27 * Supports: Example:
28 *
29 * Comments <!-- a comment -->
30 * Processing Instructions <?xml ?>
31 * CDATA <![CDATA[ <litteraly copied> ]]>
32 * Label references &
33 * Character references A
34 * Empty tags <tagname/>
35 *
36 *
37 * Not supported:
38 *
39 * UTF-16 (mandatory by standard)
40 * Intelligent parsing of <!DOCTYPE>
41 * Entity declarations
42 *
43 */
44
45
46 #include <assert.h>
47 #include <sys/types.h>
48 #include <stdio.h>
49 #include <unistd.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include "tvheadend.h"
54
55 #include "htsmsg_xml.h"
56 #include "htsbuf.h"
57
58 TAILQ_HEAD(cdata_content_queue, cdata_content);
59
60 LIST_HEAD(xmlns_list, xmlns);
61
62 typedef struct xmlns {
63 LIST_ENTRY(xmlns) xmlns_global_link;
64 LIST_ENTRY(xmlns) xmlns_scope_link;
65
66 char *xmlns_prefix;
67 unsigned int xmlns_prefix_len;
68
69 char *xmlns_norm;
70 unsigned int xmlns_norm_len;
71
72 } xmlns_t;
73
74 typedef struct xmlparser {
75 enum {
76 XML_ENCODING_UTF8,
77 XML_ENCODING_8859_1,
78 } xp_encoding;
79
80 char xp_errmsg[128];
81
82 int xp_srcdataused;
83
84 struct xmlns_list xp_namespaces;
85
86 } xmlparser_t;
87
88 #define xmlerr(xp, fmt...) \
89 snprintf((xp)->xp_errmsg, sizeof((xp)->xp_errmsg), fmt)
90
91
92 typedef struct cdata_content {
93 TAILQ_ENTRY(cdata_content) cc_link;
94 char *cc_start, *cc_end; /* end points to byte AFTER last char */
95 int cc_encoding;
96 char cc_buf[0];
97 } cdata_content_t;
98
99 static char *htsmsg_xml_parse_cd(xmlparser_t *xp,
100 htsmsg_t *parent, char *src);
101
102 /**
103 *
104 */
105 static void
add_unicode(struct cdata_content_queue * ccq,int c)106 add_unicode(struct cdata_content_queue *ccq, int c)
107 {
108 cdata_content_t *cc;
109 int r;
110
111 cc = malloc(sizeof(cdata_content_t) + 6);
112 r = put_utf8(cc->cc_buf, c);
113 if(r == 0) {
114 free(cc);
115 return;
116 }
117
118 cc->cc_encoding = XML_ENCODING_UTF8;
119 TAILQ_INSERT_TAIL(ccq, cc, cc_link);
120 cc->cc_start = cc->cc_buf;
121 cc->cc_end = cc->cc_buf + r;
122 }
123
124 /**
125 *
126 */
127 static int
decode_character_reference(char ** src)128 decode_character_reference(char **src)
129 {
130 int v = 0;
131 char c;
132
133 if(**src == 'x') {
134 /* hexadecimal */
135 (*src)++;
136
137 /* decimal */
138 while(1) {
139 c = **src;
140 switch(c) {
141 case '0' ... '9':
142 v = v * 0x10 + c - '0';
143 break;
144 case 'a' ... 'f':
145 v = v * 0x10 + c - 'a' + 10;
146 break;
147 case 'A' ... 'F':
148 v = v * 0x10 + c - 'A' + 10;
149 break;
150 case ';':
151 (*src)++;
152 return v;
153 default:
154 return 0;
155 }
156 (*src)++;
157 }
158
159 } else {
160
161 /* decimal */
162 while(1) {
163 c = **src;
164 switch(c) {
165 case '0' ... '9':
166 v = v * 10 + c - '0';
167 (*src)++;
168 break;
169 case ';':
170 (*src)++;
171 return v;
172 default:
173 return 0;
174 }
175 }
176 }
177 }
178
179 /**
180 *
181 */
182 static inline int
is_xmlws(char c)183 is_xmlws(char c)
184 {
185 return c > 0 && c <= 32;
186 // return c == 32 || c == 9 || c = 10 || c = 13;
187 }
188
189
190 /**
191 *
192 */
193 static void
xmlns_destroy(xmlns_t * ns)194 xmlns_destroy(xmlns_t *ns)
195 {
196 LIST_REMOVE(ns, xmlns_global_link);
197 LIST_REMOVE(ns, xmlns_scope_link);
198 free(ns->xmlns_prefix);
199 free(ns->xmlns_norm);
200 free(ns);
201 }
202
203 /**
204 *
205 */
206 static char *
htsmsg_xml_parse_attrib(xmlparser_t * xp,htsmsg_t * msg,char * src,struct xmlns_list * xmlns_scope_list)207 htsmsg_xml_parse_attrib(xmlparser_t *xp, htsmsg_t *msg, char *src,
208 struct xmlns_list *xmlns_scope_list)
209 {
210 char *attribname, *payload;
211 int attriblen, payloadlen;
212 char quote;
213 htsmsg_field_t *f;
214 xmlns_t *ns;
215
216 attribname = src;
217 /* Parse attribute name */
218 while(1) {
219 if(*src == 0) {
220 xmlerr(xp, "Unexpected end of file during attribute name parsing");
221 return NULL;
222 }
223
224 if(is_xmlws(*src) || *src == '=')
225 break;
226 src++;
227 }
228
229 attriblen = src - attribname;
230 if(attriblen < 1 || attriblen > 65535) {
231 xmlerr(xp, "Invalid attribute name");
232 return NULL;
233 }
234
235 while(is_xmlws(*src))
236 src++;
237
238 if(*src != '=') {
239 xmlerr(xp, "Expected '=' in attribute parsing");
240 return NULL;
241 }
242 src++;
243
244 while(is_xmlws(*src))
245 src++;
246
247
248 /* Parse attribute payload */
249 quote = *src++;
250 if(quote != '"' && quote != '\'') {
251 xmlerr(xp, "Expected ' or \" before attribute value");
252 return NULL;
253 }
254
255 payload = src;
256 while(1) {
257 if(*src == 0) {
258 xmlerr(xp, "Unexpected end of file during attribute value parsing");
259 return NULL;
260 }
261 if(*src == quote)
262 break;
263 src++;
264 }
265
266 payloadlen = src - payload;
267 if(payloadlen < 0 || payloadlen > 65535) {
268 xmlerr(xp, "Invalid attribute value");
269 return NULL;
270 }
271
272 src++;
273 while(is_xmlws(*src))
274 src++;
275
276 if(xmlns_scope_list != NULL &&
277 attriblen > 6 && !memcmp(attribname, "xmlns:", 6)) {
278
279 attribname += 6;
280 attriblen -= 6;
281
282 ns = malloc(sizeof(xmlns_t));
283
284 ns->xmlns_prefix = malloc(attriblen + 1);
285 memcpy(ns->xmlns_prefix, attribname, attriblen);
286 ns->xmlns_prefix[attriblen] = 0;
287 ns->xmlns_prefix_len = attriblen;
288
289 ns->xmlns_norm = malloc(payloadlen + 1);
290 memcpy(ns->xmlns_norm, payload, payloadlen);
291 ns->xmlns_norm[payloadlen] = 0;
292 ns->xmlns_norm_len = payloadlen;
293
294 LIST_INSERT_HEAD(&xp->xp_namespaces, ns, xmlns_global_link);
295 LIST_INSERT_HEAD(xmlns_scope_list, ns, xmlns_scope_link);
296 return src;
297 }
298
299 xp->xp_srcdataused = 1;
300 attribname[attriblen] = 0;
301 payload[payloadlen] = 0;
302
303 f = htsmsg_field_add(msg, attribname, HMF_STR, 0, 0);
304 f->hmf_str = payload;
305 return src;
306 }
307
308 /**
309 *
310 */
311 static char *
htsmsg_xml_parse_tag(xmlparser_t * xp,htsmsg_t * parent,char * src)312 htsmsg_xml_parse_tag(xmlparser_t *xp, htsmsg_t *parent, char *src)
313 {
314 htsmsg_t *m, *attrs;
315 struct xmlns_list nslist;
316 char *tagname;
317 int taglen, empty = 0, i;
318 xmlns_t *ns;
319
320 tagname = src;
321
322 LIST_INIT(&nslist);
323
324 while(1) {
325 if(*src == 0) {
326 xmlerr(xp, "Unexpected end of file during tag name parsing");
327 return NULL;
328 }
329 if(is_xmlws(*src) || *src == '>' || *src == '/')
330 break;
331 src++;
332 }
333
334 taglen = src - tagname;
335 if(taglen < 1 || taglen > 65535) {
336 xmlerr(xp, "Invalid tag name");
337 return NULL;
338 }
339
340 attrs = htsmsg_create_map();
341
342 while(1) {
343
344 while(is_xmlws(*src))
345 src++;
346
347 if(*src == 0) {
348 htsmsg_destroy(attrs);
349 xmlerr(xp, "Unexpected end of file in tag");
350 return NULL;
351 }
352
353 if(src[0] == '/' && src[1] == '>') {
354 empty = 1;
355 src += 2;
356 break;
357 }
358
359 if(*src == '>') {
360 src++;
361 break;
362 }
363
364 if((src = htsmsg_xml_parse_attrib(xp, attrs, src, &nslist)) == NULL) {
365 htsmsg_destroy(attrs);
366 return NULL;
367 }
368 }
369
370 m = htsmsg_create_map();
371
372 if(TAILQ_FIRST(&attrs->hm_fields) != NULL) {
373 htsmsg_add_msg_extname(m, "attrib", attrs);
374 } else {
375 htsmsg_destroy(attrs);
376 }
377
378 if(!empty)
379 src = htsmsg_xml_parse_cd(xp, m, src);
380
381 for(i = 0; i < taglen - 1; i++) {
382 if(tagname[i] == ':') {
383
384 LIST_FOREACH(ns, &xp->xp_namespaces, xmlns_global_link) {
385 if(ns->xmlns_prefix_len == i &&
386 !memcmp(ns->xmlns_prefix, tagname, ns->xmlns_prefix_len)) {
387
388 int llen = taglen - i - 1;
389 char *n = malloc(ns->xmlns_norm_len + llen + 1);
390
391 n[ns->xmlns_norm_len + llen] = 0;
392 memcpy(n, ns->xmlns_norm, ns->xmlns_norm_len);
393 memcpy(n + ns->xmlns_norm_len, tagname + i + 1, llen);
394 htsmsg_add_msg(parent, n, m);
395 free(n);
396 goto done;
397 }
398 }
399 }
400 }
401
402 xp->xp_srcdataused = 1;
403 tagname[taglen] = 0;
404 htsmsg_add_msg_extname(parent, tagname, m);
405
406 done:
407 while((ns = LIST_FIRST(&nslist)) != NULL)
408 xmlns_destroy(ns);
409 return src;
410 }
411
412
413
414
415
416 /**
417 *
418 */
419 static char *
htsmsg_xml_parse_pi(xmlparser_t * xp,htsmsg_t * parent,char * src)420 htsmsg_xml_parse_pi(xmlparser_t *xp, htsmsg_t *parent, char *src)
421 {
422 htsmsg_t *attrs;
423 char *s = src;
424 char *piname;
425 int l;
426
427 while(1) {
428 if(*src == 0) {
429 xmlerr(xp, "Unexpected end of file during parsing of "
430 "Processing instructions");
431 return NULL;
432 }
433
434 if(is_xmlws(*src) || *src == '?')
435 break;
436 src++;
437 }
438
439 l = src - s;
440 if(l < 1 || l > 65536) {
441 xmlerr(xp, "Invalid 'Processing instructions' name");
442 return NULL;
443 }
444 piname = alloca(l + 1);
445 memcpy(piname, s, l);
446 piname[l] = 0;
447
448 attrs = htsmsg_create_map();
449
450 while(1) {
451
452 while(is_xmlws(*src))
453 src++;
454
455 if(*src == 0) {
456 htsmsg_destroy(attrs);
457 xmlerr(xp, "Unexpected end of file during parsing of "
458 "Processing instructions");
459 return NULL;
460 }
461
462 if(src[0] == '?' && src[1] == '>') {
463 src += 2;
464 break;
465 }
466
467 if((src = htsmsg_xml_parse_attrib(xp, attrs, src, NULL)) == NULL) {
468 htsmsg_destroy(attrs);
469 return NULL;
470 }
471 }
472
473
474 if(TAILQ_FIRST(&attrs->hm_fields) != NULL && parent != NULL) {
475 htsmsg_add_msg(parent, piname, attrs);
476 } else {
477 htsmsg_destroy(attrs);
478 }
479 return src;
480 }
481
482
483 /**
484 *
485 */
486 static char *
xml_parse_comment(xmlparser_t * xp,char * src)487 xml_parse_comment(xmlparser_t *xp, char *src)
488 {
489 /* comment */
490 while(1) {
491 if(*src == 0) { /* EOF inside comment is invalid */
492 xmlerr(xp, "Unexpected end of file inside a comment");
493 return NULL;
494 }
495
496 if(src[0] == '-' && src[1] == '-' && src[2] == '>')
497 return src + 3;
498 src++;
499 }
500 }
501
502 /**
503 *
504 */
505 static char *
decode_label_reference(xmlparser_t * xp,struct cdata_content_queue * ccq,char * src)506 decode_label_reference(xmlparser_t *xp,
507 struct cdata_content_queue *ccq, char *src)
508 {
509 char *s = src;
510 int l;
511 char *label;
512
513 while(*src != 0 && *src != ';')
514 src++;
515 if(*src == 0) {
516 xmlerr(xp, "Unexpected end of file during parsing of label reference");
517 return NULL;
518 }
519
520 l = src - s;
521 if(l < 1 || l > 65535)
522 return NULL;
523 label = alloca(l + 1);
524 memcpy(label, s, l);
525 label[l] = 0;
526 src++;
527
528 if(!strcmp(label, "amp"))
529 add_unicode(ccq, '&');
530 else if(!strcmp(label, "gt"))
531 add_unicode(ccq, '>');
532 else if(!strcmp(label, "lt"))
533 add_unicode(ccq, '<');
534 else if(!strcmp(label, "apos"))
535 add_unicode(ccq, '\'');
536 else if(!strcmp(label, "quot"))
537 add_unicode(ccq, '"');
538 else {
539 xmlerr(xp, "Unknown label referense: \"&%s;\"\n", label);
540 return NULL;
541 }
542
543 return src;
544 }
545
546 /**
547 *
548 */
549 static char *
htsmsg_xml_parse_cd0(xmlparser_t * xp,struct cdata_content_queue * ccq,htsmsg_t * tags,htsmsg_t * pis,char * src,int raw)550 htsmsg_xml_parse_cd0(xmlparser_t *xp,
551 struct cdata_content_queue *ccq, htsmsg_t *tags,
552 htsmsg_t *pis, char *src, int raw)
553 {
554 cdata_content_t *cc = NULL;
555 int c;
556
557 while(src != NULL && *src != 0) {
558
559 if(raw && src[0] == ']' && src[1] == ']' && src[2] == '>') {
560 if(cc != NULL)
561 cc->cc_end = src;
562 cc = NULL;
563 src += 3;
564 break;
565 }
566
567 if(*src == '<' && !raw) {
568
569 if(cc != NULL)
570 cc->cc_end = src;
571 cc = NULL;
572
573 src++;
574 if(*src == '?') {
575 src++;
576 src = htsmsg_xml_parse_pi(xp, pis, src);
577 continue;
578 }
579
580 if(src[0] == '!') {
581
582 src++;
583
584 if(src[0] == '-' && src[1] == '-') {
585 src = xml_parse_comment(xp, src + 2);
586 continue;
587 }
588
589 if(!strncmp(src, "[CDATA[", 7)) {
590 src += 7;
591 src = htsmsg_xml_parse_cd0(xp, ccq, tags, pis, src, 1);
592 continue;
593 }
594 xmlerr(xp, "Unknown syntatic element: <!%.10s", src);
595 return NULL;
596 }
597
598 if(*src == '/') {
599 /* End-tag */
600 src++;
601 while(*src != '>') {
602 if(*src == 0) { /* EOF inside endtag */
603 xmlerr(xp, "Unexpected end of file inside close tag");
604 return NULL;
605 }
606 src++;
607 }
608 src++;
609 break;
610 }
611
612 src = htsmsg_xml_parse_tag(xp, tags, src);
613 continue;
614 }
615
616 if(*src == '&' && !raw) {
617 if(cc != NULL)
618 cc->cc_end = src;
619 cc = NULL;
620
621 src++;
622
623 if(*src == '#') {
624 src++;
625 /* Character reference */
626 if((c = decode_character_reference(&src)) != 0)
627 add_unicode(ccq, c);
628 else {
629 xmlerr(xp, "Invalid character reference");
630 return NULL;
631 }
632 } else {
633 /* Label references */
634 src = decode_label_reference(xp, ccq, src);
635 }
636 continue;
637 }
638
639 if(cc == NULL) {
640 if(*src < 32) {
641 src++;
642 continue;
643 }
644 cc = malloc(sizeof(cdata_content_t));
645 cc->cc_encoding = xp->xp_encoding;
646 TAILQ_INSERT_TAIL(ccq, cc, cc_link);
647 cc->cc_start = src;
648 }
649 src++;
650 }
651
652 if(cc != NULL) {
653 assert(src != NULL);
654 cc->cc_end = src;
655 }
656 return src;
657 }
658
659 /**
660 *
661 */
662 static char *
htsmsg_xml_parse_cd(xmlparser_t * xp,htsmsg_t * parent,char * src)663 htsmsg_xml_parse_cd(xmlparser_t *xp, htsmsg_t *parent, char *src)
664 {
665 struct cdata_content_queue ccq;
666 htsmsg_field_t *f;
667 cdata_content_t *cc;
668 int c = 0, l, y = 0;
669 char *x, *body;
670 htsmsg_t *tags = htsmsg_create_map();
671
672 TAILQ_INIT(&ccq);
673 src = htsmsg_xml_parse_cd0(xp, &ccq, tags, NULL, src, 0);
674
675 /* Assemble body */
676
677 TAILQ_FOREACH(cc, &ccq, cc_link) {
678
679 switch(cc->cc_encoding) {
680 case XML_ENCODING_UTF8:
681 c += cc->cc_end - cc->cc_start;
682 y++;
683 break;
684
685 case XML_ENCODING_8859_1:
686 l = 0;
687 for(x = cc->cc_start; x < cc->cc_end; x++)
688 l += 1 + (*x >= 0x80);
689
690 c += l;
691 y += 1 + (l != cc->cc_end - cc->cc_start);
692 break;
693 }
694 }
695
696 if(y == 1 && c > 0) {
697 /* One segment UTF-8 (or 7bit ASCII),
698 use data directly from source */
699
700 cc = TAILQ_FIRST(&ccq);
701
702 assert(cc != NULL);
703 assert(TAILQ_NEXT(cc, cc_link) == NULL);
704
705 f = htsmsg_field_add(parent, "cdata", HMF_STR, 0, 0);
706 f->hmf_str = cc->cc_start;
707 *cc->cc_end = 0;
708 free(cc);
709
710 } else if(c > 0) {
711 body = malloc(c + 1);
712 c = 0;
713
714 while((cc = TAILQ_FIRST(&ccq)) != NULL) {
715
716 switch(cc->cc_encoding) {
717 case XML_ENCODING_UTF8:
718 l = cc->cc_end - cc->cc_start;
719 memcpy(body + c, cc->cc_start, l);
720 c += l;
721 break;
722
723 case XML_ENCODING_8859_1:
724 for(x = cc->cc_start; x < cc->cc_end; x++)
725 c += put_utf8(body + c, *x);
726 break;
727 }
728
729 TAILQ_REMOVE(&ccq, cc, cc_link);
730 free(cc);
731 }
732 body[c] = 0;
733
734 f = htsmsg_field_add(parent, "cdata", HMF_STR, HMF_ALLOCED, 0);
735 f->hmf_str = body;
736
737 } else {
738
739 while((cc = TAILQ_FIRST(&ccq)) != NULL) {
740 TAILQ_REMOVE(&ccq, cc, cc_link);
741 free(cc);
742 }
743 }
744
745
746 if(src == NULL) {
747 htsmsg_destroy(tags);
748 return NULL;
749 }
750
751 if(TAILQ_FIRST(&tags->hm_fields) != NULL) {
752 htsmsg_add_msg_extname(parent, "tags", tags);
753 } else {
754 htsmsg_destroy(tags);
755 }
756
757 return src;
758 }
759
760
761 /**
762 *
763 */
764 static char *
htsmsg_parse_prolog(xmlparser_t * xp,char * src)765 htsmsg_parse_prolog(xmlparser_t *xp, char *src)
766 {
767 htsmsg_t *pis = htsmsg_create_map();
768 htsmsg_t *xmlpi;
769 const char *encoding;
770
771 while(src != NULL && *src != 0) {
772
773 while(is_xmlws(*src))
774 src++;
775
776 if(!strncmp(src, "<?", 2)) {
777 src += 2;
778 src = htsmsg_xml_parse_pi(xp, pis, src);
779 continue;
780 }
781
782 if(!strncmp(src, "<!--", 4)) {
783 src = xml_parse_comment(xp, src + 4);
784 continue;
785 }
786
787 if(!strncmp(src, "<!DOCTYPE", 9)) {
788 while(*src != 0) {
789 if(*src == '>') {
790 src++;
791 break;
792 }
793 src++;
794 }
795 continue;
796 }
797 break;
798 }
799
800 if((xmlpi = htsmsg_get_map(pis, "xml")) != NULL) {
801
802 if((encoding = htsmsg_get_str(xmlpi, "encoding")) != NULL) {
803 if(!strcasecmp(encoding, "iso-8859-1") ||
804 !strcasecmp(encoding, "iso-8859_1") ||
805 !strcasecmp(encoding, "iso_8859-1") ||
806 !strcasecmp(encoding, "iso_8859_1")) {
807 xp->xp_encoding = XML_ENCODING_8859_1;
808 }
809 }
810 }
811
812 htsmsg_destroy(pis);
813
814 return src;
815 }
816
817
818
819 /**
820 *
821 */
822 htsmsg_t *
htsmsg_xml_deserialize(char * src,char * errbuf,size_t errbufsize)823 htsmsg_xml_deserialize(char *src, char *errbuf, size_t errbufsize)
824 {
825 htsmsg_t *m;
826 xmlparser_t xp;
827 char *src0 = src;
828 int i;
829
830 memset(&xp, 0, sizeof(xp));
831 xp.xp_encoding = XML_ENCODING_UTF8;
832 LIST_INIT(&xp.xp_namespaces);
833
834 /* check for UTF-8 BOM */
835 if(src[0] == 0xef && src[1] == 0xbb && src[2] == 0xbf)
836 memmove(src, src + 3, strlen(src) - 2);
837
838 if((src = htsmsg_parse_prolog(&xp, src)) == NULL)
839 goto err;
840
841 m = htsmsg_create_map();
842
843 if(htsmsg_xml_parse_cd(&xp, m, src) == NULL) {
844 htsmsg_destroy(m);
845 goto err;
846 }
847
848 if(xp.xp_srcdataused) {
849 m->hm_data = src0;
850 m->hm_data_size = strlen(src0) + 1;
851 } else {
852 free(src0);
853 }
854
855 return m;
856
857 err:
858 free(src0);
859 snprintf(errbuf, errbufsize, "%s", xp.xp_errmsg);
860
861 /* Remove any odd chars inside of errmsg */
862 for(i = 0; i < errbufsize; i++) {
863 if(errbuf[i] < 32) {
864 errbuf[i] = 0;
865 break;
866 }
867 }
868
869 return NULL;
870 }
871
872 /*
873 * Get cdata string field
874 */
875 const char *
htsmsg_xml_get_cdata_str(htsmsg_t * tags,const char * name)876 htsmsg_xml_get_cdata_str(htsmsg_t *tags, const char *name)
877 {
878 htsmsg_t *sub;
879 if((sub = htsmsg_get_map(tags, name)) == NULL)
880 return NULL;
881 return htsmsg_get_str(sub, "cdata");
882 }
883
884 /*
885 * Get cdata u32 field
886 */
887 int
htsmsg_xml_get_cdata_u32(htsmsg_t * tags,const char * name,uint32_t * u32)888 htsmsg_xml_get_cdata_u32(htsmsg_t *tags, const char *name, uint32_t *u32)
889 {
890 htsmsg_t *sub;
891 if((sub = htsmsg_get_map(tags, name)) == NULL)
892 return HTSMSG_ERR_FIELD_NOT_FOUND;
893 return htsmsg_get_u32(sub, "cdata", u32);
894 }
895
896 /*
897 * Get tag attribute
898 */
899 const char *
htsmsg_xml_get_attr_str(htsmsg_t * tag,const char * name)900 htsmsg_xml_get_attr_str ( htsmsg_t *tag, const char *name )
901 {
902 htsmsg_t *attr = htsmsg_get_map(tag, "attrib");
903 if (attr) return htsmsg_get_str(attr, name);
904 return NULL;
905 }
906
907 int
htsmsg_xml_get_attr_u32(htsmsg_t * tag,const char * name,uint32_t * ret)908 htsmsg_xml_get_attr_u32 ( htsmsg_t *tag, const char *name, uint32_t *ret )
909 {
910 htsmsg_t *attr = htsmsg_get_map(tag, "attrib");
911 if (attr) return htsmsg_get_u32(attr, name, ret);
912 return HTSMSG_ERR_FIELD_NOT_FOUND;
913 }
914