/*
 *  xml.c
 *
 *    Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
 */

#include <stdio.h>
#include "xml.h"

#ifndef NULL
#define NULL ((void *)0)
#endif

char	*xml_message[ ] = {
				"OK.",
				"Non ASCII character (%d) in XML. Line %d.",
				"Closing tag \"%s\" found, but no tag was open. Line %d.",
				"End Of XML, but Quote is still open.",
				"Found attribute without '=' in \"%s\". Line %d.",
				"Opening \"%s\" and closing \"%s\" do not match.",
				"Out of memory.",
				"Expected one top level element. Got %d.",
				"Expected hexadecimal value. Got \"%s\". Line %d.",
				"Found \"%s\" in <? ... ?>. Don't know what to do with that. Line %d.",
				NULL								
};

int	xml_lineNumber = 1;

#ifndef __KERNEL__
#ifdef XML_DEBUG
	#define DEBUG(x,s)	{ if( x<XML_DEBUG ) PRINTF( "XML: debug(%02d) %s\n", x, ( char* )s ); }
	#define DEBUG_S(x,s,s2)	{ if( x<XML_DEBUG ) PRINTF( "XML: debug(%02d) %s\n\"%s\"\n", x, ( char* )s, ( char* )s2 ); }
	#define DEBUG_C(x,s,c)	{ if( x<XML_DEBUG ) PRINTF( "XML: debug(%02d) %s\n\"%c\"\n", x, ( char* )s, ( char )c ); }
	#define DEBUG_D(x,s,d)	{ if( x<XML_DEBUG ) PRINTF( "XML: debug(%02d) %s\n\"%d\"\n", x, ( char* )s, d ); }
#else
	#define DEBUG(x,s)
	#define DEBUG_S(x,s,s2)
	#define DEBUG_C(x,s,c)
	#define DEBUG_D(x,s,d)
#endif
#else
	#define DEBUG(x,s)
	#define DEBUG_S(x,s,s2)
	#define DEBUG_C(x,s,c)
	#define DEBUG_D(x,s,d)
#endif
	
/* string functions */

#ifdef KERNEL	
char *xml_strdup( char *psz )
{
	int	iLen = 0;
	char	*pszNew = NULL;
	
	while( psz[ iLen++ ] != '\0' ){
	}
	
	pszNew = ( char* )MALLOC( iLen );
	if( !pszNew ){
		XML_MESSAGE(6);
		return( NULL );
	}

	while( ( iLen-- ) >=0 ){
		pszNew[ iLen ] = psz[ iLen ];
	}
	
	return( pszNew );	
}
#else
	#include <string.h>
	#define xml_strdup strdup
#endif

int xml_strcmp( char *psz1, char *psz2 )
{
	int	iPos = 0;
	int	iDelta = 0;
	
	while( !iDelta ){
		iDelta = psz1[ iPos ] - psz2[ iPos ];
		if( !( psz1[ iPos ]|psz2[ iPos ] ) ){
			return( 0 );
		}
		iPos++;
	}
	return( iDelta );
}

int xml_strlen( char *psz )
{
	int	iLen = 0;
	
	if( !psz ){			/* if we get a NULL pointer we return length 0 */
		return( 0 );		/* this differs from strlen !!! */
	}
	
	while( psz[ iLen++ ] != 0 ){
	}
	
	return( iLen );	
}

char *xml_strcpy( char* pszDest, char* pszSrc )
{
	char	*pszDest2 = pszDest;
	while( *pszSrc ){
		*( pszDest2++ )=*( pszSrc++ );
	}
	*pszDest2 = 0;
	return( pszDest );
}

/* iMin and iMax are number of digits (not including "0x") */
int xml_isHex( char *pszHex, int iMin, int iMax, int iClean )
{
	int	iCnt = 0;
	char	cQuote = 0;
	char	pszValue[65] = { 0 };
	char	*psz = pszHex;
	
	/* skip leading whitespace */
	while( *psz == ' ' ){
		psz++;
	}
	if( !*psz ){
		return( -1 );	/* string is empty */
	}
	
	
	if( *psz == '0' ){						/* skip leading "0x" or "0X" if it's there */
		if( ( *( psz+1 ) == 'x' )||( *( psz+1 ) == 'X' ) ){
			psz+=2;
		}
	}else if( ( *psz == 'x' )||( *psz == 'X' ) ){			/* skip leading "x'", "X'", "x\"" and "X\"" */
		if( ( *( psz+1 ) == '\'' )||( *( psz+1 ) == '"' ) ){
			cQuote = *( psz+1 );
			psz+=2;
		}
#ifdef	XML_REQUIRE_HEX_MARK
	}else{
		return( -2 );	/* not marked as hex */
#endif
	}
	
	while( 
		( ( *psz >= '0' )&&( *psz <= '9' ) )||		/* 0-9 */
		( ( *psz >= 'a' )&&( *psz <= 'f' ) )||		/* a-f */
		( ( *psz >= 'A' )&&( *psz <= 'F' ) )		/* A-F */
		){
			pszValue[ iCnt++ ] = *( psz++ );
	}
	if( cQuote ){							/* if we opened a quote it has to be closed */
		if( cQuote != *psz ){
			return( -3 );					/* quote not closed */
		}
		psz++;
	}
	
	while( *psz == ' ' ){
		psz++;
	}
	if( *psz ){
		return( -4 );					/* invalid character */
	}
	if( iCnt<iMin ){
		return( -5 );					/* not enough digits */
	}
	if( iCnt>iMax ){
		return( -6 );					/* too many digits */
	}
	if( iClean ){
		xml_strcpy( pszHex, pszValue );
	}
	return( 0 );
}


/* helper functions */

void xml_msgOutput( xml_msg_t *pxm )
{
	char	*pszBuffer = NULL;
	int	iSize = 0;
	char	*pszMsg = NULL;
	char	cChar;
	int	iInsertNo = 0;
	
	int	iMag = 10;
	int	iFact = 0;
	int	iRest = 0;
	int	iDigits = 1;
	
	pszBuffer = MALLOC( 4096 );					/* if a message is over 4K we truncate it */

	if( !pszBuffer ){						/* if we run out of memory here, there's no way to cry */
		return;							/* really it's impossible */
	}
	pszMsg = xml_message[ pxm->iMsgNo ];				/* get the base message */
	
	while( ( *pszMsg )&&( iSize<4095 ) ){
		cChar = *pszMsg++;
		switch( cChar ){
			case '%' :					/* houston we got an insert */
				cChar = *pszMsg++;
				switch( cChar ){
					case 's':
					case 'S':
						xml_strcpy( &( pszBuffer[ iSize ] ), ( char* )pxm->ppvInsert[ iInsertNo ] );
						iSize += xml_strlen( ( char* )pxm->ppvInsert[ iInsertNo++ ] );
						iSize--;		/* overwrite the terminating 0 */
						break;
					case 'd':
					case 'D':
						iMag = 10;
						iDigits = 1;
						iFact = *( int* )pxm->ppvInsert[ iInsertNo ];
						while( iFact/iMag ){
							iMag *= 10;
							iDigits++;
						}
						if( iFact<0 ){
							pszBuffer[ iSize++ ] = '-';
							iFact *= -1;
							iDigits++;
						}
						if( iSize+iDigits>4095 ){			/* our buffer is full */
							goto print_now;				/* just write out what we have so far */
						}
						while( iMag>1 ){
							iMag /=10;
							iRest = iFact/iMag;
							iFact = iFact%iMag;
							pszBuffer[ iSize++ ] = '0'+iRest;	/* this only works in ASCII */
						}
						break;
						
					default:			/* unknown insert type */
						break;			/* ignore it */
				}
				break;
			default:					/* just copy everything else */
				pszBuffer[ iSize++ ] = cChar;
		}
	}
print_now:	
	pszBuffer[ iSize ] = 0;
	
	PRINTF( "%s\n", pszBuffer );

	FREE( pszBuffer );

	return;
}

/* xml functions */
int xml_getNextLine( char *pLine, int iSize, char **ppPos )
{
	int	iLen = 0;
	char	cChar = 0;

	
	if( !( *ppPos ) ){						/* done ? */
		return( 0 );
	}
	
	while( iLen < ( iSize-1 ) ){					/* scan at most till the return buffer is full*/
		cChar = *( ( *ppPos )++ );
		DEBUG_D(8,"cChar=", cChar );
		if( !cChar ){						/* got 0, we are done */
			*ppPos = NULL;					/* make sure it's really over */
			break;						/* == goto done */
		}
		switch( cChar ){
			case '"' :					/* quoted text */
				*( pLine++ ) = cChar;
				iLen++;
				do{
					cChar = *( ( *ppPos )++ );
					if( !cChar ){			/* end of buffer */
						XML_MESSAGE( 3 );
						return( -1 );
					}
					*( pLine++ ) = cChar;
					iLen++;
				}while( ( cChar!='"' )&&( iLen<iSize ) );/* is copied untouched */ 
				break;
			case '\n' :					/* count new lines */
				xml_lineNumber++;
			case ' ' :					/* whitespace */
			case '\t' :

				*( pLine++ ) = ' ';			/* is reduced to a simple space */
				iLen++;
				cChar = *( ( *ppPos )++ );
				while( ( cChar == ' ' )||( cChar == '\t' )||( cChar == '\n' ) ){
					if( cChar == '\n' ){
						xml_lineNumber++;
					}
					cChar = *( ( *ppPos )++ );
				}					/* swallow every following "space" */
				( *ppPos )--;				/* spit out the first non-"space" */
				break;
			case '>' :					/* we (might) have a complete tag */
				*( pLine++ ) = cChar;
				iLen++;
				goto done;
				break;
			case '<' :					/* we want the (possible) tag in the next "line" */
				if( iLen ){				/* if we have something in the buffer */
					( *ppPos )--;
					goto done;			/* we return it */
				}else{					/* if not */
					*( pLine++ ) = cChar;
					iLen++;				/* we just move on */
				}
				break;
			default:					/* anything else */
				*( pLine++ ) = cChar;			/* is copied */
				iLen++;
		}
	}
done:									/* if you tell me "goto is bad", you have to write a better */
									/* readable version if this function, that doesn't result */
									/* in slower code */
	
	*( pLine++ ) = 0;						/* add the zero termination */
	iLen++;
	
	return( iLen );
}

/* -------------------------------------------------------------------------- */
/* the sole purpose of this function is to return if the string contains a valid tag and what kind it is */
/* return values:
			0 - no tag
			1 - opening tag
			2 - closing tag
			3 - head tag						*/
/* ---------------------------------------------------------------------------- */

int xml_checkTag( char *pszLine, char *pszTag, char *pszAttr )
{
	char	cChar = ' ';
	int	iClosing = 0;
	int	iHead = 0;
	char	*pPos = NULL;
	
	DEBUG_S( 7, "checking for tag", pszLine );

	/* clear the tag, just in case there is none */	
	pszAttr[ 0 ] = 0;
	
	/* skip all leading spaces, since we don't care about them */
	do{
		cChar = *( pszLine++ );
	}while( cChar == ' ' );

	/* now we expect a '<', if it's not there, then we don't have a tag */
	if( cChar != '<' ){
		return( 0 );
	}

	pPos = pszTag;
	
	cChar = *( pszLine++ );
	if( cChar == '/' ){
		iClosing = 1;						/* this might be a closing tag */
	}else if( cChar == '?' ){
		iHead = 1;
	}else{
		*( pPos++ ) = cChar;					/* store the character */
	}
	
	/* scan for ending '>' or first ' ' */
	do{
		cChar = *( pszLine++ );
		*( pPos++ ) = cChar;
	}while( ( cChar != '>' )&&( cChar != 0 )&&( cChar != ' ' ) );
	*( pPos-1 ) = 0;						/* terminate the tag */

	if( cChar == 0 ){						/* it's over */
		return( 0 );						/* we are done */
	}
	
	if( cChar == '>' ){						/* the tag is closed */
		goto noAttr;						/* no need to scan for attributes */
	}
	
	/* we just copy everything, errors in attributes will be detected later */
	pPos = pszAttr;	
	do{
		cChar = *( pszLine++ );
		*( pPos++ ) = cChar;
	}while( ( cChar != '>' )&&( cChar != 0 ) );

	if( cChar != '>' ){						/* we didn't get a complete tag */
		return( 0 );						/* let the caller handle this */
	}
	
	*( pPos-1 ) = 0;

noAttr:	
	if( iClosing ){
		DEBUG_S( 7, "closing tag", pszTag );
		return( 2 );
	}else if( iHead ){
		/* we have to check the '?' */
		pszLine-=2;
		cChar = *pszLine;
		if( cChar != '?' ){
			return( 0 );
		}
		/* fix the attributes */
		*( pPos-2 ) = 0;
		DEBUG_S( 7, "head (\"<? ... ?>\") tag", pszTag );
		return( 3 );
	}else{
		DEBUG_S( 7, "opening tag", pszTag );
		return( 1 );
	}
	
}

/* */
/* these functions don't have prototypes to get at least a warning if we create loops/recursions */

int xml_verify_fcp_lun( xml_node_t *pxn )
{
	/* has to be a hexvalue{1,16} */
	if( xml_isHex( pxn->pszBody, 1, 16, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_did( xml_node_t *pxn )
{
	/* has to be a hexvalue{1,6} */
	if( xml_isHex( pxn->pszBody, 1, 6, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_wwpn( xml_node_t *pxn )
{
	/* has to be a hexvalue{1,16} */
	if( xml_isHex( pxn->pszBody, 1, 16, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_mbr_lba( xml_node_t *pxn )
{
	/* has to be a hexvalue{1,16} */
	if( xml_isHex( pxn->pszBody, 1, 16, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_configuration( xml_node_t *pxn )
{
	/* has to be a hexvalue{1,8} */
	if( xml_isHex( pxn->pszBody, 1, 8, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_devno( xml_node_t *pxn )
{
	/* has to be a hexvalue{1,4} */
	if( xml_isHex( pxn->pszBody, 1, 4, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_cssid( xml_node_t *pxn )
{
	/* has to be a hexvalue */
	if( xml_isHex( pxn->pszBody, 1, 64, 1 ) ){
		XML_MESSAGE_SD(8, pxn->pszBody, pxn->iLineNumber)
		return( XML_VE_INVALID_HEX_VALUE );
	}
	return( 0 );
}
int xml_verify_file_name( xml_node_t *pxn )
{
	/* has to be a string */
	
	return( 0 );
}
int xml_verify_parameter_string( xml_node_t *pxn )
{
	/* has to be a string */
	
	return( 0 );
}
int xml_verify_scp_loader( xml_node_t *pxn )
{
	/* has to be a string */
	
	return( 0 );
}
int xml_verify_fcp_ipl( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_cssid = 0;
	int		iCnt_devno = 0;
	int		iCnt_wwpn = 0;
	int		iCnt_did = 0;	
	int		iCnt_fcp_lun = 0;	
	int		iCnt_configuration = 0;	
	int		iCnt_file_name = 0;	
	int		iCnt_mbr_lba = 0;	

	/* allowed entities
		- ccsid			0-1
		- devno			1
		- wwpn			0-1
		- did			0-1
		- fcp_lun		0-1
		- configuration		0-1
		- file_name		0-1
		- mbr_lba		0-1 */
	/* needs either wwpn or did */
	/* can't contain file_name and mbr_lba */

	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_cssid:
				rc = xml_verify_cssid( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_cssid++;
				break;
			case XML_TAGTYPE_devno:
				rc = xml_verify_devno( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_devno++;
				break;
			case XML_TAGTYPE_wwpn:
				rc = xml_verify_wwpn( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_wwpn++;
				break;
			case XML_TAGTYPE_did:
				rc = xml_verify_did( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_did++;
				break;
			case XML_TAGTYPE_fcp_lun:
				rc = xml_verify_fcp_lun( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_fcp_lun++;
				break;
			case XML_TAGTYPE_configuration:
				rc = xml_verify_configuration( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_configuration++;
				break;
			case XML_TAGTYPE_file_name:
				rc = xml_verify_file_name( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_file_name++;
				break;
			case XML_TAGTYPE_mbr_lba:
				rc = xml_verify_mbr_lba( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_mbr_lba++;
				break;
			default:
				return( XML_VE_fcp_ipl_INVTAG );
		}
		iChild++;
	}
	if( iCnt_cssid > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_cssid );
	}
	if( iCnt_devno > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_devno );
	}
	if( iCnt_devno < 1 ){
		return( XML_VE_fcp_ipl_NO_devno );
	}
	if( iCnt_wwpn > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_wwpn );
	}
	if( iCnt_did > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_did );
	}
	if( iCnt_fcp_lun > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_fcp_lun );
	}
	if( iCnt_configuration > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_configuration );
	}
	if( iCnt_file_name > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_file_name );
	}
	if( iCnt_mbr_lba > 1 ){
		return( XML_VE_fcp_ipl_TOOMANY_mbr_lba );
	}

	if( iCnt_wwpn+iCnt_did < 1 ){
		return( XML_VE_fcp_ipl_N_wwpn_N_did );
	}

	if( iCnt_file_name+iCnt_mbr_lba > 1 ){
		return( XML_VE_fcp_ipl_N_file_name_N_mbr_lba );
	}

	return( 0 );
}

int xml_verify_fcp_dump( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_cssid = 0;
	int		iCnt_devno = 0;
	int		iCnt_wwpn = 0;
	int		iCnt_did = 0;	
	int		iCnt_fcp_lun = 0;	
	int		iCnt_configuration = 0;	
	int		iCnt_file_name = 0;	
	
	/* allowed entities
		- ccsid			0-1
		- devno			1
		- wwpn			0-1
		- did			0-1
		- fcp_lun			1
		- configuration		0-1
		- file_name		0-1 */
	/* needs either wwpn or did */

	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_cssid:
				rc = xml_verify_cssid( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_cssid++;
				break;
			case XML_TAGTYPE_devno:
				rc = xml_verify_devno( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_devno++;
				break;
			case XML_TAGTYPE_wwpn:
				rc = xml_verify_wwpn( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_wwpn++;
				break;
			case XML_TAGTYPE_did:
				rc = xml_verify_did( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_did++;
				break;
			case XML_TAGTYPE_fcp_lun:
				rc = xml_verify_fcp_lun( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_fcp_lun++;
				break;
			case XML_TAGTYPE_configuration:
				rc = xml_verify_configuration( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_configuration++;
				break;
			case XML_TAGTYPE_file_name:
				rc = xml_verify_file_name( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_file_name++;
				break;
			default:
				return( XML_VE_fcp_dump_INVTAG );
		}
		iChild++;
	}
	if( iCnt_cssid > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_cssid );
	}
	if( iCnt_devno > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_devno );
	}
	if( iCnt_devno < 1 ){
		return( XML_VE_fcp_dump_NO_devno );
	}
	if( iCnt_wwpn > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_wwpn );
	}
	if( iCnt_did > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_did );
	}
	if( iCnt_fcp_lun > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_fcp_lun );
	}
	if( iCnt_fcp_lun < 1 ){
		return( XML_VE_fcp_dump_NO_fcp_lun );
	}
	if( iCnt_configuration > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_configuration );
	}
	if( iCnt_file_name > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_file_name );
	}
	
	if( ( iCnt_wwpn+iCnt_did ) < 1 ){
		return( XML_VE_fcp_dump_N_wwpn_N_did );
	}
	if( ( iCnt_wwpn+iCnt_did ) > 1 ){
		return( XML_VE_fcp_dump_TOOMANY_wwpn_ANDOR_did );
	}

	return( 0 );
}

int xml_verify_ipl_platform_loader( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_fcp_ipl = 0;
	
	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_fcp_ipl:
				rc = xml_verify_fcp_ipl( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_fcp_ipl++;
				break;
			default:
				return( XML_VE_ipl_platform_loader_INVTAG );
		}
		iChild++;
	}
	if( iCnt_fcp_ipl < 1 ){
		return( XML_VE_ipl_platform_loader_N_ipl_N_fcp_ipl );
	}
	if( iCnt_fcp_ipl > 1 ){
		return( XML_VE_ipl_platform_loader_TOOMANY_ipl_ANDOR_fcp_ipl );
	}
	
	return( 0 );
}
int xml_verify_dump_platform_loader( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_fcp_dump = 0;
	
	
	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_fcp_dump:
				rc = xml_verify_fcp_dump( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_fcp_dump++;
				break;
			default:
				return( XML_VE_dump_platform_loader_INVTAG );
		}
		iChild++;
	}
	if( iCnt_fcp_dump < 1 ){
		return( XML_VE_dump_platform_loader_N_dump_N_fcp_dump );
	}
	if( iCnt_fcp_dump  > 1 ){
		return( XML_VE_dump_platform_loader_TOOMANY_dump_ANDOR_fcp_dump );
	}
	
	return( 0 );
}
int xml_verify_system_control_program( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_parameter_string = 0;
	int		iCnt_scp_loader = 0;
	
	/* allowed entities
		- parameter_string	0-1
		- scp_loader		0-1 */
	
	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_parameter_string:
				rc = xml_verify_parameter_string( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_parameter_string++;
				break;
			case XML_TAGTYPE_scp_loader:
				rc = xml_verify_scp_loader( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_scp_loader++;
				break;
			default:
				return( XML_VE_system_control_program_INVTAG );
		}
		iChild++;
	}
	if( iCnt_parameter_string > 1 ){
		return( XML_VE_system_control_program_TOOMANY_parameter_string );
	}
	if( iCnt_scp_loader > 1 ){
		return( XML_VE_system_control_program_TOOMANY_scp_loader );
	}
	
	return( 0 );
}
int xml_verify_type( xml_node_t *pxn )
{
	return( 0 );
}
int xml_verify_start( xml_node_t *pxn )
{
	if( !pxn->pszBody[ 0 ] ){
		return( -102 );
	}
	
	return( 0 );
}
int xml_verify_timeout( xml_node_t *pxn )
{
	return( 0 );
}
int xml_verify_ipl_control_section( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_ipl_platform_loader = 0;
	int		iCnt_system_control_program = 0;
	
	/* allowed entities
		- ipl_platform_loader	1
		- system_control_program	0-1 */
	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_ipl_platform_loader:
				rc = xml_verify_ipl_platform_loader( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_ipl_platform_loader++;
				break;
			case XML_TAGTYPE_system_control_program:
				rc = xml_verify_system_control_program( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_system_control_program++;
				break;
			default:
				return( XML_VE_ipl_control_section_INVTAG );
		}
		iChild++;
	}
	if( iCnt_ipl_platform_loader > 1 ){
		return( XML_VE_ipl_control_section_TOOMANY_ipl_platform_loader );
	}
	if( iCnt_ipl_platform_loader < 1 ){
		return( XML_VE_ipl_control_section_NO_ipl_platform_loader );
	}
	if( iCnt_system_control_program > 1 ){
		return( XML_VE_ipl_control_section_TOOMANY_system_control_program );
	}
	
	return( 0 );
}
int xml_verify_dump_control_section( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_dump_platform_loader = 0;
	int		iCnt_mbr_lba = 0;
	int		iCnt_system_control_program = 0;
	int		iCnt_scp_loader = 0;
	int		iCnt_parameter_string = 0;
	
	/* allowed entities
		- dump_platform_loader		1
		- mbr_lba			0-1 
		- system_control_program	0-1
		- scp_loader			0-1
		- parameter_string		0-1 */
	
	/* count childs and abort on invalid child */
	while( iChild<pxn->iNumChild ){
		pxnThis = ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_dump_platform_loader:
				rc = xml_verify_dump_platform_loader( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_dump_platform_loader++;
				break;
			case XML_TAGTYPE_mbr_lba:
				rc = xml_verify_mbr_lba( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_mbr_lba++;
				break;
			case XML_TAGTYPE_system_control_program:
				rc = xml_verify_system_control_program( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_system_control_program++;
				break;
			case XML_TAGTYPE_scp_loader:
				rc = xml_verify_scp_loader( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_scp_loader++;
				break;
			case XML_TAGTYPE_parameter_string:
				rc = xml_verify_parameter_string( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_parameter_string++;
				break;
			default:
				return( XML_VE_dump_control_section_INVTAG );
		}
		iChild++;
	}
	
	if( iCnt_dump_platform_loader > 1 ){
		return( XML_VE_dump_control_section_TOOMANY_dump_platform_loader );
	}
	if( iCnt_dump_platform_loader < 1 ){
		return( XML_VE_dump_control_section_NO_dump_platform_loader );
	}
	if( iCnt_mbr_lba > 1 ){
		return( XML_VE_dump_control_section_INVTAG );
	}
	if( iCnt_system_control_program > 1 ){
		return( XML_VE_dump_control_section_TOOMANY_system_control_program );
	}
	if( iCnt_scp_loader > 1 ){
		return( XML_VE_dump_control_section_TOOMANY_scp_loader );
	}
	if( iCnt_parameter_string > 1 ){
		return( XML_VE_dump_control_section_TOOMANY_parameter_string );
	}
	
	return( 0 );
}

int xml_verify_eServer_ipl_script( xml_node_t *pxn )
{
	xml_node_t	*pxnThis = NULL;
	int		iChild = 0;
	int		rc = 0;
	int		iCnt_type    = 0;
	int		iCnt_start   = 0;
	int		iCnt_timeout = 0;
	
	/* allowed entities
		- type			1
		- start			0-1
		- timeout		0-1
		- ipl_control_section	0-n
		- dump_control_section	0-n */
	
	while( iChild<pxn->iNumChild ){
		pxnThis= ( xml_node_t* )pxn->ppChild[ iChild ];
		switch( pxnThis->iTagType ){
			case XML_TAGTYPE_type:
				rc = xml_verify_type( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_type++;
				break;
			case XML_TAGTYPE_start:
				rc = xml_verify_start( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_start++;
				break;
			case XML_TAGTYPE_timeout:
				rc = xml_verify_timeout( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				iCnt_timeout++;
				break;
			case XML_TAGTYPE_ipl_control_section:
				rc = xml_verify_ipl_control_section( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				break;
			case XML_TAGTYPE_dump_control_section:
				rc = xml_verify_dump_control_section( pxnThis );
				if( rc<0 ){
					return( rc );
				}
				break;
			default:
				return( XML_VE_eServer_ipl_script_INVTAG );			
		}
		iChild++;
	}
	if( iCnt_type > 1 ){
		return( XML_VE_eServer_ipl_script_TOOMANY_type );
	}
	if( iCnt_type < 1 ){
		return( XML_VE_eServer_ipl_script_NO_type );
	}
	if( iCnt_start > 1 ){
		return( XML_VE_eServer_ipl_script_TOOMANY_start );
	}
	if( iCnt_timeout > 1 ){
		return( XML_VE_eServer_ipl_script_TOOMANY_timeout );
	}
	
	return( 0 );
}
/* this function does a simple verification of the xml tree */
int xml_verify( xml_doc_t *pxd )
{	
	xml_node_t	*pxnThis = NULL;

	pxnThis = pxd->pxnRoot;

	/* we expect exactly one toplevel tag */
	if( pxnThis->iNumChild != 1 ){
		return( XML_VE_TOPLEVEL_TOOMANY );
	}
	pxnThis= ( xml_node_t* )pxnThis->ppChild[ 0 ];
	/* top level tag has to be eServer_ipl_script */
	if( pxnThis->iTagType != XML_TAGTYPE_eServer_ipl_script ){
		return( XML_VE_TOPLEVEL_INVTAG );
	}

	return( xml_verify_eServer_ipl_script( pxnThis ) );	
}


/* xml_node functions */

int xml_nodeTagTypeFromString( xml_node_t *pxn, char *psz )
{
	if( !xml_strcmp( psz, "eServer_ipl_script" ) ){
		pxn->iTagType = XML_TAGTYPE_eServer_ipl_script;
	}else if( !xml_strcmp( psz, "type" ) ){
		pxn->iTagType = XML_TAGTYPE_type;
	}else if( !xml_strcmp( psz, "start" ) ){
		pxn->iTagType = XML_TAGTYPE_start;
	}else if( !xml_strcmp( psz, "ipl_control_section" ) ){
		pxn->iTagType = XML_TAGTYPE_ipl_control_section;
	}else if( !xml_strcmp( psz, "ipl_platform_loader" ) ){
		pxn->iTagType = XML_TAGTYPE_ipl_platform_loader;
	}else if( !xml_strcmp( psz, "fcp_ipl" ) ){
		pxn->iTagType = XML_TAGTYPE_fcp_ipl;
	}else if( !xml_strcmp( psz, "devno" ) ){
		pxn->iTagType = XML_TAGTYPE_devno;
	}else if( !xml_strcmp( psz, "wwpn" ) ){
		pxn->iTagType = XML_TAGTYPE_wwpn;
	}else if( !xml_strcmp( psz, "did" ) ){
		pxn->iTagType = XML_TAGTYPE_did;
	}else if( !xml_strcmp( psz, "lun" ) ){
		pxn->iTagType = XML_TAGTYPE_fcp_lun;
	}else if( !xml_strcmp( psz, "br_lba" ) ){
		pxn->iTagType = XML_TAGTYPE_mbr_lba;
	}else if( !xml_strcmp( psz, "boot_program_selector" ) ){
		pxn->iTagType = XML_TAGTYPE_configuration;
	}else if( !xml_strcmp( psz, "system_control_program" ) ){
		pxn->iTagType = XML_TAGTYPE_system_control_program;
	}else if( !xml_strcmp( psz, "parameter_string" ) ){
		pxn->iTagType = XML_TAGTYPE_parameter_string;
	}else if( !xml_strcmp( psz, "cssid" ) ){
		pxn->iTagType = XML_TAGTYPE_cssid;
	}else if( !xml_strcmp( psz, "file_name" ) ){
		pxn->iTagType = XML_TAGTYPE_file_name;
	}else if( !xml_strcmp( psz, "scp_loader" ) ){
		pxn->iTagType = XML_TAGTYPE_scp_loader;
	}else if( !xml_strcmp( psz, "dump_control_section" ) ){
		pxn->iTagType = XML_TAGTYPE_dump_control_section;
	}else if( !xml_strcmp( psz, "dump_platform_loader" ) ){
		pxn->iTagType = XML_TAGTYPE_dump_platform_loader;
	}else if( !xml_strcmp( psz, "fcp_dump" ) ){
		pxn->iTagType = XML_TAGTYPE_fcp_dump;
	}else if( !xml_strcmp( psz, "timeout" ) ){
		pxn->iTagType = XML_TAGTYPE_timeout;
	}else{
		pxn->iTagType = XML_TAGTYPE_UNKNOWN;
	}
	return( pxn->iTagType );
}

xml_node_t *xml_nodeNew( xml_node_t *pnParent )
{
	xml_node_t	*pnNew = NULL;
	
	pnNew = ( xml_node_t* )MALLOC( sizeof( xml_node_t ) );

	if( !pnNew ){
		XML_MESSAGE(6);
		return( NULL );
	}
	pnNew->iTagType = XML_TAGTYPE_UNKNOWN;
	pnNew->pParent = pnParent;
	pnNew->iNumChild = 0;
	pnNew->ppChild = NULL;
	pnNew->pszTag = NULL;
	
	pnNew->iNumAttr = 0;
	pnNew->ppszAttrName = NULL;
	pnNew->ppszAttrValue = NULL;

	pnNew->pszBody = ( char* )MALLOC( 1 );
	if( !pnNew->pszBody ){
		XML_MESSAGE(6);
		return( NULL );
	}
	*( pnNew->pszBody ) = 0;
	
	return( pnNew );
}

int xml_nodeAddChild( xml_node_t *pnParent, xml_node_t *pnChild )
{
	xml_node_t	**ppChildList = NULL;
	int		i = 0;
	
	ppChildList = ( xml_node_t** )MALLOC( ( pnParent->iNumChild+1 )*sizeof( xml_node_t* ) );
	if( !ppChildList ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	/* copy the old list */
	while( i<pnParent->iNumChild ){
		ppChildList[ i ] = ( xml_node_t* )( ( pnParent->ppChild )[ i ] );
		i++;
	}
	
	
	FREE( pnParent->ppChild );
	
	pnParent->ppChild = ( void* )ppChildList;
	pnParent->iNumChild++;
	ppChildList[ i ] = pnChild;

	return( 0 );
}

int xml_nodeAddAttr( xml_node_t *pn, char *pszName, char *pszValue )
{
	char		**ppszAttrNameNew = NULL;
	char		**ppszAttrValueNew = NULL;
	int		i = 0;

	DEBUG_D( 5, "adding attrs - for node", pn );
	DEBUG_D( 5, "adding attrs - iNumAttr", pn->iNumAttr );
	
	ppszAttrNameNew = ( char** )MALLOC( ( pn->iNumAttr )*sizeof( char* ) );
	if( !ppszAttrNameNew ){
		XML_MESSAGE(6);
		return( -1 );
	}
	ppszAttrValueNew = ( char** )MALLOC( ( pn->iNumAttr )*sizeof( char* ) );
	if( !ppszAttrValueNew ){
		XML_MESSAGE(6);
		return( -1 );
	}
		
	/* copy the old list */
	while( i<pn->iNumAttr ){
		ppszAttrNameNew[ i ] = ( pn->ppszAttrName )[ i ];
		ppszAttrValueNew[ i ] = ( pn->ppszAttrValue )[ i ];
		i++;
	}
	
	FREE( pn->ppszAttrName );
	FREE( pn->ppszAttrValue );
	
	pn->ppszAttrName = ppszAttrNameNew;
	pn->ppszAttrValue = ppszAttrValueNew;
	
	DEBUG_D( 5, "adding attrs - names at", pn->ppszAttrName );
	
	pn->iNumAttr++;
	
	pn->ppszAttrName[ i ] = xml_strdup( pszName );
	pn->ppszAttrValue[ i ] = xml_strdup( pszValue );

	if( ( !pn->ppszAttrName[ i ] )||( !pn->ppszAttrValue[ i ] ) ){
		XML_MESSAGE(6);
		return( -1 );
	}

	return( 0 );
}

int xml_nodeAddAttrFromString( xml_node_t *pn, char *pszAttr )
{
	char	*pszAttrName = NULL;
	char	*pszAttrValue = NULL;
	int	iPos = 0;
	char	*pszAttrStart = pszAttr;
	int	iRc = 0;
	
	DEBUG_S( 5, "parsing attrs", pszAttr );
	DEBUG_D( 5, "parsing attrs - for node", pn );
	
	pszAttrName = ( char* )MALLOC( 1024 );
	if( !pszAttrName ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	pszAttrValue = ( char* )MALLOC( 1024 );
	if( !pszAttrValue ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	while( *pszAttr ){
		DEBUG_S( 6, "still parsing attrs", pszAttr );
		
		while( *pszAttr == ' ' ){	/* Skip blanks */
			pszAttr++;
		}
		
		/* find '=' */
		iPos = 0;
		while( ( *pszAttr )&&( *pszAttr != '=' )&&( iPos<1023 ) ){
			pszAttrName[ iPos++ ] = *pszAttr;
			pszAttr++;
		}
		if( !*pszAttr ){
			DEBUG( 5, "attr without '='" );
			XML_MESSAGE_SD( 4, pszAttrStart, xml_lineNumber );
			iRc = -1;
			goto done;
		}
		pszAttr++;
		/* check '"' */
		if( *pszAttr != '"' ){
			DEBUG_C( 5, "parsing attrs - expected \" but got", *pszAttr );
			iRc = -1;
			goto done;
		}
		pszAttrName[ iPos ] = 0;
		DEBUG_S( 7, "still parsing attrs - attr name", pszAttrName );
		iPos = 0;
		pszAttr++;
		while( ( *pszAttr )&&( *pszAttr != '"' )&&( iPos<1023 ) ){
			pszAttrValue[ iPos++ ] = *pszAttr;
			pszAttr++;
		}
		pszAttrValue[ iPos ] = 0;
		if( !*pszAttr ){
			DEBUG( 5, "attr ends without opening '\"'" );
			iRc = -1;
			goto done;
		}
		/* check '"' */
		if( *pszAttr != '"' ){
			DEBUG_C( 5, "parsing attrs - expected \" but got", *pszAttr );
			iRc = -1;
			goto done;
		}
		DEBUG_S( 7, "still parsing attrs - attr value", pszAttrValue );
		pszAttr++;	/* skip the closing '" ' */
		if( xml_nodeAddAttr( pn, pszAttrName, pszAttrValue )<0 ){	/* failure */
			iRc = -1;
			goto done;
		}
	}
done:
	FREE( pszAttrName );
	FREE( pszAttrValue );

	return( iRc );
}

int xml_nodeAppendToBody( xml_node_t *pxn, char *psz )
{
	int	i;
	char	*pszNewBody;
	char	*pcSrc;
	char	*pcDest;
	
	i = xml_strlen( pxn->pszBody );
	i += xml_strlen( psz );
	i++;							/* zero termination */
	DEBUG_D( 7, "appending to body - new length", i );
	
	pszNewBody=( char* )MALLOC( i );
	if( !pszNewBody ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	pcSrc = pxn->pszBody;
	pcDest = pszNewBody;
	while( *pcSrc ){
		*( pcDest++ ) = *( pcSrc++ );
	}
	pcSrc = psz;
	while( *pcSrc ){
		*( pcDest++ ) = *( pcSrc++ );
	}
	*( pcDest ) = 0;

	FREE( pxn->pszBody );
	pxn->pszBody = pszNewBody;

	return( 0 );
}

int xml_docXmlSetAttrFromString( xml_doc_t *pxd, char *pszAttr )
{
	char	*pszAttrName = NULL;
	char	*pszAttrValue = NULL;
	int	iPos = 0;
	char	*pszAttrStart = pszAttr;
	
	DEBUG_S( 5, "parsing attrs", pszAttr );
	DEBUG_D( 5, "parsing attrs - for node", pn );
	
	pszAttrName = ( char* )MALLOC( 1024 );
	if( !pszAttrName ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	pszAttrValue = ( char* )MALLOC( 1024 );
	if( !pszAttrValue ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	while( *pszAttr ){
		DEBUG_S( 6, "still parsing attrs", pszAttr );
		
		while( *pszAttr == ' ' ){	/* Skip blanks */
			pszAttr++;
		}
		
		/* find '=' */
		iPos = 0;
		while( ( *pszAttr )&&( *pszAttr != '=' )&&( iPos<1023 ) ){
			pszAttrName[ iPos++ ] = *pszAttr;
			pszAttr++;
		}
		if( !*pszAttr ){
			DEBUG( 5, "attr without '='" );
			XML_MESSAGE_SD( 4, pszAttrStart, xml_lineNumber );
			return( -1 );
		}
		pszAttr++;
		/* check '"' */
		if( *pszAttr != '"' ){
			DEBUG_C( 5, "parsing attrs - expected \" but got", *pszAttr );
			return( -1 );
		}
		pszAttrName[ iPos ] = 0;
		DEBUG_S( 7, "still parsing attrs - attr name", pszAttrName );
		iPos = 0;
		pszAttr++;
		while( ( *pszAttr )&&( *pszAttr != '"' )&&( iPos<1023 ) ){
			pszAttrValue[ iPos++ ] = *pszAttr;
			pszAttr++;
		}
		pszAttrValue[ iPos ] = 0;
		if( !*pszAttr ){
			DEBUG( 5, "attr ends without opening '\"'" );
			return( -1 );
		}
		/* check '"' */
		if( *pszAttr != '"' ){
			DEBUG_C( 5, "parsing attrs - expected \" but got", *pszAttr );
			return( -1 );
		}
		DEBUG_S( 7, "still parsing attrs - attr value", pszAttrValue );
		pszAttr++;	/* skip the closing '" ' */
		if( !xml_strcmp( pszAttrName, "encoding" ) ){
			pxd->pszXmlEncoding = xml_strdup( pszAttrValue );
		}else if( !xml_strcmp( pszAttrName, "version" ) ){
			pxd->pszXmlVersion = xml_strdup( pszAttrValue );
		}else{
			XML_MESSAGE_SD(9, pszAttrName, xml_lineNumber)
			return( -1 );
		}

	}
	
	FREE( pszAttrName );
	FREE( pszAttrValue );
	
	return( 0 );
}

xml_doc_t *xml_docNew( void )
{
	xml_doc_t	*pxd = NULL;
	
	pxd = ( xml_doc_t* )MALLOC( sizeof( xml_doc_t ) );
	if( !pxd ){
		XML_MESSAGE(6);
		return( NULL );
	}
	
	pxd->pszXmlEncoding = NULL;
	pxd->pszXmlVersion = NULL;
	pxd->pszDocType = NULL;
	
	return( pxd );
}

int xml_parse( char *pXmlDoc, xml_doc_t **ppxd )
{
	char		*pLine = NULL;
	int		iLen = 0;
	xml_node_t	*pnThis = NULL;
	xml_node_t	*pnNew = NULL;
	char		*pszTag = NULL;
	char		*pszAttr = NULL;
	int		iVerify = 0;
	
	DEBUG( 1,"DEBUG is on" );
	
	pLine = MALLOC( 1024 );
	if( !pLine ){
		XML_MESSAGE(6);
		return( -1 );
	}

	*ppxd = xml_docNew( );
	pnThis = xml_nodeNew( NULL );
	if( !pnThis ){
		return( -1 );
	}
	
	( *ppxd )->pxnRoot = pnThis;
	
	pszTag = ( char* )MALLOC( 1024 ); 
	if( !pszTag ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	pszAttr = ( char* )MALLOC( 1024 ); 
	if( !pszAttr ){
		XML_MESSAGE(6);
		return( -1 );
	}
	
	
	while( ( iLen = xml_getNextLine( pLine, 1024, &pXmlDoc ) )>0 ){		/* 0 is end of buffer, <0 is error */
		DEBUG_D( 5, "got line with x chars. x=", iLen );
		DEBUG_S( 5, "got line", pLine );
		switch( xml_checkTag( pLine, pszTag, pszAttr  ) ){
			case 0:	/* No tag */
				xml_nodeAppendToBody( pnThis, pLine );
				break;
			case 1:	/* Opening tag */
				DEBUG_S( 6, "got tag", pszTag );
				DEBUG_S( 6, "with attributes", pszAttr );
				/* create a new node */
				pnNew = xml_nodeNew( pnThis );
				if( !pnNew ){				/* out of memory */
					return( -1 );			/* nodeNew already put out the error message */
				}
				pnNew->pszTag = xml_strdup( pszTag );
				if( !pnNew->pszTag ){				/* out of memory */
					XML_MESSAGE(6);
					return( -1 );
				}
				/* add it to the parent/active node */
				if( xml_nodeAddChild( pnThis, pnNew )<0 ){		/* failure */
					return( -1 );
				}
				/* add the attributes */
				if( xml_nodeAddAttrFromString( pnNew, pszAttr )<0 ){	/* failure */
					return( -1 );
				}
				/* save the line number */
				pnNew->iLineNumber = xml_lineNumber;
				/* make the new node the active node */
				pnThis = pnNew;
				DEBUG_D( 7, "new node is", pnThis );
				break;
			case 2:	/* Closing tag */
				DEBUG_S( 6, "got closing tag", pszTag );
				if( !pnThis->pParent ){
					/* this is the root node */
					DEBUG( 4, "no more tags open" );
					XML_MESSAGE_SD(2, pszTag, xml_lineNumber );
					return( -1 );
				}
				DEBUG_S( 6, "opening was", pnThis->pszTag );
				if( strcmp( pnThis->pszTag, pszTag ) ){
					/* the opening and closing tags don't match */
					DEBUG( 4, "tags don't match" );
					DEBUG_S( 5, "opening was", pnThis->pszTag );
					DEBUG_S( 5, "closing is", pszTag );
					XML_MESSAGE_SS(5, pnThis->pszTag, pszTag );
					return( -1 );
				}
				if( xml_nodeTagTypeFromString( pnThis, pszTag )==XML_TAGTYPE_UNKNOWN ){
					DEBUG_S( 4, "got unknown tag:", pszTag );
					return( -1 );
				}
					
				/* climb up one level */
				pnThis = pnThis->pParent;
				break;
			case 3:	/* "head" tag */
				if( !xml_strcmp( pszTag, "xml" ) ){
					xml_docXmlSetAttrFromString( *ppxd, pszAttr );
				}else{
					return( -1 );
				}
				break;
			default:
				return( -2 );						/* something totally unexpected happened, better bail out */
		}
	}
	if( iLen<0 ){
		return( -1 );
	}
	iVerify =  xml_verify( *ppxd );
	if( iVerify ){
		return( iVerify );
	}
	/* remove the empty top node */
	pnThis = ( *ppxd )->pxnRoot;
	/* check to make sure there is really only one child */
	/*if( pnThis->iNumChild != 1 ){
		XML_MESSAGE_D(7, pnThis->iNumChild)
		return( -1 );
	}*/
	( *ppxd )->pxnRoot = pnThis->ppChild[ 0 ];
	FREE( pnThis );
	
	return( 0 );
}
