/*
 * 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/library.c,v 1.12 2005/10/18 15:31:06 stefanf Exp $
 */


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

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

#include "library.h"
#include "capsule.h"
#include "debug.h"
#include "exception.h"
#include "file-name.h"
#include "msgcat.h"
#include "tdf.h"
#include "tdf-write.h"

#include "solve-cycles.h"

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

static ExceptionP XX_library_error   = EXCEPTION ("error in TDF library");

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

static TDFReaderP
library_reader(LibraryP library)
{
	ASSERT (library->type == LT_INPUT);
	return (&(library->u.reader));
}

static TDFWriterP
library_writer(LibraryP library)
{
	ASSERT (library->type == LT_OUTPUT);
	return (&(library->u.writer));
}

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

static void
library_check_index_entry(LibraryP library, ShapeEntryP entry, BoolT need_dec,
	BoolT no_mult, NStringP shape_key, NameKeyP key, unsigned use,
	LibCapsuleP lib_capsule, NameTableP table)
{
	NameEntryP  name_entry  = name_table_add (table, key, entry);
	unsigned    name_use    = name_entry_get_use (name_entry);

	if (use & ~(U_USED | U_DECD | U_DEFD | U_MULT)) {
		MSG_lib_bad_usage (library, shape_key, key, use);
		THROW (XX_library_error);
		UNREACHED;
	} else if (no_mult && (use & U_MULT)) {
		MSG_lib_illegally_mult_defined (library, shape_key, key);
		THROW (XX_library_error);
		UNREACHED;
	} else if (need_dec &&
			   (((use & (U_DEFD | U_DECD)) == U_DEFD) ||
				((use & (U_MULT | U_DECD)) == U_MULT))) {
		MSG_lib_defined_but_not_declared (library, shape_key, key);
		THROW (XX_library_error);
		UNREACHED;
	}
	if ((use & U_DEFD) && (name_use & U_DEFD)) {
		LibCapsuleP definition = name_entry_get_lib_definition (name_entry);

		MSG_lib_multiply_defined (library, shape_key, key, definition);
	} else if ((use & U_MULT) && (name_use & U_MULT) &&
			   (!(use & U_DEFD)) && (!(name_use & U_DEFD))) {
		name_entry_set_lib_definition (name_entry, NIL (LibCapsuleP));
	} else if ((use & U_DEFD) ||
			   ((use & U_MULT) && (!(name_use & (U_MULT | U_DEFD))))) {
		name_entry_set_lib_definition (name_entry, lib_capsule);
	}
	debug_info_r_index_entry (key, use, name_use, name_entry_key (name_entry),
							  lib_capsule_name (lib_capsule));
	name_entry_merge_use (name_entry, use);
}

static unsigned
library_read_version_0_capsules(LibraryP library)
{
	TDFReaderP reader       = library_reader (library);
	unsigned   num_capsules = tdf_read_int (reader);
	unsigned   i;

	debug_info_r_start_capsules (num_capsules);
	library->num_capsules = num_capsules;
	library->capsules     = ALLOCATE_VECTOR (LibCapsuleT, num_capsules);
	for (i = 0; i < num_capsules; i ++) {
		NStringP contents = &(library->capsules [i].contents);
		NStringT nstring;
		unsigned length;

		tdf_read_string (reader, &nstring);
		if (nstring_contains (&nstring, '\0')) {
			MSG_null_in_file_name (library, &nstring);
			THROW (XX_library_error);
			UNREACHED;
		}
		library->capsules [i].name    = nstring_to_cstring (&nstring);
		library->capsules [i].library = library;
		library->capsules [i].loaded  = FALSE;
		length = tdf_read_int (reader);
		nstring_init_length (contents, length);
		tdf_read_bytes (reader, contents);
		debug_info_r_capsule (&nstring, length);
		nstring_destroy (&nstring);
	}
	return (num_capsules);
}

static void
library_read_version_0(LibraryP library, ShapeTableP shapes)
{
	TDFReaderP  reader       = library_reader (library);
	unsigned    num_capsules = library_read_version_0_capsules (library);
	ShapeEntryP token_entry  = shape_table_get_token_entry (shapes);
	ShapeEntryP tag_entry    = shape_table_get_tag_entry (shapes);
	unsigned    num_shapes   = tdf_read_int (reader);
	unsigned    i;

	debug_info_r_start_index (num_shapes);
	for (i = 0; i < num_shapes; i ++) {
		NStringT    name;
		ShapeEntryP entry;
		BoolT       need_dec;
		BoolT       no_mult;
		NameTableP  table;
		unsigned    num_names;
		unsigned    j;

		tdf_read_string (reader, &name);
		entry     = shape_table_add (shapes, &name);
		need_dec  = (entry == tag_entry);
		no_mult   = (entry == token_entry);
		table     = shape_entry_name_table (entry);
		num_names = tdf_read_int (reader);
		debug_info_r_start_shape_index (&name, num_names);
		for (j = 0; j < num_names; j ++) {
			NameKeyT    external_name;
			unsigned    use;
			unsigned    capsule_index;
			LibCapsuleP lib_capsule;

			tdf_read_name (reader, &external_name);
			use           = tdf_read_int (reader);
			capsule_index = tdf_read_int (reader);
			if (capsule_index >= num_capsules) {
				MSG_capsule_index_too_big (library, &name, &external_name,
										 capsule_index, num_capsules);
				THROW (XX_library_error);
				UNREACHED;
			}
			lib_capsule = &(library->capsules [capsule_index]);
			library_check_index_entry (library, entry, need_dec, no_mult,
									   &name, &external_name, use, lib_capsule,
									   table);
			name_key_destroy (&external_name);
		}
		nstring_destroy (&name);
	}
	tdf_read_eof (reader);
}

static void
library_extract_1(LibCapsuleP capsule, BoolT use_basename)
{
	char *old_name = lib_capsule_name (capsule);
	char *name = old_name;
	NStringP   contents = lib_capsule_contents (capsule);
	TDFWriterT writer;

	if (use_basename) {
		name = file_name_basename (name);
	}
	file_name_populate (name);
	if (tdf_writer_open (&writer, name)) {
		MSG_extracting_capsule (old_name, name);
		tdf_write_bytes (&writer, contents);
		tdf_writer_close (&writer);
	} else {
		MSG_cant_open_output_file (name);
	}
	if (use_basename) {
		DEALLOCATE (name);
	}
}

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

typedef void (*LibTypeProcP)(LibraryP, ShapeTableP);

static LibTypeProcP library_type_jump_table [] = {
	library_read_version_0
};

#define LIBRARY_TYPE_JUMP_TABLE_SIZE \
	((unsigned) (sizeof (library_type_jump_table) / sizeof (LibTypeProcP)))

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

static NStringP
library_magic(void)
{
	static NStringT const_magic;
	static BoolT    inited = FALSE;

	if (!inited) {
		nstring_copy_cstring (&const_magic, "TDFL");
		inited = TRUE;
	}
	return (&const_magic);
}

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

static void
library_read_header(LibraryP library)
{
	TDFReaderP reader        = library_reader (library);
	NStringP   const_magic   = library_magic ();
	unsigned   capsule_major = capsule_get_major_version ();
	NStringT   magic;
	unsigned   major;
	unsigned   minor;

	nstring_init_length (&magic, (unsigned) 4);
	tdf_read_bytes (reader, &magic);
	if (!nstring_equal (&magic, const_magic)) {
		MSG_library_bad_magic (library, &magic, const_magic);
		THROW (XX_library_error);
		UNREACHED;
	}
	nstring_destroy (&magic);
	major = tdf_read_int (reader);
	minor = tdf_read_int (reader);
	debug_info_r_lib_versions (major, minor);
	if (major < 4) {
		MSG_library_bad_version (library, major);
		THROW (XX_library_error);
		UNREACHED;
	} else if (capsule_major == 0) {
		capsule_set_major_version (major);
	} else if (capsule_major != major) {
		MSG_library_version_mismatch (library, capsule_major, major);
		THROW (XX_library_error);
		UNREACHED;
	}
	library->major = major;
	library->minor = minor;
	tdf_read_align (reader);
}

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

static void
library_write_header(LibraryP library)
{
	TDFWriterP writer      = library_writer (library);
	NStringP   const_magic = library_magic ();
	unsigned   major       = capsule_get_major_version ();
	unsigned   minor       = capsule_get_minor_version ();

	tdf_write_bytes (writer, const_magic);
	ASSERT (major >= 4);
	tdf_write_int (writer, major);
	tdf_write_int (writer, minor);
	debug_info_w_lib_versions (major, minor);
	tdf_write_align (writer);
}


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

char *
lib_capsule_name(LibCapsuleP capsule)
{
	return (capsule->name);
}

char *
lib_capsule_full_name(LibCapsuleP capsule)
{
	char *lib_name = library_name (capsule->library);
	unsigned lib_length = strlen (lib_name);
	char *name = lib_capsule_name (capsule);
	unsigned length     = strlen (name);
	char *full_name = ALLOCATE_VECTOR (char, lib_length + length + 3);
	char *tmp = full_name;

	(void) memcpy (tmp, lib_name, (size_t) lib_length);
	tmp += lib_length;
	*tmp = '(';
	tmp ++;
	(void) memcpy (tmp, name, (size_t) length);
	tmp += length;
	*tmp = ')';
	tmp ++;
	*tmp = '\0';
	return (full_name);
}

NStringP
lib_capsule_contents(LibCapsuleP capsule)
{
	return (&(capsule->contents));
}

BoolT
lib_capsule_is_loaded(LibCapsuleP capsule)
{
	return (capsule->loaded);
}

void
lib_capsule_loaded(LibCapsuleP capsule)
{
	capsule->loaded = TRUE;
}

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

void
write_lib_capsule_full_name(OStreamP ostream, LibCapsuleP capsule)
{
	write_cstring (ostream, library_name (capsule->library));
	write_char (ostream, '(');
	write_cstring (ostream, lib_capsule_name (capsule));
	write_char (ostream, ')');
}

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

LibraryP
library_create_stream_input(char *name)
{
	LibraryP library = ALLOCATE (LibraryT);

	library->type = LT_INPUT;
	if (!tdf_reader_open (library_reader (library), name)) {
		DEALLOCATE (library);
		return (NIL (LibraryP));
	}
	library->name     = name;
	library->complete = FALSE;
	return (library);
}

LibraryP
library_create_stream_output(char *name)
{
	LibraryP library = ALLOCATE (LibraryT);

	library->type = LT_OUTPUT;
	if (!tdf_writer_open (library_writer (library), name)) {
		DEALLOCATE (library);
		return (NIL (LibraryP));
	}
	library->name     = name;
	library->complete = FALSE;
	return (library);
}

char *
library_name(LibraryP library)
{
	return (library->name);
}

unsigned
library_num_capsules(LibraryP library)
{
	return (library->num_capsules);
}

LibCapsuleP
library_get_capsule(LibraryP library, unsigned capsule_index)
{
	ASSERT (capsule_index < library->num_capsules);
	return (&(library->capsules [capsule_index]));
}

unsigned
library_byte(LibraryP library)
{
	return (tdf_reader_byte (library_reader (library)));
}

void
library_content(LibraryP library, BoolT want_index, BoolT want_size,
	BoolT want_version)
{
	ShapeTableP shapes = shape_table_create ();

	library_read (library, shapes);
	if (library->complete) {
		unsigned i;

		if (want_version) {
			write_char     (ostream_output, '[');
			write_unsigned (ostream_output, library->major);
			write_cstring  (ostream_output, ", ");
			write_unsigned (ostream_output, library->minor);
			write_char     (ostream_output, ']');
			write_newline  (ostream_output);
		}
		for (i = 0; i < library->num_capsules; i ++) {
			LibCapsuleP capsule = &(library->capsules [i]);

			write_cstring (ostream_output, lib_capsule_name (capsule));
			if (want_size) {
				NStringP body = lib_capsule_contents (capsule);

				write_cstring (ostream_output, " (");
				write_unsigned (ostream_output, nstring_length (body));
				write_char (ostream_output, ')');
			}
			write_newline (ostream_output);
		}
		if (want_index) {
			shape_table_iter (shapes, shape_entry_show_content,
							  NULL);
		}
	}
}

void
library_extract_all(LibraryP library, BoolT use_basename)
{
	ShapeTableP shapes = shape_table_create ();

	library_read (library, shapes);
	if (library->complete) {
		unsigned i;

		for (i = 0; i < library->num_capsules; i ++) {
			LibCapsuleP capsule = &(library->capsules [i]);

			library_extract_1 (capsule, use_basename);
		}
	}
}

void
library_extract(LibraryP library, BoolT use_basename, BoolT match_basename,
	unsigned num_files, char **files)
{
	ShapeTableP shapes = shape_table_create ();

	library_read (library, shapes);
	if (library->complete) {
		unsigned i;

		for (i = 0; i < num_files; i ++) {
			BoolT    matched = FALSE;
			unsigned j;

			for (j = 0; j < library->num_capsules; j ++) {
				LibCapsuleP capsule = &(library->capsules [j]);
				char *file_name = (files [i]);
				char *lib_name  = lib_capsule_name (capsule);
				char *base_name = NULL;

				if (match_basename) {
					base_name = file_name_basename (lib_name);
				}
				if ((cstring_equal (file_name, lib_name)) ||
					(match_basename && cstring_equal (file_name, base_name))) {
					library_extract_1 (capsule, use_basename);
					matched = TRUE;
				}
				if (match_basename) {
					DEALLOCATE (base_name);
				}
			}
			if (!matched) {
				MSG_capsule_not_found (files [i], library_name (library));
			}
		}
	}
}

void
library_read(LibraryP library, ShapeTableP shapes)
{
	HANDLE {
		TDFReaderP reader = library_reader (library);
		unsigned   library_type;

		debug_info_r_start_library (library_name (library));
		library_read_header (library);
		library_type = tdf_read_int (reader);
		if (library_type >= LIBRARY_TYPE_JUMP_TABLE_SIZE) {
			MSG_lib_unknown_type (library, library_type);
			THROW (XX_library_error);
			UNREACHED;
		}
		debug_info_r_library_version (library_type);
		(*(library_type_jump_table [library_type])) (library, shapes);
		debug_info_r_end_library ();
		library->complete = TRUE;
	} WITH {
		ExceptionP exception = EXCEPTION_EXCEPTION ();

		debug_info_r_abort_library ();
		if ((exception != XX_tdf_read_error) &&
			(exception != XX_library_error)) {
			RETHROW ();
		}
	} END_HANDLE
		  }

void
library_write(LibraryP library, ShapeTableP shapes, unsigned num_capsules,
	CapsuleP *capsules)
{
	TDFWriterP writer     = library_writer (library);
	unsigned   num_shapes = 0;
	unsigned   i;

	debug_info_w_start_library (library_name (library));
	library_write_header (library);
	debug_info_w_library_version ((unsigned) 0);
	tdf_write_int (writer, (unsigned) 0);
	debug_info_w_start_capsules (num_capsules);
	tdf_write_int (writer, num_capsules);
	for (i = 0; i < num_capsules; i ++) {
		CapsuleP capsule = capsules [i];
		char *name = capsule_name (capsule);
		NStringP contents = capsule_contents (capsule);
		unsigned length = nstring_length (contents);
		NStringT nstring;

		debug_info_w_capsule (name, length);
		nstring_copy_cstring (&nstring, name);
		tdf_write_string (writer, &nstring);
		nstring_destroy (&nstring);
		tdf_write_int (writer, length);
		tdf_write_bytes (writer, contents);
	}
	shape_table_iter (shapes, shape_entry_do_lib_count,
					  (void *) &num_shapes);
	debug_info_w_start_index (num_shapes);
	tdf_write_int (writer, num_shapes);
	shape_table_iter (shapes, shape_entry_do_lib_write, (void *) writer);
	debug_info_w_end_library ();
}

void
library_close(LibraryP library)
{
	switch (library->type) EXHAUSTIVE {
	case CT_INPUT:
		tdf_reader_close (library_reader (library));
		break;
	case CT_OUTPUT:
		tdf_writer_close (library_writer (library));
		break;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1