Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

xtag.c

00001 /*****************************************************************************
00002  * xlist.c : a trivial parser for XML-like tags
00003  *****************************************************************************
00004  * Copyright (C) 2003-2004 Commonwealth Scientific and Industrial Research
00005  *                         Organisation (CSIRO) Australia
00006  * Copyright (C) 2000-2004 the VideoLAN team
00007  *
00008  * $Id: xtag.c 11664 2005-07-09 06:17:09Z courmisch $
00009  *
00010  * Authors: Conrad Parker <[email protected]>
00011  *          Andre Pang <[email protected]>
00012  *
00013  * This program is free software; you can redistribute it and/or modify
00014  * it under the terms of the GNU General Public License as published by
00015  * the Free Software Foundation; either version 2 of the License, or
00016  * (at your option) any later version.
00017  *
00018  * This program is distributed in the hope that it will be useful,
00019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00021  * GNU General Public License for more details.
00022  *
00023  * You should have received a copy of the GNU General Public License
00024  * along with this program; if not, write to the Free Software
00025  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00026  *****************************************************************************/
00027 
00028 #include "config.h"
00029 
00030 #include <ctype.h>
00031 #include <string.h>
00032 #include <stdarg.h>
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 
00036 #include <xlist.h>
00037 
00038 #undef XTAG_DEBUG
00039 
00040 #undef FALSE
00041 #undef TRUE
00042 
00043 #define FALSE (0)
00044 #define TRUE (!FALSE)
00045 
00046 #undef MIN
00047 #define MIN(a,b) ((a)<(b)?(a):(b))
00048 
00049 #undef MAX
00050 #define MAX(a,b) ((a)>(b)?(a):(b))
00051 
00052 typedef struct _XTag XTag;
00053 typedef struct _XAttribute XAttribute;
00054 typedef struct _XTagParser XTagParser;
00055 
00056 /*
00057  * struct _XTag is kind of a union ... it normally represents a whole
00058  * tag (and its children), but it could alternatively represent some
00059  * PCDATA. Basically, if tag->pcdata is non-NULL, interpret only it and
00060  * ignore the name, attributes and inner_tags.
00061  */
00062 struct _XTag {
00063   char * name;
00064   char * pcdata;
00065   XTag * parent;
00066   XList * attributes;
00067   XList * children;
00068   XList * current_child;
00069 };
00070 
00071 struct _XAttribute {
00072   char * name;
00073   char * value;
00074 };
00075 
00076 struct _XTagParser {
00077   int valid; /* boolean */
00078   XTag * current_tag;
00079   char * start;
00080   char * end;
00081 };
00082 
00083 /* Character classes */
00084 #define X_NONE           0
00085 #define X_WHITESPACE  1<<0
00086 #define X_OPENTAG     1<<1
00087 #define X_CLOSETAG    1<<2
00088 #define X_DQUOTE      1<<3
00089 #define X_SQUOTE      1<<4
00090 #define X_EQUAL       1<<5
00091 #define X_SLASH       1<<6
00092 
00093 static int
00094 xtag_cin (char c, int char_class)
00095 {
00096   if (char_class & X_WHITESPACE)
00097     if (isspace(c)) return TRUE;
00098 
00099   if (char_class & X_OPENTAG)
00100     if (c == '<') return TRUE;
00101 
00102   if (char_class & X_CLOSETAG)
00103     if (c == '>') return TRUE;
00104 
00105   if (char_class & X_DQUOTE)
00106     if (c == '"') return TRUE;
00107 
00108   if (char_class & X_SQUOTE)
00109     if (c == '\'') return TRUE;
00110 
00111   if (char_class & X_EQUAL)
00112     if (c == '=') return TRUE;
00113 
00114   if (char_class & X_SLASH)
00115     if (c == '/') return TRUE;
00116 
00117   return FALSE;
00118 }
00119 
00120 static int
00121 xtag_index (XTagParser * parser, int char_class)
00122 {
00123   char * s;
00124   int i;
00125 
00126   s = parser->start;
00127 
00128   for (i = 0; s[i] && s != parser->end; i++) {
00129     if (xtag_cin(s[i], char_class)) return i;
00130   }
00131 
00132   return -1;
00133 }
00134 
00135 static void
00136 xtag_skip_over (XTagParser * parser, int char_class)
00137 {
00138   char * s;
00139   int i;
00140 
00141   if (!parser->valid) return;
00142 
00143   s = (char *)parser->start;
00144 
00145   for (i = 0; s[i] && s != parser->end; i++) {
00146     if (!xtag_cin(s[i], char_class)) {
00147       parser->start = &s[i];
00148       return;
00149     }
00150   }
00151 
00152   return;
00153 }
00154 
00155 static void
00156 xtag_skip_whitespace (XTagParser * parser)
00157 {
00158   xtag_skip_over (parser, X_WHITESPACE);
00159 }
00160 
00161 #if 0
00162 static void
00163 xtag_skip_to (XTagParser * parser, int char_class)
00164 {
00165   char * s;
00166   int i;
00167 
00168   if (!parser->valid) return;
00169 
00170   s = (char *)parser->start;
00171 
00172   for (i = 0; s[i] && s != parser->end; i++) {
00173     if (xtag_cin(s[i], char_class)) {
00174       parser->start = &s[i];
00175       return;
00176     }
00177   }
00178 
00179   return;  
00180 }
00181 #endif
00182 
00183 static char *
00184 xtag_slurp_to (XTagParser * parser, int good_end, int bad_end)
00185 {
00186   char * s, * ret;
00187   int xi;
00188 
00189   if (!parser->valid) return NULL;
00190 
00191   s = parser->start;
00192 
00193   xi = xtag_index (parser, good_end | bad_end);
00194 
00195   if (xi > 0 && xtag_cin (s[xi], good_end)) {
00196     ret = malloc ((xi+1) * sizeof(char));
00197     strncpy (ret, s, xi);
00198     ret[xi] = '\0';
00199     parser->start = &s[xi];
00200     return ret;
00201   }
00202 
00203   return NULL;
00204 }
00205 
00206 static int
00207 xtag_assert_and_pass (XTagParser * parser, int char_class)
00208 {
00209   char * s;
00210 
00211   if (!parser->valid) return FALSE;
00212 
00213   s = parser->start;
00214 
00215   if (!xtag_cin (s[0], char_class)) {
00216     parser->valid = FALSE;
00217     return FALSE;
00218   }
00219 
00220   parser->start = &s[1];
00221 
00222   return TRUE;
00223 }
00224 
00225 static char *
00226 xtag_slurp_quoted (XTagParser * parser)
00227 {
00228   char * s, * ret;
00229   int quote = X_DQUOTE; /* quote char to match on */
00230   int xi;
00231 
00232   if (!parser->valid) return NULL;
00233 
00234   xtag_skip_whitespace (parser);
00235 
00236   s = parser->start;
00237 
00238   if (xtag_cin (s[0], X_SQUOTE)) quote = X_SQUOTE;
00239 
00240   if (!xtag_assert_and_pass (parser, quote)) return NULL;
00241 
00242   s = parser->start;
00243 
00244   for (xi = 0; s[xi]; xi++) {
00245     if (xtag_cin (s[xi], quote)) {
00246       if (!(xi > 1 && s[xi-1] == '\\')) break;
00247     }
00248   }
00249 
00250   ret = malloc ((xi+1) * sizeof(char));
00251   strncpy (ret, s, xi);
00252   ret[xi] = '\0';
00253   parser->start = &s[xi];
00254 
00255   if (!xtag_assert_and_pass (parser, quote)) return NULL;
00256 
00257   return ret;
00258 }
00259 
00260 static XAttribute *
00261 xtag_parse_attribute (XTagParser * parser)
00262 {
00263   XAttribute * attr;
00264   char * name, * value;
00265   char * s;
00266 
00267   if (!parser->valid) return NULL;
00268 
00269   xtag_skip_whitespace (parser);
00270  
00271   name = xtag_slurp_to (parser, X_WHITESPACE | X_EQUAL, X_SLASH | X_CLOSETAG);
00272 
00273   if (name == NULL) return NULL;
00274 
00275   xtag_skip_whitespace (parser);
00276   s = parser->start;
00277 
00278   if (!xtag_assert_and_pass (parser, X_EQUAL)) {
00279 #ifdef XTAG_DEBUG
00280     printf ("xtag: attr failed EQUAL on <%s>\n", name);
00281 #endif
00282     goto err_free_name;
00283   }
00284 
00285   xtag_skip_whitespace (parser);
00286 
00287   value = xtag_slurp_quoted (parser);
00288 
00289   if (value == NULL) {
00290 #ifdef XTAG_DEBUG
00291     printf ("Got NULL quoted attribute value\n");
00292 #endif
00293     goto err_free_name;
00294   }
00295 
00296   attr = malloc (sizeof (*attr));
00297   attr->name = name;
00298   attr->value = value;
00299 
00300   return attr;
00301 
00302  err_free_name:
00303   free (name);
00304 
00305   parser->valid = FALSE;
00306 
00307   return NULL;
00308 }
00309 
00310 static XTag *
00311 xtag_parse_tag (XTagParser * parser)
00312 {
00313   XTag * tag, * inner;
00314   XAttribute * attr;
00315   char * name;
00316   char * pcdata;
00317   char * s;
00318 
00319   if (!parser->valid) return NULL;
00320 
00321   if ((pcdata = xtag_slurp_to (parser, X_OPENTAG, X_NONE)) != NULL) {
00322     tag = malloc (sizeof (*tag));
00323     tag->name = NULL;
00324     tag->pcdata = pcdata;
00325     tag->parent = parser->current_tag;
00326     tag->attributes = NULL;
00327     tag->children = NULL;
00328     tag->current_child = NULL;
00329 
00330     return tag;
00331   }
00332 
00333   s = parser->start;
00334 
00335   /* if this starts a close tag, return NULL and let the parent take it */
00336   if (xtag_cin (s[0], X_OPENTAG) && xtag_cin (s[1], X_SLASH))
00337     return NULL;
00338 
00339   if (!xtag_assert_and_pass (parser, X_OPENTAG)) return NULL;
00340 
00341   name = xtag_slurp_to (parser, X_WHITESPACE | X_SLASH | X_CLOSETAG, X_NONE);
00342 
00343   if (name == NULL) return NULL;
00344 
00345 #ifdef XTAG_DEBUG
00346   printf ("<%s ...\n", name);
00347 #endif
00348 
00349   tag = malloc (sizeof (*tag));
00350   tag->name = name;
00351   tag->pcdata = NULL;
00352   tag->parent = parser->current_tag;
00353   tag->attributes = NULL;
00354   tag->children = NULL;
00355   tag->current_child = NULL;
00356 
00357   s = parser->start;
00358 
00359   if (xtag_cin (s[0], X_WHITESPACE)) {
00360     while ((attr = xtag_parse_attribute (parser)) != NULL) {
00361       tag->attributes = xlist_append (tag->attributes, attr);
00362     }
00363   }
00364 
00365   xtag_skip_whitespace (parser);
00366 
00367   s = parser->start;
00368 
00369   if (xtag_cin (s[0], X_CLOSETAG)) {
00370     parser->current_tag = tag;
00371 
00372     xtag_assert_and_pass (parser, X_CLOSETAG);
00373 
00374     while ((inner = xtag_parse_tag (parser)) != NULL) {
00375       tag->children = xlist_append (tag->children, inner);
00376     }
00377 
00378     xtag_skip_whitespace (parser);
00379 
00380     xtag_assert_and_pass (parser, X_OPENTAG);
00381     xtag_assert_and_pass (parser, X_SLASH);
00382     name = xtag_slurp_to (parser, X_WHITESPACE | X_CLOSETAG, X_NONE);
00383     if (name) {
00384       if (name && tag->name && strcmp (name, tag->name)) {
00385 #ifdef XTAG_DEBUG
00386         printf ("got %s expected %s\n", name, tag->name);
00387 #endif
00388         parser->valid = FALSE;
00389       }
00390       free (name);
00391     }
00392 
00393     xtag_skip_whitespace (parser);
00394     xtag_assert_and_pass (parser, X_CLOSETAG);
00395 
00396   } else {
00397     xtag_assert_and_pass (parser, X_SLASH);
00398     xtag_assert_and_pass (parser, X_CLOSETAG);
00399   }
00400 
00401 
00402   return tag;
00403 }
00404 
00405 XTag *
00406 xtag_free (XTag * xtag)
00407 {
00408   XList * l;
00409   XAttribute * attr;
00410   XTag * child;
00411 
00412   if (xtag == NULL) return NULL;
00413 
00414   if (xtag->name) free (xtag->name);
00415   if (xtag->pcdata) free (xtag->pcdata);
00416 
00417   for (l = xtag->attributes; l; l = l->next) {
00418     if ((attr = (XAttribute *)l->data) != NULL) {
00419       if (attr->name) free (attr->name);
00420       if (attr->value) free (attr->value);
00421       free (attr);
00422     }
00423   }
00424   xlist_free (xtag->attributes);
00425 
00426   for (l = xtag->children; l; l = l->next) {
00427     child = (XTag *)l->data;
00428     xtag_free (child);
00429   }
00430   xlist_free (xtag->children);
00431 
00432   free (xtag);
00433 
00434   return NULL;
00435 }
00436 
00437 XTag *
00438 xtag_new_parse (const char * s, int n)
00439 {
00440   XTagParser parser;
00441   XTag * tag, * ttag, * wrapper;
00442 
00443   parser.valid = TRUE;
00444   parser.current_tag = NULL;
00445   parser.start = (char *)s;
00446 
00447   if (n == -1)
00448     parser.end = NULL;
00449   else if (n == 0)
00450     return NULL;
00451   else
00452     parser.end = (char *)&s[n];
00453 
00454   tag = xtag_parse_tag (&parser);
00455 
00456   if (!parser.valid) {
00457     xtag_free (tag);
00458     return NULL;
00459   }
00460 
00461   if ((ttag = xtag_parse_tag (&parser)) != NULL) {
00462 
00463     if (!parser.valid) {
00464       xtag_free (ttag);
00465       return tag;
00466     }
00467 
00468     wrapper = malloc (sizeof (XTag));
00469     wrapper->name = NULL;
00470     wrapper->pcdata = NULL;
00471     wrapper->parent = NULL;
00472     wrapper->attributes = NULL;
00473     wrapper->children = NULL;
00474     wrapper->current_child = NULL;
00475 
00476     wrapper->children = xlist_append (wrapper->children, tag);
00477     wrapper->children = xlist_append (wrapper->children, ttag);
00478 
00479     while ((ttag = xtag_parse_tag (&parser)) != NULL) {
00480 
00481       if (!parser.valid) {
00482         xtag_free (ttag);
00483         return wrapper;
00484       }
00485 
00486       wrapper->children = xlist_append (wrapper->children, ttag);
00487     }
00488     return wrapper;
00489   }
00490 
00491   return tag;
00492 }
00493 
00494 char *
00495 xtag_get_name (XTag * xtag)
00496 {
00497   return xtag ? xtag->name : NULL;
00498 }
00499 
00500 char *
00501 xtag_get_pcdata (XTag * xtag)
00502 {
00503   XList * l;
00504   XTag * child;
00505 
00506   if (xtag == NULL) return NULL;
00507 
00508   for (l = xtag->children; l; l = l->next) {
00509     child = (XTag *)l->data;
00510     if (child->pcdata != NULL) {
00511       return child->pcdata;
00512     }
00513   }
00514 
00515   return NULL;
00516 }
00517 
00518 char *
00519 xtag_get_attribute (XTag * xtag, char * attribute)
00520 {
00521   XList * l;
00522   XAttribute * attr;
00523 
00524   if (xtag == NULL) return NULL;
00525 
00526   for (l = xtag->attributes; l; l = l->next) {
00527     if ((attr = (XAttribute *)l->data) != NULL) {
00528       if (attr->name && attribute && !strcmp (attr->name, attribute))
00529         return attr->value;
00530     }
00531   }
00532 
00533   return NULL;
00534 }
00535 
00536 XTag *
00537 xtag_first_child (XTag * xtag, char * name)
00538 {
00539   XList * l;
00540   XTag * child;
00541 
00542   if (xtag == NULL) return NULL;
00543 
00544   if ((l = xtag->children) == NULL) return NULL;
00545 
00546   if (name == NULL) {
00547     xtag->current_child = l;
00548     return (XTag *)l->data;
00549   }
00550 
00551   for (; l; l = l->next) {
00552     child = (XTag *)l->data;
00553 
00554     if (child->name && name && !strcmp(child->name, name)) {
00555       xtag->current_child = l;
00556       return child;
00557     }
00558   }
00559 
00560   xtag->current_child = NULL;
00561 
00562   return NULL;
00563 }
00564 
00565 XTag *
00566 xtag_next_child (XTag * xtag, char * name)
00567 {
00568   XList * l;
00569   XTag * child;
00570 
00571   if (xtag == NULL) return NULL;
00572 
00573   if ((l = xtag->current_child) == NULL)
00574     return xtag_first_child (xtag, name);
00575 
00576   if ((l = l->next) == NULL)
00577     return NULL;
00578 
00579   if (name == NULL) {
00580     xtag->current_child = l;
00581     return (XTag *)l->data;
00582   }
00583 
00584   for (; l; l = l->next) {
00585     child = (XTag *)l->data;
00586 
00587     if (child->name && name && !strcmp(child->name, name)) {
00588       xtag->current_child = l;
00589       return child;
00590     }
00591   }
00592 
00593   xtag->current_child = NULL;
00594 
00595   return NULL;
00596 }
00597 
00598 /*
00599  * This snprints function takes a variable list of char *, the last of
00600  * which must be NULL, and prints each in turn to buf.
00601  * Returns C99-style total length that would have been written, even if
00602  * this is larger than n.
00603  */
00604 static int
00605 xtag_snprints (char * buf, int n, ...)
00606 {
00607   va_list ap;
00608   char * s;
00609   int len, to_copy, total = 0;
00610 
00611   va_start (ap, n);
00612   
00613   for (s = va_arg (ap, char *); s; s = va_arg (ap, char *)) {
00614     len = strlen (s);
00615 
00616     if ((to_copy = MIN (n, len)) > 0) {
00617       memcpy (buf, s, to_copy);
00618       buf += to_copy;
00619       n -= to_copy;
00620     }
00621 
00622     total += len;
00623   }
00624 
00625   va_end (ap);
00626 
00627   return total;
00628 }
00629 
00630 int
00631 xtag_snprint (char * buf, int n, XTag * xtag)
00632 {
00633   int nn, written = 0;
00634   XList * l;
00635   XAttribute * attr;
00636   XTag * child;
00637 
00638 #define FORWARD(N) \
00639   buf += MIN (n, N); \
00640   n = MAX (n-N, 0);  \
00641   written += N;
00642 
00643   if (xtag == NULL) {
00644     if (n > 0) buf[0] = '\0';
00645     return 0;
00646   }
00647 
00648   if (xtag->pcdata) {
00649     nn = xtag_snprints (buf, n, xtag->pcdata, NULL);
00650     FORWARD(nn);
00651 
00652     return written;
00653   }
00654 
00655   if (xtag->name) {
00656     nn = xtag_snprints (buf, n, "<", xtag->name, NULL);
00657     FORWARD(nn);
00658 
00659     for (l = xtag->attributes; l; l = l->next) {
00660       attr = (XAttribute *)l->data;
00661       
00662       nn = xtag_snprints (buf, n, " ", attr->name, "=\"", attr->value, "\"",
00663                           NULL);
00664       FORWARD(nn);
00665     }
00666     
00667     if (xtag->children == NULL) {
00668       nn = xtag_snprints (buf, n, "/>", NULL);
00669       FORWARD(nn);
00670 
00671       return written;
00672     }
00673     
00674     nn = xtag_snprints (buf, n, ">", NULL);
00675     FORWARD(nn);
00676   }
00677 
00678   for (l = xtag->children; l; l = l->next) {
00679     child = (XTag *)l->data;
00680 
00681     nn = xtag_snprint (buf, n, child);
00682     FORWARD(nn);
00683   }
00684 
00685   if (xtag->name) {
00686     nn = xtag_snprints (buf, n, "</", xtag->name, ">", NULL);
00687     FORWARD(nn);
00688   }
00689 
00690   return written;
00691 }
00692 

Generated on Tue Dec 20 10:14:28 2005 for vlc-0.8.4a by  doxygen 1.4.2