#include <stdio.h>
#include <string.h>
#include "link.h"


#define ALLOC_SYM	10000	// Nombre de symbole allou‚ par chunk
/*
	Liste des symboles
	Pour chaque symbole, on retient son module de provenance.
*/
PUBLIC SYMBOLS::SYMBOLS()
{
	sym.cur = NULL;
	memset (hash,0,sizeof(hash));
	allocsym();
}

/*
	Alloue un nouveau buffer d'accumulation pour les symboles
*/
PROTECTED void SYMBOLS::allocsym()
{
	LIST_SYMBOL *list = (LIST_SYMBOL*) malloc_err (sizeof(LIST_SYMBOL)
		+ALLOC_SYM*sizeof(SYMBOL),1);
	list->next = sym.cur;
	sym.cur = list;
	sym.nb  = 0;
	sym.ptacc = list->alloc;
	sym.lastacc = list->alloc+ALLOC_SYM;
}

/*
	Ajoute un symbole ou localise.
	Ce symbole est soit publique ou un external (requis).

	Retourne le symbole localis‚ ou ajout‚
*/
PUBLIC SYMBOL *SYMBOLS::add (
	const char *name,
	MODULE *module,			// Module qui d‚clare ce symbole ou NULL.
	SYM_STATUS status,
	int &module_requis,		// Contiendra != 0 si on ‚tablis que le module
							// en traitement est requis par un autre
							// module vue pr‚c‚demment.
							// Assume qu'il y a deja une valeure dedans
	int is_common)
{
	// Recherche le nom
	unsigned hashval = 0;
	const char *pt = name;
	while (*pt != '\0'){
		hashval = (hashval << 1) + *pt++;
	}
	hashval %= 2048;
	SYMBOL **ffind = hash + hashval;
	SYMBOL *find = *ffind;
	while (find != NULL){
		if (strcmp(find->name,name)==0) break;
		find = find->next;
	}
	if (find == NULL){
		// Ajoute a la liste
		if (sym.ptacc == sym.lastacc) allocsym();
		find = sym.ptacc++;
		sym.nb++;
		find->name = alloctxt_add (name);
		find->module = NULL;
		find->next = *ffind;
		*ffind = find;
		find->force = 0;
		find->is_common = 0;
		find->is_dup = 0;
		if (status == SYM_REQUIS){
			find->requis = 1;
			find->defini = 0;
			find->vue_avant = 0;
		}else if (status == SYM_PASUTIL){
			find->requis = 0;
			find->defini = 0;
			find->vue_avant = 0;
		}else{
			find->requis = 0;
			find->defini = 1;
			find->vue_avant = 1;
			find->is_common = is_common;
			find->module = module;
		}
	}else{
		// Deja la
		if (status == SYM_DEFINI){
			if (find->defini){
				// Le symbole est d‚j… d‚finie
				// On ne l'insere pas dans le hashing et on note
				// qu'il est en double. Si jamais un module doit
				// ˆtre charg‚ et possŠde un symbole "duplicate"
				// on pourra signale le probleme.
				if (sym.ptacc == sym.lastacc) allocsym();
				find = sym.ptacc++;
				sym.nb++;
				find->name = alloctxt_add (name);
				find->next = NULL;
				find->force = 0;
				find->is_common = 0;
				find->is_dup = 1;
				find->requis = 0;
				find->vue_avant = 0;
			}
			find->module = module;
			find->defini = 1;
			find->is_common = is_common;
			if (find->requis) module_requis = 1;
		}else if (status == SYM_REQUIS){
			find->requis = 1;
		}
	}
	return find;
}

/*
	Trouve les symboles qui doivent ˆtre "demander" au linker.
	On fait ca pour eviter un link multi-passe.

	Retourne le nombre de symbole plac‚ dans tb.
*/
PUBLIC int SYMBOLS::findforce (
	char **tb,		// tb peut ˆtre NULL
					// Ca permet de les compter
	int maxtb)
{
	int ret = 0;
	LIST_SYMBOL *list = sym.cur;
	int nbsym = sym.nb;
	while (list != NULL){
		SYMBOL *ptsym = list->alloc;
		for (int i=0; i<nbsym; i++, ptsym++){
			if (ptsym->force){
				if (tb != NULL)
					tb[ret++] = strdup_err (ptsym->name,1);
				if (ret >= maxtb) {
					fprintf(stderr, "Too many symbols: %d\n", ret);
					return ret;
				}
			}
		}
		nbsym = ALLOC_SYM;
		list = list->next;
	}
	return ret;
}

/*
	Pr‚sente toute la liste de symbole en m‚moire
*/
PUBLIC void SYMBOLS::dump (FILE *fout)
{
	LIST_SYMBOL *list = sym.cur;
	int nbsym = sym.nb;
	while (list != NULL){
		SYMBOL *ptsym = list->alloc;
		for (int i=0; i<nbsym; i++, ptsym++){
			fprintf (fout,"%s %d %d %d %d %d\n",ptsym->name,ptsym->requis
				,ptsym->defini,ptsym->vue_avant
				,ptsym->is_common,ptsym->force);
		}
		nbsym = ALLOC_SYM;
		list = list->next;
	}
}
