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