/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   Kanji Extensions
   Copyright (C) Andrew Tridgell 1992-1994
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   Adding for Japanese language by <fujita@ainix.isac.co.jp> 1994.9.5
     and extend coding system to EUC/SJIS/JIS/HEX at 1994.10.11
   Adding for Hexadecimal code by <ohki@gssm.otuka.tsukuba.ac.jp>
*/
#ifdef KANJI

#define _KANJI_C_
#include "includes.h"

#include "kanji.h"

/* coding system keep in */
int coding_system = SJIS_CODE;

/*
 search string S2 from S1
 S1 contain SHIFT JIS chars.
*/
char *
sj_strstr (const char *s1, const char *s2)
{
    register int len = strlen ((char *) s2);
    if (!*s2) 
	return (char *) s1;
    for (;*s1;) {
	if (*s1 == *s2) {
	    if (strncmp (s1, s2, len) == 0)
		return (char *) s1;
	}
	if (is_shift_jis (*s1)) {
	    s1 += 2;
	} else {
	    s1++;
	}
    }
    return 0;
}

/*
 Search char C from beginning of S.
 S contain SHIFT JIS chars.
*/
char *
sj_strchr (const char *s, int c)
{
    for (; *s; ) {
	if (*s == c)
	    return (char *) s;
	if (is_shift_jis (*s)) {
	    s += 2;
	} else {
	    s++;
	}
    }
    return 0;
}

/*
 Search char C end of S.
 S contain SHIFT JIS chars.
*/
char *
sj_strrchr (const char *s, int c)
{
    register char *q;

    for (q = 0; *s; ) {
	if (*s == c) {
	    q = (char *) s;
	}
	if (is_shift_jis (*s)) {
	    s += 2;
	} else {
	    s++;
	}
    }
    return q;
}

/* convesion buffer */
static char cvtbuf[1024];

static int
euc2sjis (register int hi, register int lo)
{
    if (hi & 1)
	return ((hi / 2 + (hi < 0xdf ? 0x31 : 0x71)) << 8) |
	    (lo - (lo >= 0xe0 ? 0x60 : 0x61));
    else
	return ((hi / 2 + (hi < 0xdf ? 0x30 : 0x70)) << 8) | (lo - 2);
}

static int
sjis2euc (register int hi, register int lo)
{
    if (lo >= 0x9f)
	return ((hi * 2 - (hi >= 0xe0 ? 0xe0 : 0x60)) << 8) | (lo + 2);
    else
	return ((hi * 2 - (hi >= 0xe0 ? 0xe1 : 0x61)) << 8) |
	    (lo + (lo >= 0x7f ? 0x60 : 0x61));
}

/*
 Convert FROM contain SHIFT JIS codes to EUC codes
 return converted buffer
 */
static char *
sj_to_euc (const char *from, BOOL overwrite)
{
    register char *out;
    char *save;

    save = (char *) from;
    for (out = cvtbuf; *from;) {
	if (is_shift_jis (*from)) {
	    int code = sjis2euc ((int) from[0] & 0xff, (int) from[1] & 0xff);
	    *out++ = (code >> 8) & 0xff;
	    *out++ = code;
	    from += 2;
	} else if (is_kana (*from)) {
	    *out++ = euc_kana;
	    *out++ = *from++;
	} else {
	    *out++ = *from++;
	}
    }
    *out = 0;
    if (overwrite) {
	strcpy((char *) save, (const char *) cvtbuf);
	return (char *) save;
    } else {
	return cvtbuf;
    }
}

/*
 Convert FROM contain EUC codes to SHIFT JIS codes
 return converted buffer
*/
static char *
euc_to_sj (const char *from, BOOL overwrite)
{
    register char *out;
    char *save;

    save = (char *) from;
    for (out = cvtbuf; *from; ) {
	if (is_euc (*from)) {
	    int code = euc2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
	    *out++ = (code >> 8) & 0xff;
	    *out++ = code;
	    from += 2;
	} else if (is_euc_kana (*from)) {
	    *out++ = from[1];
	    from += 2;
	} else {
	    *out++ = *from++;
	}
    }
    *out = 0;
    if (overwrite) {
	strcpy(save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

static int
sjis2jis (register int hi, register int lo)
{
    if (lo >= 0x9f)
	return ((hi * 2 - (hi >= 0xe0 ? 0x160 : 0xe0)) << 8) | (lo - 0x7e);
    else
	return ((hi * 2 - (hi >= 0xe0 ? 0x161 : 0xe1)) << 8) |
	    (lo - (lo >= 0x7f ? 0x20 : 0x1f));
}

static int
jis2sjis (register int hi, register int lo)
{
    if (hi & 1)
	return ((hi / 2 + (hi < 0x5f ? 0x71 : 0xb1)) << 8) |
	    (lo + (lo >= 0x60 ? 0x20 : 0x1f));
    else
	return ((hi / 2 + (hi < 0x5f ? 0x70 : 0xb0)) << 8) | (lo + 0x7e);
}

/*
 Convert FROM contain JIS codes to SHIFT JIS codes
 return converted buffer
 */
static char *
jis8_to_sj (const char *from, BOOL overwrite)
{
    register char *out;
    register int shifted;
    char *save;

    shifted = _KJ_ROMAN;
    save = (char *) from;
    for (out = cvtbuf; *from;) {
	if (is_esc (*from)) {
	    if (is_so1 (from[1]) && is_so2 (from[2])) {
		shifted = _KJ_KANJI;
		from += 3;
	    } else if (is_si1 (from[1]) && is_si2 (from[2])) {
		shifted = _KJ_ROMAN;
		from += 3;
	    } else {			/* sequence error */
		goto normal;
	    }
	} else {
	normal:
	    switch (shifted) {
	    default:
	    case _KJ_ROMAN:
		*out++ = *from++;
		break;
	    case _KJ_KANJI:
		{
		    int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
		    *out++ = (code >> 8) & 0xff;
		    *out++ = code;
		    from += 2;
		}
		break;
	    }
	}
    }
    *out = 0;
    if (overwrite) {
	strcpy (save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

/*
 Convert FROM contain SHIFT JIS codes to JIS codes
 return converted buffer
*/
static char *
sj_to_jis8 (const char *from, BOOL overwrite)
{
    register char *out;
    register int shifted;
    char *save;

    shifted = _KJ_ROMAN;
    save = (char *) from;
    for (out = cvtbuf; *from; ) {
	if (is_shift_jis (*from)) {
	    int code;
	    switch (shifted) {
	    case _KJ_ROMAN:		/* to KANJI */
		*out++ = jis_esc;
		*out++ = jis_so1;
		*out++ = JIS_KSO;
		shifted = _KJ_KANJI;
		break;
	    }
	    code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff);
	    *out++ = (code >> 8) & 0xff;
	    *out++ = code;
	    from += 2;
	} else {
	    switch (shifted) {
	    case _KJ_KANJI:		/* to ROMAN/KANA */
		*out++ = jis_esc;
		*out++ = jis_si1;
		*out++ = JIS_KSI;
		shifted = _KJ_ROMAN;
		break;
	    }
	    *out++ = *from++;
	}
    }
    switch (shifted) {
    case _KJ_KANJI:			/* to ROMAN/KANA */
	*out++ = jis_esc;
	*out++ = jis_si1;
	*out++ = JIS_KSI;
	shifted = _KJ_ROMAN;
	break;
    }
    *out = 0;
    if (overwrite) {
	strcpy (save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

/*
 Convert FROM contain 7 bits JIS codes to SHIFT JIS codes
 return converted buffer
 */
static char *
jis7_to_sj (const char *from, BOOL overwrite)
{
    register char *out;
    register int shifted;
    char *save;

    shifted = _KJ_ROMAN;
    save = (char *) from;
    for (out = cvtbuf; *from;) {
	if (is_esc (*from)) {
	    if (is_so1 (from[1]) && is_so2 (from[2])) {
		shifted = _KJ_KANJI;
		from += 3;
	    } else if (is_si1 (from[1]) && is_si2 (from[2])) {
		shifted = _KJ_ROMAN;
		from += 3;
	    } else {			/* sequence error */
		goto normal;
	    }
	} else if (is_so (*from)) {
	    shifted = _KJ_KANA;		/* to KANA */
	    from++;
	} else if (is_si (*from)) {
	    shifted = _KJ_ROMAN;	/* to ROMAN */
	    from++;
	} else {
	normal:
	    switch (shifted) {
	    default:
	    case _KJ_ROMAN:
		*out++ = *from++;
		break;
	    case _KJ_KANJI:
		{
		    int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
		    *out++ = (code >> 8) & 0xff;
		    *out++ = code;
		    from += 2;
		}
		break;
	    case _KJ_KANA:
		*out++ = ((int) from[0]) + 0x80;
		break;
	    }
	}
    }
    *out = 0;
    if (overwrite) {
	strcpy (save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

/*
 Convert FROM contain SHIFT JIS codes to 7 bits JIS codes
 return converted buffer
*/
static char *
sj_to_jis7 (const char *from, BOOL overwrite)
{
    register char *out;
    register int shifted;
    char *save;

    shifted = _KJ_ROMAN;
    save = (char *) from;
    for (out = cvtbuf; *from; ) {
	if (is_shift_jis (*from)) {
	    int code;
	    switch (shifted) {
	    case _KJ_KANA:
		*out++ = jis_si;	/* to ROMAN and through down */
	    case _KJ_ROMAN:		/* to KANJI */
		*out++ = jis_esc;
		*out++ = jis_so1;
		*out++ = JIS_KSO;
		shifted = _KJ_KANJI;
		break;
	    }
	    code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff);
	    *out++ = (code >> 8) & 0xff;
	    *out++ = code;
	    from += 2;
	} else if (is_kana (from[0])) {
	    switch (shifted) {
	    case _KJ_KANJI:		/* to ROMAN */
		*out++ = jis_esc;
		*out++ = jis_si1;
		*out++ = JIS_KSI;
	    case _KJ_ROMAN:		/* to KANA */
		*out++ = jis_so;
		shifted = _KJ_KANA;
		break;
	    }
	    *out++ = ((int) *from++) - 0x80;
	} else {
	    switch (shifted) {
	    case _KJ_KANA:
		*out++ = jis_si;	/* to ROMAN */
		shifted = _KJ_ROMAN;
		break;
	    case _KJ_KANJI:		/* to ROMAN */
		*out++ = jis_esc;
		*out++ = jis_si1;
		*out++ = JIS_KSI;
		shifted = _KJ_ROMAN;
		break;
	    }
	    *out++ = *from++;
	}
    }
    switch (shifted) {
    case _KJ_KANA:
	*out++ = jis_si;		/* to ROMAN */
	break;
    case _KJ_KANJI:			/* to ROMAN */
	*out++ = jis_esc;
	*out++ = jis_si1;
	*out++ = JIS_KSI;
	break;
    }
    *out = 0;
    if (overwrite) {
	strcpy (save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

/*
 Convert FROM contain 7 bits JIS(junet) codes to SHIFT JIS codes
 return converted buffer
 */
static char *
junet_to_sj (const char *from, BOOL overwrite)
{
    register char *out;
    register int shifted;
    char *save;

    shifted = _KJ_ROMAN;
    save = (char *) from;
    for (out = cvtbuf; *from;) {
	if (is_esc (*from)) {
	    if (is_so1 (from[1]) && is_so2 (from[2])) {
		shifted = _KJ_KANJI;
		from += 3;
	    } else if (is_si1 (from[1]) && is_si2 (from[2])) {
		shifted = _KJ_ROMAN;
		from += 3;
	    } else if (is_juk1(from[1]) && is_juk2 (from[2])) {
		shifted = _KJ_KANA;
		break;
	    } else {			/* sequence error */
		goto normal;
	    }
	} else {
	normal:
	    switch (shifted) {
	    default:
	    case _KJ_ROMAN:
		*out++ = *from++;
		break;
	    case _KJ_KANJI:
		{
		    int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff);
		    *out++ = (code >> 8) & 0xff;
		    *out++ = code;
		    from += 2;
		}
		break;
	    case _KJ_KANA:
		*out++ = ((int) from[0]) + 0x80;
		break;
	    }
	}
    }
    *out = 0;
    if (overwrite) {
	strcpy (save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

/*
 Convert FROM contain SHIFT JIS codes to 7 bits JIS(junet) codes
 return converted buffer
*/
static char *
sj_to_junet (const char *from, BOOL overwrite)
{
    register char *out;
    register int shifted;
    char *save;

    shifted = _KJ_ROMAN;
    save = (char *) from;
    for (out = cvtbuf; *from; ) {
	if (is_shift_jis (*from)) {
	    int code;
	    switch (shifted) {
	    case _KJ_KANA:
	    case _KJ_ROMAN:		/* to KANJI */
		*out++ = jis_esc;
		*out++ = jis_so1;
		*out++ = jis_so2;
		shifted = _KJ_KANJI;
		break;
	    }
	    code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff);
	    *out++ = (code >> 8) & 0xff;
	    *out++ = code;
	    from += 2;
	} else if (is_kana (from[0])) {
	    switch (shifted) {
	    case _KJ_KANJI:		/* to ROMAN */
	    case _KJ_ROMAN:		/* to KANA */
		*out++ = jis_esc;
		*out++ = junet_kana1;
		*out++ = junet_kana2;
		shifted = _KJ_KANA;
		break;
	    }
	    *out++ = ((int) *from++) - 0x80;
	} else {
	    switch (shifted) {
	    case _KJ_KANA:
	    case _KJ_KANJI:		/* to ROMAN */
		*out++ = jis_esc;
		*out++ = jis_si1;
		*out++ = jis_si2;
		shifted = _KJ_ROMAN;
		break;
	    }
	    *out++ = *from++;
	}
    }
    switch (shifted) {
    case _KJ_KANA:
    case _KJ_KANJI:			/* to ROMAN */
	*out++ = jis_esc;
	*out++ = jis_si1;
	*out++ = jis_si2;
	break;
    }
    *out = 0;
    if (overwrite) {
	strcpy (save, (const char *) cvtbuf);
	return save;
    } else {
	return cvtbuf;
    }
}

#define HEXTAG ':'

#define hex2bin(x)						      \
    ( ((int) '0' <= ((int) (x)) && ((int) (x)) <= (int)'9')?	      \
        (((int) (x))-(int)'0'):					      \
      ((int) 'a'<= ((int) (x)) && ((int) (x))<= (int) 'f')?	      \
        (((int) (x)) - (int)'a'+10):				      \
      (((int) (x)) - (int)'A'+10) )
#define bin2hex(x)						      \
    ( (((int) (x)) >= 10)? (((int) (x))-10 + (int) 'a'): (((int) (x)) + (int) '0') )
 
/* ":xx" -> a byte */
static char *
hex_to_sj(char *from, BOOL overwrite)
{
    char *sp, *dp;
    
    sp = from; dp = cvtbuf;
    while (*sp) {
	if (*sp == HEXTAG && isxdigit(sp[1]) && isxdigit(sp[2])) {
	    *dp++ = (hex2bin(sp[1])<<4) | (hex2bin(sp[2]));
	    sp += 3;
	} else
	    *dp++ = *sp++;
    }
    *dp = '\0';
    if (overwrite) {
	strcpy((char *) from, (const char *) cvtbuf);
	return (char *) from;
    } else {
	return cvtbuf;
    }
}
 
/* kanji/kana -> ":xx" */
static char *
sj_to_hex(char *from, BOOL overwrite)
{
    unsigned char *sp, *dp;
    
    sp = (unsigned char*) from;
    dp = (unsigned char*) cvtbuf;
    while (*sp) {
	if (is_kana(*sp)) {
	    *dp++ = HEXTAG;
	    *dp++ = bin2hex(((*sp)>>4)&0x0f);
	    *dp++ = bin2hex((*sp)&0x0f);
	    sp++;
	} else if (is_shift_jis(*sp) && is_shift_jis2(sp[1])) {
	    *dp++ = HEXTAG;
	    *dp++ = bin2hex(((*sp)>>4)&0x0f);
	    *dp++ = bin2hex((*sp)&0x0f);
	    sp++;
	    *dp++ = HEXTAG;
	    *dp++ = bin2hex(((*sp)>>4)&0x0f);
	    *dp++ = bin2hex((*sp)&0x0f);
	    sp++;
	} else
	    *dp++ = *sp++;
    }
    *dp = '\0';
    if (overwrite) {
	strcpy((char *) from, (const char *) cvtbuf);
	return (char *) from;
    } else {
	return cvtbuf;
    }
}

/*
 * Convert coding_system CODE to dos CODE(sjis)
 */
char *
kj_dos_format (char *str, BOOL overwrite)
{
    static char* (*func)() = 0;
    static int func_inited = 0;

    if (!func_inited) {
	switch (coding_system) {
	case EUC_CODE:
	    func = euc_to_sj;
	    break;
	case JIS7_CODE:
	    func = jis7_to_sj;
	    break;
	case JIS8_CODE:
	    func = jis8_to_sj;
	    break;
	case JUNET_CODE:
	    func = junet_to_sj;
	    break;
	case HEX_CODE:
	    func = hex_to_sj;
	    break;
	default:
	case SJIS_CODE:
	    break;			/* nothing to convert */
	}
	func_inited = 1;
    }
    return (func ? (*func) (str, overwrite) : str);
}

/*
 * Convert dos CODE(sjis) to coding_system CODE
 */
char *
kj_unix_format (char *str, BOOL overwrite)
{
    static char* (*func)() = 0;
    static int func_inited = 0;

    if (!func_inited) {
	switch (coding_system) {
	case EUC_CODE:
	    func = sj_to_euc;
	    break;
	case JIS7_CODE:
	    func = sj_to_jis7;
	    break;
	case JIS8_CODE:
	    func = sj_to_jis8;
	    break;
	case JUNET_CODE:
	    func = sj_to_junet;
	    break;
	case HEX_CODE:
	    func = sj_to_hex;
	    break;
	default:
	case SJIS_CODE:
	    break;			/* nothing to convert */
	}
	func_inited = 1;
    }
    return (func ? (*func) (str, overwrite) : str);
}

/*
 * Interpret coding system.
 */
int 
interpret_coding_system (char *str, int def)
{
    if (strequal (str, "sjis")) {
	return SJIS_CODE;
    } else if (strequal (str, "euc")) {
	return EUC_CODE;
    } else if (strequal (str, "jis8")) {
	return JIS8_CODE;
    } else if (strequal (str, "jis7")) {
	return JIS7_CODE;
    } else if (strequal (str, "junet")) {
	return JUNET_CODE;
    } else if (strequal (str, "hex")) {
	return HEX_CODE;
    }
    return def;
}
#else 
int kanji_dummy_procedure(void)
{return 0;}
#endif /* KANJI */
