#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <expat.h>

#include "rpmbuild.h"

#include "xmlparse.h"
#include "xmlmisc.h"
#include "xmlverify.h"
#include "xmlstruct.h"

void handleXMLStartLevel0(t_structXMLParse* pParse)
{
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_SPEC:
			pParse->m_pSpec = newXMLSpec(pParse->m_pAttrs, pParse->m_szXMLFile);
			break;
		default:
			fprintf(stderr, "warning: %s(%d): Ignoring tag \"%s\" on level %d. (Function not implemented)\n",
				pParse->m_szXMLFile,
				XML_GetCurrentLineNumber(pParse->m_pParser),
				g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_szName,
				pParse->m_nLevel);
			pParse->m_nWarnings++;
			break;
	}
}

/*f (!strcasecmp(szName, "pre")) {
			pPkg = getLastXMLPackage(pParse->m_pSpec->m_pPackages);
			pPkg->m_pPre = newXMLScript(attrToStruct(szaAttrs, &(pParse->m_pAttrs)));
		}*/
void handleXMLStartLevel1(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_MACRO:
			addXMLMacro(pParse->m_pAttrs, &(pSpec->m_pMacros));
			break;
		case TAGVAL_SOURCE:
			if (pSpec->m_pSources)
				addXMLSource(pParse->m_pAttrs, &(pSpec->m_pSources));
			else {
				if ((addXMLSource(pParse->m_pAttrs, &(pSpec->m_pSources))) &&
				    (pSpec->m_pSources->m_szDirectory)) {
					newStr(pSpec->m_pSources->m_szDirectory,
					       (char**)&(pSpec->m_szBuildSubdir));
				}
			}
			break;
		case TAGVAL_PATCH:
			addXMLSource(pParse->m_pAttrs, &(pSpec->m_pPatches));
			break;
		case TAGVAL_PACKAGE:
			addXMLPackage(pParse->m_pAttrs, &(pSpec->m_pPackages));
			break;
		case TAGVAL_PREP:
			pSpec->m_pPrep = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_BUILD:
			pSpec->m_pBuild = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_INSTALL:
			pSpec->m_pInstall = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_CLEAN:
			pSpec->m_pClean = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_BUILDREQUIRES:
		case TAGVAL_CHANGELOG:
			// we don't need to do anything
			break;
		default:
			fprintf(stderr, "warning: %s(%d): Ignoring tag \"%s\" on level %d. (Function not implemented)\n",
				pParse->m_szXMLFile,
				XML_GetCurrentLineNumber(pParse->m_pParser),
				g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_szName,
				pParse->m_nLevel);
			pParse->m_nWarnings++;
			break;
	}
}

void handleXMLStartLevel2(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;
	t_structXMLSource* pSource = NULL;
	t_structXMLPackage* pPkg = NULL;
	t_structXMLScript* pScript = NULL;
	t_structXMLAttr* pAttr = NULL;
	StringBuf pSb = NULL;
	int nNum = 0;
	int nLevel = 0;
	char* szTmp = NULL;
	char* szPath = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_SOURCEMIRROR:
			pSource = getLastXMLSource(pSpec->m_pSources);
			addXMLMirror(pParse->m_pAttrs, &(pSource->m_pMirrors));
			break;
		case TAGVAL_PATCHMIRROR:
			pSource = getLastXMLSource(pSpec->m_pPatches);
			addXMLMirror(pParse->m_pAttrs, &(pSource->m_pMirrors));
			break;
		case TAGVAL_BUILDREQUIRE:
			addXMLRequire(pParse->m_pAttrs, &(pSpec->m_pBuildRequires));
			break;
		case TAGVAL_CHANGES:
			addXMLChanges(pParse->m_pAttrs, &(pSpec->m_pChangelog));
			break;
		case TAGVAL_PRE:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			pPkg->m_pPre = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_POST:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			pPkg->m_pPost = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_PREUN:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			pPkg->m_pPreUn = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_POSTUN:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			pPkg->m_pPostUn = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_VERIFY:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			pPkg->m_pVerify = newXMLScripts(pParse->m_pAttrs);
			break;
		case TAGVAL_FILES:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			pPkg->m_pFiles = newXMLFiles(pParse->m_pAttrs);
			break;
		case TAGVAL_SCRIPT: // prep, build, install, clean
			switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel-1]].m_nValue) {
				case TAGVAL_PREP:
					addXMLScript(pParse->m_pAttrs,
						     &(pSpec->m_pPrep->m_pScripts));
					break;
				case TAGVAL_BUILD:
					addXMLScript(pParse->m_pAttrs,
						     &(pSpec->m_pBuild->m_pScripts));
					break;
				case TAGVAL_INSTALL:
					addXMLScript(pParse->m_pAttrs,
						     &(pSpec->m_pInstall->m_pScripts));
					break;
				case TAGVAL_CLEAN:
					addXMLScript(pParse->m_pAttrs,
						     &(pSpec->m_pClean->m_pScripts));
					break;
				default:
					break;
			}
			break;
		case TAGVAL_SETUPMACRO:
			pScript = addXMLScript(NULL, &(pSpec->m_pPrep->m_pScripts));
			if ((pAttr = getXMLAttr("source", pParse->m_pAttrs)))
				attrSetInt(pAttr, "source", &nNum);
			if ((pAttr = getXMLAttr("path", pParse->m_pAttrs)))
				attrSetStr(pAttr, "path", &szPath);
			else
				newStrEx(pParse->m_pSpec->m_szBuildSubdir, &szPath);
			if ((pSource = getXMLSource(nNum, pSpec->m_pSources))) {
				nNum = strlen(pSource->m_szName);
				szTmp = pSource->m_szName+(nNum-3);
				pSb = newStringBuf();
				nNum = 1;
				if (!strcasecmp(szTmp, "bz2")) {
					szTmp = NULL;
					newStrEx("%{_bzip2bin} -dc ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				else if (!strcasecmp(szTmp, ".gz")) {
					szTmp = NULL;
					newStrEx("%{_gzipbin} -dc ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				else if (!strcasecmp(szTmp, "tgz")) {
					szTmp = NULL;
					newStrEx("%{_gzipbin} -dc ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				else if (!strcasecmp(szTmp, "zip")) {
					szTmp = NULL;
					newStrEx("%{_unzipbin} ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
					nNum = 0;
				}
				else if (!strcasecmp(szTmp, "tar")) {
					szTmp = NULL;
					newStrEx("cat ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				else {
					fprintf(stderr, "error: %s(%d): Invalid compressed format for source in \"setup\" script.\n",
						pParse->m_szXMLFile,
						XML_GetCurrentLineNumber(pParse->m_pParser));
					pParse->m_nErrors++;
					freeStr(&szPath);
					freeStringBuf(pSb);
					return;
				}
				if (nNum && pParse->m_nVerbose)
					appendStringBuf(pSb, " | tar -xvvf -");
				else if (nNum)
					appendStringBuf(pSb, " | tar -xf -");
				appendLineStringBuf(pSb, "");
				appendLineStringBuf(pSb, "STATUS=$?");
				appendLineStringBuf(pSb, "if [ $STATUS -ne 0 ]; then");
				appendLineStringBuf(pSb, "    exit $STATUS");
				appendLineStringBuf(pSb, "fi");
				appendStringBuf(pSb, "cd ");
				appendStringBuf(pSb, szPath);
				appendLineStringBuf(pSb, "");
				newStr(getStringBuf(pSb), (char**)&(pScript->m_szEntry));
				freeStringBuf(pSb);
				freeStr(&szTmp);
				freeStr(&szPath);
			}
			else {
				freeStr(&szPath);
				fprintf(stderr, "error: %s(%d): Invalid \"setup\" script. Source not available.\n",
					pParse->m_szXMLFile,
					XML_GetCurrentLineNumber(pParse->m_pParser));
				pParse->m_nErrors++;
			}
			break;
		case TAGVAL_PATCHMACRO:
			pScript = addXMLScript(NULL, &(pSpec->m_pPrep->m_pScripts));
			if ((pAttr = getXMLAttr("patch", pParse->m_pAttrs)))
				attrSetInt(pAttr, "patch", &nNum);
			if ((pAttr = getXMLAttr("path", pParse->m_pAttrs)))
				attrSetStr(pAttr, "path", &szPath);
			else
				newStrEx(pParse->m_pSpec->m_szBuildSubdir, &szPath);
			if ((pAttr = getXMLAttr("level", pParse->m_pAttrs)))
				attrSetInt(pAttr, "level", &nLevel);
			if ((pSource = getXMLSource(nNum, pSpec->m_pPatches))) {
				pSb = newStringBuf();
				newStrEx("cd %{_builddir}", &szTmp);
				appendLineStringBuf(pSb, szTmp);
				freeStr(&szTmp);
				appendStringBuf(pSb, "cd ");
				appendStringBuf(pSb, szPath);
				appendLineStringBuf(pSb, "");
				nNum = strlen(pSource->m_szName);
				szTmp = pSource->m_szName+(nNum-3);
				nNum = 1;
				if (!strcasecmp(szTmp, "bz2")) {
					szTmp = NULL;
					newStrEx("%{_bzip2bin} -dc ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				else if (!strcasecmp(szTmp, ".gz")) {
					szTmp = NULL;
					newStrEx("%{_gzipbin} -dc ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				else {
					szTmp = NULL;
					newStrEx("cat ", &szTmp);
					appendStringBuf(pSb, szTmp);
					appendStringBuf(pSb, pSource->m_szName);
				}
				freeStr(&szTmp);
				szTmp = malloc(strlen(" > patch -pN1234567890 -s ")+strlen(pSource->m_szName)+1);
				sprintf(szTmp, " | patch -Np%d -s", nLevel);
				appendStringBuf(pSb, szTmp);
				appendLineStringBuf(pSb, "");
				appendLineStringBuf(pSb, "STATUS=$?");
				appendLineStringBuf(pSb, "if [ $STATUS -ne 0 ]; then");
				appendLineStringBuf(pSb, "    exit $STATUS");
				appendLineStringBuf(pSb, "fi");
				appendLineStringBuf(pSb, "");
				newStr(getStringBuf(pSb), (char**)&(pScript->m_szEntry));
				freeStringBuf(pSb);
				freeStr(&szTmp);
				freeStr(&szPath);
			}
			else {
				freeStr(&szPath);
				fprintf(stderr, "error: %s(%d): Invalid \"patch\" script. Patch not available.\n",
					pParse->m_szXMLFile,
					XML_GetCurrentLineNumber(pParse->m_pParser));
				pParse->m_nErrors++;
			}
			break;
		case TAGVAL_SUMMARY:
		case TAGVAL_DESCRIPTION:
		case TAGVAL_REQUIRES:
		case TAGVAL_PROVIDES:
		case TAGVAL_OBSOLETES:
		case TAGVAL_CONFLICTS:
			// we don't need to do anything
			break;
		default:
			fprintf(stderr, "warning: %s(%d): Ignoring tag \"%s\" on level %d. (Function not implemented)\n",
				pParse->m_szXMLFile,
				XML_GetCurrentLineNumber(pParse->m_pParser),
				g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_szName,
				pParse->m_nLevel);
			pParse->m_nWarnings++;
			break;
	}
}

void handleXMLStartLevel3(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;
	t_structXMLPackage* pPkg = NULL;
	t_structXMLChanges* pChanges = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_REQUIRE:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			addXMLRequire(pParse->m_pAttrs, &(pPkg->m_pRequires));
			break;
		case TAGVAL_PROVIDE:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			addXMLRequire(pParse->m_pAttrs, &(pPkg->m_pProvides));
			break;
		case TAGVAL_OBSOLETE:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			addXMLRequire(pParse->m_pAttrs, &(pPkg->m_pObsoletes));
			break;
		case TAGVAL_CONFLICT:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			addXMLRequire(pParse->m_pAttrs, &(pPkg->m_pConflicts));
			break;
		case TAGVAL_CHANGE:
			pChanges = getLastXMLChanges(pSpec->m_pChangelog);
			addXMLChange(pParse->m_pAttrs, &(pChanges->m_pChanges));
			break;
		case TAGVAL_SCRIPT: // pre, post, ...
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel-1]].m_nValue) {
				case TAGVAL_PRE:
					addXMLScript(pParse->m_pAttrs,
						     &(pPkg->m_pPre->m_pScripts));
					break;
				case TAGVAL_POST:
					addXMLScript(pParse->m_pAttrs,
						     &(pPkg->m_pPost->m_pScripts));
					break;
				case TAGVAL_PREUN:
					addXMLScript(pParse->m_pAttrs,
						     &(pPkg->m_pPreUn->m_pScripts));
					break;
				case TAGVAL_POSTUN:
					addXMLScript(pParse->m_pAttrs,
						     &(pPkg->m_pPostUn->m_pScripts));
					break;
				case TAGVAL_VERIFY:
					addXMLScript(pParse->m_pAttrs,
						     &(pPkg->m_pVerify->m_pScripts));
					break;
				default:
					break;
			}
			break;
		default:
			fprintf(stderr, "warning: %s(%d): Ignoring tag \"%s\" on level %d. (Function not implemented)\n",
				pParse->m_szXMLFile,
				XML_GetCurrentLineNumber(pParse->m_pParser),
				g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_szName,
				pParse->m_nLevel);
			pParse->m_nWarnings++;
			break;
	}
}

void startXMLCB(void* pData,
		const XML_Char* szName,
		const XML_Char** szaAttrs)
{
	t_structXMLParse* pParse = NULL;
	int nTagPos;

	if (!(pParse = (t_structXMLParse*)pData))
		return;

	if ((nTagPos = verifyXMLPath(szName, pParse)) == -1) {
		pParse->m_nLevel++;
		return;
	}

	if (verifyXMLAttrs(szaAttrs, pParse, nTagPos) == -1) {
		pParse->m_nLevel++;
		return;
	}

	pParse->m_naTree[pParse->m_nLevel] = nTagPos;
	switch (pParse->m_nLevel) {
		case 0:
			handleXMLStartLevel0(pParse);
			break;
		case 1:
			handleXMLStartLevel1(pParse);
			break;
		case 2:
			handleXMLStartLevel2(pParse);
			break;
		case 3:
			handleXMLStartLevel3(pParse);
			break;
		default:
			fprintf(stderr, "warning: %s(%d): XML tag nesting of %d levels not handled Ignoring tag \"%s\".\n",
				pParse->m_szXMLFile,
				XML_GetCurrentLineNumber(pParse->m_pParser),
				pParse->m_nLevel,
				szName);
			pParse->m_nWarnings++;
			break;
	}

	pParse->m_nLevel++;
	pParse->m_nLastGoodLevel = pParse->m_nLevel;
	return;
}

void handleXMLEndLevel0(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		default:
			break;
	}
}

void handleXMLEndLevel1(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;
	t_structXMLMacro* pMacro = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_MACRO:
			pMacro = getLastXMLMacro(pSpec->m_pMacros);
			newStr(pParse->m_szValue, &(pMacro->m_szValue));
			break;
		default:
			break;
	}
}

void handleXMLEndLevel2(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;
	t_structXMLPackage* pPkg = NULL;
	t_structXMLScript* pScript = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_SUMMARY:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			newStrEx(pParse->m_szValue, &(pPkg->m_szSummary));
			break;
		case TAGVAL_DESCRIPTION:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			newStrEx(pParse->m_szValue, &(pPkg->m_szDescription));
			break;
		case TAGVAL_SCRIPT:
			switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel-1]].m_nValue) {
				case TAGVAL_PREP:
					pScript = getLastXMLScript(pSpec->m_pPrep->m_pScripts);
					break;
				case TAGVAL_BUILD:
					pScript = getLastXMLScript(pSpec->m_pBuild->m_pScripts);
					break;
				case TAGVAL_INSTALL:
					pScript = getLastXMLScript(pSpec->m_pInstall->m_pScripts);
					break;
				case TAGVAL_CLEAN:
					pScript = getLastXMLScript(pSpec->m_pClean->m_pScripts);
					break;
				default:
					break;
			}
			if (pScript)
				newStrEx(pParse->m_szValue, (char**)&(pScript->m_szEntry));
			break;
		default:
			break;
	}
}

void handleXMLEndLevel3(t_structXMLParse* pParse)
{
	t_structXMLSpec* pSpec = NULL;
	t_structXMLPackage* pPkg = NULL;
	t_structXMLScript* pScript = NULL;

	pSpec = pParse->m_pSpec;
	switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel]].m_nValue) {
		case TAGVAL_SCRIPT:
			pPkg = getLastXMLPackage(pSpec->m_pPackages);
			switch (g_pXMLTags[pParse->m_naTree[pParse->m_nLevel-1]].m_nValue) {
				case TAGVAL_PRE:
					pScript = getLastXMLScript(pPkg->m_pPre->m_pScripts);
					break;
				case TAGVAL_POST:
					pScript = getLastXMLScript(pPkg->m_pPost->m_pScripts);
					break;
				case TAGVAL_PREUN:
					pScript = getLastXMLScript(pPkg->m_pPreUn->m_pScripts);
					break;
				case TAGVAL_POSTUN:
					pScript = getLastXMLScript(pPkg->m_pPostUn->m_pScripts);
					break;
				case TAGVAL_VERIFY:
					pScript = getLastXMLScript(pPkg->m_pVerify->m_pScripts);
					break;
				default:
					break;
			}
			if (pScript)
				newStrEx(pParse->m_szValue,
					 (char**)&(pScript->m_szEntry));
			break;
		default:
			break;
	}
}

void endXMLCB(void* pData,
	      const XML_Char* szName)
{
	t_structXMLParse* pParse = NULL;

	if ((pParse = (t_structXMLParse*)pData)) {
		pParse->m_nLevel--;
		switch (pParse->m_nLevel) {
			case 0:
				handleXMLEndLevel0(pParse);
				break;
			case 1:
				handleXMLEndLevel1(pParse);
				break;
			case 2:
				handleXMLEndLevel2(pParse);
				break;
			case 3:
				handleXMLEndLevel3(pParse);
				break;
			default:
				break;
		}
		freeStr(&(pParse->m_szValue));
	}
}

void dataXMLCB(void* pData,
	       const XML_Char* szValue,
	       int nLen)
{
	t_structXMLParse* pParse = NULL;
	char* szVal = NULL;
	char* szTmp = NULL;

	if ((pParse = (t_structXMLParse*)pData)) {
		szVal = malloc(nLen+2);
		szVal[nLen] = '\0';
		snprintf(szVal, nLen+1, "%s", szValue);
		if (strlen(szVal)) {
			while ((szTmp = strchr(szVal, '\t')))
				*szTmp = ' ';
			if (pParse->m_szValue) {
				szTmp = malloc(strlen(szVal)+strlen(pParse->m_szValue)+1);
				sprintf(szTmp, "%s%s", pParse->m_szValue, szVal);
				newStr(szTmp, &(pParse->m_szValue));
				free(szTmp);
			}
			else
				newStr(szVal, &(pParse->m_szValue));
		}
		free(szVal);
	}
}

t_structXMLSpec* parseXMLSpec(const char* szFile, int nVerbose)
{
	XML_Parser pParser = NULL;
	t_structXMLParse* pSParse = NULL;
	t_structXMLSpec* pSpec = NULL;
	FILE* fIn = NULL;
	int nLen = 0;
	char szBuffer[1024+1];

	pSParse = malloc(sizeof(t_structXMLParse));
	pSParse->m_szXMLFile = (char*)szFile;
	pSParse->m_szValue = NULL;
	pSParse->m_nLevel = 0;
	pSParse->m_nLastGoodLevel = 0;
	pSParse->m_nWarnings = 0;
	pSParse->m_nErrors = 0;
	pSParse->m_pSpec = NULL;
	pSParse->m_pAttrs = NULL;
	pSParse->m_nVerbose = nVerbose;

	if ((fIn = fopen(szFile, "r"))) {
		if ((pParser = XML_ParserCreate(NULL))) {
			pSParse->m_pParser = pParser;
			XML_SetStartElementHandler(pParser, &startXMLCB);
			XML_SetEndElementHandler(pParser, &endXMLCB);
			XML_SetCharacterDataHandler(pParser, &dataXMLCB);
			XML_SetUserData(pParser, pSParse);
			while (!feof(fIn)) {
				nLen = fread(szBuffer, sizeof(char), 1024, fIn);
				if (!XML_Parse(pParser, szBuffer, nLen, 0)) {
					fprintf(stderr, "fatal: %s(%d): %s \n", szFile,
							XML_GetCurrentLineNumber(pParser),
							XML_ErrorString(XML_GetErrorCode(pParser)));
					pSParse->m_nErrors++;
				}
			}
			if (nVerbose)
				printf("+ XML parse completed with %d error(s), %d warning(s).\n", pSParse->m_nErrors, pSParse->m_nWarnings);
			XML_Parse(pParser, szBuffer, 0, 1);
			XML_ParserFree(pParser);
		}
		fclose(fIn);
	}
	else {
		free(pSParse);
		fprintf(stderr, "fatal: Open of \"%s\" failed.\n", szFile);
		return NULL;
	}

	freeXMLAttr(&(pSParse->m_pAttrs));
	if (pSParse->m_nErrors)
		freeXMLSpec(&(pSParse->m_pSpec));

	pSpec = pSParse->m_pSpec;
	free(pSParse);
	return pSpec;
}
