1 /*****************************************************************************
2 * xlist.c : a trivial parser for XML-like tags
3 *****************************************************************************
4 * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
5 * Organisation (CSIRO) Australia
6 * Copyright (C) 2000-2004 the VideoLAN team
10 * Authors: Conrad Parker <Conrad.Parker@csiro.au>
11 * Andre Pang <Andre.Pang@csiro.au>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26 *****************************************************************************/
50 #define MIN(a,b) ((a)<(b)?(a):(b))
53 #define MAX(a,b) ((a)>(b)?(a):(b))
55 typedef struct _XTag XTag;
56 typedef struct _XAttribute XAttribute;
57 typedef struct _XTagParser XTagParser;
60 * struct _XTag is kind of a union ... it normally represents a whole
61 * tag (and its children), but it could alternatively represent some
62 * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
63 * ignore the name, attributes and inner_tags.
71 XList * current_child;
80 int valid; /* boolean */
86 XTag * xtag_free (XTag * xtag);
87 XTag * xtag_new_parse (const char * s, int n);
88 char * xtag_get_name (XTag * xtag);
89 char * xtag_get_pcdata (XTag * xtag);
90 char * xtag_get_attribute (XTag * xtag, char * attribute);
91 XTag * xtag_first_child (XTag * xtag, char * name);
92 XTag * xtag_next_child (XTag * xtag, char * name);
93 int xtag_snprint (char * buf, int n, XTag * xtag);
95 /* Character classes */
97 #define X_WHITESPACE 1<<0
98 #define X_OPENTAG 1<<1
99 #define X_CLOSETAG 1<<2
100 #define X_DQUOTE 1<<3
101 #define X_SQUOTE 1<<4
106 xtag_cin (char c, int char_class)
108 if (char_class & X_WHITESPACE)
109 if (isspace(c)) return TRUE;
111 if (char_class & X_OPENTAG)
112 if (c == '<') return TRUE;
114 if (char_class & X_CLOSETAG)
115 if (c == '>') return TRUE;
117 if (char_class & X_DQUOTE)
118 if (c == '"') return TRUE;
120 if (char_class & X_SQUOTE)
121 if (c == '\'') return TRUE;
123 if (char_class & X_EQUAL)
124 if (c == '=') return TRUE;
126 if (char_class & X_SLASH)
127 if (c == '/') return TRUE;
133 xtag_index (XTagParser * parser, int char_class)
140 for (i = 0; s[i] && s != parser->end; i++) {
141 if (xtag_cin(s[i], char_class)) return i;
148 xtag_skip_over (XTagParser * parser, int char_class)
153 if (!parser->valid) return;
155 s = (char *)parser->start;
157 for (i = 0; s[i] && s != parser->end; i++) {
158 if (!xtag_cin(s[i], char_class)) {
159 parser->start = &s[i];
168 xtag_skip_whitespace (XTagParser * parser)
170 xtag_skip_over (parser, X_WHITESPACE);
175 xtag_skip_to (XTagParser * parser, int char_class)
180 if (!parser->valid) return;
182 s = (char *)parser->start;
184 for (i = 0; s[i] && s != parser->end; i++) {
185 if (xtag_cin(s[i], char_class)) {
186 parser->start = &s[i];
196 xtag_slurp_to (XTagParser * parser, int good_end, int bad_end)
201 if (!parser->valid) return NULL;
205 xi = xtag_index (parser, good_end | bad_end);
207 if (xi > 0 && xtag_cin (s[xi], good_end)) {
208 ret = malloc ((xi+1) * sizeof(char));
209 strncpy (ret, s, xi);
211 parser->start = &s[xi];
219 xtag_assert_and_pass (XTagParser * parser, int char_class)
223 if (!parser->valid) return FALSE;
227 if (!xtag_cin (s[0], char_class)) {
228 parser->valid = FALSE;
232 parser->start = &s[1];
238 xtag_slurp_quoted (XTagParser * parser)
241 int quote = X_DQUOTE; /* quote char to match on */
244 if (!parser->valid) return NULL;
246 xtag_skip_whitespace (parser);
250 if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE;
252 if (!xtag_assert_and_pass (parser, quote)) return NULL;
256 for (xi = 0; s[xi]; xi++) {
257 if (xtag_cin (s[xi], quote)) {
258 if (!(xi > 1 && s[xi-1] == '\\')) break;
262 ret = malloc ((xi+1) * sizeof(char));
263 strncpy (ret, s, xi);
265 parser->start = &s[xi];
267 if (!xtag_assert_and_pass (parser, quote)) return NULL;
273 xtag_parse_attribute (XTagParser * parser)
276 char * name, * value;
279 if (!parser->valid) return NULL;
281 xtag_skip_whitespace (parser);
283 name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG);
285 if (name == NULL) return NULL;
287 xtag_skip_whitespace (parser);
290 if (!xtag_assert_and_pass (parser, X_EQUAL)) {
292 printf ("xtag: attr failed EQUAL on <%s>\n", name);
297 xtag_skip_whitespace (parser);
299 value = xtag_slurp_quoted (parser);
303 printf ("Got NULL quoted attribute value\n");
308 attr = malloc (sizeof (*attr));
317 parser->valid = FALSE;
323 xtag_parse_tag (XTagParser * parser)
331 if (!parser->valid) return NULL;
333 if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) {
334 tag = malloc (sizeof (*tag));
336 tag->pcdata = pcdata;
337 tag->parent = parser->current_tag;
338 tag->attributes = NULL;
339 tag->children = NULL;
340 tag->current_child = NULL;
347 /* if this starts a close tag, return NULL and let the parent take it */
348 if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH))
351 if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL;
353 name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE);
355 if (name == NULL) return NULL;
358 printf ("<%s ...\n", name);
361 tag = malloc (sizeof (*tag));
364 tag->parent = parser->current_tag;
365 tag->attributes = NULL;
366 tag->children = NULL;
367 tag->current_child = NULL;
371 if (xtag_cin (s[0], X_WHITESPACE)) {
372 while ((attr = xtag_parse_attribute (parser)) != NULL) {
373 tag->attributes = xlist_append (tag->attributes, attr);
377 xtag_skip_whitespace (parser);
381 if (xtag_cin (s[0], X_CLOSETAG)) {
382 parser->current_tag = tag;
384 xtag_assert_and_pass (parser, X_CLOSETAG);
386 while ((inner = xtag_parse_tag (parser)) != NULL) {
387 tag->children = xlist_append (tag->children, inner);
390 xtag_skip_whitespace (parser);
392 xtag_assert_and_pass (parser, X_OPENTAG);
393 xtag_assert_and_pass (parser, X_SLASH);
394 name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE);
396 if (name && tag->name && strcmp (name, tag->name)) {
398 printf ("got %s expected %s\n", name, tag->name);
400 parser->valid = FALSE;
405 xtag_skip_whitespace (parser);
406 xtag_assert_and_pass (parser, X_CLOSETAG);
409 xtag_assert_and_pass (parser, X_SLASH);
410 xtag_assert_and_pass (parser, X_CLOSETAG);
418 xtag_free (XTag * xtag)
424 if (xtag == NULL) return NULL;
427 free( xtag->pcdata );
429 for (l = xtag->attributes; l; l = l->next) {
430 if ((attr = (XAttribute *)l->data) != NULL) {
436 xlist_free (xtag->attributes);
438 for (l = xtag->children; l; l = l->next) {
439 child = (XTag *)l->data;
442 xlist_free (xtag->children);
450 xtag_new_parse (const char * s, int n)
453 XTag * tag, * ttag, * wrapper;
456 parser.current_tag = NULL;
457 parser.start = (char *)s;
464 parser.end = (char *)&s[n];
466 tag = xtag_parse_tag (&parser);
473 if ((ttag = xtag_parse_tag (&parser)) != NULL) {
480 wrapper = malloc (sizeof (XTag));
481 wrapper->name = NULL;
482 wrapper->pcdata = NULL;
483 wrapper->parent = NULL;
484 wrapper->attributes = NULL;
485 wrapper->children = NULL;
486 wrapper->current_child = NULL;
488 wrapper->children = xlist_append (wrapper->children, tag);
489 wrapper->children = xlist_append (wrapper->children, ttag);
491 while ((ttag = xtag_parse_tag (&parser)) != NULL) {
498 wrapper->children = xlist_append (wrapper->children, ttag);
507 xtag_get_name (XTag * xtag)
509 return xtag ? xtag->name : NULL;
513 xtag_get_pcdata (XTag * xtag)
518 if (xtag == NULL) return NULL;
520 for (l = xtag->children; l; l = l->next) {
521 child = (XTag *)l->data;
522 if (child->pcdata != NULL) {
523 return child->pcdata;
531 xtag_get_attribute (XTag * xtag, char * attribute)
536 if (xtag == NULL) return NULL;
538 for (l = xtag->attributes; l; l = l->next) {
539 if ((attr = (XAttribute *)l->data) != NULL) {
540 if (attr->name && attribute && !strcmp (attr->name, attribute))
549 xtag_first_child (XTag * xtag, char * name)
554 if (xtag == NULL) return NULL;
556 if ((l = xtag->children) == NULL) return NULL;
559 xtag->current_child = l;
560 return (XTag *)l->data;
563 for (; l; l = l->next) {
564 child = (XTag *)l->data;
566 if (child->name && name && !strcmp(child->name, name)) {
567 xtag->current_child = l;
572 xtag->current_child = NULL;
578 xtag_next_child (XTag * xtag, char * name)
583 if (xtag == NULL) return NULL;
585 if ((l = xtag->current_child) == NULL)
586 return xtag_first_child (xtag, name);
588 if ((l = l->next) == NULL)
592 xtag->current_child = l;
593 return (XTag *)l->data;
596 for (; l; l = l->next) {
597 child = (XTag *)l->data;
599 if (child->name && name && !strcmp(child->name, name)) {
600 xtag->current_child = l;
605 xtag->current_child = NULL;
611 * This snprints function takes a variable list of char *, the last of
612 * which must be NULL, and prints each in turn to buf.
613 * Returns C99-style total length that would have been written, even if
614 * this is larger than n.
617 xtag_snprints (char * buf, int n, ...)
621 int len, to_copy, total = 0;
625 for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) {
628 if ((to_copy = MIN (n, len)) > 0) {
629 memcpy (buf, s, to_copy);
643 xtag_snprint (char * buf, int n, XTag * xtag)
656 if (n > 0) buf[0] = '\0';
661 nn = xtag_snprints (buf, n, xtag->pcdata, NULL);
668 nn = xtag_snprints (buf, n, "<", xtag->name, NULL);
671 for (l = xtag->attributes; l; l = l->next) {
672 attr = (XAttribute *)l->data;
674 nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"",
679 if (xtag->children == NULL) {
680 nn = xtag_snprints (buf, n, "/>", NULL);
686 nn = xtag_snprints (buf, n, ">", NULL);
690 for (l = xtag->children; l; l = l->next) {
691 child = (XTag *)l->data;
693 nn = xtag_snprint (buf, n, child);
698 nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL);