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 *****************************************************************************/
48 #define MIN(a,b) ((a)<(b)?(a):(b))
51 #define MAX(a,b) ((a)>(b)?(a):(b))
53 typedef struct _XTag XTag;
54 typedef struct _XAttribute XAttribute;
55 typedef struct _XTagParser XTagParser;
58 * struct _XTag is kind of a union ... it normally represents a whole
59 * tag (and its children), but it could alternatively represent some
60 * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
61 * ignore the name, attributes and inner_tags.
69 XList * current_child;
78 int valid; /* boolean */
84 /* Character classes */
86 #define X_WHITESPACE 1<<0
87 #define X_OPENTAG 1<<1
88 #define X_CLOSETAG 1<<2
95 xtag_cin (char c, int char_class)
97 if (char_class & X_WHITESPACE)
98 if (isspace(c)) return TRUE;
100 if (char_class & X_OPENTAG)
101 if (c == '<') return TRUE;
103 if (char_class & X_CLOSETAG)
104 if (c == '>') return TRUE;
106 if (char_class & X_DQUOTE)
107 if (c == '"') return TRUE;
109 if (char_class & X_SQUOTE)
110 if (c == '\'') return TRUE;
112 if (char_class & X_EQUAL)
113 if (c == '=') return TRUE;
115 if (char_class & X_SLASH)
116 if (c == '/') return TRUE;
122 xtag_index (XTagParser * parser, int char_class)
129 for (i = 0; s[i] && s != parser->end; i++) {
130 if (xtag_cin(s[i], char_class)) return i;
137 xtag_skip_over (XTagParser * parser, int char_class)
142 if (!parser->valid) return;
144 s = (char *)parser->start;
146 for (i = 0; s[i] && s != parser->end; i++) {
147 if (!xtag_cin(s[i], char_class)) {
148 parser->start = &s[i];
157 xtag_skip_whitespace (XTagParser * parser)
159 xtag_skip_over (parser, X_WHITESPACE);
164 xtag_skip_to (XTagParser * parser, int char_class)
169 if (!parser->valid) return;
171 s = (char *)parser->start;
173 for (i = 0; s[i] && s != parser->end; i++) {
174 if (xtag_cin(s[i], char_class)) {
175 parser->start = &s[i];
185 xtag_slurp_to (XTagParser * parser, int good_end, int bad_end)
190 if (!parser->valid) return NULL;
194 xi = xtag_index (parser, good_end | bad_end);
196 if (xi > 0 && xtag_cin (s[xi], good_end)) {
197 ret = malloc ((xi+1) * sizeof(char));
198 strncpy (ret, s, xi);
200 parser->start = &s[xi];
208 xtag_assert_and_pass (XTagParser * parser, int char_class)
212 if (!parser->valid) return FALSE;
216 if (!xtag_cin (s[0], char_class)) {
217 parser->valid = FALSE;
221 parser->start = &s[1];
227 xtag_slurp_quoted (XTagParser * parser)
230 int quote = X_DQUOTE; /* quote char to match on */
233 if (!parser->valid) return NULL;
235 xtag_skip_whitespace (parser);
239 if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE;
241 if (!xtag_assert_and_pass (parser, quote)) return NULL;
245 for (xi = 0; s[xi]; xi++) {
246 if (xtag_cin (s[xi], quote)) {
247 if (!(xi > 1 && s[xi-1] == '\\')) break;
251 ret = malloc ((xi+1) * sizeof(char));
252 strncpy (ret, s, xi);
254 parser->start = &s[xi];
256 if (!xtag_assert_and_pass (parser, quote)) return NULL;
262 xtag_parse_attribute (XTagParser * parser)
265 char * name, * value;
268 if (!parser->valid) return NULL;
270 xtag_skip_whitespace (parser);
272 name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG);
274 if (name == NULL) return NULL;
276 xtag_skip_whitespace (parser);
279 if (!xtag_assert_and_pass (parser, X_EQUAL)) {
281 printf ("xtag: attr failed EQUAL on <%s>\n", name);
286 xtag_skip_whitespace (parser);
288 value = xtag_slurp_quoted (parser);
292 printf ("Got NULL quoted attribute value\n");
297 attr = malloc (sizeof (*attr));
306 parser->valid = FALSE;
312 xtag_parse_tag (XTagParser * parser)
320 if (!parser->valid) return NULL;
322 if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) {
323 tag = malloc (sizeof (*tag));
325 tag->pcdata = pcdata;
326 tag->parent = parser->current_tag;
327 tag->attributes = NULL;
328 tag->children = NULL;
329 tag->current_child = NULL;
336 /* if this starts a close tag, return NULL and let the parent take it */
337 if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH))
340 if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL;
342 name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE);
344 if (name == NULL) return NULL;
347 printf ("<%s ...\n", name);
350 tag = malloc (sizeof (*tag));
353 tag->parent = parser->current_tag;
354 tag->attributes = NULL;
355 tag->children = NULL;
356 tag->current_child = NULL;
360 if (xtag_cin (s[0], X_WHITESPACE)) {
361 while ((attr = xtag_parse_attribute (parser)) != NULL) {
362 tag->attributes = xlist_append (tag->attributes, attr);
366 xtag_skip_whitespace (parser);
370 if (xtag_cin (s[0], X_CLOSETAG)) {
371 parser->current_tag = tag;
373 xtag_assert_and_pass (parser, X_CLOSETAG);
375 while ((inner = xtag_parse_tag (parser)) != NULL) {
376 tag->children = xlist_append (tag->children, inner);
379 xtag_skip_whitespace (parser);
381 xtag_assert_and_pass (parser, X_OPENTAG);
382 xtag_assert_and_pass (parser, X_SLASH);
383 name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE);
385 if (name && tag->name && strcmp (name, tag->name)) {
387 printf ("got %s expected %s\n", name, tag->name);
389 parser->valid = FALSE;
394 xtag_skip_whitespace (parser);
395 xtag_assert_and_pass (parser, X_CLOSETAG);
398 xtag_assert_and_pass (parser, X_SLASH);
399 xtag_assert_and_pass (parser, X_CLOSETAG);
407 xtag_free (XTag * xtag)
413 if (xtag == NULL) return NULL;
415 if (xtag->name) free (xtag->name);
416 if (xtag->pcdata) free (xtag->pcdata);
418 for (l = xtag->attributes; l; l = l->next) {
419 if ((attr = (XAttribute *)l->data) != NULL) {
420 if (attr->name) free (attr->name);
421 if (attr->value) free (attr->value);
425 xlist_free (xtag->attributes);
427 for (l = xtag->children; l; l = l->next) {
428 child = (XTag *)l->data;
431 xlist_free (xtag->children);
439 xtag_new_parse (const char * s, int n)
442 XTag * tag, * ttag, * wrapper;
445 parser.current_tag = NULL;
446 parser.start = (char *)s;
453 parser.end = (char *)&s[n];
455 tag = xtag_parse_tag (&parser);
462 if ((ttag = xtag_parse_tag (&parser)) != NULL) {
469 wrapper = malloc (sizeof (XTag));
470 wrapper->name = NULL;
471 wrapper->pcdata = NULL;
472 wrapper->parent = NULL;
473 wrapper->attributes = NULL;
474 wrapper->children = NULL;
475 wrapper->current_child = NULL;
477 wrapper->children = xlist_append (wrapper->children, tag);
478 wrapper->children = xlist_append (wrapper->children, ttag);
480 while ((ttag = xtag_parse_tag (&parser)) != NULL) {
487 wrapper->children = xlist_append (wrapper->children, ttag);
496 xtag_get_name (XTag * xtag)
498 return xtag ? xtag->name : NULL;
502 xtag_get_pcdata (XTag * xtag)
507 if (xtag == NULL) return NULL;
509 for (l = xtag->children; l; l = l->next) {
510 child = (XTag *)l->data;
511 if (child->pcdata != NULL) {
512 return child->pcdata;
520 xtag_get_attribute (XTag * xtag, char * attribute)
525 if (xtag == NULL) return NULL;
527 for (l = xtag->attributes; l; l = l->next) {
528 if ((attr = (XAttribute *)l->data) != NULL) {
529 if (attr->name && attribute && !strcmp (attr->name, attribute))
538 xtag_first_child (XTag * xtag, char * name)
543 if (xtag == NULL) return NULL;
545 if ((l = xtag->children) == NULL) return NULL;
548 xtag->current_child = l;
549 return (XTag *)l->data;
552 for (; l; l = l->next) {
553 child = (XTag *)l->data;
555 if (child->name && name && !strcmp(child->name, name)) {
556 xtag->current_child = l;
561 xtag->current_child = NULL;
567 xtag_next_child (XTag * xtag, char * name)
572 if (xtag == NULL) return NULL;
574 if ((l = xtag->current_child) == NULL)
575 return xtag_first_child (xtag, name);
577 if ((l = l->next) == NULL)
581 xtag->current_child = l;
582 return (XTag *)l->data;
585 for (; l; l = l->next) {
586 child = (XTag *)l->data;
588 if (child->name && name && !strcmp(child->name, name)) {
589 xtag->current_child = l;
594 xtag->current_child = NULL;
600 * This snprints function takes a variable list of char *, the last of
601 * which must be NULL, and prints each in turn to buf.
602 * Returns C99-style total length that would have been written, even if
603 * this is larger than n.
606 xtag_snprints (char * buf, int n, ...)
610 int len, to_copy, total = 0;
614 for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) {
617 if ((to_copy = MIN (n, len)) > 0) {
618 memcpy (buf, s, to_copy);
632 xtag_snprint (char * buf, int n, XTag * xtag)
645 if (n > 0) buf[0] = '\0';
650 nn = xtag_snprints (buf, n, xtag->pcdata, NULL);
657 nn = xtag_snprints (buf, n, "<", xtag->name, NULL);
660 for (l = xtag->attributes; l; l = l->next) {
661 attr = (XAttribute *)l->data;
663 nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"",
668 if (xtag->children == NULL) {
669 nn = xtag_snprints (buf, n, "/>", NULL);
675 nn = xtag_snprints (buf, n, ">", NULL);
679 for (l = xtag->children; l; l = l->next) {
680 child = (XTag *)l->data;
682 nn = xtag_snprint (buf, n, child);
687 nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL);