
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <guththila.h>

#define GUTHTHILA_VALIDATION_PARSER

/*
 * Read the next char from the reader and return it.
 */
static int
guththila_next_char(
    guththila_t * m,
    const axutil_env_t * env);

/* part of guththila_next_char method. this was included as macro for performance. 99% of the time
 * following will be called, so having it as next_char method is very expensive (method calling
 * overhead is higher) so, common case is checked as part of the macro and if not satisfied, method
 * is called
 */
#define GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c)\
{\
    int result_found = 0;\
    if(!buffer)\
    {\
        if(m->reader->type == GUTHTHILA_MEMORY_READER)\
        {\
            buffer = m->buffer.buff[0];\
            data_size = m->buffer.data_size[0];\
            previous_size = 0;\
        }\
        else\
        {\
            if(m->buffer.cur_buff != -1)\
            {\
                buffer = m->buffer.buff[m->buffer.cur_buff];\
                data_size = GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer);\
                previous_size = GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer);\
            }\
            else\
            {\
                c = guththila_next_char(m, env);\
                if(c < 0)\
                    return -1;\
                result_found  = 1;\
            }\
        }\
    }\
\
    if(!result_found)\
    {\
        size_t index = m->next++ - previous_size;\
        if(index < data_size)\
        {\
            c = buffer[index];\
        }\
        else\
        {\
            buffer = NULL;\
            data_size = -1;\
            --(m->next);\
            if(m->reader->type == GUTHTHILA_MEMORY_READER)\
            {\
                return -1;\
            }\
            else\
            {\
                c = guththila_next_char(m, env);\
                if(c < 0)\
                    return -1;\
            }\
        }\
    }\
}

/*
 * Read the specified number of characters at once.
 */
static int
guththila_next_no_char(
    guththila_t * m,
    int eof,
    guththila_char_t *bytes,
    size_t no,
    const axutil_env_t * env);

/*
 * Close a token that is opened previously. 
 */
static void
guththila_token_close(
    guththila_t * m,
    guththila_token_t * tok,
    int tok_type,
    int referer,
    const axutil_env_t * env);

/* 
 * Process the XMl declaration part of a XML document.
 */
static int
guththila_process_xml_dec(
    guththila_t * m,
    const axutil_env_t * env);

/*
 * Return non zero value if the given argument is a space. (c < 0x21) is added to improve the
 * performance. common case is printable characters. and if given character is printable, we can
 * return false immediately.
 */
#define GUTHTHILA_IS_SPACE(c) ((c < 0x21) && (c == 0x20 || c == 0xD || c == 0xA || c == 0x9))

/* 
 * Read characters until all the white spaces are read.
 */
#define GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, _env)while(GUTHTHILA_IS_SPACE(c)){GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, _env, c);}


#define GUTHTHILA_XML_NAME "xml"

#define GUTHTHILA_XML_URI "http://www.w3.org/XML/1998/namespace"

/*
 * Open a token. When we open a token we don't know it's type. We only 
 * set the starting values.
 */
#define GUTHTHILA_TOKEN_OPEN(m, tok, _env)					\
    m->temp_tok = guththila_tok_list_get_token(&m->tokens, _env); \
    m->temp_tok->type = _Unknown; \
    m->temp_tok->_start = (int)m->next; \
    m->last_start = (int)m->next - 1;
/* We are sure that the difference lies within the int range */

/*
 * Read until we met a = character.
 */
#define GUTHTHILA_PROCESS_EQU(m, c, ic, buffer, data_size, previous_size, _env)\
{\
    GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, _env); \
    if (c == '=')\
    { \
        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, _env, ic); \
        GUTHTHILA_SKIP_SPACES(m, ic, buffer, data_size, previous_size, _env); \
    }\
}

/*
 * Initialize a attribute to the values given.
 */
#define GUTHTHILA_ATTRIBUTE_INITIALIZE(_attr, _pref, _name, _val)	\
    (_attr->pref = (_pref)); \
    (_attr->name = (_name)); \
    (_attr->val = (_val));

/*
 * Initialize namespace to the values given.
 */
#define GUTHTHILA_NAMESPACE_INITIALIZE(_namesp, _name, _uri)	\
    (_namesp->name = _name); \
    (_namesp->uri = _uri);

/*
 * Determine weahter a given character is a valid starting char for a xml name.
 */
#define GUTHTHILA_IS_VALID_STARTING_CHAR(c) (isalpha(c) || '_' == c || ':' == c)

/*
 * Initialize the variables in the guththila_t structure.
 */
#define GUTHTHILA_VARIABLE_INITIALZE(m) \
    m->temp_prefix = NULL; \
    m->temp_name = NULL; \
    m->temp_tok = NULL; \
    if (m->value) guththila_tok_list_release_token(&m->tokens, m->value, env); \
    m->name = NULL; \
    m->prefix = NULL; \
    m->value = NULL;

/*
 * Initialize the guththila_t structure with the reader. 
 * All the values will be set to default values. 
 */
GUTHTHILA_EXPORT int GUTHTHILA_CALL
guththila_init(
    guththila_t * m,
    void *reader,
    const axutil_env_t * env)
{
    guththila_token_t* temp_name = NULL;
    guththila_token_t* temp_tok = NULL;
    guththila_elem_namesp_t* e_namesp = NULL;

    if(!((guththila_reader_t *)reader))
        return GUTHTHILA_FAILURE;
    m->reader = (guththila_reader_t *)reader;
    if(!guththila_tok_list_init(&m->tokens, env))
    {
        return GUTHTHILA_FAILURE;
    }
    if(m->reader->type == GUTHTHILA_MEMORY_READER)
    {
        guththila_buffer_init_for_buffer(&m->buffer, m->reader->buff, m->reader->buff_size, env);
    }
    else if(m->reader->type == GUTHTHILA_FILE_READER || m->reader->type == GUTHTHILA_IO_READER)
    {
        guththila_buffer_init(&m->buffer, 0, env);
    }
    guththila_stack_init(&m->elem, env);
    guththila_stack_init(&m->attrib, env);
    guththila_stack_init(&m->namesp, env);
    temp_name = guththila_tok_list_get_token(&m->tokens, env);
    temp_tok = guththila_tok_list_get_token(&m->tokens, env);
    if(temp_tok && temp_name)
    {
        guththila_set_token(temp_name, GUTHTHILA_XML_NAME, 0, (int)strlen(GUTHTHILA_XML_NAME), 1,
            0, 0, env);

        guththila_set_token(temp_tok, GUTHTHILA_XML_URI, 0, (int)strlen(GUTHTHILA_XML_URI), 1, 0,
            0, env);

    }

    e_namesp = (guththila_elem_namesp_t *)AXIS2_MALLOC(env->allocator,
        sizeof(guththila_elem_namesp_t));
    if(e_namesp && temp_tok && temp_name)
    {
        e_namesp->namesp = (guththila_namespace_t *)AXIS2_MALLOC(env->allocator,
            sizeof(guththila_namespace_t) * GUTHTHILA_NAMESPACE_DEF_SIZE);
    }
    if(e_namesp->namesp)
    {
        e_namesp->no = 1;
        e_namesp->size = GUTHTHILA_NAMESPACE_DEF_SIZE;
        e_namesp->namesp[0].name = temp_name;
        e_namesp->namesp[0].uri = temp_tok;
        guththila_stack_push(&m->namesp, e_namesp, env);
    }
    else
    {
        if(temp_name)
        {
            AXIS2_FREE(env->allocator, temp_name);
            temp_name = NULL;
        }
        if(temp_tok)
        {
            AXIS2_FREE(env->allocator, temp_tok);
            temp_tok = NULL;
        }
        if(e_namesp)
        {
            AXIS2_FREE(env->allocator, e_namesp);
            e_namesp = NULL;
        }
        return GUTHTHILA_FAILURE;
    }
    m->name = NULL;
    m->prefix = NULL;
    m->value = NULL;
    m->status = S_1;
    m->guththila_event = -1;
    m->next = 0;
    m->last_start = -1;
    m->temp_name = NULL;
    m->temp_prefix = NULL;
    m->temp_tok = NULL;
    return GUTHTHILA_SUCCESS;
}

/*
 * Uninitialize a guththila_t structure. This method deallocates all the 
 * resources that are held in the guththila_t structure.
 */
GUTHTHILA_EXPORT int GUTHTHILA_CALL
guththila_un_init(
    guththila_t * m,
    const axutil_env_t * env)
{
    int size = 0, i = 0, j = 0;
    guththila_attr_t * attr = NULL;
    guththila_element_t* elem = NULL;
    guththila_elem_namesp_t * e_namesp = NULL;

    if(m->prefix)
    {
        guththila_tok_list_release_token(&m->tokens, m->prefix, env);
    }
    if(m->name)
    {
        guththila_tok_list_release_token(&m->tokens, m->name, env);
    }
    if(m->value)
    {
        guththila_tok_list_release_token(&m->tokens, m->value, env);
    }
    if(m->temp_tok)
    {
        guththila_tok_list_release_token(&m->tokens, m->temp_tok, env);
    }
    if(m->temp_name)
    {
        guththila_tok_list_release_token(&m->tokens, m->temp_name, env);
    }
    if(m->temp_prefix)
    {
        guththila_tok_list_release_token(&m->tokens, m->temp_prefix, env);
    }
    size = GUTHTHILA_STACK_SIZE(m->attrib);
    for(i = 0; i < size; i++)
    {
        attr = (guththila_attr_t *)guththila_stack_pop(&m->attrib, env);
        if(attr)
        {
            if(attr->name)
                guththila_tok_list_release_token(&m->tokens, attr->name, env);
            if(attr->pref)
                guththila_tok_list_release_token(&m->tokens, attr->pref, env);
            AXIS2_FREE(env->allocator, attr);
        }
    }
    guththila_stack_un_init(&m->attrib, env);

#ifndef GUTHTHILA_VALIDATION_PARSER
    guththila_namespace_t * namesp = NULL;

    size = GUTHTHILA_STACK_SIZE(m->namesp);
    for (i = 0; i < size; i++)
    {
        namesp =
        (guththila_namespace_t *) guththila_stack_pop(&m->namesp, env);
        if (namesp)
        {
            if (namesp->name)
            guththila_tok_list_release_token(&m->tokens, namesp->name, env);
            if (namesp->uri)
            guththila_tok_list_release_token(&m->tokens, namesp->uri, env);
            AXIS2_FREE(env->allocator, namesp);
        }
    }
#else 

    size = GUTHTHILA_STACK_SIZE(m->namesp);
    for(i = 0; i < size; i++)
    {
        e_namesp = (guththila_elem_namesp_t *)guththila_stack_pop(&m->namesp, env);
        if(e_namesp)
        {
            for(j = 0; j < e_namesp->no; j++)
            {
                if(e_namesp->namesp[j].name)
                {
                    guththila_tok_list_release_token(&m->tokens, e_namesp->namesp[j].name, env);
                }
                if(e_namesp->namesp[j].uri)
                {
                    guththila_tok_list_release_token(&m->tokens, e_namesp->namesp[j].uri, env);
                }
            }
            AXIS2_FREE(env->allocator, e_namesp->namesp);
            AXIS2_FREE(env->allocator, e_namesp);
        }
    }
#endif  

    size = GUTHTHILA_STACK_SIZE(m->elem);
    for(i = 0; i < size; i++)
    {
        elem = (guththila_element_t *)guththila_stack_pop(&m->elem, env);
        if(elem)
        {
            if(elem->name)
                guththila_tok_list_release_token(&m->tokens, elem->name, env);
            if(elem->prefix)
                guththila_tok_list_release_token(&m->tokens, elem->prefix, env);
            AXIS2_FREE(env->allocator, elem);
        }
    }
    guththila_stack_un_init(&m->elem, env);
    guththila_stack_un_init(&m->namesp, env);
    guththila_tok_list_free_data(&m->tokens, env);
    guththila_buffer_un_init(&m->buffer, env);
    AXIS2_FREE(env->allocator, m);
    return GUTHTHILA_SUCCESS;
}

/*static int
guththila_utf8_bytes_len(
    unsigned int c)
{
    if (c < 0x80)
        return 1;
    else if (c < 0x800)
        return 2;
    else if (c < 0x10000)
        return 3;
    else if (c < 0x200000)
        return 4;
    else if (c < 0x4000000)
        return 5;
    else
        return 6;
}*/

/*
 * Stores the given unicode char as UTF-8
 */
static guththila_char_t*
guththila_utf8_bytes(
    unsigned int c,
    guththila_char_t *start)
{
    int first;
    guththila_char_t *end, *p;

    if(c <           0x80)
    {
        *start = c;
        return start + 1;
    }
    if(c <          0x800)
    {
        first = 0xc0;
        end = start + 2;
    }
    else if(c <   0x10000)
    {
        first = 0xe0;
        end = start + 3;
    }
    else if(c <  0x200000)
    {
        first = 0xf0;
        end = start + 4;
    }
    else if(c < 0x4000000)
    {
        first = 0xf8;
        end = start + 5;
    }
    else
    {
        first = 0xfc;
        end = start + 6;
    }

    p = end;
    while(--p > start)
    {
        *p = (c & 0x3f) | 0x80;
        c >>= 6;
    }
    *start = c | first;
    return end;
}


/*
 * Replace the references with the corresponding actual values.
 */
static void
guththila_string_evaluate_references(
    guththila_char_t *start, size_t size)
{
    guththila_char_t *end = start + size;
    guththila_char_t *p = start;
    guththila_char_t *q = NULL;
    guththila_char_t *entity = NULL;
    size_t entity_len = 0;

    while(p < end && *p != '&')
    {
        p++;
    }
    q = p;
    while(p < end)
    {
        /* Copy characters until the next ampersand */
        if(*p != '&')
        {
            *q++ = *p++;
            continue;
        }
        entity = ++p;
        /* Find the end of the entity, marked by ';' */
        while(p < end && *p != ';')
        {
            p++;
        }
        if(p == end)
        {
            break; /* Drop unterminated entity */
        }
        entity_len = p - entity;
        /*Replace lt '<' or gt '>'*/
        if(entity_len == 2 && entity[1] == 't')
        {
            if(entity[0] == 'g')
                *q++ = '>';
            else if(entity[0] == 'l')
                *q++ = '<';
            /* else drop */
        }
        /*Replace amp '&' */
        else if(entity_len == 3 && entity[0] == 'a' && entity[1] == 'm' && entity[2] == 'p')
        {
            *q++ = '&';
        }
        else if(entity_len == 4 && entity[2] == 'o')
        {
        	/*Replace quot '"' */
            if(entity[0] == 'q' && entity[1] == 'u' && entity[3] == 't')
            {
                *q++ = '"';
            }
            /*Replace apos '\' */
            else if(entity[0] == 'a' && entity[1] == 'p' && entity[3] == 's')
            {
                *q++ = '\'';
            }
        }
        /*  numeric character reference */
        else if(entity_len >= 2 && entity[0] == '#')
        {
            /* p points to the ';' */
            int c = 0;
            guththila_char_t b;
            guththila_char_t *digit = entity + 1;

            /* The digit is in hex*/
            if(*digit == 'x')
            { /* &#x...; */
                while(++digit < p)
                {
                    b = *digit;
                    if(b >= '0' && b <= '9')
                        c = c << 4 | (b - '0');
                    else if(b >= 'A' && b <= 'F')
                        c = c << 4 | (b - 'A' + 10);
                    else if(b >= 'a' && b <= 'f')
                        c = c << 4 | (b - 'a' + 10);
                    else
                        break; /* stop and drop */
                }
            }
            /*The digit is in decimal*/
            else
            { /* &#...; */
                while(digit < p)
                {
                    b = *digit;
                    if(b >= '0' && b <= '9')
                        c = c * 10 + (b - '0');
                    else
                        break; /* stop and drop */
                    digit++;
                }
            }
            if(digit == p && c != 0)
            { /* drop null char or unparsable entity */
                /* Replace the entity with the UTF-8 representation */
                q = guththila_utf8_bytes(c, q);
            }
        } /* else drop unknown entity */
        p++; /* go over ';' */
    }
    start[q - start] = '\0';
}

/*
 * Close a token. This method accepts the type of the token as a parameter.
 */
static void
guththila_token_close(
    guththila_t * m,
    guththila_token_t * tok,
    int tok_type,
    int referer,
    const axutil_env_t * env)
{
    guththila_attr_t * attr = NULL;
    guththila_element_t * elem = NULL;
    guththila_elem_namesp_t * e_namesp = NULL;
    guththila_namespace_t * namesp;
    int i = 0;
    /* We are sure that the difference lies within the short range */
    m->temp_tok->type = (short)tok_type;
    m->temp_tok->size = m->next - m->temp_tok->_start;
    m->temp_tok->start = GUTHTHILA_BUF_POS(m->buffer, m->next - 1) - m->temp_tok->size;
    m->temp_tok->ref = referer;
    m->last_start = -1;
    switch(tok_type)
    {
        case _attribute_name:
            m->temp_name = m->temp_tok;
            m->temp_tok = NULL;
            break;
        case _char_data:
            m->value = m->temp_tok;
            m->temp_tok = NULL;
            break;
        case _text_data:
            m->value = m->temp_tok;
            m->temp_tok = NULL;
            break;
        case _attribute_value:
            /* Chech weather we are at a xml namespace declaration */
            if((m->temp_prefix && (guththila_tok_str_cmp(m->temp_prefix, "xmlns", 5u, env) == 0))
                || (guththila_tok_str_cmp(m->temp_name, "xmlns", 5u, env) == 0))
            /*checks inside the m->temp_name to parse the default namespace*/
            /*checks inside the m->temp_prefix to parse namespace with prefix*/
            {
#ifndef GUTHTHILA_VALIDATION_PARSER
                namesp =
                (guththila_namespace_t *)
                AXIS2_MALLOC(sizeof(guththila_namespace_t));
                GUTHTHILA_NAMESPACE_INITIALIZE(namesp, m->temp_name, m->temp_tok);
                guththila_stack_push(&m->namesp, namesp);
#else   
                elem = (guththila_element_t *)guththila_stack_peek(&m->elem, env);
                /* This is the first namespace */
                if(elem && !elem->is_namesp)
                {
                    e_namesp = (guththila_elem_namesp_t *)AXIS2_MALLOC(env->allocator,
                        sizeof(guththila_elem_namesp_t));
                    if(e_namesp)
                    {
                        e_namesp->namesp = (guththila_namespace_t *)AXIS2_MALLOC(env->allocator,
                            sizeof(guththila_namespace_t) * GUTHTHILA_NAMESPACE_DEF_SIZE);
                        if(e_namesp->namesp)
                        {
                            e_namesp->no = 1;
                            e_namesp->size = GUTHTHILA_NAMESPACE_DEF_SIZE;
                            e_namesp->namesp[0].name = m->temp_name;
                            e_namesp->namesp[0].uri = m->temp_tok;
                            guththila_stack_push(&m->namesp, e_namesp, env);
                            elem->is_namesp = 1;
                        }
                        else
                        {
                            AXIS2_FREE(env->allocator, e_namesp);
                            e_namesp = NULL;
                        }
                    }
                }
                /* Already there is a namespace */
                else if(elem && elem->is_namesp)
                {
                    e_namesp = (guththila_elem_namesp_t *)guththila_stack_peek(&m->namesp, env);
                    /* if we have enough space allocated */
                    if(e_namesp->no < e_namesp->size)
                    {
                        e_namesp->namesp[e_namesp->no].name = m->temp_name;
                        e_namesp->namesp[e_namesp->no].uri = m->temp_tok;
                        e_namesp->no++;
                    }
                    else
                    {
                        namesp = (guththila_namespace_t *)AXIS2_MALLOC(env->allocator,
                            sizeof(guththila_namespace_t) * e_namesp->size * 2);
                        if(namesp)
                        {
                            for(i = 0; i < e_namesp->no; i++)
                            {
                                namesp[i].name = e_namesp->namesp[i].name;
                                namesp[i].uri = e_namesp->namesp[i].uri;
                            }
                            AXIS2_FREE(env->allocator, e_namesp->namesp);
                            e_namesp->namesp = namesp;
                            e_namesp->size *= 2;

                            e_namesp->namesp[e_namesp->no].name = m->temp_name;
                            e_namesp->namesp[e_namesp->no].uri = m->temp_tok;
                            e_namesp->no++;
                        }
                    }
                }
#endif  
            }
            else
            {
                /* It is just a attribute */
                attr = (guththila_attr_t *)AXIS2_MALLOC(env->allocator, sizeof(guththila_attr_t));

                GUTHTHILA_ATTRIBUTE_INITIALIZE(attr,
                    m->temp_prefix,
                    m->temp_name,
                    m->temp_tok);
                guththila_stack_push(&m->attrib, attr, env);
            }
            m->temp_prefix = NULL;
            m->temp_name = NULL;
            m->temp_tok = NULL;
            break;
        case _prefix:
            m->temp_prefix = m->temp_tok;
            m->temp_tok = NULL;
            break;
        default:
            m->prefix = m->temp_prefix;
            m->name = m->temp_tok;
            m->temp_tok = NULL;
            m->temp_prefix = NULL;
            break;
    }
}

int GUTHTHILA_CALL
guththila_validate_namespaces(
    guththila_t *m,
    const axutil_env_t *env)
{
    int size = 0, i = 0, nmsp_no = 0, j = 0, k = 0;
    int namesp_found = GUTHTHILA_FALSE;
    guththila_elem_namesp_t *e_namesp = NULL;

    size = GUTHTHILA_STACK_SIZE(m->attrib);
    /* Loop through all the attributes */
    for(i = 0; i < size; i++)
    {
        guththila_attr_t *attr = (guththila_attr_t *)guththila_stack_get_by_index(&m->attrib, i,
            env);
        if(attr && attr->pref)
        {
            /* We have a attribute prefix. Need to validate the prefix */
            nmsp_no = GUTHTHILA_STACK_SIZE(m->namesp);
            for(j = nmsp_no - 1; j >= 0; j--)
            {
                e_namesp = (guththila_elem_namesp_t *)guththila_stack_get_by_index(&m->namesp, j,
                    env);
                for(k = 0; k < e_namesp->no; k++)
                {
                    if(!guththila_tok_tok_cmp(e_namesp->namesp[k].name, attr->pref, env))
                    {
                        namesp_found = GUTHTHILA_TRUE;
                        j = -1; /* force exit from second for loop */
                        break;
                    }
                }
            }
            if(!namesp_found)
                return GUTHTHILA_FAILURE;
        }
    }
    /* If the element has a prefix. Need to validate the prefix*/
    if(m->prefix)
    {
        namesp_found = AXIS2_FALSE;
        nmsp_no = GUTHTHILA_STACK_SIZE(m->namesp);
        for(j = nmsp_no - 1; j >= 0; j--)
        {
            e_namesp = (guththila_elem_namesp_t *)guththila_stack_get_by_index(&m->namesp, j, env);
            for(k = 0; k < e_namesp->no; k++)
            {
                if(!guththila_tok_tok_cmp(e_namesp->namesp[k].name, m->prefix, env))
                {
                    namesp_found = GUTHTHILA_TRUE;
                    j = -1; /* force exit from outer loop */
                    break;
                }
            }
        }
        if(!namesp_found)
            return AXIS2_FAILURE;
    }
    return GUTHTHILA_SUCCESS;
}

GUTHTHILA_EXPORT int GUTHTHILA_CALL
guththila_next(
    guththila_t * m,
    const axutil_env_t * env)
{
    guththila_element_t * elem = NULL;
    guththila_elem_namesp_t * nmsp = NULL;
    guththila_token_t * tok = NULL;
    int quote = 0, ref = 0;
    int c = -1;
    guththila_attr_t * attr = NULL;
    int size = 0, i = 0, nmsp_counter, loop = 0, white_space = 0;
    size_t data_size = -1;
    size_t previous_size = -1;
    guththila_char_t *buffer = NULL;

    /* Need to release the resources for attributes */
    size = GUTHTHILA_STACK_SIZE(m->attrib);
    for(i = 0; i < size; i++)
    {
        attr = (guththila_attr_t *)guththila_stack_pop(&m->attrib, env);
        if(attr)
        {
            if(attr->name)
                guththila_tok_list_release_token(&m->tokens, attr->name, env);
            if(attr->pref)
                guththila_tok_list_release_token(&m->tokens, attr->pref, env);
            AXIS2_FREE(env->allocator, attr);
        }
    }

#ifdef GUTHTHILA_VALIDATION_PARSER    
    if(m->guththila_event == GUTHTHILA_END_ELEMENT && m->name)
    {
        guththila_tok_list_release_token(&m->tokens, m->name, env);
        if(m->prefix)
        {
            guththila_tok_list_release_token(&m->tokens, m->prefix, env);
        }
    }
    /* If the previous event was a empty element we need to do some clean up */
    else if(m->guththila_event == GUTHTHILA_EMPTY_ELEMENT)
    {
        elem = (guththila_element_t *)guththila_stack_pop(&m->elem, env);
        if(elem->is_namesp)
        {
            nmsp = (guththila_elem_namesp_t *)guththila_stack_pop(&m->namesp, env);
            for(nmsp_counter = 0; nmsp_counter < nmsp->no; nmsp_counter++)
            {
                if(nmsp->namesp[nmsp_counter].name)
                    guththila_tok_list_release_token(&m->tokens, nmsp->namesp[nmsp_counter]. name,
                        env);
                if(nmsp->namesp[nmsp_counter].uri)
                    guththila_tok_list_release_token(&m->tokens, nmsp->namesp[nmsp_counter]. uri,
                        env);
            }
            AXIS2_FREE(env->allocator, nmsp->namesp);
            AXIS2_FREE(env->allocator, nmsp);
        }
        if(elem->name)
            guththila_tok_list_release_token(&m->tokens, elem->name, env);
        if(elem->prefix)
            guththila_tok_list_release_token(&m->tokens, elem->prefix, env);
        AXIS2_FREE(env->allocator, elem);
    }
    GUTHTHILA_VARIABLE_INITIALZE(m);
#endif  
    /* Actual XML parsing logic */
    do
    {
        loop = 0;
        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
        if(m->status == S_1)
        {
            while(isspace(c))
            {
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
            }
            if('<' == c)
            {
                m->status = S_2;
            }
            else
            {
                return -1;
            }
        }
        if(m->status != S_2)
        {
            return -1;
        }
        if(c == '<')
        {
            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
            if(c != '?' && c != '!' && c != '/')
            {
                /* We are at the beginning of a xml element */
                if(GUTHTHILA_IS_VALID_STARTING_CHAR(c))
                {
                    GUTHTHILA_TOKEN_OPEN(m, tok, env);
                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                    while(!GUTHTHILA_IS_SPACE(c) && c != '>' && c != '/')
                    {
                        if(c != ':')
                        {
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        }
                        else
                        {
                            /* We know for sure that this is a prefix */
                            guththila_token_close(m, tok, _prefix, 0, env);
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                            GUTHTHILA_TOKEN_OPEN(m, tok, env);
                        }
                    }
                    /* XML element name */
                    guththila_token_close(m, tok, _name, 0, env);
#ifdef GUTHTHILA_VALIDATION_PARSER		
                    elem = (guththila_element_t *)AXIS2_MALLOC(env->allocator,
                        sizeof(guththila_element_t));
                    elem->name = m->name;
                    elem->prefix = m->prefix;
                    elem->is_namesp = 0;
                    guththila_stack_push(&m->elem, elem, env);
#endif  
                }
                GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
                /* Process the attributes */
                for(;;)
                {
                    /* Empty element */
                    if(c == '/')
                    {
                        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        if(c == '>')
                        {
                            m->guththila_event = GUTHTHILA_EMPTY_ELEMENT;
                            if(!guththila_validate_namespaces(m, env))
                                return -1;
                            else
                                return GUTHTHILA_EMPTY_ELEMENT;
                        }
                        else
                        {
                            return -1;
                        }
                    }
                    /* Normal element */
                    else if(c == '>')
                    {
                        m->guththila_event = GUTHTHILA_START_ELEMENT;
                        if(!guththila_validate_namespaces(m, env))
                            return -1;
                        else
                            return GUTHTHILA_START_ELEMENT;
                    }
                    /* We are in the middle of a element */
                    else
                    {
                        /* Process the attributes */
                        if(GUTHTHILA_IS_VALID_STARTING_CHAR(c))
                        {
                            GUTHTHILA_TOKEN_OPEN(m, tok, env);

                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                            while(!GUTHTHILA_IS_SPACE(c) && c != '=')
                            {
                                if(c != ':')
                                {
                                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                                }
                                else if(c == ':')
                                {
                                    /* Prefix */
                                    guththila_token_close(m, tok, _prefix, 0, env);
                                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                                    GUTHTHILA_TOKEN_OPEN(m, tok, env);
                                }
                            }
                            /* Attribute name*/
                            guththila_token_close(m, tok, _attribute_name, 0, env);
                        }
                        else
                        {
                            return -1;
                        }
                        /* Attribute Value */
                        GUTHTHILA_PROCESS_EQU(m, c, quote, buffer, data_size, previous_size, env);
                        if(quote == '\'' || quote == '\"')
                        {
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                            GUTHTHILA_TOKEN_OPEN(m, tok, env);
                            while(c != quote)
                            {
                                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                            }
                            guththila_token_close(m, tok, _attribute_value, 0, env);
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                            GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
                        }
                        else
                        {
                            return -1;
                        }
                    }
                } /* for(;;) */
            }
            else if(c == '/')
            {
                /* End Element */
                m->guththila_event = GUTHTHILA_END_ELEMENT;
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                if(GUTHTHILA_IS_VALID_STARTING_CHAR(c))
                {
                    GUTHTHILA_TOKEN_OPEN(m, tok, env);
                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                    while(!GUTHTHILA_IS_SPACE(c) && c != '>')
                    {
                        if(c != ':')
                        {
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        }
                        else
                        {
                            /* Prefix */
                            guththila_token_close(m, tok, _prefix, 0, env);
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                            GUTHTHILA_TOKEN_OPEN(m, tok, env);
                        }
                    }
                    /* name */
                    guththila_token_close(m, tok, _name, 0, env);

#ifdef GUTHTHILA_VALIDATION_PARSER	
                    elem = (guththila_element_t *)guththila_stack_pop(&m->elem, env);
                    if(!elem || (!elem->prefix && m->prefix) || (elem->prefix && !m->prefix))
                        return -1;
                    if(guththila_tok_tok_cmp(m->name, elem->name, env))
                    {
                        return -1;
                    }
                    if(elem->prefix && m->prefix && guththila_tok_tok_cmp(m->prefix, elem->prefix,
                        env))
                    {
                        return -1;
                    }
                    /* Releasing the namespace related resources */
                    if(elem->is_namesp)
                    {
                        nmsp = (guththila_elem_namesp_t *)guththila_stack_pop(&m->namesp, env);
                        for(nmsp_counter = 0; nmsp_counter < nmsp->no; nmsp_counter++)
                        {
                            if(nmsp->namesp[nmsp_counter].name)
                                guththila_tok_list_release_token(&m->tokens,
                                    nmsp-> namesp[nmsp_counter]. name, env);
                            if(nmsp->namesp[nmsp_counter].uri)
                                guththila_tok_list_release_token(&m->tokens,
                                    nmsp-> namesp[nmsp_counter]. uri, env);
                        }
                        AXIS2_FREE(env->allocator, nmsp->namesp);
                        AXIS2_FREE(env->allocator, nmsp);
                    }
                    /* Release the tokens */
                    if(elem->name)
                        guththila_tok_list_release_token(&m->tokens, elem->name, env);
                    if(elem->prefix)
                        guththila_tok_list_release_token(&m->tokens, elem->prefix, env);
                    AXIS2_FREE(env->allocator, elem);
#endif  
                    GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
                    if(c != '>')
                        return -1;
                    return GUTHTHILA_END_ELEMENT;
                }
                return -1;
            }
            else if(c == '!')
            {
                /* Comment */
                guththila_char_t c_arra[16] = { 0 };
                if(2 == guththila_next_no_char(m, 0, c_arra, 2, env) && '-' == c_arra[0] && '-'
                    == c_arra[1])
                {
                    /* AXIS2C-1536 FIX: Removed premature GUTHTHILA_NEXT_CHAR before while loop
                     * For empty comments like <!---->, the parser was getting stuck because
                     * the initial character read consumed the first hyphen of -->, breaking
                     * the end-of-comment detection logic.
                     */
                    int loop_state = 1;
                    GUTHTHILA_TOKEN_OPEN(m, tok, env);
                    while(loop_state)
                    {
                        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        if('-' == c)
                        {
                            if(2 == guththila_next_no_char(m, 0, c_arra, 2, env) && '-'
                                == c_arra[0])
                            {
                                if('>' == c_arra[1])
                                {
                                    m->guththila_event = GUTHTHILA_COMMENT;
                                    /* position after first hyphen, as if we just scanned it */
                                    m->next = m->next - 2;
                                    guththila_token_close(m, tok, _char_data, 0, env);
                                    m->next = m->next + 2;
                                    return GUTHTHILA_COMMENT;
                                }
                                else
                                {
                                    /* AXIS2C-1536: Advance parser position on malformed comment */
                                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                                    return -1;
                                }
                            }
                        }
                    }
                }
                else if ('[' == c_arra[0] && 'C' == c_arra[1])
                {
                    if (5 == guththila_next_no_char(m, 0, c_arra, 5, env) &&
                        'D' == c_arra[0] && 'A' == c_arra[1] && 'T' == c_arra[2] &&
                        'A' == c_arra[3] && '[' == c_arra[4])
                    {
                        /* CDATA */

                        int loop_state = 1;
                        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        GUTHTHILA_TOKEN_OPEN(m, tok, env);
                        while(loop_state)
                        {
                            if (']' == c)
                            {
                                if (2 == guththila_next_no_char(m, 0, c_arra, 2, env) &&
                                        ']' == c_arra[0])
                                {
                                    if ('>' == c_arra[1])
                                    {
                                        m->next = m->next - 2;
                                        guththila_token_close(m, tok, _char_data, 0, env);
                                        m->next = m->next + 2;
                                        loop_state = 0;
                                        m->guththila_event = GUTHTHILA_CHARACTER;
                                        return GUTHTHILA_CHARACTER;
                                    }
                                }
                            }
                            if (-1 == c) {
                                return -1;
                            }
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        }
                    }
                    else
                    {
                        return -1;
                    }
                }
                else
                {
                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                    while('<' != c)
                    {
                        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                    }
                }
            }
            else if(c == '?')
            {
                /* XML declaration */
                c = guththila_process_xml_dec(m, env);
                if(c != -1)
                    return GUTHTHILA_START_DOCUMENT;
                else
                    return -1;
            }
        }
        else
        {
            /* Text */
            m->guththila_event = GUTHTHILA_CHARACTER;
            if(!GUTHTHILA_IS_SPACE(c))
                white_space = 0;
            else
                white_space = 1;
            GUTHTHILA_TOKEN_OPEN(m, tok, env);

            /* code given below is having two do-while loop wrapped by another do-while loop.
             * This is done to improve the performance for big messages. Most of the cases, the
             * content will be not whitespace, so checking for whitespace even if we already found
             * that we have some valid characters is big overhead. Hence better to do the looping
             * separately to find white_space = false and normal case
             */
            do
            {
                if(white_space)
                {
                    do
                    {
                        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        if(!GUTHTHILA_IS_SPACE(c) && c != '<')
                        {
                            white_space = 0;
                            break;
                        }
                    }
                    while(c != '<');
                }
                else
                {
                    do
                    {
                        if(buffer)
                        {
                            guththila_char_t *pos = NULL;
                            size_t index = m->next - previous_size;
                            pos = (guththila_char_t*)memchr(buffer + index, '<', data_size - index);
                            if(pos)
                            {
                                m->next += pos - (buffer + index);
                            }
                            else
                            {
                                m->next = previous_size + data_size;
                            }

                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        }
                        else
                        {
                            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                        }
                    }while(c != '<');
                }
            }
            while(c != '<');
            guththila_token_close(m, tok, _text_data, ref, env);
            m->next--;
            if(white_space)
            {
#ifndef GUTHTHILA_IGNORE_SPACES
                m->guththila_event = GUTHTHILA_SPACE;
                return GUTHTHILA_SPACE;
#else
                loop = 1;
                if (m->value)
                {
                    guththila_tok_list_release_token(&m->tokens, m->value,
                        env);
                    m->value = NULL;
                }
#endif
            }

            else
                return GUTHTHILA_CHARACTER;
        }
    }
    while(loop);
    return c;
}

/* Process the XML declaration */
static int
guththila_process_xml_dec(
    guththila_t * m,
    const axutil_env_t * env)
{
    guththila_token_t * tok = NULL;
    guththila_char_t c_arra[16] = { 0 };
    int c = -1;
    int quote = -1;
    int nc = -1;
    size_t data_size = -1;
    size_t previous_size = -1;
    guththila_char_t *buffer = NULL;
    if(3 == guththila_next_no_char(m, GUTHTHILA_EOF, c_arra, 3, env) && 'x' == c_arra[0] && 'm'
        == c_arra[1] && 'l' == c_arra[2])
    {
        GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
        GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
        if(c == 'v')
        {
            GUTHTHILA_TOKEN_OPEN(m, tok, env);
            if(6 == guththila_next_no_char(m, 0, c_arra, 6, env) && 'e' == c_arra[0] && 'r'
                == c_arra[1] && 's' == c_arra[2] && 'i' == c_arra[3] && 'o' == c_arra[4] && 'n'
                == c_arra[5])
            {
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                guththila_token_close(m, tok, _attribute_name, 0, env);
                GUTHTHILA_PROCESS_EQU(m, c, quote, buffer, data_size, previous_size, env);
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
                GUTHTHILA_TOKEN_OPEN(m, tok, env);
                while(nc != quote)
                {
                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
                }
                guththila_token_close(m, tok, _attribute_value, 0, env);
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
            }
            else
            {
                return -1;
            }
        }
        if(c == 'e')
        {
            GUTHTHILA_TOKEN_OPEN(m, tok, env);
            if(7 == guththila_next_no_char(m, 0, c_arra, 7, env) && 'n' == c_arra[0] && 'c'
                == c_arra[1] && 'o' == c_arra[2] && 'd' == c_arra[3] && 'i' == c_arra[4] && 'n'
                == c_arra[5] && 'g' == c_arra[6])
            {
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                guththila_token_close(m, tok, _attribute_name, 0, env);
                GUTHTHILA_PROCESS_EQU(m, c, quote, buffer, data_size, previous_size, env);
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
                GUTHTHILA_TOKEN_OPEN(m, tok, env);
                while(nc != quote)
                {
                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
                }
                guththila_token_close(m, tok, _attribute_value, 0, env);
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
            }
        }
        if(c == 's')
        {
            GUTHTHILA_TOKEN_OPEN(m, tok, env);
            if(9 == guththila_next_no_char(m, 0, c_arra, 9, env) && 't' == c_arra[0] && 'a'
                == c_arra[1] && 'n' == c_arra[2] && 'd' == c_arra[3] && 'a' == c_arra[4] && 'l'
                == c_arra[5] && 'o' == c_arra[6] && 'n' == c_arra[7] && 'e' == c_arra[8])
            {
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                guththila_token_close(m, tok, _attribute_name, 0, env);
                GUTHTHILA_PROCESS_EQU(m, c, quote, buffer, data_size, previous_size, env);
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
                GUTHTHILA_TOKEN_OPEN(m, tok, env);
                while(nc != quote)
                {
                    GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
                }
                guththila_token_close(m, tok, _attribute_value, 0, env);
                GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, c);
                GUTHTHILA_SKIP_SPACES(m, c, buffer, data_size, previous_size, env);
            }
        }
        if(c == '?')
        {
            int nc;
            GUTHTHILA_NEXT_CHAR(m, buffer, data_size, previous_size, env, nc);
            if('>' == nc)
            {
                m->guththila_event = GUTHTHILA_START_DOCUMENT;
            }
            else
            {
                return -1;
            }
        }
    }
    return c;
}

GUTHTHILA_EXPORT int GUTHTHILA_CALL
guththila_get_attribute_count(
    guththila_t * m,
    const axutil_env_t * env)
{
    return GUTHTHILA_STACK_SIZE(m->attrib);
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_name(
    guththila_t * m,
    guththila_attr_t * att,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(att->name)
    {
        GUTHTHILA_TOKEN_TO_STRING(att->name, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_value(
    guththila_t * m,
    guththila_attr_t * att,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(att->val)
    {
        GUTHTHILA_TOKEN_TO_STRING(att->val, str, env);
        guththila_string_evaluate_references(str, GUTHTHILA_TOKEN_LEN(att->val));
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_prefix(
    guththila_t * m,
    guththila_attr_t * att,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(att->pref)
    {
        GUTHTHILA_TOKEN_TO_STRING(att->pref, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_attr_t *GUTHTHILA_CALL
guththila_get_attribute(
    guththila_t * m,
    const axutil_env_t * env)
{
    return (guththila_attr_t *)guththila_stack_pop(&m->attrib, env);
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_name_by_number(
    guththila_t * m,
    int i,
    const axutil_env_t *env)
{
    guththila_char_t *str = NULL;
    guththila_attr_t * attr = (guththila_attr_t *)guththila_stack_get_by_index(&m->attrib, i - 1,
        env);
    if(attr->name)
    {
        GUTHTHILA_TOKEN_TO_STRING(attr->name, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_value_by_number(
    guththila_t * m,
    int i,
    const axutil_env_t *env)
{
    guththila_char_t *str = NULL;
    guththila_attr_t * attr = (guththila_attr_t *)guththila_stack_get_by_index(&m->attrib, i - 1,
        env);
    if(attr->val)
    {
        GUTHTHILA_TOKEN_TO_STRING(attr->val, str, env);
        guththila_string_evaluate_references(str, GUTHTHILA_TOKEN_LEN(attr->val));
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_prefix_by_number(
    guththila_t * m,
    int i,
    const axutil_env_t *env)
{
    guththila_char_t *str = NULL;
    guththila_attr_t * attr = (guththila_attr_t *)guththila_stack_get_by_index(&m->attrib, i - 1,
        env);
    if(attr && attr->pref)
    {
        GUTHTHILA_TOKEN_TO_STRING(attr->pref, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_name(
    guththila_t * m,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(m->name)
    {
        GUTHTHILA_TOKEN_TO_STRING(m->name, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_prefix(
    guththila_t * m,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(m->prefix)
    {
        GUTHTHILA_TOKEN_TO_STRING(m->prefix, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t * GUTHTHILA_CALL
guththila_get_value(
    guththila_t * m,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(m->value)
    {
        GUTHTHILA_TOKEN_TO_STRING(m->value, str, env);
        /* don't eval references in comments or cdata sections */
        if (m->value->type != _char_data)
        {
            guththila_string_evaluate_references(str, GUTHTHILA_TOKEN_LEN(m->value));
        }
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_namespace_t *GUTHTHILA_CALL
guththila_get_namespace(
    guththila_t * m,
    const axutil_env_t * env)
{

#ifndef GUTHTHILA_VALIDATION_PARSER
    return (guththila_namespace_t *) guththila_stack_pop(&m->namesp, env);
#else  
    return NULL;
#endif  
}

GUTHTHILA_EXPORT int GUTHTHILA_CALL
guththila_get_namespace_count(
    guththila_t * m,
    const axutil_env_t * env)
{

#ifndef GUTHTHILA_VALIDATION_PARSER
    return GUTHTHILA_STACK_SIZE(m->namesp);
#else   
    guththila_elem_namesp_t * nmsp = NULL;
    if(((guththila_element_t *)guththila_stack_peek(&m->elem, env))->is_namesp)
    {
        nmsp = (guththila_elem_namesp_t *)guththila_stack_peek(&m->namesp, env);
        return nmsp->no;
    }
    return 0;

#endif  
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_namespace_uri(
    guththila_t * m,
    guththila_namespace_t * ns,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(ns->uri)
    {
        GUTHTHILA_TOKEN_TO_STRING(ns->uri, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_namespace_prefix(
    guththila_t * m,
    guththila_namespace_t * ns,
    const axutil_env_t * env)
{
    guththila_char_t *str = NULL;
    if(ns->name)
    {
        GUTHTHILA_TOKEN_TO_STRING(ns->name, str, env);
        return str;
    }
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_namespace_prefix_by_number(
    guththila_t * m,
    int i,
    const axutil_env_t *env)
{
    guththila_char_t *str = NULL;

#ifndef GUTHTHILA_VALIDATION_PARSER
    if (GUTHTHILA_STACK_SIZE(m->namesp) >= i)
    {
        namesp = guththila_stack_get_by_index(&m->namesp, i - 1, env);
        if (namesp && namesp->name)
        {
            GUTHTHILA_TOKEN_TO_STRING(namesp->name, str, env);
            return str;
        }
    }

#else   
    guththila_elem_namesp_t * nmsp = NULL;
    if(((guththila_element_t *)guththila_stack_peek(&m->elem, env))->is_namesp)
    {
        nmsp = (guththila_elem_namesp_t *)guththila_stack_peek(&m->namesp, env);
        if(nmsp && nmsp->no >= i)
        {
            GUTHTHILA_TOKEN_TO_STRING(nmsp->namesp[i - 1].name, str, env);
            return str;
        }
    }
#endif  /*  */
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_namespace_uri_by_number(
    guththila_t * m,
    int i,
    const axutil_env_t *env)
{
    guththila_char_t *str = NULL;
#ifndef GUTHTHILA_VALIDATION_PARSER
    if (GUTHTHILA_STACK_SIZE(m->namesp) >= i)
    {
        namesp = guththila_stack_get_by_index(&m->namesp, i - 1, env);
        if (namesp && namesp->uri)
        {
            GUTHTHILA_TOKEN_TO_STRING(namesp->uri, str, env);
            return str;
        }
    }
#else  
    guththila_elem_namesp_t * nmsp = NULL;
    if(((guththila_element_t *)guththila_stack_peek(&m->elem, env))->is_namesp)
    {
        nmsp = (guththila_elem_namesp_t *)guththila_stack_peek(&m->namesp, env);
        if(nmsp && nmsp->no >= i)
        {
            GUTHTHILA_TOKEN_TO_STRING(nmsp->namesp[i - 1].uri, str, env);
            return str;
        }
    }
#endif  
    return NULL;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_attribute_namespace_by_number(
    guththila_t * m,
    int i,
    const axutil_env_t *env)
{

#ifndef GUTHTHILA_VALIDATION_PARSER
    return NULL;

#else  
    guththila_attr_t * attr = NULL;
    guththila_char_t* str = NULL;
    int j = 0, k = 0, count = 0;
    guththila_elem_namesp_t * nmsp = NULL;
    if(i <= GUTHTHILA_STACK_SIZE(m->attrib))
    {
        attr = (guththila_attr_t *)guththila_stack_get_by_index(&m->attrib, i - 1, env);
        if(attr && attr->pref)
        {
            count = GUTHTHILA_STACK_SIZE(m->namesp);
            for(j = count - 1; j >= 0; j--)
            {
                nmsp = (guththila_elem_namesp_t *)guththila_stack_get_by_index(&m->namesp, j, env);
                for(k = 0; k < nmsp->no; k++)
                {
                    if(!guththila_tok_tok_cmp(nmsp->namesp[k].name, attr->pref, env))
                    {
                        GUTHTHILA_TOKEN_TO_STRING(nmsp->namesp[k].uri, str, env);
                        return str;
                    }
                }
            }
        }
    }
    return NULL;

#endif 
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_encoding(
    guththila_t * m,
    const axutil_env_t * env)
{
    return "UTF-8";
}

/* Return the next character */
static int
guththila_next_char(
    guththila_t * m,
    const axutil_env_t * env)
{
    int c;
    size_t data_move, i;
    int temp;
    guththila_char_t **temp1;
    size_t * temp2, *temp3;

    /* we have a buffered reader. Easiest case just fetch the character from 
     * the buffer. Here we have a single buffer. 
     * */
    if(m->reader->type == GUTHTHILA_MEMORY_READER)
    {
        size_t index = m->next++;
        if(index < m->buffer.data_size[0])
        {
            return m->buffer.buff[0][index];
        }
    }
    else
    {
        /* comlex stuff. We have a array of buffers */
        if(m->buffer.cur_buff != -1 && m->next < GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)
            + GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer))
        {
            /* What we are looking for is already in the buffer */
            c = m->buffer.buff[m->buffer.cur_buff][m->next++ - GUTHTHILA_BUFFER_PRE_DATA_SIZE(
                m->buffer)];
            return c;
        }
        else if(m->buffer.cur_buff != -1 && m->next >= GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)
            + GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer))
        {
            /* if we have don't have enough space in current buffer, have to create new buffer */
            if(m->buffer.buffs_size[m->buffer.cur_buff] <
                GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer) + GUTHTHILA_BUFFER_DEF_MIN_SIZE)
            {
                if(m->buffer.cur_buff == (int)m->buffer.no_buffers - 1)
                {
                    /* we are out of allocated buffers. Need to allocate more buffers */
                    temp = m->buffer.no_buffers * 2;
                    temp1 = (guththila_char_t **)AXIS2_MALLOC(env->allocator,
                        sizeof(guththila_char_t *) * temp);
                    temp2 = (size_t *)AXIS2_MALLOC(env->allocator, sizeof(size_t) * temp);
                    temp3 = (size_t *)AXIS2_MALLOC(env->allocator, sizeof(size_t) * temp);
                    if(!temp1 || !temp2 || !temp3)
                        return (-1);
                    for(i = 0; i < m->buffer.no_buffers; i++)
                    {
                        temp1[i] = m->buffer.buff[i];
                        temp2[i] = m->buffer.buffs_size[i];
                        temp3[i] = m->buffer.data_size[i];
                    }
                    AXIS2_FREE(env->allocator, m->buffer.buff);
                    AXIS2_FREE(env->allocator, m->buffer.data_size);
                    AXIS2_FREE(env->allocator, m->buffer.buffs_size);
                    m->buffer.buff = temp1;
                    m->buffer.buffs_size = temp2;
                    m->buffer.data_size = temp3;
                    m->buffer.no_buffers *= 2;
                }
                m->buffer.buff[m->buffer.cur_buff + 1] = (guththila_char_t *)AXIS2_MALLOC(
                    env->allocator,
                    sizeof(guththila_char_t) * m->buffer.buffs_size[m->buffer.cur_buff] * 2);
                if(!m->buffer.buff[m->buffer.cur_buff + 1])
                    return -1;
                m->buffer.cur_buff++;
                m->buffer.buffs_size[m->buffer.cur_buff] =
                    m->buffer.buffs_size[m->buffer.cur_buff - 1] * 2;
                m->buffer.data_size[m->buffer.cur_buff] = 0;
                /* We need to have the content for one token in a single buffer.
                 * So if the space is not sufficient we have to move first part
                 * of the token to the next buffer */
                if(m->last_start != -1)
                {
                    data_move = m->buffer.data_size[m->buffer.cur_buff - 1] -
                        (m->last_start - m->buffer.pre_tot_data);
                    memcpy(m->buffer.buff[m->buffer.cur_buff], m->buffer.buff[m->buffer.cur_buff - 1]
                        + m->buffer.data_size[m->buffer.cur_buff - 1] - data_move, data_move);
                    m->buffer.data_size[m->buffer.cur_buff - 1] -= data_move;
                    m->buffer.data_size[m->buffer.cur_buff] += data_move;
                }
                m->buffer.pre_tot_data += m->buffer.data_size[m->buffer.cur_buff - 1];
            }
            temp = guththila_reader_read(m->reader, GUTHTHILA_BUFFER_CURRENT_BUFF(m->buffer), 0,
                (int)GUTHTHILA_BUFFER_CURRENT_BUFF_SIZE(m->buffer), env);
            if(temp > 0)
            {
                m->buffer.data_size[m->buffer.cur_buff] += temp;
            }
            else
            {
                return -1;
            }
            c = m->buffer.buff[m->buffer.cur_buff][m->next++ - GUTHTHILA_BUFFER_PRE_DATA_SIZE(
                m->buffer)];
            return c;
        }
        /* Initial stage. We dont' have the array of buffers allocated*/
        else if(m->buffer.cur_buff == -1)
        {
            m->buffer.buff[0] = (guththila_char_t *)AXIS2_MALLOC(env->allocator,
                sizeof(guththila_char_t) * GUTHTHILA_BUFFER_DEF_SIZE);
            m->buffer.buffs_size[0] = GUTHTHILA_BUFFER_DEF_SIZE;
            m->buffer.cur_buff = 0;
            temp = guththila_reader_read(m->reader, m->buffer.buff[0], 0,
                GUTHTHILA_BUFFER_DEF_SIZE, env);
            m->buffer.data_size[0] = temp;
            c = m->buffer.buff[0][m->next++];
            return c;
        }
    }
    return -1;
}

/* Same functionality as the guththila_next_char. But insted of reading 
 * one character this function reads several characters at once 
 * */
static int
guththila_next_no_char(
    guththila_t * m,
    int eof,
    guththila_char_t *bytes,
    size_t no,
    const axutil_env_t * env)
{
    int temp, data_move;
    size_t i;
    guththila_char_t **temp1;
    size_t * temp2, *temp3;
    if(m->reader->type == GUTHTHILA_MEMORY_READER && m->next + no - 1
        < GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer) && m->buffer.cur_buff != -1)
    {
        for(i = 0; i < no; i++)
        {
            bytes[i] = m->buffer.buff[0][m->next++];
        }
        return (int)no;
        /* We are sure that the difference lies within the int range */
    }
    else if(m->reader->type == GUTHTHILA_IO_READER || m->reader->type == GUTHTHILA_FILE_READER)
    {
        if(m->next + no <= GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)
            + GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer) && m->buffer.cur_buff != -1) 
        {
            for(i = 0; i < no; i++)
            {
                bytes[i] = m->buffer.buff[m->buffer.cur_buff][m->next++
                    - GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)];
            }
            return (int)no;
            /* We are sure that the difference lies within the int range */
        }
        else if(m->next + no > GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)
            + GUTHTHILA_BUFFER_CURRENT_DATA_SIZE(m->buffer) && m->buffer.cur_buff != -1) 
        {
            /* We are sure that the difference lies within the int range */
            if(m->buffer.cur_buff == (int)m->buffer.no_buffers - 1)
            {
                temp = m->buffer.no_buffers * 2;
                temp1 = (guththila_char_t **)AXIS2_MALLOC(env->allocator,
                    sizeof(guththila_char_t *) * temp);
                temp2 = (size_t *)AXIS2_MALLOC(env->allocator, sizeof(size_t) * temp);
                temp3 = (size_t *)AXIS2_MALLOC(env->allocator, sizeof(size_t) * temp);
                if(!temp1 || !temp2 || !temp3)
                    return (-1);
                for(i = 0; i < m->buffer.no_buffers; i++)
                {
                    temp1[i] = m->buffer.buff[i];
                    temp2[i] = m->buffer.buffs_size[i];
                    temp3[i] = m->buffer.data_size[i];
                }
                AXIS2_FREE(env->allocator, m->buffer.buff);
                AXIS2_FREE(env->allocator, m->buffer.data_size);
                AXIS2_FREE(env->allocator, m->buffer.buffs_size);
                m->buffer.buff = temp1;
                m->buffer.buffs_size = temp2;
                m->buffer.data_size = temp3;
                m->buffer.no_buffers *= 2;
            }
            m->buffer.buff[m->buffer.cur_buff + 1] = (guththila_char_t *)AXIS2_MALLOC(
                env->allocator, sizeof(guththila_char_t) * m->buffer.data_size[m->buffer.cur_buff]
                    * 2);
            if(!m->buffer.buff[m->buffer.cur_buff + 1])
                return -1;
            m->buffer.cur_buff++;
            m->buffer.buffs_size[m->buffer.cur_buff] = m->buffer.buffs_size[m->buffer.cur_buff - 1]
                * 2;
            m->buffer.data_size[m->buffer.cur_buff] = 0;
            data_move = (int)m->next;
            /* We are sure that the difference lies within the int range */
            if((m->last_start != -1) && (m->last_start < data_move))
                data_move = m->last_start;
            data_move = (int)m->buffer.data_size[m->buffer.cur_buff - 1] - (data_move
                - (int)m->buffer.pre_tot_data);
            /* We are sure that the difference lies within the int range */
            if(data_move)
            {
                memcpy(m->buffer.buff[m->buffer.cur_buff], m->buffer.buff[m->buffer.cur_buff - 1]
                    + m->buffer.data_size[m->buffer.cur_buff - 1] - data_move, data_move);
                m->buffer.data_size[m->buffer.cur_buff - 1] -= data_move;
                m->buffer.data_size[m->buffer.cur_buff] += data_move;
            }
            m->buffer.pre_tot_data += m->buffer.data_size[m->buffer.cur_buff - 1];
            temp = guththila_reader_read(m->reader, GUTHTHILA_BUFFER_CURRENT_BUFF(m->buffer), 0,
                (int)GUTHTHILA_BUFFER_CURRENT_BUFF_SIZE(m-> buffer), env);
            /* We are sure that the difference lies within the int range */
            if(temp > 0)
            {
                m->buffer.data_size[m->buffer.cur_buff] += temp;
            }
            else
            {
                return -1;
            }
            for(i = 0; i < no; i++)
            {
                bytes[i] = m->buffer.buff[m->buffer.cur_buff][m->next++
                    - GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)];
            }
            return (int)no;
            /* We are sure that the difference lies within the int range */
        }
        else if(m->buffer.cur_buff == -1)
        {
            m->buffer.buff[0] = (guththila_char_t *)AXIS2_MALLOC(env->allocator,
                sizeof(guththila_char_t) * GUTHTHILA_BUFFER_DEF_SIZE);
            m->buffer.buffs_size[0] = GUTHTHILA_BUFFER_DEF_SIZE;
            m->buffer.cur_buff = 0;
            temp = guththila_reader_read(m->reader, m->buffer.buff[0], 0,
                GUTHTHILA_BUFFER_DEF_SIZE, env);
            m->buffer.data_size[0] = temp;
            for(i = 0; i < no; i++)
            {
                bytes[i] = m->buffer.buff[m->buffer.cur_buff][m->next++
                    - GUTHTHILA_BUFFER_PRE_DATA_SIZE(m->buffer)];
            }
            return (int)no;
            /* We are sure that the difference lies within the int range */
        }
    }
    return -1;
}

GUTHTHILA_EXPORT guththila_char_t *GUTHTHILA_CALL
guththila_get_current_buffer(
    guththila_t * m,
    const axutil_env_t * env)
{
    return guththila_buffer_get(&m->buffer, env);
}

