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 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
26 *****************************************************************************/
47 #define MIN(a,b) ((a)<(b)?(a):(b))
50 #define MAX(a,b) ((a)>(b)?(a):(b))
52 typedef struct _XTag XTag;
53 typedef struct _XAttribute XAttribute;
54 typedef struct _XTagParser XTagParser;
57 * struct _XTag is kind of a union ... it normally represents a whole
58 * tag (and its children), but it could alternatively represent some
59 * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
60 * ignore the name, attributes and inner_tags.
68 XList * current_child;
77 int valid; /* boolean */
83 /* Character classes */
85 #define X_WHITESPACE 1<<0
86 #define X_OPENTAG 1<<1
87 #define X_CLOSETAG 1<<2
94 xtag_cin (char c, int char_class)
96 if (char_class & X_WHITESPACE)
97 if (isspace(c)) return TRUE;
99 if (char_class & X_OPENTAG)
100 if (c == '<') return TRUE;
102 if (char_class & X_CLOSETAG)
103 if (c == '>') return TRUE;
105 if (char_class & X_DQUOTE)
106 if (c == '"') return TRUE;
108 if (char_class & X_SQUOTE)
109 if (c == '\'') return TRUE;
111 if (char_class & X_EQUAL)
112 if (c == '=') return TRUE;
114 if (char_class & X_SLASH)
115 if (c == '/') return TRUE;
121 xtag_index (XTagParser * parser, int char_class)
128 for (i = 0; s[i] && s != parser->end; i++) {
129 if (xtag_cin(s[i], char_class)) return i;
136 xtag_skip_over (XTagParser * parser, int char_class)
141 if (!parser->valid) return;
143 s = (char *)parser->start;
145 for (i = 0; s[i] && s != parser->end; i++) {
146 if (!xtag_cin(s[i], char_class)) {
147 parser->start = &s[i];
156 xtag_skip_whitespace (XTagParser * parser)
158 xtag_skip_over (parser, X_WHITESPACE);
163 xtag_skip_to (XTagParser * parser, int char_class)
168 if (!parser->valid) return;
170 s = (char *)parser->start;
172 for (i = 0; s[i] && s != parser->end; i++) {
173 if (xtag_cin(s[i], char_class)) {
174 parser->start = &s[i];
184 xtag_slurp_to (XTagParser * parser, int good_end, int bad_end)
189 if (!parser->valid) return NULL;
193 xi = xtag_index (parser, good_end | bad_end);
195 if (xi > 0 && xtag_cin (s[xi], good_end)) {
196 ret = malloc ((xi+1) * sizeof(char));
197 strncpy (ret, s, xi);
199 parser->start = &s[xi];
207 xtag_assert_and_pass (XTagParser * parser, int char_class)
211 if (!parser->valid) return FALSE;
215 if (!xtag_cin (s[0], char_class)) {
216 parser->valid = FALSE;
220 parser->start = &s[1];
226 xtag_slurp_quoted (XTagParser * parser)
229 int quote = X_DQUOTE; /* quote char to match on */
232 if (!parser->valid) return NULL;
234 xtag_skip_whitespace (parser);
238 if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE;
240 if (!xtag_assert_and_pass (parser, quote)) return NULL;
244 for (xi = 0; s[xi]; xi++) {
245 if (xtag_cin (s[xi], quote)) {
246 if (!(xi > 1 && s[xi-1] == '\\')) break;
250 ret = malloc ((xi+1) * sizeof(char));
251 strncpy (ret, s, xi);
253 parser->start = &s[xi];
255 if (!xtag_assert_and_pass (parser, quote)) return NULL;
261 xtag_parse_attribute (XTagParser * parser)
264 char * name, * value;
267 if (!parser->valid) return NULL;
269 xtag_skip_whitespace (parser);
271 name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG);
273 if (name == NULL) return NULL;
275 xtag_skip_whitespace (parser);
278 if (!xtag_assert_and_pass (parser, X_EQUAL)) {
280 printf ("xtag: attr failed EQUAL on <%s>\n", name);
285 xtag_skip_whitespace (parser);
287 value = xtag_slurp_quoted (parser);
291 printf ("Got NULL quoted attribute value\n");
296 attr = malloc (sizeof (*attr));
305 parser->valid = FALSE;
311 xtag_parse_tag (XTagParser * parser)
319 if (!parser->valid) return NULL;
321 if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) {
322 tag = malloc (sizeof (*tag));
324 tag->pcdata = pcdata;
325 tag->parent = parser->current_tag;
326 tag->attributes = NULL;
327 tag->children = NULL;
328 tag->current_child = NULL;
335 /* if this starts a close tag, return NULL and let the parent take it */
336 if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH))
339 if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL;
341 name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE);
343 if (name == NULL) return NULL;
346 printf ("<%s ...\n", name);
349 tag = malloc (sizeof (*tag));
352 tag->parent = parser->current_tag;
353 tag->attributes = NULL;
354 tag->children = NULL;
355 tag->current_child = NULL;
359 if (xtag_cin (s[0], X_WHITESPACE)) {
360 while ((attr = xtag_parse_attribute (parser)) != NULL) {
361 tag->attributes = xlist_append (tag->attributes, attr);
365 xtag_skip_whitespace (parser);
369 if (xtag_cin (s[0], X_CLOSETAG)) {
370 parser->current_tag = tag;
372 xtag_assert_and_pass (parser, X_CLOSETAG);
374 while ((inner = xtag_parse_tag (parser)) != NULL) {
375 tag->children = xlist_append (tag->children, inner);
378 xtag_skip_whitespace (parser);
380 xtag_assert_and_pass (parser, X_OPENTAG);
381 xtag_assert_and_pass (parser, X_SLASH);
382 name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE);
384 if (strcmp (name, tag->name)) {
386 printf ("got %s expected %s\n", name, tag->name);
388 parser->valid = FALSE;
393 xtag_skip_whitespace (parser);
394 xtag_assert_and_pass (parser, X_CLOSETAG);
397 xtag_assert_and_pass (parser, X_SLASH);
398 xtag_assert_and_pass (parser, X_CLOSETAG);
406 xtag_free (XTag * xtag)
412 if (xtag == NULL) return NULL;
414 if (xtag->name) free (xtag->name);
415 if (xtag->pcdata) free (xtag->pcdata);
417 for (l = xtag->attributes; l; l = l->next) {
418 if ((attr = (XAttribute *)l->data) != NULL) {
419 if (attr->name) free (attr->name);
420 if (attr->value) free (attr->value);
424 xlist_free (xtag->attributes);
426 for (l = xtag->children; l; l = l->next) {
427 child = (XTag *)l->data;
430 xlist_free (xtag->children);
438 xtag_new_parse (const char * s, int n)
441 XTag * tag, * ttag, * wrapper;
444 parser.current_tag = NULL;
445 parser.start = (char *)s;
452 parser.end = (char *)&s[n];
454 tag = xtag_parse_tag (&parser);
461 if ((ttag = xtag_parse_tag (&parser)) != NULL) {
468 wrapper = malloc (sizeof (XTag));
469 wrapper->name = NULL;
470 wrapper->pcdata = NULL;
471 wrapper->parent = NULL;
472 wrapper->attributes = NULL;
473 wrapper->children = NULL;
474 wrapper->current_child = NULL;
476 wrapper->children = xlist_append (wrapper->children, tag);
477 wrapper->children = xlist_append (wrapper->children, ttag);
479 while ((ttag = xtag_parse_tag (&parser)) != NULL) {
486 wrapper->children = xlist_append (wrapper->children, ttag);
495 xtag_get_name (XTag * xtag)
497 return xtag ? xtag->name : NULL;
501 xtag_get_pcdata (XTag * xtag)
506 if (xtag == NULL) return NULL;
508 for (l = xtag->children; l; l = l->next) {
509 child = (XTag *)l->data;
510 if (child->pcdata != NULL) {
511 return child->pcdata;
519 xtag_get_attribute (XTag * xtag, char * attribute)
524 if (xtag == NULL) return NULL;
526 for (l = xtag->attributes; l; l = l->next) {
527 if ((attr = (XAttribute *)l->data) != NULL) {
528 if (!strcmp (attr->name, attribute))
537 xtag_first_child (XTag * xtag, char * name)
542 if (xtag == NULL) return NULL;
544 if ((l = xtag->children) == NULL) return NULL;
547 xtag->current_child = l;
548 return (XTag *)l->data;
551 for (; l; l = l->next) {
552 child = (XTag *)l->data;
554 if (!strcmp(child->name, name)) {
555 xtag->current_child = l;
560 xtag->current_child = NULL;
566 xtag_next_child (XTag * xtag, char * name)
571 if (xtag == NULL) return NULL;
573 if ((l = xtag->current_child) == NULL)
574 return xtag_first_child (xtag, name);
576 if ((l = l->next) == NULL)
580 xtag->current_child = l;
581 return (XTag *)l->data;
584 for (; l; l = l->next) {
585 child = (XTag *)l->data;
587 if (!strcmp(child->name, name)) {
588 xtag->current_child = l;
593 xtag->current_child = NULL;
599 * This snprints function takes a variable list of char *, the last of
600 * which must be NULL, and prints each in turn to buf.
601 * Returns C99-style total length that would have been written, even if
602 * this is larger than n.
605 xtag_snprints (char * buf, int n, ...)
609 int len, to_copy, total = 0;
613 for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) {
616 if ((to_copy = MIN (n, len)) > 0) {
617 memcpy (buf, s, to_copy);
631 xtag_snprint (char * buf, int n, XTag * xtag)
644 if (n > 0) buf[0] = '\0';
649 nn = xtag_snprints (buf, n, xtag->pcdata, NULL);
656 nn = xtag_snprints (buf, n, "<", xtag->name, NULL);
659 for (l = xtag->attributes; l; l = l->next) {
660 attr = (XAttribute *)l->data;
662 nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"",
667 if (xtag->children == NULL) {
668 nn = xtag_snprints (buf, n, "/>", NULL);
674 nn = xtag_snprints (buf, n, ">", NULL);
678 for (l = xtag->children; l; l = l->next) {
679 child = (XTag *)l->data;
681 nn = xtag_snprint (buf, n, child);
686 nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL);