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