/* This file is part of Eclat
Copyright (C) 2012 Sergey Poznyakoff
Eclat is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Eclat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Eclat. If not, see . */
#include
#include "libeclat.h"
#define NODEREF_TEXT 0x01 /* Text is being constructed */
struct eclat_partial_tree {
int flags;
struct grecs_txtacc *acc;
struct grecs_symtab *texttab;
struct grecs_list *stack; /* Node stack */
};
static void
pt_free_node(void *ptr)
{
struct grecs_node *node = ptr;
grecs_node_free(node);
}
struct eclat_partial_tree *
eclat_partial_tree_create()
{
struct eclat_partial_tree *pt = grecs_zalloc(sizeof(*pt));
pt->acc = grecs_txtacc_create();
pt->stack = grecs_list_create();
pt->stack->free_entry = pt_free_node;
return pt;
}
void
eclat_partial_tree_destroy(struct eclat_partial_tree *pt)
{
grecs_txtacc_free(pt->acc);
if (pt->texttab)
grecs_symtab_free(pt->texttab);
grecs_list_free(pt->stack);
free(pt);
}
struct grecs_node *
eclat_partial_tree_finish(struct eclat_partial_tree *pt)
{
struct grecs_node *root = grecs_node_create(grecs_node_root, NULL);
root->v.texttab = pt->texttab;
pt->texttab = NULL;
grecs_node_bind(root, grecs_list_pop(pt->stack), 1);
eclat_partial_tree_destroy(pt);
return root;
}
static char *
install_text(struct eclat_partial_tree *nref, const char *str)
{
struct grecs_syment key;
struct grecs_syment *ent;
int install = 1;
if (!nref->texttab) {
nref->texttab = grecs_symtab_create_default(
sizeof(struct grecs_syment));
if (!nref->texttab)
grecs_alloc_die();
}
key.name = (char*) str;
ent = grecs_symtab_lookup_or_install(nref->texttab, &key, &install);
if (!ent)
grecs_alloc_die();
return ent->name;
}
static struct grecs_node *
gettos(struct eclat_partial_tree *nref)
{
if (nref->stack->head)
return nref->stack->head->data;
return NULL;
}
void
eclat_partial_tree_data_handler(void *data, const XML_Char *s, int len)
{
struct eclat_partial_tree *noderef = data;
grecs_txtacc_grow(noderef->acc, s, len);
noderef->flags |= NODEREF_TEXT;
}
/* Start handler: create a node and push it on stack */
void
eclat_partial_tree_start_handler(void *data, const XML_Char *name,
const XML_Char **atts)
{
struct eclat_partial_tree *noderef = data;
struct grecs_node *node;
if (noderef->flags & NODEREF_TEXT) {
grecs_txtacc_free_string(noderef->acc,
grecs_txtacc_finish(noderef->acc, 0));
noderef->flags &= ~NODEREF_TEXT;
}
node = grecs_node_create((enum grecs_node_type)-1, NULL);
node->ident = grecs_strdup(name);
grecs_list_push(noderef->stack, node);
noderef->flags &= ~NODEREF_TEXT;
}
void
eclat_partial_tree_end_handler(void *data, const XML_Char *name)
{
struct eclat_partial_tree *noderef = data;
struct grecs_node *node = grecs_list_pop(noderef->stack);
struct grecs_node *tos = gettos(noderef);
if (noderef->flags & NODEREF_TEXT) {
char *s, *p;
grecs_value_t *ptr;
grecs_txtacc_grow_char(noderef->acc, 0);
s = grecs_txtacc_finish(noderef->acc, 1);
for (p = s; *p && isspace(*p); p++)
;
if (!*p) {
free(s);
if (node->type == -1)
node->type = grecs_node_block;
} else {
switch (node->type) {
default:
node->type = grecs_node_stmt;
ptr = grecs_malloc(sizeof(*ptr));
ptr->type = GRECS_TYPE_STRING;
ptr->v.string = s;
node->v.value = ptr;
break;
case grecs_node_root:
abort(); /* should not happen */
case grecs_node_stmt:
case grecs_node_block:
/* FIXME: error message */
/* ignoring additional textual data */
free(s);
}
}
noderef->flags &= ~NODEREF_TEXT;
}
if (!tos)
grecs_list_push(noderef->stack, node);
else {
switch (tos->type) {
default:
tos->type = grecs_node_block;
break;
case grecs_node_root:
abort(); /* should not happen */
case grecs_node_stmt:
/* FIXME: error message */
/* ignoring additional textual data */
grecs_value_free(tos->v.value);
tos->type = grecs_node_block;
break;
}
grecs_node_bind(tos, node, 1);
}
}