#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include "gpklib.h"

const static unsigned AP_CHARBITS = 8;
const static unsigned AP_NCHARS = (1<<AP_CHARBITS);

static int illegal_attribute_name(C char *s)
{
 static BOOL firsttime = TRUE;
 static BOOL legal[AP_NCHARS];
 if(firsttime)	{
	unsigned i;
	static char ok[] = " !@#$%^&*()_+-=|\\~`{}[]:\"'<,>.?/";
	for(i=0; i<sizeof(ok)-1; i++)
		legal[(unsigned)(ok[i])] = TRUE;
	for(i=0; i<AP_NCHARS; i++)
		legal[i] = legal[i] || isalnum(i);
	}

 if(!s || !(isalpha(*s) || *s=='_'))
	return 1;

 C char *s0 = s;
 s++;
 for(; *s; s++)
	if(!legal[(unsigned)(*s)])
		return 1+(s-s0);
 return 0;
}



/* 
 * Comments in this form:  a=v; a=v; # comment
 * become entries in their own right, with an attribute of _COMMENT.
 * Comments in this form: a=v; a=v # comment
 * are assumed to be attached to the last attribute/value pair.
 */


proplist avparse(const char *cs)
{
 if(!cs)
	return proplist();

 if(cs && is_error_line(cs))	{
	proplist rv;
	rv.add("", cs, "ERROR message on input");
	return rv;
	}


 static int setup = FALSE;
 static char isval_char[AP_NCHARS];
 if(!setup)	{
	setup = TRUE;
	unsigned i;
	for(i=0; i<AP_NCHARS; i++)
		isval_char[i] = i==9 || i==0 || (i>=32 && i<=126);
	}

 char *s = strdup(cs);
 assert(s);
 char *s0 = s;

 proplist rv;
 int reallydone = FALSE;
 do	{
	/* Move to the beginning of the attribute... */
	while(*s && (*s==';' || isspace(*s)))
		s++;
	if(!*s)
		break;
	if(!*s || *s=='#')	{	/* Comments terminate the line... */
		s++;
		while(*s && isspace(*s))
			s++;
		if(*s)
			rv.add("_COMMENT", s, 0);
		break;
		}

	char *a_begin = s;	
	while(*s && *s!='=')	/* Zip through the attribute. */
		s++;
	if(!*s)	{
		rv.add("", "no equals sign", a_begin);
		break;
		}
	*s++ = '\0';	/* mark attribute end. */
	if(*a_begin == '\0')	{
		rv.add("", "empty attribute");
		break;
		}
	chomp_whitespace(a_begin);
	if(illegal_attribute_name(a_begin))	{
		char buf[512];
		sprintf(buf, "%s[position%d]", a_begin, illegal_attribute_name(a_begin));
		rv.add("", "illegal attribute name", buf);
		break;
		}


		/* Move to the beginning of the value.... */
	while(*s && isspace(*s))
		s++;
	char *v = s;
	char *c = NULL;
	int done = FALSE;
	int error = FALSE;
	while(*s && !done && !reallydone)	{
		switch(*s)	{
		case '#':		/* Pick up a comment. */
			*s = '\0';
			chomp_whitespace(v);		/* Remove whitespace at end of value before '#'. */
							/* This allows the attractive   "a=b # comment" form. */
			s++;
			while(*s && isspace(*s))
				s++;
			if(*s)	{
				chomp_whitespace(s);
				if(*s)
					c = s;	/* Remember the comment. */
				}
			reallydone = TRUE;
			break;
		case ';':
		case '\r':
		case '\n':
			*s = '\0';
			done = TRUE;
			break;
		default:
			if(!isval_char[(unsigned)(*s)])	{
				char buf[512];
				sprintf(buf, "Bad char in value: 0x%x", *s);
				rv.add("", buf, a_begin);
				done = TRUE;
				error = TRUE;
				}
			}
		s++;
		}
	if(error)
		break;

	char *vv = decode_av(v);
	if(vv)
		rv.add(a_begin, vv, c);
	else
		rv.add("", "Bad characters in escape sequence", v);
	free(vv);
	} while(*s && !reallydone);

 free(s0);
 return rv;
}
