#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
#include <locale.h>

#include "defines.h"
#include "lprintf.h"
#include "lang.h"
#define DMID 14
#include "debug.h"

#ifdef NO_THREADS
#else
#if DO_THREADS || _MT
#	define LANG_THREAD
#	include "thread.h"
#	ifdef SURGEMAIL
#		include "../dftp/common/mutex.h"
#	endif
#endif
#endif

#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE !FALSE
#endif

#include "vsnprintf.h"

/*void lang_seterr(Language *l, char *format, ...);*/
#ifdef LANG_THREAD
#	ifdef SURGEMAIL
		void lang_dolock() { mutex_wait(M_LANG); }
		void lang_unlock() { mutex_release(M_LANG); }
#	else
		static Mutex *m_lang;
		void lang_dolock() { th_mutex_get(m_lang); }
		void lang_unlock() { th_mutex_release(m_lang); }
#	endif
#else
#   define lang_dolock()
#   define lang_unlock()
#endif

extern int emsg(char *format,...);
extern int imsg(char *format,...);

Translations *lang_bin = NULL;
Translations *lang_web = NULL;

Translation *lang_find(Translations *l, char *phrase, int *index);
Translation *lang_new(Translations *l, char *phrase, int *index);

/* ignores case, ignores punctuation */
extern unsigned char charmap[];
int lang_hash(unsigned char *s, int max)		// MW: changed to unsigned char so debug character validator in sisalnum does not complain
{
	unsigned int total=0;
	int r;

	while (*s && !sisalnum(*s)) s++;
	while (*s) {
		total += charmap[*s++];
		while (*s && !sisalnum(*s)) s++;
		if (!*s) break;
		total += charmap[*s++] << 8;
		while (*s && !sisalnum(*s)) s++;
		if (!*s) break;
		total += charmap[*s++] << 16;
		while (*s && !sisalnum(*s)) s++;
		if (!*s) break;
		total += charmap[*s++] << 24;
		while (*s && !sisalnum(*s)) s++;
		if (!*s) break;
	}
    r = total % max;
    if (r<0) return -r;
    return r;
}

Translations *lang_init(int size) {
	Translations *l = calloc(1,sizeof(Translations));
	if (size) l->isize = size;
	else l->isize = 500;
	l->ref++;
#ifdef LANG_THREAD
#	ifdef SURGEMAIL
#	else
	m_lang = th_mutex_new("LANGUAGE");
#	endif
#endif
	return l;
}

int lang_allocate(Translations *l, int size) {
	if (l->size) return FALSE; //cannot re-allocate
	if (size <= 0) return FALSE; //invalid size
	l->table = calloc(size,sizeof(Translation *)); // -ve memory use recorded here!!!! 
	l->size = size;
	return TRUE;
}

void lang_free(Translations **l) {
	Translation *t,*n;
	int i;

	if (!l || !*l) return ;

	lang_dolock();
	if (--((*l)->ref) > 0) {
		lang_unlock();
		return ;
	}
	lang_unlock();

	if ((*l)->table) {
		for(i = 0; i < (*l)->size; i++) {
			for(t = (*l)->table[i]; t; t = n) {
				n = t->next;
				qpair_free(&t->translations);  // FREE CRASH HERE...
				free(t->phrase);
				free(t);
			}
		}
		free((*l)->table);
	}
	word_free(&(*l)->supported);
	free(*l);

#ifdef LANG_THREAD
#	ifdef SURGEMAIL
#	else
	th_mutex_free(m_lang);
#	endif
#endif
}

#define NODES_PER_ENTRY 8

int lang_load(Translations *l, char *file, Wordlist *only_load, int list_only, char *error, int bfsz) {
	char bf[BFSZ],language[BFSZ];
	Wordlist *support = NULL;
	Translation *t = NULL;	
	unsigned char *p;
	FILE *f;
	int i;

	f = fopen(file,"r");
	if (!f) {
		if (error) lprintf(error,bfsz,"failed to read (%s): %s",file,strerror(errno));
		return FALSE;
	}
	lang_dolock();
	while(fgets(bf,BFSZ,f)) {
		if (bf[0] == '#') continue;
		if (strncmpnc(bf,"support:",8)==0) {
			if (only_load) continue;
			for(p = strip_eol(&bf[8]); sisspace(*p); p++) {}			
			word_add(&support,p);
			continue;
		}
		if (strncmpnc(bf,"entries:",8)==0) {
			i = atoi(&bf[8]);
			if (i/NODES_PER_ENTRY) lang_allocate(l,i/NODES_PER_ENTRY);
			else lang_allocate(l,i);
		}
		p = strip_eol(bf);
		if (!p[0]) continue;
		if (!sisspace(p[0]) || !strchr(p,':')) t = NULL;
		if (!t && !list_only) {
			t = lang_find(l,p,&i);
			if (!t) t = lang_new(l,p,&i);
			if (strstr(p,"Quota usage - matched"))
				p = p;
			continue;
		}

		for(; sisspace(*p); p++);
		lib_copy_upto((char **) &p,':',language,BFSZ);
		if (*p != ':') continue;
		strlwr(language);
		
		if (only_load) {
			if (!word_findnc(only_load,language))
				continue;
		}
		else if (support) {
			if (!word_findnc(support,language))
				continue;
		}

		for(p++; sisspace(*p); p++);
		// Offset to avoid clash
		if (p[0]) word_add_nodup(&l->supported,language);

		if (list_only) continue;
		qpair_add(&t->translations,language,p);
	}
	fclose(f);
	word_free(&support);
	lang_unlock();
	return TRUE;
}

void lang_write(FILE *f, Translation *t, Wordlist *languages) {
	Wordlist *w;
	QPair *p;

	fprintf(f,"%s\n",t->phrase);

	// How do I iterate 'translations' a qpairlist..

	for(p = t->translations->start; p; p = p->next) {
		if (!word_find(languages,p->var)) continue;
		fprintf(f,"\t%c%s: %s\n",toupper(p->var[0]),&p->var[1],p->val);
	}

	for(w = languages; w; w = w->next) {
		if (!qpair_find_null(t->translations,w->word))
			fprintf(f,"\t%c%s: \n",toupper(w->word[0]),&w->word[1]);
	}

	fprintf(f,"\n");
}

int lang_save(Translations *l, char *file, char *error, int bfsz, void (*header)(FILE *f, void *), void *hdata) {
	Translation *t;
	FILE *f;
	int i;

	f = fopen(file,"w");
	if (!f) {
		if (error) lprintf(error,bfsz,"failed to write (%s): %s",file,strerror(errno));
		return FALSE;
	}

	if (header) header(f,hdata);

	lang_dolock();
	for(i = 0; i < l->size; i++) {
		for(t = l->table[i]; t; t = t->next) {
			lang_write(f,t,l->supported);
		}
	}
	fclose(f);
	lang_unlock();
	return TRUE;
}

void lang_iterate(Translations *l, int (*callback)(void *, Translation *t), void *data)
{
	Translation *t;
	int i;

	if (!l) return ;
	if (!callback) return ;

	for(i = 0; i < l->size; i++) {
		for(t = l->table[i]; t; t = t->next) {
			if (!callback(data,t))
				return ;
		}
	}
}

Wordlist *lang_list(Translations *t) {
	Wordlist *list = NULL;

	if (!t) return NULL;
	lang_dolock();
	word_copy(&list,t->supported);
	lang_unlock();
	return list;
}

void lang_ref(Translations *t, Language *l) {
	if (!t || !l) return ;
	lang_dolock();
	t->ref++;
	lang_unlock();
	l->t = t;
}

Language *lang_instance(Translations *t, char *language) {
	Language *l;

	if (!t) return NULL;
	l = calloc(1,sizeof(Language));
	lang_ref(t,l);
	if (!lang_set(l,language))
		l->isenglish = TRUE;
	return l;
}

void lang_free_instance(Language **l) {
	if (!l || !*l) return;
	if ((*l)->language)
		free((*l)->language);
/*	if ((*l)->error)
		free((*l)->error);*/
	if ((*l)->last)
		free((*l)->last);
	lang_free(&(*l)->t);
	free(*l);
}

void lang_support(Translations *t, char *language) {
	char tmp[BFSZ];

	if (!t) return ;
	if (!language) return ;
	if (!lib_strlen(language)) return ;
	strlwr(ncpy(tmp,language,BFSZ-1));
	word_add_nodup(&t->supported,tmp);
}

int lang_supported(Translations *t, char *language) {
	int r;

	if (!t) return FALSE;
	if (!language) return FALSE;
	if (strcmpnc(language,"english")==0) return TRUE;

	lang_dolock();
	r = word_findnc(t->supported,language);
	lang_unlock();

	return r;
}

int lang_exist_phrase(Translations *l, char *phrase) {
	Translation *t;
	int r;

	if (!l) return FALSE;
	if (!phrase) return FALSE;

	r = FALSE;

	lang_dolock();
	t = lang_find(l,phrase,NULL);
	r = t?TRUE:FALSE;
	lang_unlock();

	return r;
}

char *lang_selected(Language *l) {
	if (!l || l->isenglish) return "english";
	return l->language;
}

int lang_isset(Language *l, char *language) {
	if (!l) return FALSE;
	if (l->isenglish) {
		if (strcmpnc(language,"english")==0) return TRUE;
		return FALSE;
	}
	return strcmpnc(l->language,language)==0;
}

int lang_set(Language *l, char *language) {
	char bf[BFSZ],*s;
	if (!l || !language) return FALSE;
	if (!lang_supported(l->t,language)){
		// Try and trim language suffix to see if that helps
		ncpy(bf,language,BFSZ-1);
		s=strchr(bf,' ');
		if(!s) return FALSE;
		*s=0; 
		language=bf;
		if (!lang_supported(l->t,language)){
			return FALSE;
		}
	}
	// is it english?
	l->isenglish = (strcmpnc(language,"english")==0);	
	if (l->isenglish) {
		// if we have english translations, set l->isenglish to FALSE so lookup occurs.
		if (word_find(l->t->supported,"english"))
			l->isenglish = FALSE;
	}
	// assign this if we're doing lookups
	if (!l->isenglish) {
		assign(&l->language,language);
		strlwr(l->language);
	}
	return TRUE;
}

char *lang_charset(char *language) {
	if (language) {
		int i;

		for(i = 0; charset_list[i].language; i++)
			if (strcmpnc(charset_list[i].language,language) == 0)
				return charset_list[i].charset;
	}
	return "iso-8859-1"; //Chrisp changing this...
//	return "utf-8";
}

char *lang_locale(char *language) {
	if (language) {
		int i;

		for(i = 0; locale_list[i].language; i++)
			if (strcmpnc(locale_list[i].language,language) == 0)
				return locale_list[i].locale;
	}
	return "C";
}

Translation *lang_find(Translations *l, char *phrase, int *index) {
	Translation *t;
	int i;

	if (!l) return NULL;
	if (!phrase) return NULL;

	lang_allocate(l,l->isize);

	i = lang_hash(phrase,l->size);
	if (index) *index = i;

	for(t = l->table[i]; t; t = t->next) {
		if (strcmp(t->phrase,phrase)!=0) continue;
		return t;
	}

	return NULL;
}

Translation *lang_new(Translations *l, char *phrase, int *index) {
	Translation *t;
	int i;

	if (!l) return NULL;
	if (!phrase) return NULL;

	lang_allocate(l,l->isize);

	i = index?*index:lang_hash(phrase,l->size);
	t = calloc(1,sizeof(Translation));
	t->next = l->table[i];
	t->phrase = strdup(phrase);
	l->table[i] = t;

	return t;
}

int lang_exist(Language *l, char *phrase, char **result) {
	Translation *t;
	char *s;
	int r;

	if (!l) return FALSE;
	if (!phrase) return FALSE;
	if (l->isenglish) return FALSE;

	r = FALSE;

	lang_dolock();
	t = lang_find(l->t,phrase,NULL);
	if (t) {
		s = qpair_find_null(t->translations,l->language);
		if (result && s) {
			assign(&l->last,s);
			*result = l->last;
		}
		r = s?TRUE:FALSE;
	}
	lang_unlock();

	return r;
}

int lang_parse(char *string, char v[30]) {
	char *p;
	int i;

	i = 0;
	for(p = strchr(string,'%'); p; p = strchr(p,'%')) {
		if (p[1] == '%') p += 2;
		else {
			for(p++; *p && !sisalpha(*p) && !sisspace(*p); p++);
			if (sisalpha(*p)) v[i++] = *p;
		}
	}
	v[i] = 0;

	return i;
}

int lang_valid(char *phrase, char *translation) {
	char v[2][30];
	int i;

	if (!translation) return FALSE;
	if (!phrase) return FALSE;

	lang_parse(phrase,v[0]);
	lang_parse(translation,v[1]);

	for(i = 0; v[0][i]; i++) {
		if (v[0][i] != v[1][i])
			return FALSE;
	}

	return TRUE;
}

char *lang_get_bf(Language *l, char *phrase, char *bf, int bfsz) {
	return ncpy(bf,lang_get(l,phrase),bfsz-1);
}

static char product[BFSZ];
static char Product[BFSZ];
int lang_set_product(char *s)
{
	ncpy(product,s,BFSZ-1); strlwr(product);
	ncpy(Product,s,BFSZ-1);
	return TRUE;
}
char *lang_get_product(Language *l, char *phrase, char *tmp) 
{
	char *s;
	char bf[BFSZ];
	int islow=FALSE;

//if (phrase) printf("langgetprod: %s\n",phrase);


	if (!phrase) return phrase;
	if (!l) goto prod;
	if (l->isenglish) {
		// Ok, lets translate the word 'surgemail' if the product name has been set...
prod:

		if (*product) {
			if (lib_strlen(phrase)+lib_strlen(product)>BFSZ-3) {
				return phrase;
			}
			ncpy(tmp,phrase,BFSZ-1);
			for (;TRUE;) {
				ncpy(bf,tmp,BFSZ-1);
				s = strstr(bf,"surgemail");
				if (s!=NULL) islow = TRUE;
				if (s==NULL) s = strstrnc(bf,"surgemail");
				if (s==NULL) return tmp;
				*s = 0;
				lprintf(tmp,BFSZ-1,"%s%s%s",bf,islow ? product : Product,s+lib_strlen("surgemail"));
			}
			return tmp;
		}
		return phrase;
	}
	return lang_get_bf(l,phrase,tmp,BFSZ-1);
}
char *lang_get(Language *l, char *phrase) 
{
	Translation *t;
	char *s;

	if (!l) return phrase;
	if (!phrase) return phrase;
	if (l->isenglish) {
		return phrase;
	}

	lang_dolock();
	t = lang_find(l->t,phrase,NULL);
	if (t) {
		s = qpair_find_null(t->translations,l->language);
		if (s && s[0]) {
			if (lang_valid(phrase,s)) {
				assign(&l->last,s);
				lang_unlock();
				return l->last;
			}
			if (s) emsg("Translation format does not match original phrase, check %%s,%%d etc - original=(%s)",phrase);
		}
	}
	lang_unlock();

	return phrase;
}

void lang_add(Language *l, char *phrase, char *translation) {
	Translation *t;
	int i;

	if (!l) return ;
	if (!phrase) return ;
	if (l->isenglish) return ;

	lang_dolock();
	t = lang_find(l->t,phrase,&i);
	if (!t) t = lang_new(l->t,phrase,&i);
	qpair_add(&t->translations,l->language,translation);
	lang_unlock();
}

/*
char *lang_error(Language *l) {
	if (!l) return NULL;
	return l->error;
}*/


/*
void lang_seterr(Language *l, char *format, ...) {
	char bf[BFSZ];
	va_list args;
	int len;

	va_start(args,format);
	len = vsnprintf(bf,BFSZ,format,args);
	va_end(args);

	if (len < 0 || len == BFSZ)
		bf[len=BFSZ-1] = 0;

	if (l->error) free(l->error);
	l->error = strdup(bf);
}
*/

Charset charset_list[]  = {
//	{"English",				"utf-8"},
	{"English",				"iso-8859-1"}, 
	{"French",				"iso-8859-1"},
	{"German",				"iso-8859-1"},
	{"Spanish",				"iso-8859-1"},
	{"Portuguese",			"iso-8859-1"},
	{"Italian",				"iso-8859-1"},
	{"Japanese",			"iso-2022-jp"},

	{"Simplified Chinese",	"GB2312"},
	{"Chinese",				"BIG5"},

	{"Afrikaans", 			"iso-8859-1"},
	{"Albanian", 			"iso-8859-1"},
	{"Arabic", 				"iso-8859-6"},
	{"Basque",				"iso-8859-1"},
	{"Bulgarian", 			"iso-8859-5"},
	{"Byelorussian", 		"iso-8859-5"},
	{"Catalan", 			"iso-8859-1"},
	{"Croatian", 			"iso-8859-2"},
	{"Czech",				"iso-8859-2"},
	{"Danish", 				"iso-8859-1"},
	{"Dutch", 				"iso-8859-1"},
	{"Estonian", 			"iso-8859-15"},
	{"Faroese", 			"iso-8859-1"},
	{"Finnish", 			"iso-8859-1"},
	{"Galician", 			"iso-8859-1"},
	{"Greek", 				"iso-8859-7"},
	{"Hebrew", 				"iso-8859-8"},
	{"Hungarian", 			"iso-8859-2"},
	{"Icelandic", 			"iso-8859-1"},
	{"Irish", 				"iso-8859-1"},
	{"Korean", 				"euc-kr"},
	{"Latvian", 			"iso-8859-13"},
	{"Lithuanian",			"iso-8859-13"},
	{"Macedonian",			"iso-8859-5"},
	{"Norwegian",			"iso-8859-1"},
	{"Polish",				"iso-8859-2"},
	{"Romanian",	  		"iso-8859-2"},
	{"Russian",				"windows-1251"}, //was:iso-8859-5 //choice:windows-1251
	{"Scottish",			"iso-8859-1"},
	{"Serbian",				"iso-8859-2"},
	{"Slovak",				"iso-8859-2"},
	{"Slovenian", 			"iso-8859-2"},
	{"Swedish",				"iso-8859-1"},
	{"Turkish",				"iso-8859-9"},
	{"Ukrainian",			"windows-1251"}, //was:iso-8859-5 //choice:windows-1251 //choice:koi8-u

	{NULL,NULL}
};

Locale locale_list[] = {
	{"Chinese",		"chinese"},
	{"Czech",		"czech"},
	{"Danish",		"danish"},
	{"Dutch",		"dutch"},
	{"English",		"english"},
	{"Finnish",		"finnish"},
	{"French",		"french"},
	{"German",		"german"},
	{"Greek",		"greek"},
	{"Hungarian",	"hungarian"},
	{"Icelandic",	"icelandic"},
	{"Italian",		"italian"},
	{"Japanese",	"japanese"},
	{"Korean",		"korean"},
	{"Norwegian",	"norwegian"},
	{"Polish",		"polish"},
	{"Portuguese",	"portuguese"},
	{"Russian",		"russian"},
	{"Slovak",		"slovak"},
	{"Spanish",		"spanish"},
	{"Swedish",		"swedish"},
	{"Turkish",		"turkish"},

	{NULL,NULL}
};

void strftime_parse(Language *l, char *strDest, size_t maxsize, char *text, const struct tm *timeptr)
{
	char *start, *end;
	char c;

	for(end = text; end; ) {
		start = end;
		if (sisalpha(*start)) {
			for(; sisalpha(*end); end++) {}
			if (*end == 0) end = NULL;
			else {
				c = *end;
				*end = 0;
			}
			ncat(strDest,lang_get(l,start),(int)maxsize-1);
			if (end) *end = c;
			continue;
		}

		for(; *end && !sisalpha(*end); end++) {}
		if (*end == 0) end = NULL;
		else {
			c = *end;
			*end = 0;
		}
		ncat(strDest,start,(int)maxsize-1);
		if (end) *end = c;
	}
}

size_t strftime_translate(Language *l, char *strDest, size_t maxsize, const char *format, const struct tm *timeptr)
{
	char *start, *end;
	char tmp[BFSZ];
	char *raw;
	char c;

	raw = strdup(format);
	for(end = raw; end; ) {
		start = end;
		if (start[0] == '%') {
			end++;
			if (*end == 0) end = NULL;
			else if (*end == '%') ncat(strDest,"%",(int)maxsize-1);
			else {				
				end++;
				c = *end;
				*end = 0;
				strftime(tmp,BFSZ,start,timeptr);
				if (*end == 'c' || *end == 'x' || *end == 'X') strftime_parse(l,strDest,maxsize,tmp,timeptr);
				else ncat(strDest,lang_get(l,tmp),(int)maxsize-1);
				*end = c;
			}

			continue;
		}

		end = strchr(start,'%');
		if (end) {
			c = *end;
			*end = 0;
		}
		ncat(strDest,start,(int)maxsize-1);
		if (end) *end = c;
	}
	free(raw);
	return TRUE;
}

// Function to list languages: netauth_languages
// Function to select language based on cookie etc: netauth_setup_lang / netauth_set_lang

// *************************************************************************
// Dedicated language based string tables for faster language string lookups (primarily for surgeweb)
//	- UTF8 encoded rather than ascii in locale based character table
//	- More efficient lookups (for fully translated interfaces)
//	- Supports replaceable format specifiers in string eg "item %s out of %s"
//	- All template string references are actually indexes into the central string table, for easy finding/customising of strings
//	- Allows the same test to be translated to different text in different parts of the interface
// *************************************************************************
#ifdef sisspace
#undef sisspace
#endif

int log_extra = false;

#define sisspace(a) (a>0 && sisspace(a))
Stringtable *lang_tables = NULL;

void stringtable_log_extra(int enable)
{
	log_extra = enable;
}

Stringtable *stringtable_retrieve(char *lang, int create)
{
	Stringtable *p,*last; 	// Walk memory structure storing table per language

	for (p=lang_tables;p;p=p->next){
		if (strcmpnc(p->language,lang)==0){
			return p;
		}
	}

	if(!create) return NULL;

	// Not found so lets create and initialise a new one
	p=calloc(1,sizeof(Stringtable));
	if(!p) return NULL;

	p->language=strdup(lang);
	if(!lang_tables){
		lang_tables=p;
	}else{
		for (last=lang_tables;last->next;last=last->next) ;
		last->next = p;
	}
	return p;
}

int stringtable_load(char *fname, char *result)	// cf lang_load qpair_loadfile
{
	char curr_phrase[BFSZ],curr_lang[BFSZ],bf[BFSZ*5],show[BFSZ],*p,*s;
	FILE *f;
	Stringtable *pp;
	int count=0;

	curr_phrase[0]=0;
	ncpy(curr_lang,"English",BFSZ-1);
	f = fopen(fname,"r");
	if (!f) {
		if (result) lprintf(result,BFSZ,"failed to read (%s): %s",fname,strerror(errno));
		return FALSE;
	}
	while(fgets(bf,BFSZ*5-1,f)) {
		p=bf;
		// Handle the UTF8 BOM if it is there
		if(p[0]==(char)0xEF && p[1]==(char)0xBB && p[2]==(char)0xBF){
			p+=3;
		}

#ifdef SURGEMAIL
		count++;
//		if(count%10==0) imsg("stringtable: line %d [%s]",count,p);
#endif

		if (*p == '#') { continue;}
		lib_trim_end(p);
		if(!*p) continue;

		// deal with language specification line
		if (strncmpnc(p,"lang:",5)==0) {
			s=p=p+5;
			if(!p) continue;
			p=strchr(p,':');
			// Line with syntax "lang:French"
			if(!p) {
				ncpy(curr_lang,s,BFSZ-1);
				continue;
			}
			// Line with syntax "lang:*:French:Français"
			if(!*(p+1)) continue;
			*p='\0';
			ncpy(show,s,BFSZ-1);
			s=p=p+1;
			p=strchr(p,':');
			if(!p || !*(p+1)) continue;
			*p='\0';
			p++;
			if(!*p) p=s;
			ncpy(curr_lang,s,BFSZ-1);

#ifndef MARIJN_DEBUG			
			if(strcmp(curr_lang,"Test")==0) continue;
#endif
			pp=stringtable_retrieve(s,TRUE);
			if(!pp) continue;
			pp->native=strdup(p);
			if(strchr(show,'-')){
				pp->show=L_NONE;
			}else if(strchr(show,'+')){
				pp->show=L_DEMO_MORE;
			}else if(strchr(show,'o')){
				pp->show=L_DEMO_STD;
			}else if(strchr(show,'*')){
				pp->show=L_FULL;
			}
			continue;
		}

		// check for new alternate language translation for most recent phrase
		if(sisspace(*p)){
			if(!curr_phrase[0]) continue;
			for(; sisspace(*p); p++);
			if(!*p) continue;
			s=p;
			p=strchr(p,':');
			if(!p || !*(p+1)) continue;
			*p='\0';
			p++;
			for(; sisspace(*p); p++);
			if(!*p) continue;
			pp=stringtable_retrieve(s,TRUE);
			if(!pp || !pp->show) continue;
			qpair_add(&pp->table,curr_phrase,p);
			continue;
		}

		// new phrase for string table in current language
		s=p;
		if(*p=='"'){
			s=p+1; p++;
			for(; *p && (*p!='"'); p++);
			*p='\0'; p++;
		}else{
			for(; *p && !sisspace(*p); p++);
		}
		if(sisspace(*p)) *p++='\0';
		ncpy(curr_phrase,s,BFSZ-1);
		for(; sisspace(*p); p++);
		if(!*p) continue;
		pp=stringtable_retrieve(curr_lang,TRUE);
		if(!pp || !pp->show) continue;
		qpair_add(&pp->table,curr_phrase,p);
	}
	fclose(f);
	return TRUE;
}

int stringtable_fallbacks(char *lang)
{
	Stringtable *p,*def=NULL,*test=NULL;

	for (p=lang_tables;p;p=p->next){
		if (strcmpnc(p->language,lang)==0) def=p;
		if (strcmpnc(p->language,"Test")==0) test=p;
	}
	for (p=lang_tables;p;p=p->next){
		if (p!=def)	p->base=def;
		if (!p->native) p->native=strdup(p->language);
	}
	// Test string table to look for untranslated strings
	if(test){
		test->istest=TRUE;
		if(test->show && def){
			QPair *q;
			char bf[BFSZ*5];
			
			if(test->table)
				qpair_free(&test->table);

			for(q=def->table->start;q;q=q->next){
				lprintf(bf,BFSZ*5,"{%s}",q->val);
				qpair_add(&test->table,q->var,bf);
			}
		}
	}
	return TRUE;
}
int st_sort_native_cb(const void *arg1, const void *arg2) {
	Stringtable *p1 = *(Stringtable **)arg1;
	Stringtable *p2 = *(Stringtable **)arg2;
	return strcmpnc(p1->native,p2->native);
}
int st_sort_english_cb(const void *arg1, const void *arg2) {
	Stringtable *p1 = *(Stringtable **)arg1;
	Stringtable *p2 = *(Stringtable **)arg2;
	return strcmpnc(p1->language,p2->language);
}
// Use the list of native languages as the primary iteration mechanism
Wordlist *stringtable_list(int sort_native, int nodemo, int all) 
{
	Wordlist *list = NULL;
	Stringtable *p=NULL,*index[100];
	int i,n=0;
	for(p=lang_tables;p;p=p->next){
		index[n]=p;
		n++;
		if(n>=90) break;
	}
	qsort(index,n,sizeof(Stringtable*),sort_native?st_sort_native_cb:st_sort_english_cb);
	for(i=0;i<n;i++){
		p=index[i];
		if(!p->show) continue;
		if(!all && p->show==L_DEMO_MORE) continue;
		if(nodemo && p->show!=L_FULL) continue;
		word_add_atend(&list,p->language);
	}
	return list;
}

// Return UTF8 native language descriptions
char *stringtable_native(char *name) 
{
	Stringtable *p=NULL;
	for(p=lang_tables;p;p=p->next){
		if(strcmpnc(p->language,name)==0)
			return p->native;
	}
	return name;
}

//char *stringtable_get()
//stringtable_list_english
//stringtable_list_native

