/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/

#include "light_xml_parser.h"
#include <stdlib.h>
#include <string.h>

void free_xml_attr(XML_ATTR *attr){
	if(!attr)
		return;
	if(attr->szName)
		free(attr->szName);
	if(attr->szValue)
		free(attr->szValue);
	free(attr);
}

void free_xml_element(XML_ELEMENT *ele)
{
	XML_ATTR *attr,*nextAttr;
	if (!ele)
		return;
	for(attr = ele->attr; attr; attr=nextAttr){
		nextAttr = attr->pNext;
		free_xml_attr(attr);
	}
	if(ele->szName)
		free(ele->szName);
	if(ele->szText)
		free(ele->szText);
	free(ele);
}

char ReadAndFindFirstNotOf(FILE *fp, const char *szArg, char *szBuff, int nBuffSize){
	int len;
	char *pBuff;
	const char *p;
	char c;
	
	pBuff = szBuff;
	for(len=0;(c=fgetc(fp))!=EOF; ++len, ++pBuff){
		for(p=szArg; *p; ++p){
			if(c==*p){
				break;
			}
		}
		if(!*p){
			return c;
		}
		if(len > nBuffSize){
			fprintf(stderr, "not enough buff ...\n");
			return 0;
		}
		*pBuff = c;
	}
	*pBuff = '\0';
	return 0;
}

char ReadAndFindOneOf(FILE *fp, const char *szDelims, char *szBuff, int nBuffSize){
	int len;
	char *pBuff;
	const char *pDelim;
	char c;
	
	pBuff = szBuff;
	for(len=0;(c=fgetc(fp))!=EOF; ++len, ++pBuff){
		for(pDelim=szDelims; *pDelim; ++pDelim){
			if(c==*pDelim){
				*pBuff = '\0';
				return c;
			}
		}
		if(len > nBuffSize){
			fprintf(stderr, "not enough buff ...\n");
			return 0;
		}
		*pBuff = c;
	}
	*pBuff = '\0';
	return 0;
}

void XMLParse(FILE *fp, XML_EVENT *eve, void *Param){
	char c;
	char cDelim;
	XML_ELEMENT *pEle=NULL;
	XML_ATTR *pAttr=NULL;
	char *pBuff;
	char *szBuff=NULL;
	int len;
	int nBuff = 256*256*16;
	pBuff = szBuff;
	szBuff = (char*)calloc(sizeof(char)*nBuff, 1);
	while(!feof(fp)){
		if(!ReadAndFindOneOf(fp, "<", szBuff, nBuff))
			break;
		if(szBuff[0] != '\0' && eve->characters)
			Param = (*(eve->characters))(szBuff, Param);
		
		if(!(c=ReadAndFindOneOf(fp, "/> ", szBuff, nBuff)))
			break;
		
		if(szBuff[0] == '\0' && c=='/'){
			if(!(c=ReadAndFindOneOf(fp, " >", szBuff, nBuff)))
				break;
			if(eve->endElement)
				Param = (*(eve->endElement))(szBuff, Param);
			if(c != '>')
				ReadAndFindOneOf(fp,">", szBuff, nBuff);
			continue;
		}
		
		if(szBuff[0]=='?' || szBuff[0]=='!'){
			if(c != '>'){
				ReadAndFindOneOf(fp, ">", szBuff,nBuff);
			}
			continue;
		}
		
		pEle = (XML_ELEMENT *)calloc(sizeof(XML_ELEMENT),1);
		if(!pEle){
			fprintf(stderr, "not enough memory ...\n");
			goto end_xml_parse;	
		}
		pEle->szName = (char *)malloc(strlen(szBuff)+1);
		if(!pEle->szName){
			fprintf(stderr, "not enough memory ...\n");
			goto end_xml_parse;	
		}
		strcpy(pEle->szName, szBuff);
		
		while(c != '>'){
			pAttr = NULL;
			c = ReadAndFindFirstNotOf(fp, " \t\n\r", szBuff, nBuff);
			if(c==0 || c=='>' || c=='/'){
				break;
			}
			ungetc(c, fp);
			
			if(!ReadAndFindOneOf(fp, "=", szBuff, nBuff))
				break;
			
			++(pEle->nAttrCount);
			pAttr = calloc(sizeof(XML_ATTR),1);
			
			pAttr->szName = (char *)malloc(strlen(szBuff)+1);
			strcpy(pAttr->szName, szBuff);
			
			cDelim = fgetc(fp);
			for(pBuff=szBuff,len=0; ((c=fgetc(fp))!=EOF) && (c != cDelim); ++len, ++pBuff){
				if(len > nBuff){
					fprintf(stderr, "not enough buff ...\n");
					goto end_xml_parse;
				}
				*pBuff = c;
			}
			*pBuff = '\0';
			
			pAttr->szValue = (char *)malloc(strlen(szBuff)+1);
			strcpy(pAttr->szValue, szBuff);
			
			pAttr->pNext = pEle->attr;
			pEle->attr = pAttr;
		}

		if(eve->beginElement)
			Param = (*(eve->beginElement))(pEle,Param);
		if(c == '/'){
			c = fgetc(fp);
			if(eve->endElement)
				Param = (*(eve->endElement))(pEle->szName, Param);
		}
		free_xml_element(pEle);
		pEle = NULL;
	}

end_xml_parse:
	if(szBuff)
		free(szBuff);
	if(pEle)
		free_xml_element(pEle);
}