/*
 * Copyright (C) 1985-1992  New York University
 * Copyright (C) 1994 George Washington University
 * 
 * This file is part of the GWAdaEd system, an extension of the Ada/Ed-C
 * system.  See the Ada/Ed README file for warranty (none) and distribution
 * info and also the GNU General Public License for more details.
 */



/* liblist.c: translation of code generator read.stl*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "config.h"
#include "MacMemory.h"

/* An IFILE is a FILE with a header block at the front consisting of
 * the following as defined in the struct fh.
 */
typedef struct fh {
	char	fh_mode;        /* mode: 'r' or 'w' */
    long	fh_slots;	/* offset from start of slots info */
    long	fh_units_end;   /* offset from start of end of units info */
    FILE	*fh_file;	/* associated file when open */
} IFILE;


#include "MacAdalib.h"

static void load_library(IFILE *file, Library *lib);
static void get_local_ref_maps(IFILE *, int);
static long get_cde_slots(IFILE *, Library *lib);
static void get_slot(IFILE *, char *, LSlot *slot);
static char *unit_name_name(char *);
static char	*convert_date(char *);
static Boolean is_subunit(char *);
UnitKind unit_name_kind(char *uname);
UnitKind stub_name_kind(char *sname);


long ifseek(IFILE *ifile, char *desc, long offset, int ptr);
long iftell(IFILE *ifile);
void ifclose(IFILE *ifile);
IFILE *libopen(char *filename);
void	libclose(IFILE *f);
char *strjoin(char *s1, char *s2);

int getnum(IFILE *, char *);
char *getstr(IFILE *, char *);
void freeslot(LSlot *slot);

#define Free(p) if (p) { mfree(p); p = NULL; }


static OSErr err;

int adalib(char *fullPath, Library *lib)
{
char *libname;
IFILE *libfile;


	assert(fullPath);
	assert(lib);

	err = noErr;

	/******** THIS IS THE MAC VERSION OF THIS PROGRAM.
	 ********  IT HAS BEEN CONSIDERABLY MODIFIED.
	 ********/
	libname = strjoin(fullPath,"lib");	// add lib name
	
	libfile = libopen(libname);
	if (libfile) {
		load_library(libfile, lib);
		libclose(libfile);
		err = noErr;
	}

	mfree(libname);

	return err;
}

void freeadalib(Library lib)
{
	int i;

	//assert(lib);
	// Release units
	for (i = 1; i <= lib.unit_count; i++) {
		Free(lib.units[i-1].uname);
		Free(lib.units[i-1].aisname);
	}
	
	if (lib.unit_count > 0)
		Free(lib.units);
	lib.unit_count = 0;

	for (i = 1; i <= lib.stubs_count; i++) {
		Free(lib.stubs[i-1].uname);
		Free(lib.stubs[i-1].aisname);
	}
	if (lib.stubs_count > 0)
		Free(lib.stubs);
	lib.stubs_count = 0;

	freeslot(&lib.code);
	freeslot(&lib.data);
	freeslot(&lib.exceptions);
}

static void freeslot(LSlot *slot)
{
	int i;

	for (i = 1; i <= slot->numEntries; i++)
		Free(slot->entries[i-1].slot_name);
	if (slot->numEntries > 0)
		Free(slot->entries);
	slot->numEntries = 0;
}

static void load_library(IFILE *file, Library *lib)								/*;load_library*/
{
	/*
	 * retrieve information from LIBFILE
	 * Called only if lib_option and not newlib.
	 */

	int		i, j, n, m;
	char	*status_str, *tmp_str;
	char	*main_string;
	int		empty_unit_slots;
	int		ignore;
	int		comp_status;
	char	*uname;
	char	*comp_date;

	lib->unit_count = getnum(file, "lib-unit-count");
	lib->units = (LibUnit *)mmalloc(lib->unit_count * sizeof(LibUnit));
	if (lib->units == NULL) {
		lib->unit_count = 0;
		err = memFullErr;
		return;
	}

	n = getnum(file, "lib-n");
	empty_unit_slots = getnum(file, "lib-empty-slots");
	tmp_str = getstr(file, "lib-tmp-str");
	mfree(tmp_str);

	for (i = 1; i <= lib->unit_count; i++) {

		uname = getstr(file, "lib-unit-name");
		lib->units[i-1].uname = unit_name_name(uname);
		lib->units[i-1].ukind = unit_name_kind(uname);

		lib->units[i-1].unumber = getnum(file, "lib-unit-number");
		lib->units[i-1].aisname = getstr(file, "lib-ais-name");

		comp_date = getstr(file, "unit-date");
		strcpy(lib->units[i-1].comp_date, convert_date(comp_date));
		mfree(comp_date);

		lib->units[i-1].symbols = getnum(file, "lib-symbols");
		lib->units[i-1].nodes = getnum(file, "lib-nodes");
		lib->units[i-1].is_main = getnum(file, "lib-is-main");
		if (lib->units[i-1].is_main) {

			// changed
			//if (streq(unit_name_type(uname), "ma")) {
			if (lib->units[i-1].ukind == kBindingUnit) {

				//main_string = "(Interface)";
				lib->units[i-1].mkind = kMainInterface;
			}
			else {
				//main_string = "(Main)";
				lib->units[i-1].mkind = kMain;
			}
		}
		else {
			//main_string = "";
			lib->units[i-1].mkind = kNotMain;
		}

		comp_status = getnum(file, "lib-status");
		lib->units[i-1].cstatus = comp_status ? kActive : kObsolete;

		mfree(uname);
	}


	//printf("\n");
	lib->stubs_count = getnum(file, "lib-n");
	if (lib->stubs_count) {

		lib->stubs = (Stubs *) mmalloc(lib->stubs_count * sizeof(Stubs));
		if (lib->stubs == NULL) {
			lib->stubs_count = 0;
			err = memFullErr;
		}
		else {

			for (i = 1; i <= lib->stubs_count; i++) {
				lib->stubs[i-1].uname = getstr(file, "lib-unit-name");
				lib->stubs[i-1].aisname = getstr(file, "lib-ais-name");
				lib->stubs[i-1].parent = getnum(file, "lib-parent");
				lib->stubs[i-1].cur_level = getnum(file, "lib-cur-level");
				lib->stubs[i-1].m = getnum(file, "stub-file-size");
				for (j = 1; j <= lib->stubs[i-1].m; j++)
					ignore = getnum(file, "stub-file");
			}
		}
	}

#ifdef TBSL
	lib->prec_count = getnum(file, "precedes-map-size");
	lib->prec = (PrecMapSize *)mmalloc(lib->prec_count * sizeof(PrecMapSize));

	printf("precedes map\n");
	for (i = 1; i <= lib->prec_count; i += 2) {
		lib->prec[i-1].dom = getnum(file, "precedes-map-dom");
		lib->prec[i-1].m = getnum(file, "precedes-map-nelt");

		printf("  %4d:", dom);
		lib->prec[i-1].range = (int *)mmalloc(lib->prec[i-1].m * sizeof(int));

		for (j = 1; j <= lib->prec[i-1].m; j++) {
			lib->prec[i-1].range[j-1] = getnum(file, "precedes-map-ent");
			printf(" %4d", range);

		}

		printf("\n");

	}
	lib->compTableSize = getnum(file, "compilation_table_size");
	if (lib->compTableSize) {

		lib->compTableEntry = (int *)mmalloc(lib->compTableSize * sizeof(int));
		printf("\ncompilation table\n");
		for (i = 1; i <= n; i++) {
			lib->compTableEntry[i-1] = (int) getnum(file, "compilation-table-ent");
			printf("  %d\n", lib->compTableEntry[i-1]);
		}
		printf("\n");
	}
	/* late_instances */
	lib->late_inst_count = getnum(file, "late-instances-size");
	if (lib->late_inst_count) {

		lib->late_inst_str = (char **) mmalloc(lib->late_inst_count *
			sizeof(char *));
		printf("late instances\n");
		for (i = 1; i <= lib->late_inst_count; i++) {
			lib->late_inst_str[i-1] = (char *) getstr(file, "late-instances-str");
			printf("  %s\n", lib->late_inst_str[i-1]);
		}
	}

	/* current code segment */
	lib->unit_size = getnum(file, "unit-size");
	printf("\ncurrent code segments\n");
	printf("  unit cs\n");
	lib->cs = (int *) mmalloc(lib->unit_size * sizeof(int));
	for (i = 1; i <= lib->unit_size; i++) {
		lib->cs[i-1] = getnum(file, "current-code-segment");
		if (lib->cs[i-1]) printf("   %d: %d\n", i, lib->cs[i-1]);
	}
	/* local reference maps */
	lib->local_ref_maps = getnum(file, "unit-size");
	get_local_ref_maps(file, lib->local_ref_maps);
	cde_pos = get_cde_slots(file, axq);


	/* could free axq_data_slots, etc., but keep for now */
	/* read out LIB_STUB map (always empty for now) */
	ifclose(file);
	return;
#endif

	get_cde_slots(file, lib);
	return;
}

static void get_local_ref_maps(IFILE *ifile, int units)	/*;get_local_ref_map*/
{
	int		unit, defined, i, off, n;
	int		sym_seq, sym_unit;

	printf("\nlocal reference maps\n");
	for (unit = 1; unit <= units; unit++) {
		/* ignore empty ref maps (predef units) and obselete units */
		defined = getnum(ifile, "local-ref-map-defined");
		if (!defined) continue;
		printf("%d: ", unit);
		n = getnum(ifile, "local-ref-map-size");
		n = n/2;
		for (i = 1; i <= n; i++) {
			sym_seq = getnum(ifile, "local-ref-map-sym-seq");
			sym_unit = getnum(ifile, "local-ref-map-sym-unit");
			off = getnum(ifile, "local-ref-map-off");
			/* if all three values are zero ignore this entry. It is a fake
			 * entry created by put_local_ref_map. see comment there.
			 */
			if (sym_seq == 0 && sym_unit == 0 && off == 0) continue;
			printf("%d %d %d ", sym_seq, sym_unit, off);
		}
		printf("\n");
	}
	printf("\n");
}

static long get_cde_slots(IFILE *file, Library *lib)	/*;get_cde_slots*/
{
	long	dpos;
	int		n_code, n_data, n_exception;


	dpos = file->fh_slots;

	/* position to start of slot info */
	ifseek(file, "get-cde-slots-start", dpos, 0);
	n_code = getnum(file, "n-code");
	n_data = getnum(file, "n-data");
	n_exception = getnum(file, "n-exception");

	get_slot(file, "code", &lib->code);
	get_slot(file, "data", &lib->data);
	get_slot(file, "exceptions", &lib->exceptions);
	return dpos; /* return offset of start of slot info */
}

static void get_slot(IFILE *file, char *name, LSlot *slot)
{
	/* This procedure reads in the SLOTS information. 
	 * Entries are Slots structures. nmax is guess at needed dimension,
	 * dim is set to dimension actually found.
	 */

	int i;

	slot->numEntries = getnum(file, "slot-entries");

	slot->entries = (SlotEntry *)mmalloc(slot->numEntries * sizeof(SlotEntry));
	if (slot->entries == NULL) {
		slot->numEntries = 0;
		err = memFullErr;
	}
	else {
		for (i = 1; i <= slot->numEntries; i++) {
			slot->entries[i-1].exists = getnum(file, "slot-exists");
			if (slot->entries[i-1].exists) {
				slot->entries[i-1].slot_seq = getnum(file, "slot-seq");
				slot->entries[i-1].slot_unit = getnum(file, "slot-unit");
				slot->entries[i-1].slot_number = getnum(file, "slot-number");
				slot->entries[i-1].slot_name = getstr(file, "slot_name");
#ifdef MONITOR
				slot->entries[i-1].slot_file = getstr(file, "slot_file");
#endif
			}
		}
	}
}

static char *convert_date(char *comp_date)					/*;convert_date*/
{
	static char new_date[15];
	int	i;

	if (comp_date == (char *)0)
		return(" ");
	comp_date++;
	comp_date++;
	for (i = 6; i < 8; i++)
		new_date[i] = *comp_date++;
	comp_date++;
	for (i = 0; i < 2; i++)
		new_date[i] = *comp_date++;
	new_date[2] = '/';
	comp_date++;
	for (i = 3; i < 5; i++)
		new_date[i] = *comp_date++;
	new_date[5] = '/';
	new_date[8] = ' ';
	comp_date++;
	for (i = 9; i < 14; i++)
		new_date[i] = *comp_date++;
	new_date[11] = ':';

	new_date[14] = '\0';
	return new_date;
}

static char *unit_name_name(char *u)						/*;unit_name_name*/
{
	int	n;
	char	*s1, *s2;

	n = strlen(u);
	if (n <= 2)
		return (char *)0;

	s1 = u+2; 				/* point to start of name*/
	s2 = strchr(s1, '.'); 	/* look for dot after first name */
	if (s2 == (char *)0) 	/* if no dot take rest of string */
		s2 = u + n; 		/* find end */
	n = s2 - s1;
	s2 = mmalloc((unsigned) n+1);
	strncpy(s2, s1, n);
	s2[n] = '\0'; /* terminate result */
	return (s2);
}

static Boolean is_subunit(char *u)
{
	/* In C, IS_SUBUNIT is procedure is_subunit():
	 *	IS_SUBUNIT(na);           (#na > 2)                          endm;
	 */

	int	n;
	char	*s1, *s2;

	if (u == (char *)0)
		return false;


	n = strlen(u);
	if (n <= 2)
		return FALSE;
	s1 = u + 2; /* point to start of name*/
	s2 = strchr(s1, '.'); /* look for dot after first name */
	if (s2 == (char *)0) /* if no dot take rest of string */
		return FALSE;
	return TRUE; /* if subunit*/
}

void	formatted_name(LibUnit *unit, char *name)
{

	switch (unit->ukind) {
		case kSubUnit:
			strcpy(name, "proper body ");
			break;
		case kPackageSpec:
			strcpy(name, "package spec ");
			break;
		case kPackageBody:
			strcpy(name, "package body ");
			break;
		case kSubprogramSpec:
			strcpy(name, "subprogram spec ");
			break;
		case kSubprogram:
			strcpy(name, "subprogram ");
			break;
		case kBindingUnit:
			strcpy(name, "binding unit ");
			break;
		case kUnit:
			strcpy(name, "unit ");
			break;
		default:
			strcpy(name, "");
			break;
	}

	strcat(name, unit->uname);
}

void	formatted_stub(Stubs *unit, char *name)
{

	switch (unit->stype) {
		case kPackageTaskStub:
			strcpy(name, "package (task) stub ");
			break;
		case kSubprogramStub:
			strcpy(name, "subprogram stub ");
			break;
		case kStub:
			strcpy(name, "stub ");
			break;
		default:
			strcpy(name, "INVALID TYPE ");
			break;
	}

	strcat(name, unit->uname);
}

UnitKind unit_name_kind(char *uname)
{
	int	n;
	char	kind[10];

	n = strlen(uname);
	if (n < 2) {
		kind[0] = '\0';
	}
	else {
		/* otherwise, return first two characters */
		kind[0] = uname[0];
		kind[1] = uname[1];
		kind[2] = '\0';
	}

	if (is_subunit(uname))	 		  return kSubUnit;
	else if (strncmp(kind, "sp", 2) == 0)  return kPackageSpec;
	else if (strncmp(kind, "bo", 2) == 0)  return kPackageBody;
	else if (strncmp(kind, "ss", 2) == 0)  return kSubprogramSpec;
	else if (strncmp(kind, "su", 2) == 0)  return kSubprogram;
	else if (strncmp(kind, "ma", 2) == 0)  return kBindingUnit;
	else							  return kUnit;

}

UnitKind stub_name_kind(char *sname)
{
	int	n;
	char	kind[10];

	n = strlen(sname);
	if (n < 2) {
		kind[0] = '\0';
	}
	else {
		/* otherwise, return first two characters */
		kind[0] = sname[0];
		kind[1] = sname[1];
		kind[2] = '\0';
	}

	if (is_subunit(sname))	 		  return kSubUnit;
	else if (strncmp(kind, "sp", 2) == 0)  return kPackageSpec;
	else if (strncmp(kind, "bo", 2) == 0)  return kPackageBody;
	else if (strncmp(kind, "ss", 2) == 0)  return kSubprogramSpec;
	else if (strncmp(kind, "su", 2) == 0)  return kSubprogram;
	else if (strncmp(kind, "ma", 2) == 0)  return kBindingUnit;
	else							  return kUnit;

}


char *strjoin(char *s1, char *s2)
{
	/* return string obtained by concatenating argument strings
	 * watch for either argument being (char *)0 and treat this as null string
	 */

	char   *s;

	if (s1 == (char *)0) s1= "";
	if (s2 == (char *)0) s2 = "";
	s = mmalloc((unsigned) strlen(s1) + strlen(s2) + 1);
	strcpy(s, s1);
	strcat(s, s2);
	return s;
}

/**** File Routines (borrowed from misc.c and slightly modified ****/

IFILE *libopen(char *fname)
{
	IFILE  *ifile;
	FILE  *file;
	int   nr;


	file = fopen(fname, "rb");		// read in binary
	if (file == NULL) {
		err = fnfErr;
		return NULL;
	}

	ifile = (IFILE *) mmalloc(sizeof(IFILE));
	if (ifile) {
		nr = fread((char *) ifile, sizeof(IFILE), 1, file);
	
		if (nr != 1) {
			fclose(file);
			err = ioErr;
			return NULL;
		}
	
		ifile->fh_file = file;
		ifile->fh_mode = 'r';
	}

	return ifile;
}

void	libclose(IFILE *f)
{

	if (f->fh_file)
		fclose(f->fh_file);
	mfree(f);
}


long ifseek(IFILE *ifile, char *desc, long offset, int ptr)		/*;ifseek*/
{
	long begpos, endpos, seekval;
	begpos = iftell(ifile);
	seekval = fseek(ifile->fh_file, offset, ptr);
	// assume everything will be ok!
	//if (seekval == -1)
	//	chaos("ifseek: improper seek");

	endpos = iftell(ifile);
	return endpos;
}

long iftell(IFILE *ifile)									/*;iftell*/
{
	/* ftell, but arg is IFILE */
	return ftell(ifile->fh_file);
}

void ifclose(IFILE *ifile)									/*;ifclose*/
{
	FILE *file;


	file = ifile->fh_file;
	if (file == NULL)
		return;

	fclose(file);
	ifile->fh_file = (FILE *)0;
}

int getnum(IFILE *ifile, char *desc)								/*;getnum*/
{
	/* read integer (only 2 bytes) from input file */

	short si;
	int n = 0;

	fread((char *) &si, sizeof(short), 1, ifile->fh_file);
	n = si;
	return n;
}

char *getstr(IFILE *ifile, char *desc)							/*;getstr*/
{
	char	*s, *p;
	int		n, i;


	n = getnum(ifile, "");
	if (n == 0) return (char *)0;
	s = (char *) mmalloc((unsigned) n);
	p = s;
	for (i = 1; i < n; i++) {
		*p++ = getc(ifile->fh_file);
	}
	*p = '\0'; /* set end of string*/

	return s;
}

