/*  Copyright (C) 2001-2002  Kenichi Suto
 *
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/*
#include <glib.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
*/

#include "defs.h"
#include "xml.h"
#include "xmlinternal.h"

static void xml_save_file_internal(GNode *node, gpointer data);
static void xml_print_tree_internal(GNode *node, gpointer data);
static void xml_destroy_tree_internal(GNode *node, gpointer data);

struct special_char {
	guchar special;
	gchar  *encoded;
};

static struct special_char special[] = {{'&', "&amp;"}, {'\"', "&quot;"}, {'<', "&lt;"}, {'>', "&gt;"}, {0, NULL}};



xmlDoc *xml_doc_new()
{

	GNode *root;
	xmlDoc *doc;
	NODE_DATA *node_data;

	doc = (xmlDoc *)calloc(sizeof(xmlDoc), 1);
	doc->version = NULL;
	doc->encoding = NULL;
	
	node_data = (NODE_DATA *)calloc(sizeof(NODE_DATA), 1);
	node_data->name = NULL;
	node_data->content = NULL;
	node_data->attr = NULL;
	node_data->depth = 0;
	node_data->doc = doc;
	root = g_node_new((gpointer)node_data);

	doc->root = root;

	return(doc);
}

xmlDoc *xml_parse_file(gchar *filename)
{
	unsigned int l;
	GNode *root;
	xmlDoc *doc;
	NODE_DATA *node_data;
	char buff[65535];
	FILE *fp;

	fp = fopen(filename, "r");
	if(fp == NULL){
		perror("fopen");
		return(NULL);
	}

	memset (buff, 0, sizeof(buff)) ;
	//$B%5%$%:$O:GBg(B65535$B%P%$%H(B
	l = fread(buff, 1, sizeof(buff), fp);
	fclose(fp);
	if(l <= 0){
		return(NULL);
	}
	
	doc = (xmlDoc *)calloc(sizeof(xmlDoc), 1);
	doc->version = NULL;
	doc->encoding = NULL;
	
	node_data = (NODE_DATA *)calloc(sizeof(NODE_DATA), 1);
	node_data->name = NULL;
	node_data->content = NULL;
	node_data->attr = NULL;
	node_data->depth = 0;
	node_data->doc = doc;
	root = g_node_new((gpointer)node_data);

	doc->root = root;

	parse_buffer(root, buff, l);

	return(doc);
}

void tag_indent(FILE *fp, int l){
	int i;
	for(i=0;i<l-1;i++)
		fprintf(fp, "  ");
}

xmlResult xml_save_file(gchar *filename, xmlDoc *doc)
{
	FILE *fp;

	fp = fopen(filename, "w");
	if(fp == NULL){
		perror("fopen");
		return(XML_NG);
	}

	fprintf(fp, "<?xml");
	if(doc->version)
		fprintf(fp, " version=\"%s\"", doc->version);
	if(doc->encoding)
		fprintf(fp, " encoding=\"%s\"", doc->encoding);
	fprintf(fp, "?>\n");

	xml_save_file_internal(doc->root, fp);

	fclose(fp);

	return(XML_OK);
}

static void xml_save_file_internal(GNode *node, gpointer data){
	FILE *fp;
	NODE_DATA *node_data;
	gchar *tmp_p;

	fp = (FILE *)data;
	node_data = (NODE_DATA *)(node->data);


	if(node_data->name){
		tag_indent(fp, node_data->depth);
		fprintf(fp, "<%s",node_data->name);
		if(node_data->attr != NULL){
			GList *list;
			NODE_ATTR *attr;
			list = g_list_first(node_data->attr);
			while(list){
				attr = (NODE_ATTR *)(list->data);
				tmp_p = special_to_encoded(attr->value);
				fprintf(fp, " %s=\"%s\"", attr->name, tmp_p);
				free(tmp_p);
				list = g_list_next(list);
			}
		}
		fprintf(fp, ">");

		if(G_NODE_IS_LEAF(node)){
			if (node_data->content != NULL){
				tmp_p = special_to_encoded(node_data->content);
				fprintf(fp, "%s", tmp_p);
				free(tmp_p);
			}
		} else {
			fprintf(fp, "\n");
			g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc)xml_save_file_internal, (gpointer)fp);
			tag_indent(fp, node_data->depth);
		}

		if(node_data->name)
			fprintf(fp, "</%s>\n",node_data->name);
	} else {
		g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc)xml_save_file_internal, (gpointer)fp);
	}



}

xmlResult xml_print_tree(xmlDoc *doc){
	xml_print_tree_internal((GNode *)doc->root, NULL);
	return(XML_OK);
}

static void print_indent(int l){
	int i;
	for(i=0;i<l;i++)
		printf("| ");
	printf("|- ");
}

static void print_indent2(int l){
	int i;
	for(i=0;i<l;i++)
		printf("| ");
	printf("|    ");
}

static void xml_print_tree_internal(GNode *node, gpointer data){
	NODE_DATA *node_data;

	node_data = (NODE_DATA *)(node->data);

	if(node_data->name){
		print_indent(node_data->depth);
		printf("%s", node_data->name);
		if(node_data->attr != NULL){
			GList *list;
			NODE_ATTR *attr;
			list = g_list_first(node_data->attr);
			while(list){
				attr = (NODE_ATTR *)(list->data);
				g_print(" %s=%s", attr->name, attr->value);
				list = g_list_next(list);
			}
		}
		g_print("\n");

		if(G_NODE_IS_LEAF(node)){
			print_indent2(node_data->depth);
			if(node_data->content != NULL){
				printf(">%s<\n", node_data->content);
			} else {
				printf("NULL\n");
			}
		} else {
			g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc)xml_print_tree_internal, (gpointer)NULL);
		}
	} else {
		g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc)xml_print_tree_internal, (gpointer)NULL);
	}
}


xmlNode *xml_add_child(xmlNode *parent, gchar *name, gchar *content){
	NODE_DATA *node_data;
	GNode *child;

	if(name == NULL)
		return(NULL);

	node_data = (NODE_DATA *)calloc(sizeof(NODE_DATA), 1);
	if(!node_data){
		return(NULL);
	}
	node_data->name = strdup(name);
	node_data->attr = NULL;
	node_data->depth = ((NODE_DATA *)(parent->data))->depth + 1;
	node_data->doc = ((NODE_DATA *)(parent->data))->doc;
	if(content == NULL)
		node_data->content = NULL;
	else
		node_data->content = strdup(content);

	child = g_node_new((gpointer)node_data);
	g_node_append(parent, child);

	return(child);
}

xmlNode *xml_get_child(xmlNode *node){
	return(node->children);
}

xmlNode *xml_get_next(xmlNode *node){
	return(node->next);
}

gchar *xml_get_content(xmlNode *node){
	if(((NODE_DATA *)(node->data))->content)
		return(((NODE_DATA *)(node->data))->content);
	else
		return(strdup(""));
}

gchar *xml_get_name(xmlNode *node){
	return(((NODE_DATA *)(node->data))->name);
}


static void xml_destroy_tree_internal(GNode *node, gpointer data){
	NODE_DATA *node_data;

	node_data = (NODE_DATA *)(node->data);

	if(G_NODE_IS_LEAF(node)){
		if(node_data->name)
			free(node_data->name);
		if(node_data->content)
			free(node_data->content);
		if(node_data->attr != NULL){
			GList *list;
			NODE_ATTR *attr;
			list = g_list_first(node_data->attr);
			while(list){
				attr = (NODE_ATTR *)(list->data);
				if(attr->name)
					free(attr->name);
				if(attr->value)
					free(attr->value);
				free(attr);
				list = g_list_next(list);
			}
		}
	} else {
		g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc)xml_destroy_tree_internal, (gpointer)NULL);
	}
}


xmlResult xml_destroy_document(xmlDoc *doc){
	xml_destroy_tree_internal(doc->root, NULL);
	g_node_destroy((GNode *)doc->root);
	free(doc->version);
	free(doc->encoding);
	free(doc);
	
	return(XML_OK);
}


gchar *special_to_encoded(gchar *text){
	gchar buff[65536];
	gchar *p;
	gint i;
	gint j;

	p = text;
	j = 0;

	while(*p){
		for(i=0; ; i ++){
			if(special[i].encoded == NULL) {
				buff[j] = *p;
				j++;
				break;
			}
			if(*p == special[i].special){
				strcpy(&buff[j], special[i].encoded);
				j += strlen(special[i].encoded);
				break;
			}
		}
		p++;
	}

	buff[j] = '\0';
	return(strdup(buff));
}

gchar *encoded_to_special(gchar *text){
	gchar buff[65536];
	gchar *p;
	gint i;
	gint j;

//	g_print("encoded_to_special\n");

	p = text;
	j = 0;

	while(*p){

		if(*p == '&'){
//			g_print("& found\n");
			for(i=0; ; i ++){
				if(special[i].encoded == NULL) {
					buff[j] = *p;
					j++;
					p++;
					break;
				}
				if(strstr(p, special[i].encoded) == p){
//					g_print("encoded %s found\n", special[i].encoded);
					buff[j] = special[i].special;
					j ++;
					p += strlen(special[i].encoded);
				}
			}
		} else {
			buff[j] = *p;
			j++;
			p++;
		}
	}

	buff[j] = '\0';
	return(strdup(buff));
}


gchar *xml_get_attr(xmlNode *node, gchar *name){

	GList *list;
	NODE_ATTR *attr;

	list = ((NODE_DATA *)(node->data))->attr;

	while(list){
		attr = (NODE_ATTR *)(list->data);
		if(strcmp(attr->name, name) == 0){
			return(attr->value);
		}
		list = g_list_next(list);
	}
	return(NULL);
}

xmlResult xml_set_attr(xmlNode *node, gchar *name, gchar *value){

	NODE_ATTR *attr;

	attr = (NODE_ATTR *)calloc(sizeof(NODE_ATTR), 1);
	attr->name = strdup(name);
	attr->value = strdup(value);

	((NODE_DATA *)(node->data))->attr  = g_list_append(((NODE_DATA *)(node->data))->attr, attr);

	return(XML_OK);
}


xmlResult parse_attribute(GNode *node, gchar *tag){
	gchar *p, *p2, *p3;
	NODE_ATTR *attr;
	gint end;
	gchar *tmp_val;

//	g_print("parse_attribute() start\n");
//	g_print("tag = %s\n", tag);

	p = strchr(tag, ' ');
	if(p == NULL){
//	g_print("parse_attribute() end1\n");
		return(XML_OK);
	}

	
	p++;

	while(1){
//		g_print("p = %s\n", p);
		p2 = strchr(p, '=');
		if(p2 == NULL){
//	g_print("parse_attribute() end2\n");
			return(XML_OK);
		}

		attr = (NODE_ATTR *)calloc(sizeof(NODE_ATTR), 1);
		attr->name = g_strndup(p, p2 - p);
		p2 ++;

		if(*p2 == '\"')
			p2++;

		p3 = p2;
		while(1){
			if((*p3 == ' ') || (*p3 == '\"')) {
				end = 0;
				break;
			}
			if((*p3 == '\0') || (*p3 == ' ') || (*p3 == '>') || (*p3 == '\"')) {
				end = 1;
				break;
			}
			p3 ++;
		}
		tmp_val = g_strndup(p2, p3 - p2);
		attr->value = encoded_to_special(tmp_val);
		free(tmp_val);
//		g_print("attr : %s = %s\n", attr->name, attr->value);
		((NODE_DATA *)node->data)->attr = g_list_append(((NODE_DATA *)node->data)->attr, attr);
		if(end) {
//	g_print("parse_attribute() end3\n");
			return(XML_OK);
		}
		else {
			p3 ++;
			p = p3 + 1;
		}
	}

//	g_print("parse_attribute() end4\n");

}

void parse_declaration(GNode *node, gchar *tag)
{
	gchar attr[512];
	xmlDoc *doc;

	g_assert(node != NULL);
	doc = ((NODE_DATA *)(node->data))->doc;
	g_assert(doc != NULL);
	get_attr(tag, "version", attr);
	doc->version = strdup(attr);
	get_attr(tag, "encoding", attr);
	doc->encoding = strdup(attr);

}

xmlResult parse_buffer(GNode *parent, gchar *text, guint length)
{
	gchar *p;
	gchar start_tag[512];
	gchar tag_name[512];
	gchar body[65536];
	gchar *content;
	gint  content_length;
	gint  body_length;
	GNode *node;
	gboolean no_end_tag;

//	g_print("parse_buffer() start\n");

/*
	{
		gchar *p;

		p = g_strndup(text, length);
pppppppp		g_print("text = %s\n", p);
		free(p);
	}
*/

	g_assert(text != NULL);

	body_length = 0;
	p = text;

	while((p - text) <  length){
		if(*p == '<'){
			if(body_length != 0){
//				((NODE_DATA *)(parent->data))->content = strdup(body);
				((NODE_DATA *)(parent->data))->content = encoded_to_special(body);
				body[0] = '\0';
				body_length = 0;
			}
			
			no_end_tag = FALSE;

			get_start_tag(p, start_tag);

			// <xxx/>$B$N>l9g$K$OBP1~$9$k%(%s%I%?%0$,$J$$(B
			if(start_tag[strlen(start_tag) - 1] == '/'){
				start_tag[strlen(start_tag) - 1] = '\0';
				no_end_tag = TRUE;
			}
			
			get_tag_name(start_tag, tag_name);

			if(start_tag[0] == '?'){
				if(start_tag[strlen(start_tag) - 1] == '?'){
					start_tag[strlen(start_tag) - 1] = '\0';
				}
				parse_declaration(parent, &start_tag[1]);
				skip_start_tag(&p, tag_name);
				continue;
			}

			node = xml_add_child(parent, tag_name, NULL);
			parse_attribute(node, start_tag);

			if(no_end_tag == FALSE){
				get_content(p, tag_name, &content, &content_length);
				parse_buffer(node, content, content_length);
				skip_end_tag(&p, tag_name);
			} else {
				skip_start_tag(&p, tag_name);
			}

		} else if (*p == '\n') {
			p++;
		} else {
			body[body_length] = *p;
			body_length ++;
			body[body_length] = '\0';
			p++;
		}

	}

	if(body_length != 0){
//		((NODE_DATA *)(parent->data))->content = strdup(body);
		((NODE_DATA *)(parent->data))->content = encoded_to_special(body);
	}

//	g_print("parse_buffer() end\n");

	return XML_OK;
}



syntax highlighted by Code2HTML, v. 0.9.1