#include "mendex.h"

#include <kpathsea/tex-file.h>

#include "exvar.h"

#include "kp.h"

FILE *fp;
static char icu_attr_str[STYBUFSIZE];

static void convline(char *buff1, int start, char *buff2);
static int scompare(char *buff1, const char *buff2);
static int getparam(char *buff, const char *paraname, char *param);
static int getparachar(char *buff, const char *paraname, char *param);
static size_t sstrlen(const char *buff);
static int sstrcmp(const char *s1, const char *s2);
static int sstrncmp(const char *s1, const char *s2, size_t len);
static int escape_mode=0;

static char *
bfgets (char *buf, int size, FILE *fp)
{
	char *p, *q;
	p = fgets (buf, size, fp);
	for (q = buf; *q; q++) {
		if(*q == '\r') {
			*q++ = '\n';
			*q = '\0';
			break;
		}
	}
	return p;
}
#define fgets bfgets

/*   read style file   */
void styread(const char *filename)
{
	int i,j,q,cc;
	char buff[4096],tmp[4096],tmp2[4096];

	filename = KP_find_ist_file(filename);
	if(kpse_in_name_ok(filename))
		fp=fopen(filename,"rb");
	else
		fp=NULL;
	if (fp==NULL) {
		fprintf(stderr,"%s does not exist.\n",filename);
		exit(255);
	}
	verb_printf(efp,"Scanning style file %s.",filename);

	for (i=0;;i++) {
		if (fgets(buff,4095,fp)==NULL) break;
		if (getparam(buff,"keyword",keyword)) continue;
		if (getparachar(buff,"arg_open",&arg_open)) continue;
		if (getparachar(buff,"arg_close",&arg_close)) continue;
		if (getparachar(buff,"range_open",&range_open)) continue;
		if (getparachar(buff,"range_close",&range_close)) continue;
		if (getparachar(buff,"level",&level)) continue;
		if (getparachar(buff,"actual",&actual)) continue;
		if (getparachar(buff,"encap",&encap)) continue;
		if (getparachar(buff,"quote",&quote)) continue;
		if (getparachar(buff,"escape",&escape)) continue;
		if (getparam(buff,"preamble",preamble)) continue;
		if (getparam(buff,"postamble",postamble)) continue;
		if (getparam(buff,"setpage_prefix",setpage_prefix)) continue;
		if (getparam(buff,"setpage_suffix",setpage_suffix)) continue;
		if (getparam(buff,"group_skip",group_skip)) continue;
		if (getparam(buff,"lethead_prefix",lethead_prefix)) continue;
		if (getparam(buff,"heading_prefix",lethead_prefix)) continue;
		if (getparam(buff,"lethead_suffix",lethead_suffix)) continue;
		if (getparam(buff,"heading_suffix",lethead_suffix)) continue;
		if (getparam(buff,"symhead_positive",symhead_positive)) continue;
		if (getparam(buff,"symhead_negative",symhead_negative)) continue;
		if (getparam(buff,"numhead_positive",numhead_positive)) continue;
		if (getparam(buff,"numhead_negative",numhead_negative)) continue;
		if ( (cc=scompare(buff,"lethead_flag")) != -1 ||
		     (cc=scompare(buff,"heading_flag")) != -1 ||
		     (cc=scompare(buff,"headings_flag")) != -1 ) {
			lethead_flag=atoi(&buff[cc]);
			continue;
		}
		cc=scompare(buff,"priority");
		if (cc!= -1) {
			priority=atoi(&buff[cc]);
			continue;
		}
		q=0;
		for (j=0;;j++) {
			sprintf(tmp,"item_%d",j);
			if (getparam(buff,tmp,item_0[j])) {
				q=1;
				break;
			}
			sprintf(tmp,"delim_%d",j);
			if (getparam(buff,tmp,delim_0[j])) {
				q=1;
				break;
			}
			if (j==MAXDEPTH-1) break;
			sprintf(tmp,"item_%d%d",j,j+1);
			if (getparam(buff,tmp,item_01[j])) {
				q=1;
				break;
			}
			sprintf(tmp,"item_x%d",j+1);
			if (getparam(buff,tmp,item_x[j])) {
				q=1;
				break;
			}
		}
		if (q==1) continue;
		if (getparam(buff,"delim_n",delim_n)) continue;
		if (getparam(buff,"delim_r",delim_r)) continue;
		if (getparam(buff,"delim_t",delim_t)) continue;
		if (getparam(buff,"suffix_2p",suffix_2p)) continue;
		if (getparam(buff,"suffix_3p",suffix_3p)) continue;
		if (getparam(buff,"suffix_mp",suffix_mp)) continue;
		if (getparam(buff,"encap_prefix",encap_prefix)) continue;
		if (getparam(buff,"encap_infix",encap_infix)) continue;
		if (getparam(buff,"encap_suffix",encap_suffix)) continue;
		cc=scompare(buff,"line_max");
		if (cc!= -1) {
			line_max=atoi(&buff[cc]);
			continue;
		}
		if (getparam(buff,"indent_space",indent_space)) continue;
		cc=scompare(buff,"indent_length");
		if (cc!= -1) {
			indent_length=atoi(&buff[cc]);
			continue;
		}
		if (getparam(buff,"symbol",symhead)) continue;
		cc=scompare(buff,"symbol_flag");
		if (cc!= -1) {
			symbol_flag=atoi(&buff[cc]);
			continue;
		}
		cc=scompare(buff,"letter_head");
		if (cc!= -1) {
			letter_head=atoi(&buff[cc]);
			continue;
		}
		if (getparam(buff,"kana_head",tmp)) {
			multibyte_to_widechar(kana_head,STYBUFSIZE,tmp);
			continue;
		}
		if (getparam(buff,"hangul_head",tmp)) {
			multibyte_to_widechar(hangul_head,STYBUFSIZE,tmp);
			continue;
		}
		if (getparam(buff,"hanzi_head",tmp)) {
			multibyte_to_widechar(hanzi_head,STYBUFSIZE,tmp);
			continue;
		}
		if (getparam(buff,"thai_head",tmp)) {
			multibyte_to_widechar(brahmic_head[BR_THAI],STYBUFSIZE,tmp);
			continue;
		}
		if (getparam(buff,"devanagari_head",tmp)) {
			multibyte_to_widechar(brahmic_head[BR_DEVA],STYBUFSIZE,tmp);
			continue;
		}
		if (getparam(buff,"page_compositor",page_compositor)) continue;
		if (getparam(buff,"page_precedence",page_precedence)) continue;
		if (getparam(buff,"character_order",character_order)) continue;
		if (getparam(buff,"icu_locale",     icu_locale     )) continue;
		cc=scompare(buff,"icu_rules");
		if (cc!= -1) {
			escape_mode=1;
			getparam(buff,"icu_rules",icu_rules);
			escape_mode=0;
			continue;
		}
		if (getparam(buff,"icu_attributes", icu_attr_str   )) continue;

		cc=scompare(buff,"script_preamble");
		if (cc!= -1) {
			strcpy(tmp,buff+strlen("script_preamble"));
			if (getparam(tmp,"latin",     script_preamble[CH_LATIN]      )) continue;
			if (getparam(tmp,"cyrillic",  script_preamble[CH_CYRILLIC]   )) continue;
			if (getparam(tmp,"greek",     script_preamble[CH_GREEK]      )) continue;
			if (getparam(tmp,"kana",      script_preamble[CH_KANA]       )) continue;
			if (getparam(tmp,"hangul",    script_preamble[CH_HANGUL]     )) continue;
			if (getparam(tmp,"hanzi",     script_preamble[CH_HANZI]      )) continue;
			if (getparam(tmp,"arabic",    script_preamble[CH_ARABIC]     )) continue;
			if (getparam(tmp,"hebrew",    script_preamble[CH_HEBREW]     )) continue;
			if (getparam(tmp,"devanagari",script_preamble[CH_DEVANAGARI] )) continue;
			if (getparam(tmp,"bengali",   script_preamble[CH_BENGALI]    )) continue;
			if (getparam(tmp,"gurmukhi",  script_preamble[CH_GURMUKHI]   )) continue;
			if (getparam(tmp,"gujarati",  script_preamble[CH_GUJARATI]   )) continue;
			if (getparam(tmp,"oriya",     script_preamble[CH_ORIYA]      )) continue;
			if (getparam(tmp,"tamil",     script_preamble[CH_TAMIL]      )) continue;
			if (getparam(tmp,"telugu",    script_preamble[CH_TELUGU]     )) continue;
			if (getparam(tmp,"kannada",   script_preamble[CH_KANNADA]    )) continue;
			if (getparam(tmp,"malayalam", script_preamble[CH_MALAYALAM]  )) continue;
			if (getparam(tmp,"sinhala",   script_preamble[CH_SINHALA]    )) continue;
			if (getparam(tmp,"thai",      script_preamble[CH_THAI]       )) continue;
			if (getparam(tmp,"lao",       script_preamble[CH_LAO]        )) continue;
			if (strlen(tmp)>0) {
				verb_printf(efp,"\nWarning: Unknown script for specifier \"script_preamble\" (%s).", tmp);
			}
			continue;
		}
		cc=scompare(buff,"script_postamble");
		if (cc!= -1) {
			strcpy(tmp,buff+strlen("script_postamble"));
			if (getparam(tmp,"latin",     script_postamble[CH_LATIN]      )) continue;
			if (getparam(tmp,"cyrillic",  script_postamble[CH_CYRILLIC]   )) continue;
			if (getparam(tmp,"greek",     script_postamble[CH_GREEK]      )) continue;
			if (getparam(tmp,"kana",      script_postamble[CH_KANA]       )) continue;
			if (getparam(tmp,"hangul",    script_postamble[CH_HANGUL]     )) continue;
			if (getparam(tmp,"hanzi",     script_postamble[CH_HANZI]      )) continue;
			if (getparam(tmp,"arabic",    script_postamble[CH_ARABIC]     )) continue;
			if (getparam(tmp,"hebrew",    script_postamble[CH_HEBREW]     )) continue;
			if (getparam(tmp,"devanagari",script_postamble[CH_DEVANAGARI] )) continue;
			if (getparam(tmp,"bengali",   script_postamble[CH_BENGALI]    )) continue;
			if (getparam(tmp,"gurmukhi",  script_postamble[CH_GURMUKHI]   )) continue;
			if (getparam(tmp,"gujarati",  script_postamble[CH_GUJARATI]   )) continue;
			if (getparam(tmp,"oriya",     script_postamble[CH_ORIYA]      )) continue;
			if (getparam(tmp,"tamil",     script_postamble[CH_TAMIL]      )) continue;
			if (getparam(tmp,"telugu",    script_postamble[CH_TELUGU]     )) continue;
			if (getparam(tmp,"kannada",   script_postamble[CH_KANNADA]    )) continue;
			if (getparam(tmp,"malayalam", script_postamble[CH_MALAYALAM]  )) continue;
			if (getparam(tmp,"sinhala",   script_postamble[CH_SINHALA]    )) continue;
			if (getparam(tmp,"thai",      script_postamble[CH_THAI]       )) continue;
			if (getparam(tmp,"lao",       script_postamble[CH_LAO]        )) continue;
			if (strlen(tmp)>0) {
				verb_printf(efp,"\nWarning: Unknown script for specifier \"script_postamble\" (%s).", tmp);
			}
			continue;
		}
		cc=scompare(buff,"script_head");
		if (cc!= -1) {
			strcpy(tmp,buff+strlen("script_head"));
			if (getparam(tmp,"kana",      tmp2)){ multibyte_to_widechar(kana_head            ,STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"hangul",    tmp2)){ multibyte_to_widechar(hangul_head          ,STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"hanzi",     tmp2)){ multibyte_to_widechar(hanzi_head           ,STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"devanagari",tmp2)){ multibyte_to_widechar(brahmic_head[BR_DEVA],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"bengali",   tmp2)){ multibyte_to_widechar(brahmic_head[BR_BENG],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"gurmukhi",  tmp2)){ multibyte_to_widechar(brahmic_head[BR_GURU],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"gujarati",  tmp2)){ multibyte_to_widechar(brahmic_head[BR_GUJR],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"oriya",     tmp2)){ multibyte_to_widechar(brahmic_head[BR_ORYA],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"tamil",     tmp2)){ multibyte_to_widechar(brahmic_head[BR_TAML],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"telugu",    tmp2)){ multibyte_to_widechar(brahmic_head[BR_TELU],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"kannada",   tmp2)){ multibyte_to_widechar(brahmic_head[BR_KNDA],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"malayalam", tmp2)){ multibyte_to_widechar(brahmic_head[BR_MLYM],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"sinhala",   tmp2)){ multibyte_to_widechar(brahmic_head[BR_SINH],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"thai",      tmp2)){ multibyte_to_widechar(brahmic_head[BR_THAI],STYBUFSIZE,tmp2); continue; }
			if (getparam(tmp,"lao",       tmp2)){ multibyte_to_widechar(brahmic_head[BR_LAO ],STYBUFSIZE,tmp2); continue; }
			if (strlen(tmp)>0) {
				verb_printf(efp,"\nWarning: Unknown script for specifier \"script_head\" (%s).", tmp);
			}
			continue;
		}

		cc=strcspn(buff," \t\r\n");
		if (cc>0) buff[cc]='\0';
		if (buff[0]=='%' || buff[0]=='\n') continue;
		if (strlen(buff)>0) {
			verb_printf(efp,"\nWarning: Unknown specifier (%s).", buff);
		}
	}
	fclose(fp);

	verb_printf(efp,"...done.\n");
}

/*   analyze string parameter of style file   */
static void convline(char *buff1, int start, char *buff2)
{
	int i,j,cc;

	for (i=start,j=cc=0;;i++) {
		if (j==STYBUFSIZE-1) {
			buff2[j]='\0';
			break;
		}
		if (buff1[i]=='\"') {
			if (cc==0) {
				cc=1;
			}
			else {
				buff2[j]='\0';
				break;
			}
		}
		else if ((buff1[i]=='\n')||(buff1[i]=='\0')) {

			if (cc==1) {
				buff2[j++]='\n';
			}
			if (fgets(buff1,4095,fp)==NULL) {
				buff2[j]='\0';
				break;
			}
			i= -1;
		}
		else if (cc==1) {
			if ((unsigned char)buff1[i]>0x80) {
				int len = multibytelen((unsigned char)buff1[i]);
				if (len<0) {
					verb_printf(efp,"\nWarning: Illegal input of lead byte 0x%x in UTF-8.", (unsigned char)buff1[i]);
					continue;
				}
				else if (j+len>STYBUFSIZE-1) {
					buff2[j]='\0';
					break;
				}
				while(len--) {
					buff2[j++]=buff1[i++];
				}
				i--;
				continue;
			}
			else if (buff1[i]=='\\' && !escape_mode) {
				i++;
				if (buff1[i]=='\\') buff2[j]='\\';
				else if (buff1[i]=='n') buff2[j]='\n';
				else if (buff1[i]=='t') buff2[j]='\t';
				else if (buff1[i]=='r') buff2[j]='\r';
				else if (buff1[i]=='\"') buff2[j]='\"';
				else {
					/* Otherwise, the '\' is simply ignored here and the
					   following character is copied as is in the next loop. */
					i--;
					continue;
				}
			}
			else buff2[j]=buff1[i];
			j++;
		}
	}
}

/*   compare strings   */
static int scompare(char *buff1, const char *buff2)
{
	int i;

	if (sstrlen(buff1) == sstrlen(buff2)) {
		if (sstrcmp(buff1,buff2) == 0) {
			if (fgets(buff1,4095,fp)==NULL) return -1;
			return 0;
		}
		return -1;
	}
	for (i=0;i<sstrlen(buff1)-sstrlen(buff2);i++) {
		if (buff1[i]==' ' || buff1[i]=='\t') {
			continue;
		}
		else if ((sstrncmp(&buff1[i],buff2,sstrlen(buff2))==0)
			&&((buff1[i+strlen(buff2)]==' ')||(buff1[i+strlen(buff2)]=='\t')||(buff1[i+strlen(buff2)]=='\n'))) {
			return i+strlen(buff2);
		}
		else return -1;
	}
	return -1;
}

/*   get string of style patameter   */
static int getparam(char *buff, const char *paraname, char *param)
{
	int cc;

	cc=scompare(buff,paraname);
	if (cc!=-1) {
		convline(buff,cc,param);
		return 1;
	}
	return 0;
}

/*   get character of style parameter   */
static int getparachar(char *buff, const char *paraname, char *param)
{
	int j,cc;

	cc=scompare(buff,paraname);
	if (cc!=-1) {
		for (j=cc;j<strlen(buff);j++) {
			if (buff[j]=='\'') {
				(*param)=buff[j+1];
				break;
			}
			else if (buff[j]=='\n') {
				if (fgets(buff,4095,fp)==NULL) {
					break;
				}
				else j= -1;
			}
		}
		return 1;
	}
	return 0;
}

static size_t sstrlen(const char *s)
{
	if (s == NULL) return 0;
	return strlen(s);
}

static int sstrcmp(const char *s1, const char *s2)
{
	if (s1 == NULL) return -1;
	if (s2 == NULL) return 1;
	return strcmp(s1, s2);
}

static int sstrncmp(const char *s1, const char *s2, size_t len)
{
	if (s1 == NULL) return -1;
	if (s2 == NULL) return 1;
	return strncmp(s1, s2, len);
}

void set_icu_attributes(void)
{
	int i,attr;
	char *pos, *head, *tmp;

	for (i=0;i<UCOL_ATTRIBUTE_COUNT;i++) icu_attributes[i]=UCOL_DEFAULT;
	tmp=icu_attr_str;
	head=strtok(tmp, " \t\r\n");
	while (head!=NULL) {
	    pos=head;
	    if (strncmp(pos,"alternate:",10)==0) {
		pos+=10;  attr=UCOL_ALTERNATE_HANDLING;
		if      (strcmp(pos,"shifted")==0)       icu_attributes[attr]=UCOL_SHIFTED;
		else if (strcmp(pos,"non-ignorable")==0) icu_attributes[attr]=UCOL_NON_IGNORABLE;
		else	goto illegal;
	    } else
	    if (strncmp(pos,"strength:",9)==0) {
		pos+=9;   attr=UCOL_STRENGTH;
		if      (strcmp(pos,"primary")==0)       icu_attributes[attr]=UCOL_PRIMARY;
		else if (strcmp(pos,"secondary")==0)     icu_attributes[attr]=UCOL_SECONDARY;
		else if (strcmp(pos,"tertiary")==0)      icu_attributes[attr]=UCOL_TERTIARY;
		else if (strcmp(pos,"quaternary")==0)    icu_attributes[attr]=UCOL_QUATERNARY;
		else if (strcmp(pos,"identical")==0)     icu_attributes[attr]=UCOL_IDENTICAL;
		else	goto illegal;
	    } else
	      if (strncmp(pos,"french-collation:",17)==0) {
		pos+=17;  attr=UCOL_FRENCH_COLLATION;
		if      (strcmp(pos,"on")==0)            icu_attributes[attr]=UCOL_ON;
		else if (strcmp(pos,"off")==0)           icu_attributes[attr]=UCOL_OFF;
		else	goto illegal;
	    } else
	    if (strncmp(pos,"case-first:",11)==0) {
		pos+=11;  attr=UCOL_CASE_FIRST;
		if      (strcmp(pos,"off")==0)           icu_attributes[attr]=UCOL_OFF;
		else if (strcmp(pos,"upper-first")==0)   icu_attributes[attr]=UCOL_UPPER_FIRST;
		else if (strcmp(pos,"lower-first")==0)   icu_attributes[attr]=UCOL_LOWER_FIRST;
		else	goto illegal;
	    } else
	    if (strncmp(pos,"case-level:",11)==0) {
		pos+=11;  attr=UCOL_CASE_LEVEL;
		if      (strcmp(pos,"on")==0)            icu_attributes[attr]=UCOL_ON;
		else if (strcmp(pos,"off")==0)           icu_attributes[attr]=UCOL_OFF;
		else	goto illegal;
	    } else
	    if (strncmp(pos,"normalization-mode:",19)==0) {
		pos+=19;  attr=UCOL_NORMALIZATION_MODE;
		if      (strcmp(pos,"on")==0)            icu_attributes[attr]=UCOL_ON;
		else if (strcmp(pos,"off")==0)           icu_attributes[attr]=UCOL_OFF;
		else	goto illegal;
	    } else
	    if (strncmp(pos,"numeric-ordering:",17)==0) {
		pos+=17;  attr=UCOL_NUMERIC_COLLATION;
		if      (strcmp(pos,"on")==0)            icu_attributes[attr]=UCOL_ON;
		else if (strcmp(pos,"off")==0)           icu_attributes[attr]=UCOL_OFF;
		else	goto illegal;
	    } else {
	illegal:
		verb_printf(efp,"\nWarning: Illegal input for icu_attributes (%s).",head);
	    }
	    head=strtok(NULL, " \t\r\n");
	}
}
