/*====================================================================*
 *
 *   xmledit.c -
 *
 *   node.h
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

#ifndef XMLEDIT_SOURCE
#define XMLEDIT_SOURCE

/*====================================================================*
 *   system header files;
 *--------------------------------------------------------------------*/

#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <errno.h>

/*====================================================================*
 *  custom header files;
 *--------------------------------------------------------------------*/

#include "../tools/number.h"
#include "../tools/memory.h"
#include "../tools/error.h"
#include "../nodes/node.h"

/*====================================================================*
 *   constants;
 *--------------------------------------------------------------------*/

#define XML_BAD_NUMBER 1
#define XML_BAD_OFFSET 2
#define XML_BAD_EXTENT 3

/*====================================================================*
 *   variables;
 *--------------------------------------------------------------------*/

static char const * member = "";
static char const * string = "";
static unsigned offset = 0;
static unsigned length = 0;
static bool series = false;

/*====================================================================*
 *
 *   void position (size_t extent);
 *
 *   sanity check offset and extent before editing memory;
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static void position (size_t extent)

{
	if (!length)
	{
		error (XML_BAD_EXTENT, EPERM, "%s has no length", member);
	}
	if (offset > extent)
	{
		error (XML_BAD_OFFSET, EPERM, "%s offset of 0x%04X exceeds " DATA_OBJECT " offset of 0x%04X", member, offset, (unsigned int) extent);
	}
	if ((offset + length) > extent)
	{
		error (XML_BAD_EXTENT, EPERM, "%s length of %u bytes exceeds " DATA_OBJECT " length of " SIZE_T_SPEC " bytes", member, length, extent); 
	}
	return;
}

/*====================================================================*
 *
 *   signed xmlinteger (NODE const * node, unsigned radix);
 *
 *   convert numeric string to an unsigned integer; all string digits
 *   string digits must be valid for the specifid radix; radix can be
 *   1 through 16 but 2, 8, 10 and 16 are the only sensible choices;
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static unsigned xmlinteger (NODE const * node, unsigned radix)

{
	unsigned digit;
	unsigned value = 0;
	while ((digit = todigit (*string)) < radix)
	{
		value *= radix;
		value += digit;
		string++;
	}
	if (*string)
	{
		error (XML_BAD_NUMBER, EPERM, "%s %s is not numeric", member, node->text);
	}
	return (value);
}

/*====================================================================*
 *
 *   void xmlstring (void * memory, size_t extent);
 *
 *   xmlstring is expressed as character text;  truncate string and
 *   pad memory with NULs as needed;
 *
 *   per the schema, an series cannot have a string member;
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static void xmlstring (void * memory, size_t extent)

{
	char * buffer = (char *)(memory);
	if (series)
	{
		error (XML_BAD_NUMBER, ENOTSUP, "%s found inside struct", member);
	}
	if (length)
	{
		while (length > 1)
		{
			if (isprint (*string))
			{
				buffer [offset] = *string++;
			}
			else
			{
				buffer [offset] = (char)(0);
			}
			offset++;
			length--;
		}
		buffer [offset] = (char)(0);
		offset++;
		length--;
	}
	return;
}

/*====================================================================*
 *
 *   void xmlmemory (void * memory, size_t extent);
 *
 *   xmlmemory is a hexadecimal string of variable extent; an empty
 *   string increments offset and decrements length but nothing is
 *   written to the memory;
 *
 *   per the schema, if xmlmemory is not inside an series then it must
 *   match the object extent;
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static void xmlmemory (void * memory, size_t extent)

{
	uint8_t * buffer = (uint8_t *)(memory);
	if (!*string)
	{
		offset++;
		length--;
	}
	while ((*string) && (length))
	{
		uint8_t msb = todigit (*string++);
		uint8_t lsb = todigit (*string++);
		if ((msb > 0x0F) || (lsb > 0x0F))
		{
			error (XML_BAD_NUMBER, EINVAL, "%s value is not hexadecimal", member);
		}
		buffer [offset] = (msb << 4) + lsb;
		offset++;
		length--;
	}
	if ((length) && (!series))
	{
		error (XML_BAD_NUMBER, EINVAL, "%s value is too short", member);
	}
	if (*string)
	{
		error (XML_BAD_NUMBER, EINVAL, "%s value is too long", member);
	}
	return;
}

/*====================================================================*
 *
 *   void xmlnumber (void * memory, size_t extent);
 *
 *   xmlnumber is a decimal integer string of variable length; the
 *   value cannot exceed length bytes and offset is incremented by
 *   length bytes;
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static void xmlnumber (void * memory, size_t extent)

{
	uint64_t number = 0;
	uint64_t maximum = 0;
	maximum = ~maximum;
	if (length < sizeof (number))
	{
		maximum <<= (length << 3);
		maximum = ~maximum;
	}
	while (isdigit (*string))
	{
		number *= 10;
		number += *string - '0';
		if (number > maximum)
		{
			error (XML_BAD_NUMBER, EINVAL, "%s value exceeds %u bytes", member, length); 
		}
		string++;
	}
	if (*string)
	{
		error (XML_BAD_NUMBER, EINVAL, "%s value is not decimal", member);
	}
	memcpy ((uint8_t *)(memory) + offset, &number, length);
	offset += length;
	length -= length;
	return;
}

/*====================================================================*
 *
 *   void xmlbyte (void * memory, unsigned extent);
 *
 *   xmlbyte is a decimal integer string of variable extent; the
 *   value cannot exceed 255; an empty string increments offset and
 *   decrements length but nothing is written to the memory;
 *
 *   per the schema, if xmlbyte is not inside an series then it must
 *   it must match the object extent which must be 1 by implication;
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static void xmlbyte (void * memory, size_t extent)

{
	if (*string)
	{
		uint16_t number = 0;
		while (isdigit (*string))
		{
			number *= 10;
			number += *string - '0';
			if (number > 0xFF)
			{
				error (XML_BAD_NUMBER, EINVAL, "%s value exceeds 8 bits", member);
			}
			string++;
		}
		if (*string)
		{
			error (XML_BAD_NUMBER, EINVAL, "%s value is not decimal", member);
		}
		memcpy ((uint8_t *)(memory) + offset, &number, sizeof (uint8_t));
	}
	offset++;
	length--;
	if ((length) && (!series))
	{
		error (XML_BAD_NUMBER, EINVAL, "%s is too short", member);
	}
	return;
}

/*====================================================================*
 *
 *   static char const * xmlcontent1 (struct node const * node);
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static char const * xmlcontent1 (struct node const * node)

{
	if (node)
	{
		node = node->below;
	}
	while (node)
	{
		if (node->type == NODE_DATA)
		{
			return (node->text);
		}
		node = node->after;
	}
	return ("");
}

/*====================================================================*
 *
 *   char const * xmlvalue1 (struct node const * node);
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

char const * xmlvalue1 (struct node const * node)

{
	if (node)
	{
		node = node->below;
	}
	while (node)
	{
		if (node->type == NODE_VALU)
		{
			return (node->text);
		}
		node = node->after;
	}
	return ("");
}

/*====================================================================*
 *
 *   static char const * xmlattribute1 (struct node const * node, char const * name);
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

static char const * xmlattribute1 (struct node const * node, char const * name)

{
	if (node)
	{
		node = node->below;
	}
	while (node)
	{
		if (node->type == NODE_ATTR)
		{
			if (!strcmp (node->text, name))
			{
				name = xmlvalue1 (node);
				return (name);
			}
		}
		node=node->after;
	}
	return ("");
}

/*====================================================================*
 *
 *   signed xmledit (struct node const * node, void * memory, size_t extent);
 *
 *   Motley Tools by Charles Maier <cmaier@cmassoc.net>;
 *   Copyright (c) 2001-2006 by Charles Maier Associates;
 *   Licensed under the Internet Software Consortium License;
 *
 *--------------------------------------------------------------------*/

signed xmledit (struct node const * node, void * memory, size_t extent)

{
	if (node)
	{
		node = node->below;
	}
	while (node)
	{
		if (node->type == NODE_ELEM)
		{
			if (!strcmp (node->text, DATA_MEMBER))
			{
				member = xmlattribute1 (node, DATA_NAME);
				offset = (unsigned)(-1);
				length = (unsigned)(-1);
				series = false;
			}
			else if (!strcmp (node->text, DATA_OFFSET))
			{
				string = xmlcontent1 (node);
				offset = xmlinteger (node, 16);
			}
			else if (!strcmp (node->text, DATA_LENGTH))
			{
				string = xmlcontent1 (node);
				length = xmlinteger (node, 10);
			}
			else if (!strcmp (node->text, DATA_STRUCT))
			{
				series = true;
			}
			else if (!strcmp (node->text, DATA_STRING))
			{
				string = xmlcontent1 (node);
				position (extent);
				xmlstring (memory, extent);
			}
			else if (!strcmp (node->text, DATA_MEMORY))
			{
				string = xmlcontent1 (node);
				position (extent);
				xmlmemory (memory, extent);
			}
			else if (!strcmp (node->text, DATA_HUGE))
			{
				length = sizeof (uint64_t);
				position (extent);
				string = xmlcontent1 (node);
				xmlnumber (memory, extent);
			}
			else if (!strcmp (node->text, DATA_LONG))
			{
				length = sizeof (uint32_t);
				position (extent);
				string = xmlcontent1 (node);
				xmlnumber (memory, extent);
			}
			else if (!strcmp (node->text, DATA_WORD))
			{
				length = sizeof (uint16_t);
				position (extent);
				string = xmlcontent1 (node);
				xmlnumber (memory, extent);
			}
			else if (!strcmp (node->text, DATA_BYTE))
			{
				position (extent);
				string = xmlcontent1 (node);
				xmlbyte (memory, extent);
			}
			xmledit (node, memory, extent);
		}
		node = node->after;
	}
	return (0);
}

/*====================================================================*
 *
 *--------------------------------------------------------------------*/

#endif