/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *    		 Crown Copyright (c) 1997
 *
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/tools/tld/name-key.c,v 1.11 2005/10/18 15:31:06 stefanf Exp $
 */


/*** name-key.c --- External name key ADT.
 *
 ** Author: Steve Folkes <smf@hermes.mod.uk>
 *
 *** Commentary:
 *
 * This file implements the external name key routines used by the TDF linker.
 *
 *** Change Log:*/

/****************************************************************************/

#include "msgcat.h"

#include "name-key.h"
#include "syntax.h"

#include "solve-cycles.h"

/*--------------------------------------------------------------------------*/

static BoolT
name_key_parse_hex_char(char *name, char *c_ref)
{
	char result;
	char c;
	int  value;

	if ((c = name [0]), ((value = syntax_value (c)) != SYNTAX_NO_VALUE)) {
		result = (char) ((unsigned) value << 4);
	} else {
		return (FALSE);
	}
	if ((c = name [1]), ((value = syntax_value (c)) != SYNTAX_NO_VALUE)) {
		result |= (char) value;
	} else {
		return (FALSE);
	}
	*c_ref = result;
	return (TRUE);
}

static BoolT
name_key_parse_escaped(char **name_ref, char *c_ref)
{
	char *name = (*name_ref);

	switch ((*++ name)) {
	case 'x': case 'X':
		if (!name_key_parse_hex_char (name, c_ref)) {
			return (FALSE);
		}
		name += 3;
		break;
	case 'n': case 'N':
		*c_ref = '\n';
		name ++;
		break;
	case 'r': case 'R':
		*c_ref = '\r';
		name ++;
		break;
	case 't': case 'T':
		*c_ref= '\t';
		name ++;
		break;
	case '0':
		*c_ref = '\0';
		name ++;
		break;
	case '\\': case '.': case '[': case ']':
		*c_ref = *name ++;
		break;
	default:
		return (FALSE);
	}
	*name_ref = name;
	return (TRUE);
}

static BoolT
name_key_parse_cstring_unique(NameKeyP key, char *name)
{
	unsigned length   = 1;
	char *tmp_name = name;
	NStringP components;
	unsigned i;

	while (*++ tmp_name) {
		if (*tmp_name == '.') {
			length ++;
		}
	}
	components = ALLOCATE_VECTOR (NStringT, length);
	length     = 0;
	for (;;) {
		DStringT dstring;

		name ++;
		dstring_init (&dstring);
		while ((*name != '.') && (*name != ']')) {
			if ((*name == '\0') || (*name == '[')) {
				dstring_destroy (&dstring);
				goto fail;
			} else if (*name == '\\') {
				char c;

				if (name_key_parse_escaped (&name, &c)) {
					dstring_append_char (&dstring, c);
				} else {
					dstring_destroy (&dstring);
					goto fail;
				}
			} else {
				dstring_append_char (&dstring, *name ++);
			}
		}
		dstring_to_nstring (&dstring, &(components [length ++]));
		if (*name == ']') {
			break;
		}
	}
	if (*name) {
  	fail:
		for (i = 0; i < length; i ++) {
			nstring_destroy (&(components [i]));
		}
		DEALLOCATE (components);
		return (FALSE);
	}
	name_key_init_unique (key, length);
	for (i = 0; i < length; i ++) {
		name_key_set_component (key, i, &(components [i]));
	}
	DEALLOCATE (components);
	return (TRUE);
}

static BoolT
name_key_parse_cstring_string(NameKeyP key, char *name)
{
	DStringT dstring;
	NStringT nstring;

	dstring_init (&dstring);
	while (*name) {
		if ((*name == '[') || (*name == ']') || (*name == '.')) {
			dstring_destroy (&dstring);
			return (FALSE);
		} else if (*name == '\\') {
			char c;

			if (name_key_parse_escaped (&name, &c)) {
				dstring_append_char (&dstring, c);
			} else {
				dstring_destroy (&dstring);
				return (FALSE);
			}
		} else {
			dstring_append_char (&dstring, *name ++);
		}
	}
	dstring_to_nstring (&dstring, &nstring);
	name_key_init_string (key, &nstring);
	return (TRUE);
}

static void
write_name_key_1(OStreamP ostream, NStringP nstring)
{
	unsigned length = nstring_length (nstring);
	char *bytes = nstring_contents (nstring);

	while (length --) {
		switch (*bytes) {
		case '[': case ']': case '.': case '\\':
			write_char (ostream, '\\');
			FALL_THROUGH;
		default:
			write_char (ostream, *bytes);
		}
		bytes ++;
	}
}

/*--------------------------------------------------------------------------*/

void
name_key_init_string(NameKeyP key, NStringP string)
{
	key->type = KT_STRING;
	nstring_assign (&(key->u.string), string);
}

void
name_key_init_unique(NameKeyP key, unsigned components)
{
	key->type                = KT_UNIQUE;
	key->u.unique.length     = components;
	key->u.unique.components = ALLOCATE_VECTOR (NStringT, components);
}

BoolT
name_key_parse_cstring(NameKeyP key, char *name)
{
	if (*name == '[') {
		return (name_key_parse_cstring_unique (key, name));
	} else {
		return (name_key_parse_cstring_string (key, name));
	}
}

void
name_key_set_component(NameKeyP key, unsigned component, NStringP string)
{
	ASSERT ((key->type == KT_UNIQUE) && (component < key->u.unique.length));
	nstring_assign (&(key->u.unique.components [component]), string);
}

NameKeyTypeT
name_key_type(NameKeyP key)
{
	return (key->type);
}

NStringP
name_key_string(NameKeyP key)
{
	ASSERT (key->type == KT_STRING);
	return (&(key->u.string));
}

unsigned
name_key_components(NameKeyP key)
{
	ASSERT (key->type == KT_UNIQUE);
	return (key->u.unique.length);
}

NStringP
name_key_get_component(NameKeyP key, unsigned component)
{
	ASSERT ((key->type == KT_UNIQUE) && (component < key->u.unique.length));
	return (&(key->u.unique.components [component]));
}

unsigned
name_key_hash_value(NameKeyP key)
{
#ifdef __TenDRA__
	unsigned hash_value; /* "tcc" complains if this is initialised */
#else
	unsigned hash_value = 0; /* "gcc" complains if this is not initialised */
#endif /* defined (__TenDRA__) */
	unsigned components;
	unsigned i;

	switch (key->type) EXHAUSTIVE {
	case KT_STRING:
		hash_value = nstring_hash_value (&(key->u.string));
		break;
	case KT_UNIQUE:
		components = key->u.unique.length;
		hash_value = components;
		for (i = 0; i < components; i ++) {
			hash_value += nstring_hash_value (&(key->u.unique.components [i]));
		}
		break;
	}
	return (hash_value);
}

BoolT
name_key_equal(NameKeyP key1, NameKeyP key2)
{
	unsigned components;
	unsigned i;

	if (key1->type != key2->type) {
		return (FALSE);
	}
	switch (key1->type) EXHAUSTIVE {
	case KT_STRING:
		return (nstring_equal (&(key1->u.string), &(key2->u.string)));
	case KT_UNIQUE:
		if ((components = key1->u.unique.length) != key2->u.unique.length) {
			return (FALSE);
		}
		for (i = 0; i < components; i ++) {
			if (!nstring_equal (&(key1->u.unique.components [i]),
								&(key2->u.unique.components [i]))) {
				return (FALSE);
			}
		}
		break;
	}
	return (TRUE);
}

void
name_key_assign(NameKeyP to, NameKeyP from)
{
	switch (to->type = from->type) EXHAUSTIVE {
	case KT_STRING:
		nstring_assign (&(to->u.string), &(from->u.string));
		break;
	case KT_UNIQUE:
		to->u.unique.length     = from->u.unique.length;
		to->u.unique.components = from->u.unique.components;
		break;
	}
}

void
name_key_copy(NameKeyP to, NameKeyP from)
{
	unsigned components;
	unsigned i;

	switch (to->type = from->type) EXHAUSTIVE {
	case KT_STRING:
		nstring_copy (&(to->u.string), &(from->u.string));
		break;
	case KT_UNIQUE:
		components = to->u.unique.length = from->u.unique.length;
		to->u.unique.components = ALLOCATE_VECTOR (NStringT, components);
		for (i = 0; i < components; i ++) {
			nstring_copy (&(to->u.unique.components [i]),
						  &(from->u.unique.components [i]));
		}
		break;
	}
}

void
name_key_destroy(NameKeyP key)
{
	unsigned components;
	unsigned i;

	switch (key->type) EXHAUSTIVE {
	case KT_STRING:
		nstring_destroy (&(key->u.string));
		break;
	case KT_UNIQUE:
		components = key->u.unique.length;
		for (i = 0; i < components; i ++) {
			nstring_destroy (&(key->u.unique.components [i]));
		}
		DEALLOCATE (key->u.unique.components);
		break;
	}
}

void
write_name_key(OStreamP ostream, NameKeyP key)
{
	char     sep = '[';
	unsigned components;
	unsigned i;

	switch (key->type) EXHAUSTIVE {
	case KT_STRING:
		write_name_key_1 (ostream, &(key->u.string));
		break;
	case KT_UNIQUE:
		components = key->u.unique.length;
		for (i = 0; i < components; i ++) {
			NStringP nstring = &(key->u.unique.components [i]);

			write_char (ostream, sep);
			write_name_key_1 (ostream, nstring);
			sep = '.';
		}
		write_char (ostream, ']');
		break;
	}
}

/*--------------------------------------------------------------------------*/

void
name_key_list_init(NameKeyListP list)
{
	list->head = NIL (NameKeyListEntryP);
}

void
name_key_list_add(NameKeyListP list, NameKeyP key)
{
	NameKeyListEntryP entry;

	for (entry = name_key_list_head (list); entry;
		 entry = name_key_list_entry_next (entry)) {
		if (name_key_equal (key, &(entry->key))) {
			name_key_destroy (key);
			return;
		}
	}
	entry       = ALLOCATE (NameKeyListEntryT);
	entry->next = list->head;
	name_key_assign (&(entry->key), key);
	list->head  = entry;
}

NameKeyListEntryP
name_key_list_head(NameKeyListP list)
{
	return (list->head);
}

NameKeyP
name_key_list_entry_key(NameKeyListEntryP entry)
{
	return (&(entry->key));
}

NameKeyListEntryP
name_key_list_entry_next(NameKeyListEntryP entry)
{
	return (entry->next);
}

/*--------------------------------------------------------------------------*/

void
name_key_pair_list_init(NameKeyPairListP list)
{
	list->head = NIL (NameKeyPairListEntryP);
}

BoolT
name_key_pair_list_add(NameKeyPairListP list, NameKeyP from, NameKeyP to)
{
	NameKeyPairListEntryP entry;

	for (entry = name_key_pair_list_head (list); entry;
		 entry = name_key_pair_list_entry_next (entry)) {
		if (name_key_equal (from, &(entry->from))) {
			return (FALSE);
		}
	}
	entry       = ALLOCATE (NameKeyPairListEntryT);
	entry->next = list->head;
	name_key_assign (&(entry->from), from);
	name_key_assign (&(entry->to), to);
	list->head  = entry;
	return (TRUE);
}

NameKeyPairListEntryP
name_key_pair_list_head(NameKeyPairListP list)
{
	return (list->head);
}

NameKeyP
name_key_pair_list_entry_from(NameKeyPairListEntryP entry)
{
	return (&(entry->from));
}

NameKeyP
name_key_pair_list_entry_to(NameKeyPairListEntryP entry)
{
	return (&(entry->to));
}

NameKeyPairListEntryP
name_key_pair_list_entry_next(NameKeyPairListEntryP entry)
{
	return (entry->next);
}
