// Written by John Newbigin
// jn@it.swin.edu.au
// Copyright (c) 1999 John Newbigin
// Covered by the terms of the GPL.

// This is a token generator

// You create an object, assign a file and read token by token
// you can also peek ahead

/* This tokeniser recognises bind 8 named.conf files

Single words, seperated by white-space, or one of the following symbols:
	

Comments start with a // and end with an EOL
or start with a # and end with an EOL

*/

// Implement with an FSM
/*
	State

*/


#include <stdio.h>
#include "string.h"
#include "token.h"

#define WHITESPACE   "\r\n \t"
#define defaultWORDCHARS    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_.-/"
#define SPECIALCHARS "=()[],;:+*<>#@^${}/"

TToken::TToken(HugeString *extraWordChars)
{
	filename = 0;
	state    = 0;
	peekedChar = EOF;
	lineNo   = 1;

	if(extraWordChars)
	{
		newWORDCHARS = new HugeString();
		newWORDCHARS->cat(defaultWORDCHARS);
		newWORDCHARS->cat(extraWordChars->pchar());
		WORDCHARS = newWORDCHARS->pchar();
	}
	else
	{
		newWORDCHARS = 0;
		WORDCHARS = defaultWORDCHARS;
	}

	//printf("Token using %s WORDCHARS\n", WORDCHARS);
}

TToken::~TToken()
{
	if(newWORDCHARS)
	{
		delete newWORDCHARS;
	}
}

int TToken::AssignFile(char *FileName)
{
	filename = FileName;
	f = fopen(filename, "r");
	if(f)
	{
		state = 1;
		return 1;
	}
	else
	{
		return 0;
	}
}

int TToken::instring(char c, char *str)
{
	// loop through str for an instance of c
	int i=0;
	while(1)
	{
		if(str[i] == 0)
		{
			return -1;
		}
		if(c == str[i])
		{
			return i;
		}
		i++;
	}
}


int TToken::getToken(HugeString *string)
{
	// return values
	// 0 - error or EOF
	// 1 - word
	// 2 - comment
	// 3 - string

	string->clear();

	// Skip leading ws
	while(instring(PeekChar(), WHITESPACE) >= 0)
	{
		AcceptChar();
	}

	if(instring(PeekChar(), SPECIALCHARS) >= 0)
	{
		int c = AcceptChar();
		string->cat(c);

		// there are some special cases here for 2 byte special chars, :=, // and (*
		if(c == ':' && PeekChar() == '=')
		{
			string->cat(AcceptChar());
		}
		if(c == '(' && PeekChar() == '*')
		{
			string->cat(AcceptChar());
			// skip to end of comment
			while(1)
			{
				while(PeekChar() != '*')
				{
					if(PeekChar() == EOF)
					{
						return 0;
					}
					string->cat(AcceptChar());
					if(PeekChar() == ')')
					{
						string->cat(AcceptChar());
						return 2;
					}
				}
			}
		}
		if((c == '/') && (PeekChar() == '/'))
		{
			string->cat(AcceptChar());
			while((PeekChar() != 10) && (PeekChar() != EOF))
			{
				string->cat(AcceptChar());
			}
			return 2;
		}
		return 1;
	}

	if(instring(PeekChar(), WORDCHARS) >= 0)
	{
		string->cat(AcceptChar());
		while(instring(PeekChar(), WORDCHARS) >= 0)
		{
			string->cat(AcceptChar());
		}
		return 1;
	}

	// strings
	if(PeekChar() == '"')
	{
		AcceptChar();
		//string->cat(AcceptChar());
		while(PeekChar() != '"')
		{
			if(PeekChar() == EOF)
			{
				return 0;
			}
			string->cat(AcceptChar());
		}
		//string->cat(AcceptChar());
		AcceptChar();
		return 3;
	}

	// comments
/*	if(PeekChar() == '{')
	{
		while(PeekChar() != '}')
		{
			if(PeekChar() == EOF)
			{
				return 0;
			}
			string->cat(AcceptChar());
		}
		string->cat(AcceptChar());
		return 2;
	} */
	if(PeekChar() == '/')
	{
		string->cat(AcceptChar());
		if(PeekChar() == '/')
		{
			while((PeekChar() != 10) && (PeekChar() != EOF))
			{
				string->cat(AcceptChar());
			}
			return 2;
		}
	}
	if(PeekChar() == '#')
	{
		string->cat(AcceptChar());
		while((PeekChar() != 10) && (PeekChar() != EOF))
		{
			string->cat(AcceptChar());
		}
		return 2;
	}
	return 0;
}


int TToken::GetChar()
{
	return fgetc(f);
}

int TToken::AcceptChar()
{
	int r = EOF;
	if(peekedChar == EOF)
	{
		r = GetChar();
	}
	else
	{
		r = peekedChar;
		peekedChar = EOF;
	}
	if(r == 10)
	{
		lineNo++;
	}
	return r;
}

int TToken::PeekChar()
{
	if(peekedChar == EOF)
	{
		peekedChar = GetChar();
	}
	return peekedChar;
}

int TToken::getLineNo()
{
	return lineNo;
}
