]> git.sesse.net Git - vlc/blob - modules/codec/cmml/xtag.c
2387e1d18cb264cb81b8451aa43c22f30e722efe
[vlc] / modules / codec / cmml / xtag.c
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
7  *
8  * $Id$
9  *
10  * Authors: Conrad Parker <Conrad.Parker@csiro.au>
11  *          Andre Pang <Andre.Pang@csiro.au>
12  *
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.
17  *
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.
22  *
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  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <ctype.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <xlist.h>
38
39 #undef XTAG_DEBUG
40
41 #undef FALSE
42 #undef TRUE
43
44 #define FALSE (0)
45 #define TRUE (!FALSE)
46
47 #undef MIN
48 #define MIN(a,b) ((a)<(b)?(a):(b))
49
50 #undef MAX
51 #define MAX(a,b) ((a)>(b)?(a):(b))
52
53 typedef struct _XTag XTag;
54 typedef struct _XAttribute XAttribute;
55 typedef struct _XTagParser XTagParser;
56
57 /*
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.
62  */
63 struct _XTag {
64   char * name;
65   char * pcdata;
66   XTag * parent;
67   XList * attributes;
68   XList * children;
69   XList * current_child;
70 };
71
72 struct _XAttribute {
73   char * name;
74   char * value;
75 };
76
77 struct _XTagParser {
78   int valid; /* boolean */
79   XTag * current_tag;
80   char * start;
81   char * end;
82 };
83
84 /* Character classes */
85 #define X_NONE           0
86 #define X_WHITESPACE  1<<0
87 #define X_OPENTAG     1<<1
88 #define X_CLOSETAG    1<<2
89 #define X_DQUOTE      1<<3
90 #define X_SQUOTE      1<<4
91 #define X_EQUAL       1<<5
92 #define X_SLASH       1<<6
93
94 static int
95 xtag_cin (char c, int char_class)
96 {
97   if (char_class & X_WHITESPACE)
98     if (isspace(c)) return TRUE;
99
100   if (char_class & X_OPENTAG)
101     if (c == '<') return TRUE;
102
103   if (char_class & X_CLOSETAG)
104     if (c == '>') return TRUE;
105
106   if (char_class & X_DQUOTE)
107     if (c == '"') return TRUE;
108
109   if (char_class & X_SQUOTE)
110     if (c == '\'') return TRUE;
111
112   if (char_class & X_EQUAL)
113     if (c == '=') return TRUE;
114
115   if (char_class & X_SLASH)
116     if (c == '/') return TRUE;
117
118   return FALSE;
119 }
120
121 static int
122 xtag_index (XTagParser * parser, int char_class)
123 {
124   char * s;
125   int i;
126
127   s = parser->start;
128
129   for (i = 0; s[i] && s != parser->end; i++) {
130     if (xtag_cin(s[i], char_class)) return i;
131   }
132
133   return -1;
134 }
135
136 static void
137 xtag_skip_over (XTagParser * parser, int char_class)
138 {
139   char * s;
140   int i;
141
142   if (!parser->valid) return;
143
144   s = (char *)parser->start;
145
146   for (i = 0; s[i] && s != parser->end; i++) {
147     if (!xtag_cin(s[i], char_class)) {
148       parser->start = &s[i];
149       return;
150     }
151   }
152
153   return;
154 }
155
156 static void
157 xtag_skip_whitespace (XTagParser * parser)
158 {
159   xtag_skip_over (parser, X_WHITESPACE);
160 }
161
162 #if 0
163 static void
164 xtag_skip_to (XTagParser * parser, int char_class)
165 {
166   char * s;
167   int i;
168
169   if (!parser->valid) return;
170
171   s = (char *)parser->start;
172
173   for (i = 0; s[i] && s != parser->end; i++) {
174     if (xtag_cin(s[i], char_class)) {
175       parser->start = &s[i];
176       return;
177     }
178   }
179
180   return;
181 }
182 #endif
183
184 static char *
185 xtag_slurp_to (XTagParser * parser, int good_end, int bad_end)
186 {
187   char * s, * ret;
188   int xi;
189
190   if (!parser->valid) return NULL;
191
192   s = parser->start;
193
194   xi = xtag_index (parser, good_end | bad_end);
195
196   if (xi > 0 && xtag_cin (s[xi], good_end)) {
197     ret = malloc ((xi+1) * sizeof(char));
198     strncpy (ret, s, xi);
199     ret[xi] = '\0';
200     parser->start = &s[xi];
201     return ret;
202   }
203
204   return NULL;
205 }
206
207 static int
208 xtag_assert_and_pass (XTagParser * parser, int char_class)
209 {
210   char * s;
211
212   if (!parser->valid) return FALSE;
213
214   s = parser->start;
215
216   if (!xtag_cin (s[0], char_class)) {
217     parser->valid = FALSE;
218     return FALSE;
219   }
220
221   parser->start = &s[1];
222
223   return TRUE;
224 }
225
226 static char *
227 xtag_slurp_quoted (XTagParser * parser)
228 {
229   char * s, * ret;
230   int quote = X_DQUOTE; /* quote char to match on */
231   int xi;
232
233   if (!parser->valid) return NULL;
234
235   xtag_skip_whitespace (parser);
236
237   s = parser->start;
238
239   if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE;
240
241   if (!xtag_assert_and_pass (parser, quote)) return NULL;
242
243   s = parser->start;
244
245   for (xi = 0; s[xi]; xi++) {
246     if (xtag_cin (s[xi], quote)) {
247       if (!(xi > 1 && s[xi-1] == '\\')) break;
248     }
249   }
250
251   ret = malloc ((xi+1) * sizeof(char));
252   strncpy (ret, s, xi);
253   ret[xi] = '\0';
254   parser->start = &s[xi];
255
256   if (!xtag_assert_and_pass (parser, quote)) return NULL;
257
258   return ret;
259 }
260
261 static XAttribute *
262 xtag_parse_attribute (XTagParser * parser)
263 {
264   XAttribute * attr;
265   char * name, * value;
266   char * s;
267
268   if (!parser->valid) return NULL;
269
270   xtag_skip_whitespace (parser);
271  
272   name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG);
273
274   if (name == NULL) return NULL;
275
276   xtag_skip_whitespace (parser);
277   s = parser->start;
278
279   if (!xtag_assert_and_pass (parser, X_EQUAL)) {
280 #ifdef XTAG_DEBUG
281     printf ("xtag: attr failed EQUAL on <%s>\n", name);
282 #endif
283     goto err_free_name;
284   }
285
286   xtag_skip_whitespace (parser);
287
288   value = xtag_slurp_quoted (parser);
289
290   if (value == NULL) {
291 #ifdef XTAG_DEBUG
292     printf ("Got NULL quoted attribute value\n");
293 #endif
294     goto err_free_name;
295   }
296
297   attr = malloc (sizeof (*attr));
298   attr->name = name;
299   attr->value = value;
300
301   return attr;
302
303  err_free_name:
304   free (name);
305
306   parser->valid = FALSE;
307
308   return NULL;
309 }
310
311 static XTag *
312 xtag_parse_tag (XTagParser * parser)
313 {
314   XTag * tag, * inner;
315   XAttribute * attr;
316   char * name;
317   char * pcdata;
318   char * s;
319
320   if (!parser->valid) return NULL;
321
322   if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) {
323     tag = malloc (sizeof (*tag));
324     tag->name = NULL;
325     tag->pcdata = pcdata;
326     tag->parent = parser->current_tag;
327     tag->attributes = NULL;
328     tag->children = NULL;
329     tag->current_child = NULL;
330
331     return tag;
332   }
333
334   s = parser->start;
335
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))
338     return NULL;
339
340   if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL;
341
342   name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE);
343
344   if (name == NULL) return NULL;
345
346 #ifdef XTAG_DEBUG
347   printf ("<%s ...\n", name);
348 #endif
349
350   tag = malloc (sizeof (*tag));
351   tag->name = name;
352   tag->pcdata = NULL;
353   tag->parent = parser->current_tag;
354   tag->attributes = NULL;
355   tag->children = NULL;
356   tag->current_child = NULL;
357
358   s = parser->start;
359
360   if (xtag_cin (s[0], X_WHITESPACE)) {
361     while ((attr = xtag_parse_attribute (parser)) != NULL) {
362       tag->attributes = xlist_append (tag->attributes, attr);
363     }
364   }
365
366   xtag_skip_whitespace (parser);
367
368   s = parser->start;
369
370   if (xtag_cin (s[0], X_CLOSETAG)) {
371     parser->current_tag = tag;
372
373     xtag_assert_and_pass (parser, X_CLOSETAG);
374
375     while ((inner = xtag_parse_tag (parser)) != NULL) {
376       tag->children = xlist_append (tag->children, inner);
377     }
378
379     xtag_skip_whitespace (parser);
380
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);
384     if (name) {
385       if (name && tag->name && strcmp (name, tag->name)) {
386 #ifdef XTAG_DEBUG
387         printf ("got %s expected %s\n", name, tag->name);
388 #endif
389         parser->valid = FALSE;
390       }
391       free (name);
392     }
393
394     xtag_skip_whitespace (parser);
395     xtag_assert_and_pass (parser, X_CLOSETAG);
396
397   } else {
398     xtag_assert_and_pass (parser, X_SLASH);
399     xtag_assert_and_pass (parser, X_CLOSETAG);
400   }
401
402
403   return tag;
404 }
405
406 XTag *
407 xtag_free (XTag * xtag)
408 {
409   XList * l;
410   XAttribute * attr;
411   XTag * child;
412
413   if (xtag == NULL) return NULL;
414
415   if (xtag->name) free (xtag->name);
416   if (xtag->pcdata) free (xtag->pcdata);
417
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);
422       free (attr);
423     }
424   }
425   xlist_free (xtag->attributes);
426
427   for (l = xtag->children; l; l = l->next) {
428     child = (XTag *)l->data;
429     xtag_free (child);
430   }
431   xlist_free (xtag->children);
432
433   free (xtag);
434
435   return NULL;
436 }
437
438 XTag *
439 xtag_new_parse (const char * s, int n)
440 {
441   XTagParser parser;
442   XTag * tag, * ttag, * wrapper;
443
444   parser.valid = TRUE;
445   parser.current_tag = NULL;
446   parser.start = (char *)s;
447
448   if (n == -1)
449     parser.end = NULL;
450   else if (n == 0)
451     return NULL;
452   else
453     parser.end = (char *)&s[n];
454
455   tag = xtag_parse_tag (&parser);
456
457   if (!parser.valid) {
458     xtag_free (tag);
459     return NULL;
460   }
461
462   if ((ttag = xtag_parse_tag (&parser)) != NULL) {
463
464     if (!parser.valid) {
465       xtag_free (ttag);
466       return tag;
467     }
468
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;
476
477     wrapper->children = xlist_append (wrapper->children, tag);
478     wrapper->children = xlist_append (wrapper->children, ttag);
479
480     while ((ttag = xtag_parse_tag (&parser)) != NULL) {
481
482       if (!parser.valid) {
483         xtag_free (ttag);
484         return wrapper;
485       }
486
487       wrapper->children = xlist_append (wrapper->children, ttag);
488     }
489     return wrapper;
490   }
491
492   return tag;
493 }
494
495 char *
496 xtag_get_name (XTag * xtag)
497 {
498   return xtag ? xtag->name : NULL;
499 }
500
501 char *
502 xtag_get_pcdata (XTag * xtag)
503 {
504   XList * l;
505   XTag * child;
506
507   if (xtag == NULL) return NULL;
508
509   for (l = xtag->children; l; l = l->next) {
510     child = (XTag *)l->data;
511     if (child->pcdata != NULL) {
512       return child->pcdata;
513     }
514   }
515
516   return NULL;
517 }
518
519 char *
520 xtag_get_attribute (XTag * xtag, char * attribute)
521 {
522   XList * l;
523   XAttribute * attr;
524
525   if (xtag == NULL) return NULL;
526
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))
530         return attr->value;
531     }
532   }
533
534   return NULL;
535 }
536
537 XTag *
538 xtag_first_child (XTag * xtag, char * name)
539 {
540   XList * l;
541   XTag * child;
542
543   if (xtag == NULL) return NULL;
544
545   if ((l = xtag->children) == NULL) return NULL;
546
547   if (name == NULL) {
548     xtag->current_child = l;
549     return (XTag *)l->data;
550   }
551
552   for (; l; l = l->next) {
553     child = (XTag *)l->data;
554
555     if (child->name && name && !strcmp(child->name, name)) {
556       xtag->current_child = l;
557       return child;
558     }
559   }
560
561   xtag->current_child = NULL;
562
563   return NULL;
564 }
565
566 XTag *
567 xtag_next_child (XTag * xtag, char * name)
568 {
569   XList * l;
570   XTag * child;
571
572   if (xtag == NULL) return NULL;
573
574   if ((l = xtag->current_child) == NULL)
575     return xtag_first_child (xtag, name);
576
577   if ((l = l->next) == NULL)
578     return NULL;
579
580   if (name == NULL) {
581     xtag->current_child = l;
582     return (XTag *)l->data;
583   }
584
585   for (; l; l = l->next) {
586     child = (XTag *)l->data;
587
588     if (child->name && name && !strcmp(child->name, name)) {
589       xtag->current_child = l;
590       return child;
591     }
592   }
593
594   xtag->current_child = NULL;
595
596   return NULL;
597 }
598
599 /*
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.
604  */
605 static int
606 xtag_snprints (char * buf, int n, ...)
607 {
608   va_list ap;
609   char * s;
610   int len, to_copy, total = 0;
611
612   va_start (ap, n);
613  
614   for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) {
615     len = strlen (s);
616
617     if ((to_copy = MIN (n, len)) > 0) {
618       memcpy (buf, s, to_copy);
619       buf += to_copy;
620       n -= to_copy;
621     }
622
623     total += len;
624   }
625
626   va_end (ap);
627
628   return total;
629 }
630
631 int
632 xtag_snprint (char * buf, int n, XTag * xtag)
633 {
634   int nn, written = 0;
635   XList * l;
636   XAttribute * attr;
637   XTag * child;
638
639 #define FORWARD(N) \
640   buf += MIN (n, N); \
641   n = MAX (n-N, 0);  \
642   written += N;
643
644   if (xtag == NULL) {
645     if (n > 0) buf[0] = '\0';
646     return 0;
647   }
648
649   if (xtag->pcdata) {
650     nn = xtag_snprints (buf, n, xtag->pcdata, NULL);
651     FORWARD(nn);
652
653     return written;
654   }
655
656   if (xtag->name) {
657     nn = xtag_snprints (buf, n, "<", xtag->name, NULL);
658     FORWARD(nn);
659
660     for (l = xtag->attributes; l; l = l->next) {
661       attr = (XAttribute *)l->data;
662  
663       nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"",
664                           NULL);
665       FORWARD(nn);
666     }
667  
668     if (xtag->children == NULL) {
669       nn = xtag_snprints (buf, n, "/>", NULL);
670       FORWARD(nn);
671
672       return written;
673     }
674  
675     nn = xtag_snprints (buf, n, ">", NULL);
676     FORWARD(nn);
677   }
678
679   for (l = xtag->children; l; l = l->next) {
680     child = (XTag *)l->data;
681
682     nn = xtag_snprint (buf, n, child);
683     FORWARD(nn);
684   }
685
686   if (xtag->name) {
687     nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL);
688     FORWARD(nn);
689   }
690
691   return written;
692 }
693