/* Copyright (C) 1988-1998 Takafumi Sakurai
 * Permission to use, copy, modify, and distribute this file for any
 * purpose is hereby granted without fee, provided that both the above
 * copyright notice and this permission appear in all copies.
*/

#define NO_BIBTEX_HELP
#define	NOFIXPUTC
#define	EXTERN extern
#include "texd.h"

#define	MAXOPEN		64
kaninfo kanjiinfo[MAXOPEN];
static int (*in_ln_tab[])() = {
    in_ln_ascii,
    in_ln_jis,
    in_ln_jis,
    in_ln_jis,
    in_ln_jis,
    in_ln_jis,
    in_ln_jis,
    0, 0, 0,
    in_ln_sjis,
    in_ln_jis7,
    in_ln_jis7,
    in_ln_jis7,
    in_ln_jis7,
    in_ln_jis7,
    in_ln_jis7,
    0, 0, 0,
    in_ln_jis
};
static int (*out_ch_tab[])() = {
    out_ch_ascii,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    0, 0, 0,
    out_ch_sjis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    out_ch_jis,
    0, 0, 0,
    out_ch_euc
};
struct {
    char esc_in, esc_out;
} jis_esc_tab[] = {
    {'\0', '\0'}, /* dummy */
    {'@', 'J'},
    {'@', 'H'},
    {'@', 'B'},
    {'B', 'J'},
    {'B', 'H'},
    {'B', 'B'},
    {'\0', '\0'}, {'\0', '\0'}, {'\0', '\0'},
    {'\0', '\0'},
    {'@', 'J'},
    {'@', 'H'},
    {'@', 'B'},
    {'B', 'J'},
    {'B', 'H'},
    {'B', 'B'}
};

int tbstrict = false;

int in_ln_ascii(FILE *f, kaninfo *ki)
{
    int i;

    while (last < bufsize - 30 && ((i = getc(f)) != EOF) && i != '\n' && i != '\r')
	if (is_eightbit(i)) {
	    buffer[last++] = ext_eightbit;
	    buffer[last++] = de_eightbit(i);
	} else
	    buffer[last++] = i;
    return (i);
}

int in_ln_jis(FILE *f, kaninfo *ki)
{
    return (in_ln_gen(f, ki, check_euc_kanji, euc2TeXkanji));
}

int in_ln_jis7(FILE *f, kaninfo *ki)
{
    return (in_ln_gen(f, ki, check_eightbit, error_makekanji));
}

int in_ln_sjis(FILE *f, kaninfo *ki)
{
    return (in_ln_gen(f, ki, check_sj_kanji, sj2TeXkanji));
}

check_eightbit(textchar c, kaninfo *ki)
{
    if (is_eightbit(c)) {
	buffer[last++] = ext_eightbit;
	buffer[last++] = de_eightbit(c);
    } else
	buffer[last++] = c;
}

check_euc_kanji(textchar c, kaninfo *ki)
{
    if (is_eightbit(c)) {
	ki->kpend = c;
	ki->kstat = KAN_ASCII_KAN;
    } else
	buffer[last++] = c;
}

check_sj_kanji(textchar c, kaninfo *ki)
{
    if (c >= 0xe0 || c >= 0x80 && c < 0xa0) {
	ki->kpend = c;
	ki->kstat = KAN_ASCII_KAN;
    } else if (c < 0x80)
	buffer[last++] = c;
    else {
	if (!tbstrict) {
	    /* We allow 8bit char here. */
	    buffer[last++] = ext_eightbit;
	    buffer[last++] = de_eightbit(c);
	} else {
	    /* Web procedure should be called for a better error handling. */
	    /*fprintf(stderr, "\n1-byte kana (shift-jis) ignored.\n");*/
	    /*buffer[last++] = '?';*/
	}
    }
}

get_onechar(textchar c)
{
    if (is_eightbit(c)) {
	if (!tbstrict) {
	    buffer[last++] = ext_eightbit;
	    buffer[last++] = de_eightbit(c);
	}
    } else
	buffer[last++] = c;
}

int in_ln_gen(FILE *f, kaninfo *ki, int (*checkkanji)(), int (*makekanji)())
{
    int i;
    textchar c1, c2;

    while (last < bufsize - 30 &&
	   ((i = getc(f)) != EOF) && i != '\n' && i != '\r') {
	if (ki->kstat == KAN_ASCII) {
	    if (i == ESC) {
		if ((i = getc(f)) == '$') {
		    getc(f);
		    ki->kstat = KAN_JIS_SECOND;
		} else if (i == '(') {
		    getc(f);
		    /* ki->kstat = KAN_ASCII; */
		} else {
		    /* Web procedure should be called for a better error handling. */
		    fprintf(stderr,"\nText line contains an invalid escape sequence\n");
		    exit(1);
		}
	    } else
		(*checkkanji)(i, ki);
	} else if (ki->kstat == KAN_JIS_FIRST) {
	    if (last >= bufsize-1)
		return(i);
	    if (jis2TeXkanji(ki->kpend, i, &c1, &c2)) {
		buffer[last++] = make_ext_kanji(c1);
		buffer[last++] = c2;
	    } else {
		get_onechar(c1);
		get_onechar(c2);
	    }
	    ki->kstat = KAN_JIS_SECOND;
	} else if (ki->kstat == KAN_JIS_SECOND) {
	    if (i == ESC) {
		if ((i = getc(f)) == '$') {
		    getc(f);
		    /* ki->kstat = KAN_JIS_SECOND; */
		} else if (i == '(') {
		    getc(f);
		    ki->kstat = KAN_ASCII;
		} else {
		    /* Web procedure should be called for a better error handling. */
		    fprintf(stderr,"\nText line contains an invalid escape sequence\n");
		    exit(1);
		}
	    } else if (i < ' ') {
		buffer[last++] = i;
	    } else {
		ki->kpend = i;
		ki->kstat = KAN_JIS_FIRST;
	    }
	} else /*if (ki->kstat == KAN_ASCII_KAN)*/ {
	    if (last >= bufsize-1)
		return (i);
	    if ((*makekanji)(ki->kpend, i, &c1, &c2)) {
		buffer[last++] = make_ext_kanji(c1);
		buffer[last++] = c2;
	    } else {
		get_onechar(c1);
		get_onechar(c2);
	    }
	    ki->kstat = KAN_ASCII;
	}
    }
    return (i);
}

Fputc(textchar c, FILE *f)
{
    kaninfo *ki;

    ki = &kanjiinfo[fileno(f)];
    (*(ki->out_ch))(c, f, ki);
}

Fputs(FILE *f, textchar *s)
{
    kaninfo *ki;

    ki = &kanjiinfo[fileno(f)];
    for (; *s; s++)
	(*(ki->out_ch))(*s, f, ki);
}

out_ch_ascii(textchar c, FILE *f, kaninfo *ki)
{
    if (ki->kstat == KAN_EIGHT) {
	putc(make_eightbit(c), f);
	ki->kstat = KAN_ASCII;
    } else if (is_ext_eightbit(c)) {
	ki->kstat = KAN_EIGHT;
    } else
	putc(c, f);
}

out_ch_jis(textchar c, FILE *f, kaninfo *ki)
{
    textchar c1, c2;

    if (ki->kstat == KAN_FIRST) {
	TeXkanji2jis(ki->kpend, c, &c1, &c2);
	putc(c1, f); putc(c2, f);
	ki->kstat = KAN_SECOND;
    } else if (ki->kstat == KAN_EIGHT) {
	putc(make_eightbit(c), f);
	ki->kstat = KAN_ASCII;
    } else if (is_ext_kanji(c)) {
	if (ki->kstat == KAN_ASCII) {
	    putc(ESC, f); putc('$', f);
	    putc(jis_esc_tab[ki->ktype].esc_in, f);
	}
	ki->kpend = de_ext_kanji(c);
	ki->kstat = KAN_FIRST;
    } else {
	if (ki->kstat == KAN_SECOND) {
	    putc(ESC, f); putc('(', f);
	    putc(jis_esc_tab[ki->ktype].esc_out, f);
	}
	if (is_ext_eightbit(c)) {
	    ki->kstat = KAN_EIGHT;
	} else {
	    putc(c, f);
	    ki->kstat = KAN_ASCII;
	}
    }
}

out_ch_euc(textchar c, FILE *f, kaninfo *ki)
{
    textchar c1, c2;

    if (ki->kstat == KAN_FIRST) {
	TeXkanji2euc(ki->kpend, c, &c1, &c2);
	putc(c1, f); putc(c2, f);
	ki->kstat = KAN_SECOND;
    } else if (is_ext_kanji(c)) {
	ki->kpend = de_ext_kanji(c);
	ki->kstat = KAN_FIRST;
    } else {
	putc(c, f);
	ki->kstat = KAN_ASCII;
    }
}

out_ch_sjis(textchar c, FILE *f, kaninfo *ki)
{
    textchar c1, c2;

    if (ki->kstat == KAN_FIRST) {
	TeXkanji2sj(ki->kpend, c, &c1, &c2);
	putc(c1, f); putc(c2, f);
	ki->kstat = KAN_SECOND;
    } else if (is_ext_kanji(c)) {
	ki->kpend = de_ext_kanji(c);
	ki->kstat = KAN_FIRST;
    } else {
	putc(c, f);
	ki->kstat = KAN_ASCII;
    }
}

/*
 * Kanji conversion functions
 */
jis2TeXkanji(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
    if (!is_eightbit(i2)) {
	*c1 = i1;
	*c2 = i2;
	if (fix_kanji_range(c1, c2)) {
#ifndef	TeX
	    *c2 = *c2|0x80;
#endif
	    return true;
	} else {
	    *c1 = i1;
	    *c2 = i2;
	    return false;
	}
    } else {
	*c1 = i1;
	*c2 = i2;
	return false;
    }
}

TeXkanji2jis(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
    *c1 = i1;
#ifdef	TeX
    *c2 = i2;
#else
    *c2 = i2&0x7f;
#endif
}

euc2TeXkanji(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
    if (is_eightbit(i2)) {
	*c1 = i1&0x7f;
	*c2 = i2&0x7f;
	if (fix_kanji_range(c1, c2)) {
#ifndef	TeX
	    *c2 = *c2|0x80;
#endif
	    return true;
	} else {
	    *c1 = i1;
	    *c2 = i2;
	    return false;
	}
    } else {
	*c1 = i1;
	*c2 = i2;
	return false;
    }
}

TeXkanji2euc(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
    *c1 = i1|0x80;
    *c2 = i2|0x80;
}

sj2TeXkanji(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
    if (i2 >= 0x9f) {
	if (i1 >= 0xe0) *c1 = (i1 - 0xb0) * 2;
	else *c1 = (i1 - 0x70) * 2;
	*c2 = i2 - 0x7e;
    } else {
	if (i1 >= 0xe0) *c1 = (i1 - 0xb0) * 2 - 1;
	else *c1 = (i1 - 0x70) * 2 - 1;
	if (i2 >= 0x7f) *c2 = i2 - 0x20;
	else *c2 = i2 -  0x1f;
    }
    if (fix_kanji_range(c1, c2)) {
#ifndef	TeX
	*c2 = *c2|0x80;
#endif
	return true;
    } else {
	*c1 = i1;
	*c2 = i2;
	return false;
    }
}

TeXkanji2sj(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
#ifndef	TeX
    i2 &= 0x7f;
#endif
    if (i1 & 1) {
	if (i1 < 0x5f) *c1 = i1/2 + 0x71;
	else *c1 = i1/2 + 0xb1;
	if (i2 >= 0x60) *c2 = i2 + 0x20;
	else *c2 = i2 + 0x1f;
    } else {
	if (i1 < 0x5f) *c1 = i1/2 + 0x70;
	else  *c1 = i1/2 + 0xb0;
	*c2 = i2 + 0x7e;
    }
}

fix_kanji_range(textchar *c1, textchar *c2)
{
    if (*c1 < 0x21 || (*c1 >= 0x29 && *c1 <= 0x2f) || *c1 >= 0x75) {
	*c1 = 0x22;
	*c2 = 0x23;
	return false;
    }
    return true;
}

error_makekanji(textchar i1, textchar i2, textchar *c1, textchar *c2)
{
    fprintf(stderr, "\nImplementation error (eight-bit char).\n");
}

/*
 * Initialize
 */
str2code(char *code, int *iscode)
{
    int c;

    *iscode = true;
    if (strcmp(code, "ascii") == 0)
	return ascii_only;
    else if (strcmp(code, "jis") == 0)
	return jis_6;
    else if (strcmp(code, "jis7") == 0)
	return jis7_6;
    else if (strcmp(code, "euc") == 0)
	return EUC;
    else if (strcmp(code, "sjis") == 0)
	return shift_jis;
    else if (strlen(code) == 1 && '0' <= *code && *code <= '6' ||
	     strcmp(code, "10") == 0 ||
	     strlen(code) == 2 && *code == '1' &&
	     '1' <= *(code+1) && *(code+1) <= '6' ||
	     strcmp(code, "20") == 0)
	return atoi(code);
    else {
	*iscode = false;
	return ascii_only;
    }
}

int hasargcode = false;
static int argcode;

argkanjicode(char *code)
{
    int iscode;

    hasargcode = true;
    argcode = str2code(code, &iscode);
    if (!iscode) {
	argcode = jis_6;
	fprintf(stderr, "No such file type %s, I will change it to jis (6).\n",
		code);
    }
}

getargcode()
{
    return argcode;
}

int hasargterm = false;
static int argterm;

argtermcode(code)
char *code;
{
    int iscode;

    hasargterm = true;
    argterm = str2code(code, &iscode);
    if (!iscode) {
	argterm = jis_6;
	fprintf(stderr, "No such terminal type %s, I will change it to jis (6).\n",
		code);
    }
}

getargterm(void)
{
    return argterm;
}

setinkanjitype(FILE *f, int ktype)
{
    kanjiinfo[fileno(f)].ktype = ktype;
    kanjiinfo[fileno(f)].kstat = KAN_ASCII;
    kanjiinfo[fileno(f)].in_ln = in_ln_tab[ktype];
}

setoutkanjitype(FILE *f, int ktype)
{
    kanjiinfo[fileno(f)].ktype = ktype;
    kanjiinfo[fileno(f)].kstat = KAN_ASCII;
    kanjiinfo[fileno(f)].out_ch = out_ch_tab[ktype];
}
