/*
 * XML Catalog Manager (xmlcatmgr)
 * $Id: xmlnode.c,v 1.5 2004/01/23 13:19:28 jmmv Exp $
 *
 * Copyright (c) 2003, 2004 Julio M. Merino Vidal.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of the author nor the names of contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * This file implements several functions to handle 'xmlattr' and
 * 'xmlnode' objects.
 */

#include "system.h"

#ifndef lint
__RCSID("$Id: xmlnode.c,v 1.5 2004/01/23 13:19:28 jmmv Exp $");
#endif

#include "mem.h"
#include "linklist.h"
#include "xmlnode.h"

/* --------------------------------------------------------------------- */

/*
 * Creates a new xmlattr object.  'name' and 'value' must be pointers to
 * memory regions allocated by malloc(3), as they will be free(3)'d when
 * calling xmlattr_free.
 */
struct xmlattr *
xmlattr_new(char *name, char *value)
{
    struct xmlattr *xa;

    assert(name != NULL && value != NULL);

    xa = (struct xmlattr *)malloc(sizeof(struct xmlattr));
    if (xa != NULL) {
        xa->xa_name = name;
        xa->xa_value = value;
    }

    return xa;
}

/* --------------------------------------------------------------------- */

/*
 * Destroys an xmlattr object.
 */
void
xmlattr_free(struct xmlattr *xa)
{
    assert(xa != NULL);

    free(xa->xa_name);
    free(xa->xa_value);
    free(xa);
}

/* --------------------------------------------------------------------- */

/*
 * Dumps an xmlattr object to a stream.
 */
bool
xmlattr_write(struct xmlattr *xa, FILE *f)
{
    assert(xa != NULL && f != NULL);

    return fprintf(f, "%s=\"%s\"", xa->xa_name, xa->xa_value) != -1;
}

/* --------------------------------------------------------------------- */

/*
 * Creates a new xmlnode object.  'tag' must be a pointer to a memory
 * region allocated by malloc(3), as it will be free(3)'d by xmlnode_free.
 */
struct xmlnode *
xmlnode_new(int type, char *tag)
{
    struct xmlnode *xn;

    assert(tag != NULL);

    xn = (struct xmlnode *)malloc(sizeof(struct xmlnode));
    if (xn != NULL) {
        xn->xn_type = type;
        xn->xn_tag = tag;
        xn->xn_text = NULL;
        xn->xn_parent = NULL;
        LINKLIST_INIT(&xn->xn_attrs);
        LINKLIST_INIT(&xn->xn_childs);
    }

    return xn;
}

/* --------------------------------------------------------------------- */

/*
 * Destroys an xmlnode object.  This also destroys all related attributes
 * and all attached childs (recursively).
 */
void
xmlnode_free(struct xmlnode *xn)
{
    assert(xn != NULL);

    free(xn->xn_tag);

    switch (xn->xn_type) {
    case XMLNODE_TYPE_COMMENT:
    case XMLNODE_TYPE_TEXT:
        if (xn->xn_text != NULL)
            free(xn->xn_text);
        break;
    case XMLNODE_TYPE_ELEMENT:
    case XMLNODE_TYPE_ROOT:
        if (!LINKLIST_EMPTY(&xn->xn_attrs)) {
            struct xmlattr *xa;

            xa = LINKLIST_FIRST(&xn->xn_attrs);
            while (xa != NULL) {
                struct xmlattr *tmp;

                tmp = LINKLIST_NEXT(xa);
                xmlattr_free(xa);
                xa = tmp;
            }
        }

        if (!LINKLIST_EMPTY(&xn->xn_childs)) {
            struct xmlnode *xn2;

            xn2 = LINKLIST_FIRST(&xn->xn_childs);
            while (xn2 != NULL) {
                struct xmlnode *tmp;

                tmp = LINKLIST_NEXT(xn2);
                xmlnode_free(xn2);
                xn2 = tmp;
            }
        }
        break;
    default:
        assert(false);
    }

    free(xn);
}

/* --------------------------------------------------------------------- */

/*
 * Search for an attribute in the given xmlnode, based on its name, and
 * return its value.
 */
char *
xmlnode_get_attr_value(struct xmlnode *xn, const char *name)
{
    struct xmlattr *xa;

    assert(xn != NULL && name != NULL);

    XMLNODE_FOREACH_ATTR(xa, xn) {
        if (strcmp(xa->xa_name, name) == 0)
            return xa->xa_value;
    }

    return NULL;
}

/* --------------------------------------------------------------------- */

/*
 * Set the text field associated to the node.  Only valid when the node
 * is a comment or a text node.  Any information previously stored in it
 * is discarded.
 */
void
xmlnode_set_text(struct xmlnode *xn, char *text)
{
    assert(xn != NULL && (xn->xn_type == XMLNODE_TYPE_COMMENT ||
                          xn->xn_type == XMLNODE_TYPE_TEXT) && text != NULL);

    if (xn->xn_text != NULL)
        free(xn->xn_text);
    xn->xn_text = text;
}

/* --------------------------------------------------------------------- */

/*
 * Turn a regular element node into a root node.
 */
void
xmlnode_become_root(struct xmlnode *xn)
{
    assert(xn != NULL && xn->xn_type == XMLNODE_TYPE_ELEMENT);

    xn->xn_type = XMLNODE_TYPE_ROOT;
}

/* --------------------------------------------------------------------- */

/*
 * Dump the given node to a stream.  If it is an element node or a root
 * node, its attributes and childs are also dumped recursively.
 */
bool
xmlnode_write(struct xmlnode *xn, FILE *f)
{
    bool res;
    struct xmlattr *aiter;
    struct xmlnode *niter;

    assert(xn != NULL && f != NULL);

    res = true;

    switch (xn->xn_type) {
    case XMLNODE_TYPE_COMMENT:
        res &= fprintf(f, "<!--%s-->\n", xn->xn_text) != -1;
        break;
    case XMLNODE_TYPE_TEXT:
        res &= fprintf(f, "%s\n", xn->xn_text) != -1;
        break;
    case XMLNODE_TYPE_ELEMENT:
    case XMLNODE_TYPE_ROOT:
        res &= fprintf(f, "<%s", xn->xn_tag) != -1;

        XMLNODE_FOREACH_ATTR(aiter, xn) {
            res &= fprintf(f, " ") != -1;
            res &= xmlattr_write(aiter, f);
        }

        if (LINKLIST_EMPTY(&(xn->xn_childs)))
            res &= fprintf(f, " />\n") != -1;
        else {
            res &= fprintf(f, ">\n") != -1;

            XMLNODE_FOREACH_CHILD(niter, xn) {
                res &= xmlnode_write(niter, f);
            }

            res &= fprintf(f, "</%s>\n", xn->xn_tag) != -1;
        }
        break;
    default:
        assert(false);
    }

    return res;
}

/*
 * Local Variables: ***
 * mode: c ***
 * c-file-style: "stroustrup" ***
 * End: ***
 * vim: syntax=c:expandtab:shiftwidth=4:softtabstop=4
 */
