/*
 * Copyright (C) 2008 - 2012 Vivien Malerba <malerba@gnome-db.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/*
 * This program generates tokens'ID transformation because the GdaSqlParser object uses 2 Lemon generated
 * parsers at once, but with only one tokenizer (because each Lemon generated parser generates it own IDs for
 * tokens).
 */

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

#define MAX_SYMBOLS 500
#define PARSER_HEADER "parser.h"
#define FALSE 0
#define TRUE (!FALSE)

typedef struct {
	char *key;
	int   parser_value;
} HashEntry;

HashEntry entries[MAX_SYMBOLS];
int nb_entries; /* must remain < MAX_SYMBOLS */

typedef enum {
	TYPE_IMPOSED,
	TYPE_PARSER
} SourceType;

static void parse_contents (FILE *stream, SourceType type);
static HashEntry *find_entry_for_token (char *token);
int 
main ()
{
	int i;
	FILE *fd_imposed;
	FILE *fd_parser;
	HashEntry *illegal_entry;

	memset (entries, 0, sizeof (entries));
	/* printf ("Imposed header: %s\n", IMPOSED_HEADER); */
	fd_imposed = fopen (IMPOSED_HEADER, "r");
	if (!fd_imposed) {
		printf ("Can't open '%s':%s\n", IMPOSED_HEADER, strerror (errno));
		return 1;
	}
	/* printf ("Parser header: %s\n", PARSER_HEADER); */
#ifdef PARSER_HEADER_PATH
	fd_parser = fopen (PARSER_HEADER_PATH, "r");
#else
	fd_parser = fopen (PARSER_HEADER, "r");
#endif
	if (!fd_parser) {
		printf ("Can't open '%s':%s\n", PARSER_HEADER, strerror (errno));
		return 1;
	}

	nb_entries = 0;
	parse_contents (fd_imposed, TYPE_IMPOSED);
	parse_contents (fd_parser, TYPE_PARSER);

	fclose (fd_imposed);
	fclose (fd_parser);

	/* output notice */
	fprintf (stdout, "/*\n * This file is generated by the gen_def program (see the gen_def.c file \n"
		" * for some explanations)\n"
		" * DO NOT EDIT MANUALLY\n */\n\n\n");

	/* output */
	illegal_entry = find_entry_for_token ("ILLEGAL");
	fprintf (stdout, "gint postgres_parser_tokens[] = {\n");
	for (i = 0; i < nb_entries; i++) {
		HashEntry *entry = &(entries[i]);
		if (i!= 0)
			fprintf (stdout, ",\n");
		if (entry->parser_value >= 0)
			fprintf (stdout, "/* %03d */ %d", i, entry->parser_value);
		else
			fprintf (stdout, "/* %03d */ %d", i, illegal_entry->parser_value);
	}
	fprintf (stdout, "};\n");

	return 0;
}

static HashEntry *
find_entry_for_token (char *token) 
{
	int i;

	for (i = 0; i < nb_entries; i++) {
		HashEntry *e = &(entries[i]);
		if (!strcmp (e->key, token))
			return e;
	}
	return NULL;
}



static void 
parse_line (char *line, SourceType type) 
{
	char *z, *token;
	int value;
	HashEntry *entry;
	
	z = line;
	if (strncmp (z, "#define ", 8))
		return;
	z += 8;
	token = z + 2;
	for (; *z && *z != ' '; z++);
	*z = 0;
	z++;
	for (; *z == ' '; z++);
	value = atoi (z);
	/* printf ("%d Token: /%s/, value=%d\n", type, token, value); */

	entry = find_entry_for_token (token);
	if (!entry) {
		nb_entries++;
		entry = &(entries[nb_entries - 1]);
		entry->key = malloc (sizeof (char) * (strlen (token) + 1));
		memcpy (entry->key, token, strlen (token) + 1);
		entry->parser_value = -1;
	}
	if (type == TYPE_PARSER)
		entry->parser_value = value;
}

static void
parse_contents (FILE *stream, SourceType type)
{
#define BUFSIZE 500
	char buffer[BUFSIZE];
	int read;
	char *end;

	read = fread (buffer, 1, BUFSIZE, stream);
	end = buffer + read;
	while (read > 0) {
		char *ptr;

		/* read all complete lines in buffer */
		while (end > buffer) {
			char *hold = NULL;
			for (ptr = buffer; (ptr < end) && *ptr && (*ptr != '\n'); ptr++);
			if (ptr == end)
				break;
			if (*ptr)
				hold = ptr+1;
			*ptr = 0;

			/* treat the line */
			parse_line (buffer, type);

			if (hold) {
				int l = end - hold;
				end -= hold - buffer;
				memmove (buffer, hold, l);
			}
			else
				break;
		}

		read = fread (end, 1, BUFSIZE - (end - buffer), stream);
		end += read;
	}
}

