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