Enabled g_mem_set_vtable through the configure option --with-overridable-allocators...
[mono.git] / eglib / src / gmarkup.c
index 4c8e81d18b9163e99ba9447b6f0c238b26d712e1..4e6c6641fef542ac4fb758a51d27281baf556340 100644 (file)
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #include <stdio.h>
+#include <ctype.h>
 #include <glib.h>
 
-#define set_error(msg...) do { if (error != NULL) *error = g_error_new (GINT_TO_POINTER (1), 1, msg); } while (0);
+#define set_error(msg, ...) do { if (error != NULL) *error = g_error_new (GINT_TO_POINTER (1), 1, msg, __VA_ARGS__); } while (0);
 
 typedef enum {
        START,
@@ -50,7 +51,8 @@ typedef enum {
        TEXT,
        FLUSH_TEXT,
        CLOSING_ELEMENT,
-       COMMENT
+       COMMENT,
+       SKIP_XML_DECLARATION
 } ParseState;
 
 struct _GMarkupParseContext {
@@ -98,10 +100,37 @@ g_markup_parse_context_free (GMarkupParseContext *context)
        g_free (context);
 }
 
+static gboolean
+my_isspace (char c)
+{
+       if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v')
+               return TRUE;
+       return FALSE;
+}
+
+static gboolean
+my_isalnum (char c)
+{
+       if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+               return TRUE;
+       if (c >= '0' && c <= '9')
+               return TRUE;
+
+       return FALSE;
+}
+
+static gboolean
+my_isalpha (char c)
+{
+       if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+               return TRUE;
+       return FALSE;
+}
+
 static const char *
 skip_space (const char *p, const char *end)
 {
-       for (; p < end && isspace (*p); p++)
+       for (; p < end && my_isspace (*p); p++)
                ;
        return p;
 }
@@ -113,16 +142,17 @@ parse_value (const char *p, const char *end, char **value, GError **error)
        int l;
        
        if (*p != '"'){
-               set_error ("Expected the attribute value to start with a quote");
+               set_error ("%s", "Expected the attribute value to start with a quote");
                return end;
        }
        start = ++p;
-       for (++p; p < end && *p != '"'; p++)
+       for (; p < end && *p != '"'; p++)
+               ;
        if (p == end)
                return end;
-       l = p - start;
+       l = (int)(p - start);
        p++;
-       *value = malloc (l + 1);
+       *value = g_malloc (l + 1);
        if (*value == NULL)
                return end;
        strncpy (*value, start, l);
@@ -136,13 +166,13 @@ parse_name (const char *p, const char *end, char **value)
        const char *start = p;
        int l;
        
-       for (; p < end && isalnum (*p); p++)
+       for (; p < end && my_isalnum (*p); p++)
                ;
        if (p == end)
                return end;
 
-       l = p - start;
-       *value = malloc (l + 1);
+       l = (int)(p - start);
+       *value = g_malloc (l + 1);
        if (*value == NULL)
                return end;
        strncpy (*value, start, l);
@@ -151,7 +181,7 @@ parse_name (const char *p, const char *end, char **value)
 }
 
 static const char *
-parse_attributes (const char *p, const char *end, char ***names, char ***values, GError **error, int *full_stop)
+parse_attributes (const char *p, const char *end, char ***names, char ***values, GError **error, int *full_stop, int state)
 {
        int nnames = 0;
 
@@ -164,6 +194,11 @@ parse_attributes (const char *p, const char *end, char ***names, char ***values,
                        *full_stop = 0;
                        return p; 
                }
+               if (state == SKIP_XML_DECLARATION && *p == '?' && ((p+1) < end) && *(p+1) == '>'){
+                       *full_stop = 0;
+                       return p+1;
+               }
+               
                if (*p == '/' && ((p+1) < end && *(p+1) == '>')){
                        *full_stop = 1;
                        return p+1;
@@ -176,24 +211,24 @@ parse_attributes (const char *p, const char *end, char ***names, char ***values,
 
                        p = skip_space (p, end);
                        if (p == end){
-                               free (name);
+                               g_free (name);
                                return p;
                        }
                        if (*p != '='){
                                set_error ("Expected an = after the attribute name `%s'", name);
-                               free (name);
+                               g_free (name);
                                return end;
                        }
                        p++;
                        p = skip_space (p, end);
                        if (p == end){
-                               free (name);
+                               g_free (name);
                                return end;
                        }
 
                        p = parse_value (p, end, &value, error);
                        if (p == end){
-                               free (name);
+                               g_free (name);
                                return p;
                        }
 
@@ -238,29 +273,33 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
        
        for (p = text; p < end; p++){
                char c = *p;
-               
+
                switch (context->state){
                case START:
-                       if (c == ' ' || c == '\t' || c == '\f' || c == '\n')
+                       if (c == ' ' || c == '\t' || c == '\f' || c == '\n' || (c & 0x80))
                                continue;
                        if (c == '<'){
-                               context->state = START_ELEMENT;
+                               if (p+1 < end && p [1] == '?'){
+                                       context->state = SKIP_XML_DECLARATION;
+                                       p++;
+                               } else
+                                       context->state = START_ELEMENT;
                                continue;
                        }
-                       set_error ("Expected < to start the document");
+                       set_error ("%s", "Expected < to start the document");
                        goto fail;
 
-
+               case SKIP_XML_DECLARATION:
                case START_ELEMENT: {
                        const char *element_start = p, *element_end;
                        char *ename = NULL;
                        int full_stop = 0, l;
                        gchar **names = NULL, **values = NULL;
 
-                       for (; p < end && isspace (*p); p++)
+                       for (; p < end && my_isspace (*p); p++)
                                ;
                        if (p == end){
-                               set_error ("Unfinished element");
+                               set_error ("%s", "Unfinished element");
                                goto fail;
                        }
 
@@ -270,26 +309,26 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
                                break;
                        }
                        
-                       if (!(isascii (*p) && isalpha (*p))){
-                               set_error ("Expected an element name");
+                       if (!my_isalpha (*p)){
+                               set_error ("%s", "Expected an element name");
                                goto fail;
                        }
                        
-                       for (++p; p < end && isalnum (*p); p++)
+                       for (++p; p < end && (my_isalnum (*p) || (*p == '.')); p++)
                                ;
                        if (p == end){
-                               set_error ("Expected an element");
+                               set_error ("%s", "Expected an element");
                                goto fail;
                        }
                        element_end = p;
                        
-                       for (; p < end && isspace (*p); p++)
+                       for (; p < end && my_isspace (*p); p++)
                                ;
                        if (p == end){
-                               set_error ("Unfinished element");
+                               set_error ("%s", "Unfinished element");
                                goto fail;
                        }
-                       p = parse_attributes (p, end, &names, &values, error, &full_stop);
+                       p = parse_attributes (p, end, &names, &values, error, &full_stop, context->state);
                        if (p == end){
                                if (names != NULL) {
                                        g_strfreev (names);
@@ -297,21 +336,22 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
                                }
                                /* Only set the error if parse_attributes did not */
                                if (error != NULL && *error == NULL)
-                                       set_error ("Unfinished sequence");
+                                       set_error ("%s", "Unfinished sequence");
                                goto fail;
                        }
-                       l = element_end - element_start;
-                       ename = malloc (l + 1);
+                       l = (int)(element_end - element_start);
+                       ename = g_malloc (l + 1);
                        if (ename == NULL)
                                goto fail;
                        strncpy (ename, element_start, l);
                        ename [l] = 0;
-                       
-                       if (context->parser.start_element != NULL)
-                               context->parser.start_element (context, ename,
-                                                              (const gchar **) names,
-                                                              (const gchar **) values,
-                                                              context->user_data, error);
+
+                       if (context->state == START_ELEMENT)
+                               if (context->parser.start_element != NULL)
+                                       context->parser.start_element (context, ename,
+                                                                      (const gchar **) names,
+                                                                      (const gchar **) values,
+                                                                      context->user_data, error);
 
                        if (names != NULL){
                                g_strfreev (names);
@@ -319,21 +359,22 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
                        }
 
                        if (error != NULL && *error != NULL){
-                               free (ename);
+                               g_free (ename);
                                goto fail;
                        }
                        
                        if (full_stop){
-                               if (context->parser.end_element != NULL){
+                               if (context->parser.end_element != NULL &&  context->state == START_ELEMENT){
                                        context->parser.end_element (context, ename, context->user_data, error);
                                        if (error != NULL && *error != NULL){
-                                               free (ename);
+                                               g_free (ename);
                                                goto fail;
                                        }
                                }
-                               free (ename);
-                       } else
+                               g_free (ename);
+                       } else {
                                context->level = g_slist_prepend (context->level, ename);
+                       }
                        
                        context->state = TEXT;
                        break;
@@ -360,10 +401,10 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
                                p += 2;
                                break;
                        }
-
+                       break;
                        
                case FLUSH_TEXT:
-                       if (context->parser.text != NULL){
+                       if (context->parser.text != NULL && context->text != NULL){
                                context->parser.text (context, context->text->str, context->text->len,
                                                      context->user_data, error);
                                if (error != NULL && *error != NULL)
@@ -383,22 +424,26 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
                        char *text;
 
                        if (context->level == NULL){
-                               set_error ("Too many closing tags, not enough open tags");
+                               set_error ("%s", "Too many closing tags, not enough open tags");
                                goto fail;
                        }
-                       text = current->data;
                        
+                       text = current->data;
                        if (context->parser.end_element != NULL){
                                context->parser.end_element (context, text, context->user_data, error);
                                if (error != NULL && *error != NULL){
-                                       free (text);
+                                       g_free (text);
                                        goto fail;
                                }
                        }
-                       free (text);
+                       g_free (text);
+
+                       while (p < end && *p != '>')
+                               p++;
                        
                        context->level = context->level->next;
                        g_slist_free_1 (current);
+                       context->state = TEXT;
                        break;
                } /* case CLOSING_ELEMENT */
                        
@@ -408,7 +453,7 @@ g_markup_parse_context_parse (GMarkupParseContext *context,
 
        return TRUE;
  fail:
-       if (context->parser.error)
+       if (context->parser.error && error != NULL && *error)
                context->parser.error (context, *error, context->user_data);
        
        destroy_parse_state (context);