/*
 * The routines here generate legal Ada identifiers from
 * C identifiers.  All the klugy unique name munging is
 * done here as well
 */
#include <sys/types.h>
#include "ansi.h"
#include "config.h"
#include "host.h"
#include "allocate.h"
#include "ada_name.h"
#include "hash.h"
#include "files.h"
#include "il.h"

extern int auto_package;

#undef NULL
#define NULL			0

#define HTAB			512		/* unique name hash table size */

/*
 * Unique name hash table node type
 */
typedef struct uniq_name_t {
	char				*uname;
	int					 uord;
	hash_t				 uhash;
	struct uniq_name_t	*ulink;
} uniq_name_t;

static uniq_name_t *hash_table[HTAB];

/*
 * allocator for unique name hash table nodes
 */
static uniq_name_t*
new_uniq_name()
{
	static uniq_name_t *free = NULL;
	static int free_index;

	if (free == NULL || free_index > 63) {
		free = (uniq_name_t*) allocate(sizeof(uniq_name_t) * 64);
		free_index = 0;
	}

	return &free[free_index++];
}

/*
 * Function to determine if a name is an Ada83 keyword
 */
int
is_ada_keyword(name)
	char *name;
{
#ifdef PUBLIC
	struct resword {char *name; short token;};
	extern struct resword *ada_rsvd();
	struct resword *r;
#endif
	char buf[16];
	char *p;
	
	assert(name != NULL);

	if (strlen(name) > 9) {
		return 0;
	}

	for (p = buf; *name; name++) {
		*p++ = lcase(*name);
	}
	*p = 0;

#ifdef PUBLIC
	return ada_rsvd(buf, p-buf) != NULL;
#else
	return ada_rsvd(buf) != -1;
#endif
}

/*
 * Function to generate a legal Ada83 identifier
 */
void
make_ada_identifier(name, buf)
	char *name, *buf;
{
	assert(name != NULL);
	assert(buf != NULL);

	while (*name == '_' || *name == '$') {
		name++;
	}

	if (*name == 0) {
		strcpy(buf, "YIKES");
	}

	while (*name) {
		switch (*name) {
		  case '$':
			name++;
			break;
		  case '_':
			if (name[1] != '_' && name[1] != 0) {
				*buf++ = *name;
			}
			name++;
			break;
		  default:
			*buf++ = *name++;
			break;
		}
	}

	*buf = 0;
}

/*
 * Function to determine if an identifier has already
 * been used.
 */
static uniq_name_t*
find_uniq(name, unit)
	char *name;
	int unit;
{
	uniq_name_t *un;
	hash_t hash;
	int index;

	hash = lcase_hash(name);
	index = hash & (HTAB-1);

	if (!auto_package) {
		unit = 0;
	}

	for (un = hash_table[index]; un; un = un->ulink) {
		if (un->uord == unit && un->uhash == hash && !lcasecmp(un->uname, name)) {
			return un;
		}
	}

	return NULL;
}

int
is_uniq_name(name, unit)
	char *name;
	int unit;
{
	assert(name != NULL);
	return find_uniq(name,unit) == NULL;
}

char*
uniq_name(name, unit)
	char *name;
	int unit;
{
	char buf[2048];
	uniq_name_t *un;
	int index, i, j, k;
	int last;

	strcpy(buf, name);

	if (find_uniq(name, unit) == NULL) {
		goto done;
	}

	strcat(buf, "_c");
	last = strlen(buf);

	buf[last+1] = 0;
	for (i = '0'; i <= '9'; i++) {
		buf[last] = i;
		if (find_uniq(buf) == NULL) {
			goto done;
		}
	}

	buf[last+2] = 0;
	for (i = '0'; i <= '9'; i++) {
		buf[last] = i;
		for (j = '0'; j <= '9'; j++) {
			buf[last+1] = j;
			if (find_uniq(buf) == NULL) {
				goto done;
			}
		}
	}

	buf[last+3] = 0;
	for (i = '0'; i <= '9'; i++) {
		buf[last] = i;
		for (j = '0'; j <= '9'; j++) {
			buf[last+1] = j;
			for (k = '0'; k <= '9'; k++) {
				buf[last+2] = k;
				if (find_uniq(buf) == NULL) {
					goto done;
				}
			}
		}
	}

	assert(0);

  done:

	un = new_uniq_name();
	un->uname = new_string(buf);
	un->uhash = common_hash(buf);
	un->uord = (auto_package) ? unit : 0;

	index = un->uhash & (HTAB-1);

	un->ulink = hash_table[index];
	hash_table[index] = un;

	return un->uname;
}

/*
 * I do a half-hearted attempt at preserving the case
 * of the original C identifier.  This is done so that
 * SIGINT doesn't turn into sigint.
 */
ident_case_t
id_case(id)
	char *id;
{
	if (id[0] >= 'A' && id[0] <= 'Z') {
		if (id[1] >= 'A' && id[1] <= 'Z') {
			return Upper;
		}
		return Cap;
	}
	return Lower;
}

static void
uppercase(id)
	char *id;
{
	char c;

	for (; *id; id++) {
		c = *id;
		if (c >= 'a' && c <= 'z') {
			*id = 'A' + (c - 'a');
		}
	}
}

static void
capitalize(id)
	char *id;
{
	char *prev;
	char c;

	for (prev = id; *id; id++) {
		if (*id == '_') {
			c = *prev;
			if (c >= 'a' && c <= 'z') {
				*prev = 'A' + (c - 'a');
			}
			prev = id + 1;
		}
	}
}

void
id_format(id, icase)
	char *id;
	ident_case_t icase;
{
	switch (icase) {
	  case Upper:
		uppercase(id);
		break;
	  case Cap:
		capitalize(id);
		break;
	}
}

char*
ada_name(name, unit)
	char *name;
	int unit;
{
	char buf[2048];
	char *p;
	ident_case_t icase;

	for (; *name == '_'; name++);

	if (*name == 0) {
		name = "YIKES";
	}

	if (is_ada_keyword(name)) {
		icase = id_case(name);

		strcpy(buf, ADA_RESERVED_PREFIX);
		strcat(buf, name);
	}
	else {
		switch (name[0]) {
		  case ENUM_PREFIX:
			strcpy(buf, ENUM_TYPE_PREFIX);
			name++;
			break;
		  case STRUCT_PREFIX:
			strcpy(buf, STRUCT_TYPE_PREFIX);
			name++;
			break;
		  case UNION_PREFIX:
			strcpy(buf, UNION_TYPE_PREFIX);
			name++;
			break;
		  default:
			buf[0] = 0;
			break;
		}

		icase = id_case(name);

		for (p = buf; *p; p++);

		make_ada_identifier(name, p);
	}

	if (unit == -1) {
		p = new_string(buf);
	}
	else {
		p = uniq_name(buf, unit);
	}

	id_format(p, icase);

	return p;
}


syntax highlighted by Code2HTML, v. 0.9.1