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