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 *****************************************************************************/
45 #define MIN(a,b) ((a)<(b)?(a):(b))
48 #define MAX(a,b) ((a)>(b)?(a):(b))
50 typedef struct _XTag XTag;
51 typedef struct _XAttribute XAttribute;
52 typedef struct _XTagParser XTagParser;
55 * struct _XTag is kind of a union ... it normally represents a whole
56 * tag (and its children), but it could alternatively represent some
57 * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
58 * ignore the name, attributes and inner_tags.
66 XList * current_child;
75 int valid; /* boolean */
81 /* Character classes */
83 #define X_WHITESPACE 1<<0
84 #define X_OPENTAG 1<<1
85 #define X_CLOSETAG 1<<2
92 xtag_cin (char c, int char_class)
94 if (char_class & X_WHITESPACE)
95 if (isspace(c)) return TRUE;
97 if (char_class & X_OPENTAG)
98 if (c == '<') return TRUE;
100 if (char_class & X_CLOSETAG)
101 if (c == '>') return TRUE;
103 if (char_class & X_DQUOTE)
104 if (c == '"') return TRUE;
106 if (char_class & X_SQUOTE)
107 if (c == '\'') return TRUE;
109 if (char_class & X_EQUAL)
110 if (c == '=') return TRUE;
112 if (char_class & X_SLASH)
113 if (c == '/') return TRUE;
119 xtag_index (XTagParser * parser, int char_class)
126 for (i = 0; s[i] && s != parser->end; i++) {
127 if (xtag_cin(s[i], char_class)) return i;
134 xtag_skip_over (XTagParser * parser, int char_class)
139 if (!parser->valid) return;
141 s = (char *)parser->start;
143 for (i = 0; s[i] && s != parser->end; i++) {
144 if (!xtag_cin(s[i], char_class)) {
145 parser->start = &s[i];
154 xtag_skip_whitespace (XTagParser * parser)
156 xtag_skip_over (parser, X_WHITESPACE);
161 xtag_skip_to (XTagParser * parser, int char_class)
166 if (!parser->valid) return;
168 s = (char *)parser->start;
170 for (i = 0; s[i] && s != parser->end; i++) {
171 if (xtag_cin(s[i], char_class)) {
172 parser->start = &s[i];
182 xtag_slurp_to (XTagParser * parser, int good_end, int bad_end)
187 if (!parser->valid) return NULL;
191 xi = xtag_index (parser, good_end | bad_end);
193 if (xi > 0 && xtag_cin (s[xi], good_end)) {
194 ret = malloc ((xi+1) * sizeof(char));
195 strncpy (ret, s, xi);
197 parser->start = &s[xi];
205 xtag_assert_and_pass (XTagParser * parser, int char_class)
209 if (!parser->valid) return FALSE;
213 if (!xtag_cin (s[0], char_class)) {
214 parser->valid = FALSE;
218 parser->start = &s[1];
224 xtag_slurp_quoted (XTagParser * parser)
227 int quote = X_DQUOTE; /* quote char to match on */
230 if (!parser->valid) return NULL;
232 xtag_skip_whitespace (parser);
236 if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE;
238 if (!xtag_assert_and_pass (parser, quote)) return NULL;
242 for (xi = 0; s[xi]; xi++) {
243 if (xtag_cin (s[xi], quote)) {
244 if (!(xi > 1 && s[xi-1] == '\\')) break;
248 ret = malloc ((xi+1) * sizeof(char));
249 strncpy (ret, s, xi);
251 parser->start = &s[xi];
253 if (!xtag_assert_and_pass (parser, quote)) return NULL;
259 xtag_parse_attribute (XTagParser * parser)
262 char * name, * value;
265 if (!parser->valid) return NULL;
267 xtag_skip_whitespace (parser);
269 name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG);
271 if (name == NULL) return NULL;
273 xtag_skip_whitespace (parser);
276 if (!xtag_assert_and_pass (parser, X_EQUAL)) {
278 printf ("xtag: attr failed EQUAL on <%s>\n", name);
283 xtag_skip_whitespace (parser);
285 value = xtag_slurp_quoted (parser);
289 printf ("Got NULL quoted attribute value\n");
294 attr = malloc (sizeof (*attr));
303 parser->valid = FALSE;
309 xtag_parse_tag (XTagParser * parser)
317 if (!parser->valid) return NULL;
319 if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) {
320 tag = malloc (sizeof (*tag));
322 tag->pcdata = pcdata;
323 tag->parent = parser->current_tag;
324 tag->attributes = NULL;
325 tag->children = NULL;
326 tag->current_child = NULL;
333 /* if this starts a close tag, return NULL and let the parent take it */
334 if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH))
337 if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL;
339 name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE);
341 if (name == NULL) return NULL;
344 printf ("<%s ...\n", name);
347 tag = malloc (sizeof (*tag));
350 tag->parent = parser->current_tag;
351 tag->attributes = NULL;
352 tag->children = NULL;
353 tag->current_child = NULL;
357 if (xtag_cin (s[0], X_WHITESPACE)) {
358 while ((attr = xtag_parse_attribute (parser)) != NULL) {
359 tag->attributes = xlist_append (tag->attributes, attr);
363 xtag_skip_whitespace (parser);
367 if (xtag_cin (s[0], X_CLOSETAG)) {
368 parser->current_tag = tag;
370 xtag_assert_and_pass (parser, X_CLOSETAG);
372 while ((inner = xtag_parse_tag (parser)) != NULL) {
373 tag->children = xlist_append (tag->children, inner);
376 xtag_skip_whitespace (parser);
378 xtag_assert_and_pass (parser, X_OPENTAG);
379 xtag_assert_and_pass (parser, X_SLASH);
380 name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE);
382 if (name && tag->name && strcmp (name, tag->name)) {
384 printf ("got %s expected %s\n", name, tag->name);
386 parser->valid = FALSE;
391 xtag_skip_whitespace (parser);
392 xtag_assert_and_pass (parser, X_CLOSETAG);
395 xtag_assert_and_pass (parser, X_SLASH);
396 xtag_assert_and_pass (parser, X_CLOSETAG);
404 xtag_free (XTag * xtag)
410 if (xtag == NULL) return NULL;
412 if (xtag->name) free (xtag->name);
413 if (xtag->pcdata) free (xtag->pcdata);
415 for (l = xtag->attributes; l; l = l->next) {
416 if ((attr = (XAttribute *)l->data) != NULL) {
417 if (attr->name) free (attr->name);
418 if (attr->value) free (attr->value);
422 xlist_free (xtag->attributes);
424 for (l = xtag->children; l; l = l->next) {
425 child = (XTag *)l->data;
428 xlist_free (xtag->children);
436 xtag_new_parse (const char * s, int n)
439 XTag * tag, * ttag, * wrapper;
442 parser.current_tag = NULL;
443 parser.start = (char *)s;
450 parser.end = (char *)&s[n];
452 tag = xtag_parse_tag (&parser);
459 if ((ttag = xtag_parse_tag (&parser)) != NULL) {
466 wrapper = malloc (sizeof (XTag));
467 wrapper->name = NULL;
468 wrapper->pcdata = NULL;
469 wrapper->parent = NULL;
470 wrapper->attributes = NULL;
471 wrapper->children = NULL;
472 wrapper->current_child = NULL;
474 wrapper->children = xlist_append (wrapper->children, tag);
475 wrapper->children = xlist_append (wrapper->children, ttag);
477 while ((ttag = xtag_parse_tag (&parser)) != NULL) {
484 wrapper->children = xlist_append (wrapper->children, ttag);
493 xtag_get_name (XTag * xtag)
495 return xtag ? xtag->name : NULL;
499 xtag_get_pcdata (XTag * xtag)
504 if (xtag == NULL) return NULL;
506 for (l = xtag->children; l; l = l->next) {
507 child = (XTag *)l->data;
508 if (child->pcdata != NULL) {
509 return child->pcdata;
517 xtag_get_attribute (XTag * xtag, char * attribute)
522 if (xtag == NULL) return NULL;
524 for (l = xtag->attributes; l; l = l->next) {
525 if ((attr = (XAttribute *)l->data) != NULL) {
526 if (attr->name && attribute && !strcmp (attr->name, attribute))
535 xtag_first_child (XTag * xtag, char * name)
540 if (xtag == NULL) return NULL;
542 if ((l = xtag->children) == NULL) return NULL;
545 xtag->current_child = l;
546 return (XTag *)l->data;
549 for (; l; l = l->next) {
550 child = (XTag *)l->data;
552 if (child->name && name && !strcmp(child->name, name)) {
553 xtag->current_child = l;
558 xtag->current_child = NULL;
564 xtag_next_child (XTag * xtag, char * name)
569 if (xtag == NULL) return NULL;
571 if ((l = xtag->current_child) == NULL)
572 return xtag_first_child (xtag, name);
574 if ((l = l->next) == NULL)
578 xtag->current_child = l;
579 return (XTag *)l->data;
582 for (; l; l = l->next) {
583 child = (XTag *)l->data;
585 if (child->name && name && !strcmp(child->name, name)) {
586 xtag->current_child = l;
591 xtag->current_child = NULL;
597 * This snprints function takes a variable list of char *, the last of
598 * which must be NULL, and prints each in turn to buf.
599 * Returns C99-style total length that would have been written, even if
600 * this is larger than n.
603 xtag_snprints (char * buf, int n, ...)
607 int len, to_copy, total = 0;
611 for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) {
614 if ((to_copy = MIN (n, len)) > 0) {
615 memcpy (buf, s, to_copy);
629 xtag_snprint (char * buf, int n, XTag * xtag)
642 if (n > 0) buf[0] = '\0';
647 nn = xtag_snprints (buf, n, xtag->pcdata, NULL);
654 nn = xtag_snprints (buf, n, "<", xtag->name, NULL);
657 for (l = xtag->attributes; l; l = l->next) {
658 attr = (XAttribute *)l->data;
660 nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"",
665 if (xtag->children == NULL) {
666 nn = xtag_snprints (buf, n, "/>", NULL);
672 nn = xtag_snprints (buf, n, ">", NULL);
676 for (l = xtag->children; l; l = l->next) {
677 child = (XTag *)l->data;
679 nn = xtag_snprint (buf, n, child);
684 nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL);