/*  
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/xml.c,v 1.21 2004/04/30 13:41:56 aleigh Exp $
 */

/*
 * Copyright (c) 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>

#include "mecha.h"

#undef _XML_DEBUG

struct xmlSiterator {
    const struct xmlStag *first;
    const struct xmlStag *tag;
};

struct xmlSconfig {
    struct xmlStag *tags;
};

struct xmlStag {
    char *name;                 /* My name */
    size_t name_len;
    char *value;                /* My string value, if I have one */
    size_t value_len;
    lstTset *attrib_list;
    struct xmlStag *linear_child;       /* Child tags */
    struct xmlStag *linear_next;        /* Brother Tag */
};

static const char *xmlF_parse(const char *str, const char *level,
                              xmlTtag * parent, lstTset * arglist);
static void xmlFtag_addchild(xmlTtag * parent, xmlTtag * child);
static xmlTconfig *xmlFconfig_init(void);
static xmlTtag *xmlFtag_init(void);
static void xmlFtag_free(xmlTtag * tag);

const xmlTtag *xmlFiterator_tag(xmlTiterator *it) {
    assert(it!=NULL);

    return it->tag;
}

const char *xmlFtag_get_name(const xmlTtag * tag, size_t * name_len)
{
    assert(tag != NULL);
    assert(name_len != NULL);

    *name_len = tag->name_len;
    return tag->name;
}

const char *xmlFtag_get_value_str(const xmlTtag * tag)
{
    assert(tag != NULL);

    return tag->value;
}

const char *xmlFtag_get_name_str(const xmlTtag * tag)
{
    assert(tag != NULL);

    return tag->name;
}

const char *xmlFtag_get_value(const xmlTtag * tag, size_t * value_len)
{
    assert(tag != NULL);
    assert(value_len != NULL);

    *value_len = tag->value_len;
    return tag->value;
}

const lstTset *xmlFtag_get_attribs(const xmlTtag * tag)
{
    assert(tag != NULL);
    return tag->attrib_list;
}

const xmlTtag *xmlFtag_get_children(const xmlTtag * tag)
{
    assert(tag != NULL);

    return tag->linear_child;
}

xmlTiterator *xmlFiterator_init(const xmlTtag * tag)
{
    xmlTiterator *iterator;
        
    assert(tag != NULL);
    
    iterator = (xmlTiterator *) malloc(sizeof(xmlTiterator));
    
    assert(iterator != NULL);

    iterator->tag = iterator->first = tag;
    
    return iterator;
}

void xmlFiterator_reset(xmlTiterator *iterator) {
    assert(iterator!=NULL);
    iterator->tag=iterator->first;
}

xmlTiterator *xmlFiterate_children(const xmlTtag * tag)
{
    xmlTiterator *iterator;

    assert(tag != NULL);

    if (tag->linear_child == NULL)
        return NULL;

    iterator = (xmlTiterator *) malloc(sizeof(xmlTiterator));

    iterator->tag = tag->linear_child;

    return iterator;
}

void xmlFiterator_free(xmlTiterator * iterator)
{
    assert(iterator != NULL);
    
    free(iterator);
}

const xmlTtag *xmlFiterator_next_name(xmlTiterator * iterator,
                                      const char *name)
{
    const xmlTtag *t;

    assert(iterator!=NULL);
    assert(name!=NULL);

#ifdef _XML_DEBUG
    logFmsg(3,"xmlFiterator_next_name: Called to search for %s", name);
#endif
    
    
    while ((t = xmlFiterator_next(iterator)) != NULL) {
#ifdef _XML_DEBUG
        logFmsg(3,"xmlFiterator_next_name: Testing '%s' == '%s'", t->name, name);
#endif
        if (strcmp(t->name, name) == 0) {
#ifdef _XML_DEBUG
  	    logFmsg(3,"xmlFiterator_next_name: Returning 0x%x", t);
#endif
            return t;
        }
    }

#ifdef _XML_DEBUG
    logFmsg(3,"xmlFiterator_next_name: Returning NULL");
#endif

    return NULL;
}

const xmlTtag *xmlFfind_first_tag_children(const xmlTtag * tag,
                                           const char *name)
{
    const xmlTtag *t;

    assert(tag!=NULL);
    assert(name!=NULL);

    t = xmlFfind_first_tag(tag, name);

    if (t == NULL) {
        return NULL;
    } else {
        return t->linear_child;
    }
}

const xmlTtag *xmlFfind_first_child(const xmlTtag *tag, const char *name) {
    xmlTiterator *it;
    const xmlTtag *match;
    
    assert(tag!=NULL);
    assert(name!=NULL);

    it=xmlFiterate_children(tag);
    
    if(it==NULL) {
        return NULL;
    }
    
    match=xmlFiterator_next_name(it,name);
    
    xmlFiterator_free(it);
    
    return match;
}

const xmlTtag *xmlFfind_first_tag(const xmlTtag * tag, const char *name)
{
    xmlTiterator *it;
    const xmlTtag *t;
    
    assert(tag != NULL);
    assert(name != NULL);
    
    it = xmlFiterator_init(tag);
    
    assert(it != NULL);

    t = xmlFiterator_next_name(it, name);

    xmlFiterator_free(it);

    return t;
}

const xmlTtag *xmlFiterator_next(xmlTiterator * iterator)
{
    const xmlTtag *t;

    assert(iterator != NULL);

    if (iterator->tag == NULL) {
        return NULL;
    }
    
    t = iterator->tag;

    iterator->tag = iterator->tag->linear_next;

    return t;
}

static xmlTconfig *xmlFconfig_init(void)
{
    xmlTconfig *config = (xmlTconfig *) malloc(sizeof(xmlTconfig));
    memFclear(config, sizeof(xmlTconfig));
    return config;
}

/*
 * But I will go down with this ship, and I won't hurt myself and surrender,
 * there will be no white flag above my door -aleigh
 */

static xmlTtag *xmlFtag_init(void)
{
    xmlTtag *tag = (xmlTtag *) malloc(sizeof(xmlTtag));
    assert(tag != NULL);
    memFclear(tag, sizeof(xmlTtag));
    return tag;
}

static void xmlFtag_free(xmlTtag * tag)
{
    assert(tag != NULL);
    if (tag->name != NULL)
        free(tag->name);
    if (tag->value != NULL)
        free(tag->value);
    free(tag);
}

static void xmlFtag_addchild(xmlTtag * parent, xmlTtag * child)
{
    xmlTtag *p;

    assert(parent != NULL);
    assert(child != NULL);

    if (parent->linear_child == NULL) {
        parent->linear_child = child;
    } else {
        for (p = parent->linear_child; p->linear_next != NULL;
             p = p->linear_next) {
            /* Do Nothing */
        }
        assert(p != NULL);
        p->linear_next = child;
    }
}

/* I really hope to whatever god is convinient that we do not have to change
 * anything in this function ever again -aleigh
 */
#define P_SEARCH 1              /* Searching for 1 */
#define P_TAG 2                 /* In <> tag, break > */
#define P_NAME 3                /* submode: copying tag name */
#define P_ARG 4                 /* submode: copying tag attributes */

static const char *xmlF_parse(const char *str, const char *level,
                              xmlTtag * parent, lstTset * my_arglist)
{
    const char *p;
    int mode = P_SEARCH;
    int submode = P_NAME;
    char buf[128];
    int buf_p = 0;
    char arglist[128];
    int arglist_p = 0;
    lstTset *argset = NULL;
    xmlTtag *my_tag = xmlFtag_init();

    assert(str != NULL);
    assert(level != NULL);
    assert(parent != NULL);

    /* make sure that at least we have an empty arglist */
    arglist[0] = 0;

    my_tag->name = strFcopy_size(level, &my_tag->name_len);
    my_tag->attrib_list = my_arglist;

    xmlFtag_addchild(parent, my_tag);

    /*
     * When P_SEARCH is high the parser is searching for a tag, while copying
     * the value into buf. When P_TAG comes high, the parser is inside a tag
     * and is copying the tag name into buf.
     */

    for (p = str; *p != 0; p++) {
        switch (mode) {
        case P_SEARCH:
            if (*p == '<') {
                if (buf_p > 0) {
                    buf[buf_p] = 0;

                    if (strFis_blank(buf) == 0) {
                        my_tag->value = strFcopy(buf);
                        my_tag->value_len = strlen(buf);
                    }
                    buf_p = 0;
                }
                if (*(p + 1) == '!' && *(p + 2) == '-' && *(p + 3) == '-') {
                    /* It's <!--, so a comment. we now need to look for --> */
                    for (; *p != 0; p++) {
                        if (*p == '>' && *(p - 1) == '-'
                            && *(p - 2) == '-') {
                            break;
                        }
                    }
                    continue;
                } else {
                    mode = P_TAG;
                    break;
                }
            }
            /* Copy in value */
            buf[buf_p] = *p;
            buf_p++;
            break;
        case P_TAG:
            if (*p == ' ' && submode == P_NAME) {
                /* We were doign the name, now we have an arglist */
                /* Finish buf */
                buf[buf_p] = 0;
                buf_p = 0;
                submode = P_ARG;
                break;
            }
            if (*p == '>') {

                if (submode == P_NAME) {
                    /* we were copying the tags; so finish that */
                    buf[buf_p] = 0;
                    buf_p = 0;
                } else {
                    /* We were copying attributes; so finish that */
                    arglist[arglist_p] = 0;
                    arglist_p = 0;
                }

                if (buf[0] == '/') {

                    for (; *p != 0; p++)
                        if (*p == '>')
                            return p;
                    return NULL;
                }
                argset = strFattrib_parser(arglist);

                /* let's see if this tag closes itself */
                if (buf[0] != '/' && *(p - 1) == '/') {
                    xmlTtag *child = xmlFtag_init();

                    child->name = strFcopy_size(buf, &child->name_len);
                    child->attrib_list = argset;

                    /*
                     * add this new child to my_tag (which would be the
                     * parent
                     */
                    xmlFtag_addchild(my_tag, child);
                } else {
                    p = xmlF_parse(p + 1, (char *) &buf, my_tag, argset);
                    argset = NULL;
                    /* Short input stream - collapse */
                    if (p == NULL)
                        return NULL;
                }

                mode = P_SEARCH;
                submode = P_NAME;
                break;
            }
            if (submode == P_NAME) {
                /* We are copying the tag name */
                buf[buf_p] = *p;
                buf_p++;
            } else {
                /* We are copying the tag arglist */
                arglist[arglist_p] = *p;
                arglist_p++;
            }
            break;
        }
    }

    return NULL;
}

xmlTconfig *xmlFparse(const char *str)
{
    xmlTtag *tag = xmlFtag_init();
    xmlTconfig *config = xmlFconfig_init();

    assert(str != NULL);

    tag->name = strFcopy("__base");
    tag->name_len = strlen("__base");

    xmlF_parse(str, "__base_config", tag, NULL);

    config->tags = tag->linear_child->linear_child;

    xmlFtag_free(tag->linear_child);
    xmlFtag_free(tag);

    return config;
}

xmlTconfig *xmlFload_file(const char *filename)
{
    char *config;
    size_t config_len;
    xmlTconfig *c;
    dynTstring *clean;

    assert(filename != NULL);

    config = fleFload_file(filename, &config_len);

    if (config == NULL) {
        return NULL;
    }
    clean = strFcollapse_whitespace(config);
    assert(clean != NULL);

    c = xmlFparse(dynFgetstr(clean));

    dynFfree(clean);
    free(config);

    return c;
}

xmlTtag *xmlFbase_tag(const xmlTconfig * config)
{
    assert(config != NULL);

    return config->tags;
}

const char *xmlFtag_get_attrib_value(const xmlTtag * tag, const char *name)
{
    const lstTset *set;

    assert(tag != NULL);
    assert(name != NULL);

    set = xmlFtag_get_attribs(tag);

    if (set != NULL)
        return lstFset_get(set, name);
    else
        return NULL;
}


lstTset *xmlFcreate_list(const xmlTtag * tag, const char *tagname,
                         const char *attribname)
{
    lstTset *list;
    xmlTiterator *tags;
    const xmlTtag *t;
    const char *key, *val;

    assert(tag != NULL);
    assert(tagname != NULL);
    assert(attribname != NULL);

    tags = xmlFiterator_init(tag);

    assert(tags != NULL);

    list = lstFset_init();

    while ((t = xmlFiterator_next_name(tags, tagname)) != NULL) {
        key = xmlFtag_get_attrib_value(t, attribname);
        if (key == NULL)
            continue;
        val = xmlFtag_get_value_str(t);
        if (val == NULL)
            continue;
        lstFset_add(list, key, val);
    }

    return list;
}

const char *xmlFfirst_child_value_str (const xmlTtag *tag, const char *name) {
    size_t unused;

    assert(tag!=NULL);
    assert(name!=NULL);

    return (xmlFfirst_child_value (tag, name, &unused));
}

const char *xmlFfirst_child_value(const xmlTtag *tag, const char *name, size_t *value_len) {
    const xmlTtag *child;

    assert(tag!=NULL);
    assert(name!=NULL);
    
    child=xmlFfind_first_child(tag,name);

    if(child==NULL) {
        return NULL;
    }

    return xmlFtag_get_value(child,value_len);
}
