/*
 * Copyright (c) 1999 Sun Microsystems, Inc.
 * Copyright (c) 1999 Nihon Sun Microsystems K.K.
 * All rights reserved.
 */

/*
 * "$Id: utf16_ct_map_comp.c,v 1.1.1.1 2000/10/29 16:45:20 himi Exp $"
 */

#pragma ident	"@(#)utf16_ct_map_comp.c 1.10	99/04/28 SMI"


#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <sys/param.h>

#include "csc_conf_info.h"
#include "ct_map.h"
#include "utf16_ct_map.h"


#define RIGHT_MASK		(0x8080)


void	dump_u_n_map(FILE * fp, char * map_name, ct_map_conf_t * ct_map_conf);


unsigned int code_set_map[CS_VALUE_MAX];


void
dump_code_set_map(
	FILE *		fp,
	char *		map_name,
	char *		map_set_name,
	ct_map_conf_t *	ct_map_conf)
{
	unsigned int	high;
	unsigned int	low;
	unsigned int	base;
	char		group[256];
	int		col;

	memset(group, 0, sizeof (group));

#if 0
	fprintf(fp, "#include <stddef.h>\n");
#endif /* 0 */

	dump_u_n_map(fp, map_set_name, ct_map_conf);

	for (high = 0; high < 256; high++) {
		base = (high << 8);
		for (low = 0; low < 256; low++) {
			if (0 != code_set_map[base + low]) {
				group[high] = 1;
				break;
			}
		}
		if (0 == group[high]) {
			fprintf(fp,
				"/* static const unsigned int	%s_%02x[] = {}; */\n",
				map_name, high);
			continue;
		}

		fprintf(fp,
			"static const unsigned int	%s_%02x[] = {\n",
			map_name, high);

		for (low = 0, col = 0; low < 256; low++) {
			if (0 == col) {
				putc('\t', fp);
			}
			fprintf(fp, "0x%08x,", code_set_map[base + low]);
			if (3 == col) {
				putc('\n', fp);
				col = 0;
			} else {
				putc(' ', fp);
				col += 1;
			}
		}

		fprintf(fp, "};\n");
	}

	fprintf(fp, "const unsigned int *	%s[] = {\n", map_name);

	for (high = 0; high < 256; high++) {
		if (0 == group[high]) {
			fprintf(fp,
				"\tNULL, /* %s_%02x, */\n", map_name, high);
		} else {
			fprintf(fp, "\t%s_%02x,\n", map_name, high);
		}
	}

	fprintf(fp, "};\n");

	return;
}


void
dump_u_n_map(FILE * fp, char * map_name, ct_map_conf_t * ct_map_conf)
{
	unsigned int	high;
	unsigned int	low;
	unsigned int	base;
	char		group[256];
	int		col;
	ct_map_conf_t *	ctmp;

	fprintf(fp, "#include <stddef.h>\n\n");
	fprintf(fp, "#include \"utf16_ct_map.h\"\n\n");

	for (ctmp = ct_map_conf; NULL != ctmp; ctmp = ctmp->next) {
		fprintf(fp,
			"extern const csc_utf16_ct_map_t %s;\n",
			ctmp->cs_u_n_map_name);
	}

	fprintf(fp, "\nconst csc_utf16_ct_map_t * %s[] = {\n", map_name);

	for (ctmp = ct_map_conf; NULL != ctmp; ctmp = ctmp->next) {
		fprintf(fp, "\t&%s,\n", ctmp->cs_u_n_map_name);
	}

	fprintf(fp, "\tNULL\n");
	fprintf(fp, "};\n");

	return;
}


void
dump_n_u_map(FILE * fp, char * map_name, ct_map_conf_t * ct_map_conf)
{
	unsigned int	high;
	unsigned int	low;
	unsigned int	base;
	char		group[256];
	int		col;
	ct_map_conf_t *	ctmp;

	fprintf(fp, "#include <stddef.h>\n\n");
	fprintf(fp, "#include \"utf16_ct_map.h\"\n\n");

	for (ctmp = ct_map_conf; NULL != ctmp; ctmp = ctmp->next) {
		fprintf(fp,
			"extern const csc_utf16_ct_map_t %s;\n",
			ctmp->cs_n_u_map_name);
	}

	fprintf(fp, "\nconst csc_utf16_ct_map_t * %s[] = {\n", map_name);

	for (ctmp = ct_map_conf; NULL != ctmp; ctmp = ctmp->next) {
		fprintf(fp, "\t&%s,\n", ctmp->cs_n_u_map_name);
	}

	fprintf(fp, "\tNULL\n");
	fprintf(fp, "};\n");

	return;
}


void
dump_cs_conversion_map(
	FILE *			fp,
	char *			encoding_name,
	char *			gl_gr,
	int			extended_segment,
	int			in_code_length,
	int			out_code_length,
	char *			design_name,
	unsigned char *		design_str,
	int			design_len,
	char *			map_name,
	unsigned short *	conversion_map,
	unsigned int		id)
{
	int	i;
	unsigned int	high;
	unsigned int	low;
	unsigned int	base;
	char		group[256];
	int		col;

	memset(group, 0, sizeof (group));

	fprintf(fp, "#include <stddef.h>\n");
	fprintf(fp, "#include \"utf16_ct_map.h\"\n\n");

	for (high = 0; high < 256; high++) {
		base = (high << 8);
		for (low = 0; low < 256; low++) {
			if (id & code_set_map[base + low]) {
				group[high] = 1;
				break;
			}
		}
		if (0 == group[high]) {
			fprintf(fp,
				"/* static const unsigned %s	%s_%02x[] = {}; */\n",
				(1 == out_code_length) ? "char" : "short",
				map_name, high);
			continue;
		}

		fprintf(fp,
			"static const unsigned %s	%s_%02x[] = {\n",
			(1 == out_code_length) ? "char" : "short",
			map_name, high);

		for (low = 0, col = 0; low < 256; low++) {
			if (0 == col) {
				putc('\t', fp);
			}
			if (1 == out_code_length) {
				fprintf(fp,
					"0x%02x,",
					*(conversion_map + base + low));
			} else {
				fprintf(fp,
					"0x%04x,",
					*(conversion_map + base + low));
			}
			if (3 == col) {
				putc('\n', fp);
				col = 0;
			} else {
				putc(' ', fp);
				col += 1;
			}
		}

		fprintf(fp, "};\n");
	}

	fprintf(fp, "\n");

	fprintf(fp, "static const unsigned char\t%s[] = {", design_name);
	for (i = 0; i < (design_len - 1); i++) {
		fprintf(fp, "0x%02x, ", *(design_str + i));
	}
	fprintf(fp, "0x%02x};\n\n", *(design_str + i));

	fprintf(fp, "const csc_utf16_ct_map_t %s = {\n", map_name);
	fprintf(fp, "\t\"%s\",\n", encoding_name);
	fprintf(fp, "\t\"%s\",\n", gl_gr);
	fprintf(fp, "\t0x%08x,\n", id);
	fprintf(fp, "\t%d,\n", extended_segment);
	fprintf(fp, "\t%d,\n", in_code_length);
	fprintf(fp, "\t%d,\n", out_code_length);
	fprintf(fp, "\t%s,\n", design_name);
	fprintf(fp, "\t%d,\n", design_len);
	fprintf(fp, "\t{\n");
	for (high = 0; high < 256; high++) {
		if (0 == group[high]) {
			fprintf(fp,
				"\t\tNULL, /* %s_%02x, */\n", map_name, high);
		} else {
			fprintf(fp, "\t\t%s_%02x,\n", map_name, high);
		}
	}
	fprintf(fp, "\t}\n");
	fprintf(fp, "};\n");
}


void
dump_cs_n_u_map(
	FILE *			fp,
	char *			encoding_name,
	char *			gl_gr,
	int			extended_segment,
	int			in_code_length,
	int			out_code_length,
	char *			design_name,
	unsigned char *		design_str,
	int			design_len,
	char *			map_name,
	unsigned short *	conversion_map,
	char *			n_u_v_map,
	unsigned int		id)
{
	int	i;
	unsigned int	high;
	unsigned int	low;
	unsigned int	base;
	char		group[256];
	int		col;

	memset(group, 0, sizeof (group));

	fprintf(fp, "#include <stddef.h>\n");
	fprintf(fp, "#include \"utf16_ct_map.h\"\n\n");

	for (high = 0; high < 256; high++) {
		base = (high << 8);
		for (low = 0; low < 256; low++) {
			if (1 == *(n_u_v_map + base + low)) {
				group[high] = 1;
				break;
			}
		}
		if (0 == group[high]) {
			fprintf(fp,
				"/* static const unsigned %s	%s_%02x[] = {}; */\n",
				"short", map_name, high);
			continue;
		}

		fprintf(fp,
			"static const unsigned %s	%s_%02x[] = {\n",
			"short",
			map_name, high);

		for (low = 0, col = 0; low < 256; low++) {
			if (0 == col) {
				putc('\t', fp);
			}
			if (1 == *(n_u_v_map + base + low)) {
				fprintf(fp,
					"0x%04x,",
					*(conversion_map + base + low));
			} else {
				/* REPLACEMENT CHARACTER */
				fprintf(fp, "0xfffd,");
			}
			if (3 == col) {
				putc('\n', fp);
				col = 0;
			} else {
				putc(' ', fp);
				col += 1;
			}
		}

		fprintf(fp, "};\n");
	}

	fprintf(fp, "\n");

	fprintf(fp, "static const unsigned char\t%s[] = {", design_name);
	for (i = 0; i < (design_len - 1); i++) {
		fprintf(fp, "0x%02x, ", *(design_str + i));
	}
	fprintf(fp, "0x%02x};\n\n", *(design_str + i));

	fprintf(fp, "const csc_utf16_ct_map_t %s = {\n", map_name);
	fprintf(fp, "\t\"%s\",\n", encoding_name);
	fprintf(fp, "\t\"%s\",\n", gl_gr);
	fprintf(fp, "\t0x%08x,\n", id);
	fprintf(fp, "\t%d,\n", extended_segment);
	fprintf(fp, "\t%d,\n", in_code_length);
	fprintf(fp, "\t%d,\n", out_code_length);
	fprintf(fp, "\t%s,\n", design_name);
	fprintf(fp, "\t%d,\n", design_len);
	fprintf(fp, "\t{\n");
	for (high = 0; high < 256; high++) {
		if (0 == group[high]) {
			fprintf(fp,
				"\t\tNULL, /* %s_%02x, */\n", map_name, high);
		} else {
			fprintf(fp, "\t\t%s_%02x,\n", map_name, high);
		}
	}
	fprintf(fp, "\t}\n");
	fprintf(fp, "};\n");
}


void
update_map(
	char *			file_name,
	csc_conf_str_t *	str_national,
	csc_conf_str_t *	str_utf16,
	int			length,
	int			enc_gl_gr,
	int			cs_value_id,
	unsigned short *	cs_u_national,
	unsigned short *	cs_national_u,
	char *			cs_n_u_v)
{
	unsigned int	code_national;
	unsigned int	code_utf16;
	char	buf_n[32];
	char	buf_u[32];

	if ((((sizeof (buf_n)) - 1) <= str_national->len) ||
	    (((sizeof (buf_u)) - 1) <= str_utf16->len)) {
		fprintf(stderr,
			"update_map: %s: number too long\n", file_name);
		exit(1);
	}

	memcpy(buf_n, str_national->ptr, str_national->len);
	buf_n[str_national->len] = '\0';
	memcpy(buf_u, str_utf16->ptr, str_utf16->len);
	buf_u[str_utf16->len] = '\0';

	code_national = strtol(buf_n, NULL, 0);
	if (((code_national) < 0) || (CS_VALUE_MAX <= code_national)) {
		fprintf(stderr, "%s: national: invalid value 0x%08x\n",
			file_name, code_national);
		exit(1);
	}

	code_utf16 = strtol(buf_u, NULL, 0);
	if (((code_utf16) < 0) || (CS_VALUE_MAX <= code_utf16)) {
		fprintf(stderr, "%s: utf16: invalid value 0x%08x\n",
			file_name, code_utf16);
		exit(1);
	}

	if (0x0000 != (*(cs_u_national + code_utf16))) {
		fprintf(stderr,
			"%s: "
			"0x%04x == *(cs_u_national + 0x%04x), <--> 0x%04x\n",
			file_name,
			*(cs_u_national + code_utf16), code_utf16, code_national);
	}
	if (0x0000 != (*(cs_national_u + code_national))) {
		fprintf(stderr,
			"%s: "
			"0x%04x == *(cs_national_u + 0x%04x), <--> 0x%04x\n",
			file_name,
			*(cs_u_national + code_national), code_national, code_utf16);
	}

	if ((1 == length) && (0 == enc_gl_gr)) {
		if (!(RIGHT_MASK & code_national)) {
			code_set_map[code_utf16] |= cs_value_id;
			*(cs_u_national + code_utf16) = code_national;
			*(cs_national_u + code_national) = code_utf16;
			*(cs_n_u_v + code_national) = 1;
		}

	} else if ((1 == length) && (1 == enc_gl_gr)) {
		if (RIGHT_MASK & code_national) {
			code_set_map[code_utf16] |= cs_value_id;
			*(cs_u_national + code_utf16) = code_national;
			*(cs_national_u + code_national) = code_utf16;
			*(cs_n_u_v + code_national) = 1;
		}

	} else if ((1 == length) && (2 == enc_gl_gr)) {
		code_set_map[code_utf16] |= cs_value_id;
		*(cs_u_national + code_utf16) = code_national;
		*(cs_national_u + code_national) = code_utf16;
		*(cs_n_u_v + code_national) = 1;

	} else if ((2 == length) && (0 == enc_gl_gr)) {
		code_national &= ~RIGHT_MASK;
		code_set_map[code_utf16] |= cs_value_id;
		*(cs_u_national + code_utf16) = code_national;
		*(cs_national_u + code_national) = code_utf16;
		*(cs_n_u_v + code_national) = 1;

	} else if ((2 == length) && (1 == enc_gl_gr)) {
		code_national |= RIGHT_MASK;
		code_set_map[code_utf16] |= cs_value_id;
		*(cs_u_national + code_utf16) = code_national;
		*(cs_national_u + code_national) = code_utf16;
		*(cs_n_u_v + code_national) = 1;

	} else if ((2 == length) && (2 == enc_gl_gr)) {
		code_set_map[code_utf16] |= cs_value_id;
		*(cs_u_national + code_utf16) = code_national;
		*(cs_national_u + code_national) = code_utf16;
		*(cs_n_u_v + code_national) = 1;
	}

	return;
}


void
parse_file(
	char *			file_name,
	int			index_n,
	int			index_u,
	int			length,
	int			enc_gl_gr,
	int			cs_id,
	unsigned short *	cs_u_national,
	unsigned short *	cs_national_u,
	char *			cs_n_u_v)
{
	csc_conf_file_t	*	conf_file;
	csc_conf_str_t		conf_str[16];
	int			num;
	int			index_m;

	index_m = (((index_n < index_u) ? index_u : index_n) + 1);

	conf_file = csc_conf_open(file_name, 0, NULL, 0);
	if (NULL == conf_file) {
		fprintf(stderr,
			"csc_conf_open: failed to open %s\n", file_name);
		exit(1);
	}

	while (0 < (num = csc_conf_read(conf_file, conf_str, index_m))) {
		if (num < index_m) {continue;}

		update_map(file_name,
			   conf_str + index_n, conf_str + index_u,
			   length, enc_gl_gr, cs_id,
			   cs_u_national, cs_national_u, cs_n_u_v);
	}

	csc_conf_close(conf_file);

	return;
}


void *
malloc_vital(size_t size)
{
	void *	p;

	if (NULL == (p = malloc(size))) {
		perror("malloc");
		exit(1);
	}

	memset(p, 0, size);

	return p;
}


int
main(int argc, char ** argv)
{
	csc_conf_file_t	*	conf_file;
	csc_conf_str_t		conf_str[16];
	int			num;
	FILE *			fp;
	ct_map_conf_t *		ct_map_conf_first;
	ct_map_conf_t *		ctmp;
	int			cs_num;
	unsigned short **	cs_u_n_map;
	unsigned short **	cs_n_u_map;
        char **			cs_n_u_v_map;
	const char *		p;
	int			i;

	ct_map_conf_first = NULL;


	/*
	 * initialize ct_map_conf structure
	 */
	ct_map_conf_first = parse_ct_map_conf();
	cs_num = 0;
	for (ctmp = ct_map_conf_first; NULL != ctmp; ctmp = ctmp->next) {
		cs_num += 1;
	}

	/*
	 * allocate cs_u_n_map/cs_n_u_map/cs_n_u_v_map
	 */
	cs_u_n_map = malloc_vital((sizeof (short *)) * cs_num);
	cs_n_u_map = malloc_vital((sizeof (short *)) * cs_num);
	cs_n_u_v_map = malloc_vital((sizeof (char *)) * cs_num);
	for (i = 0; i < cs_num; i++) {
		*(cs_u_n_map + i) = malloc_vital(CS_VALUE_MAX * 2);
		*(cs_n_u_map + i) = malloc_vital(CS_VALUE_MAX * 2);
		*(cs_n_u_v_map + i) = malloc_vital(CS_VALUE_MAX * 1);
	}


	/*
	 * build all map
	 */
	for (ctmp = ct_map_conf_first; NULL != ctmp; ctmp = ctmp->next) {
		parse_file(ctmp->path_data, ctmp->index_n, ctmp->index_u,
			   ctmp->byte_number, ctmp->enc_gl_gr, ctmp->cs_id,
			   *(cs_u_n_map + ctmp->cs_num),
			   *(cs_n_u_map + ctmp->cs_num),
			   *(cs_n_u_v_map + ctmp->cs_num));
	}


	/*
	 * build map_utf16_national.c
	 */
	fp = fopen("map_utf16_national.c", "w");
	if (NULL == fp) {
		perror("fopen: map_utf16_national.c");
		exit(1);
	}
	dump_code_set_map(fp,
			  "csc_utf16_codeset_map",
			  "csc_utf16_national_map",
			  ct_map_conf_first);
	fclose(fp);


	/*
	 * build map_national_utf16.c
	 */
	fp = fopen("map_national_utf16.c", "w");
	if (NULL == fp) {
		perror("fopen: map_national_utf16.c");
		exit(1);
	}
	dump_n_u_map(fp, "csc_national_utf16_map", ct_map_conf_first);
	fclose(fp);


	/*
	 * dump map into C source file
	 */
	for (ctmp = ct_map_conf_first; NULL != ctmp; ctmp = ctmp->next) {
		fp = fopen(ctmp->path_u_n, "w");
		if (NULL == fp) {
			perror("fopen");
			fprintf(stderr, "cannot open %s\n", ctmp->path_u_n);
			exit(1);
		}
		dump_cs_conversion_map(fp, ctmp->enc_name, ctmp->enc_gl_gr_str,
				       ctmp->extended_segment,
				       2, ctmp->byte_number,
				       ctmp->design_name, ctmp->design_str,
				       ctmp->design_length,
				       ctmp->cs_u_n_map_name,
				       *(cs_u_n_map + ctmp->cs_num), ctmp->cs_id);
		fclose(fp);

		fp = fopen(ctmp->path_n_u, "w");
		if (NULL == fp) {
			perror("fopen");
			fprintf(stderr, "cannot open %s\n", ctmp->path_n_u);
			exit(1);
		}
		dump_cs_n_u_map(fp, ctmp->enc_name, ctmp->enc_gl_gr_str,
				ctmp->extended_segment,
				ctmp->byte_number,  2,
				ctmp->design_name, ctmp->design_str,
				ctmp->design_length,
				ctmp->cs_n_u_map_name,
				*(cs_n_u_map + ctmp->cs_num),
				*(cs_n_u_v_map + ctmp->cs_num),
				ctmp->cs_id);
		fclose(fp);

	}

	return 0;
}
