/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* lcCT.c 1.1 - Fujitsu source for CDEnext    95/11/06 20:32:33 	*/
/* $XConsortium: _fallcCT.c /main/1 1996/04/08 15:15:33 cde-fuj $ */
/*
 * Copyright 1992, 1993 by TOSHIBA Corp.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of TOSHIBA not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission. TOSHIBA make no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * TOSHIBA DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * TOSHIBA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Author: Katsuhisa Yano	TOSHIBA Corp.
 *			   	mopi@osa.ilab.toshiba.co.jp
 */

#include "_fallibint.h"
#include "_fallcPubI.h"
#include <X11/Xos.h>
#include <stdio.h>

typedef struct _StateRec {
    XlcCharSet charset;
    XlcCharSet GL_charset;
    XlcCharSet GR_charset;
    XlcCharSet ext_seg_charset;
    int ext_seg_left;
} StateRec, *State;

typedef struct _CTDataRec {
    char *name;
    char *encoding;		/* Compound Text encoding */
} CTDataRec, *CTData;

typedef struct _CTInfoRec {
    XlcCharSet charset;
    int encoding_len;
    char *encoding;		/* Compound Text encoding */
    int ext_segment_len;
    char *ext_segment;		/* extended segment */
    struct _CTInfoRec *next;
} CTInfoRec, *CTInfo;

static CTDataRec default_ct_data[] =
{
    { "ISO8859-1:GL", "\033(B" },
    { "ISO8859-1:GR", "\033-A" },
    { "ISO8859-2:GR", "\033-B" },
    { "ISO8859-3:GR", "\033-C" },
    { "ISO8859-4:GR", "\033-D" },
    { "ISO8859-7:GR", "\033-F" },
    { "ISO8859-6:GR", "\033-G" },
    { "ISO8859-8:GR", "\033-H" },
    { "ISO8859-5:GR", "\033-L" },
    { "ISO8859-9:GR", "\033-M" },
    { "JISX0201.1976-0:GL", "\033(J" },
    { "JISX0201.1976-0:GR", "\033)I" },

    { "GB2312.1980-0:GL", "\033$(A" },
    { "GB2312.1980-0:GR", "\033$)A" },
    { "JISX0208.1983-0:GL", "\033$(B" },
    { "JISX0208.1983-0:GR", "\033$)B" },
    { "KSC5601.1987-0:GL", "\033$(C" },
    { "KSC5601.1987-0:GR", "\033$)C" },
} ;

#define XctC0		0x0000
#define XctHT		0x0009
#define XctNL		0x000a
#define XctESC		0x001b
#define XctGL		0x0020
#define XctC1		0x0080
#define XctCSI		0x009b
#define XctGR		0x00a0

#define XctCntrlFunc	0x0023
#define XctMB		0x0024
#define XctOtherCoding	0x0025
#define XctGL94		0x0028
#define XctGR94		0x0029
#define XctGR96		0x002d
#define XctNonStandard	0x002f
#define XctIgnoreExt	0x0030
#define XctNotIgnoreExt	0x0031
#define XctLeftToRight	0x0031
#define XctRightToLeft	0x0032
#define XctDirection	0x005d
#define XctDirectionEnd	0x005d

#define XctGL94MB	0x2428
#define XctGR94MB	0x2429
#define XctExtSeg	0x252f
#define XctOtherSeg	0x2f00

#define XctESCSeq	0x1b00
#define XctCSISeq	0x9b00

#define SKIP_I(str)	while (*(str) >= 0x20 && *(str) <=  0x2f) (str)++;
#define SKIP_P(str)	while (*(str) >= 0x30 && *(str) <=  0x3f) (str)++;

typedef struct {
    XlcSide side;
    int char_size;
    int set_size;
    int ext_seg_length;
    int version;
    CTInfo ct_info;
} CTParseRec, *CTParse;

static CTInfo ct_list = NULL;

static CTInfo
_XlcGetCTInfoFromEncoding(char *encoding, int length)
{
    CTInfo ct_info;

    for (ct_info = ct_list; ct_info; ct_info = ct_info->next) {
	if (length >= ct_info->encoding_len) {
	    if (ct_info->ext_segment) {
		if (!strncmp(ct_info->encoding, encoding, 4) &&
		    !strncmp(ct_info->ext_segment, encoding + 6,
			     ct_info->ext_segment_len))
		    return ct_info;
	    } else if (!strncmp(ct_info->encoding, encoding,
				ct_info->encoding_len)) {
		return ct_info;
	    }
	}
    }

    return (CTInfo) NULL;
}

static unsigned int
_XlcParseCT(CTParse parse, char **text, int *length)
{
    unsigned int ret = 0;
    unsigned char ch;
    unsigned char *str = (unsigned char *) *text;

    bzero((char *) parse, sizeof(CTParseRec));

    switch (ch = *str++) {
	case XctESC:
	    if (*str == XctOtherCoding && *(str + 1) == XctNonStandard
		&& *(str + 2) >= 0x30 && *(str + 2) <= 0x3f && *length >= 6) {

		/* non-standard encodings */
		parse->side = XlcGLGR;
		parse->set_size = 0;
		str += 2;
		if (*str <= 0x34) {
		    parse->char_size = *str - 0x30;
		    if (parse->char_size == 0) parse->char_size = 1;
		    ret = XctExtSeg;
		    parse->ct_info = _XlcGetCTInfoFromEncoding(*text, *length);
		} else
		    ret = XctOtherSeg;
		str++;
		parse->ext_seg_length = (*str - 128) * 128 + *(str + 1) - 128;
		str += 2;

		*length -= (char *) str - *text;
		*text = (char *) str;
		return ret;
	    } else if (*str == XctCntrlFunc && *length >= 4 &&
		       *(str + 1) >= 0x20 && *(str + 1) <= 0x2f &&
		       (*(str + 2) == XctIgnoreExt ||
			*(str + 2) == XctNotIgnoreExt)) {

		/* ignore extension or not */
		str++;
		parse->version = *str++ - 0x20;
		ret = *str++;

	        *length -= (char *) str - *text;
		*text = (char *) str;
		return ret;
	    }

	    if (*str == XctMB) {	/* multiple-byte sets */
		parse->char_size = 2;
		str++;
	    } else
		parse->char_size = 1;

	    switch (*str) {
		case XctGL94:
		    parse->side = XlcGL;
		    parse->set_size = 94;
		    ret = (parse->char_size == 1) ? XctGL94 : XctGL94MB;
		    break;
		case XctGR94:
		    parse->side = XlcGR;
		    parse->set_size = 94;
		    ret = (parse->char_size == 1) ? XctGR94 : XctGR94MB;
		    break;
		case XctGR96:
		    if (parse->char_size == 1) {
			parse->side = XlcGR;
			parse->set_size = 96;
			ret = XctGR96;
		    }
		    break;
	    }
	    if (ret) {
		str++;
		if (*str >= 0x24 && *str <= 0x2f) {	/* non-standard */
		    ret = 0;
		    str++;
		}
	    }

	    SKIP_I(str)

	    if (ret && *str < 0x40)			/* non-standard */
		ret = 0;

	    if (*str < 0x30 || *str > 0x7e || (char *) str - *text >= *length)
		break;

	    if (ret == 0)
		ret = XctESCSeq;
	    else {
		if (parse->char_size == 2) {
		    if (*str >= 0x70)
			parse->char_size = 4;
		    else if (*str >= 0x60)
			parse->char_size = 3;
		}
		parse->ct_info = _XlcGetCTInfoFromEncoding(*text, *length);
	    }
	    str++;
	    *length -= (char *) str - *text;
	    *text = (char *) str;
	    return ret;
	case XctCSI:
	    /* direction */
	    if (*str == XctLeftToRight && *(str + 1) == XctDirection) {
		ret = XctLeftToRight;
		str += 2;
		*length -= (char *) str - *text;
		*text = (char *) str;
		return ret;
	    } else if (*str == XctRightToLeft && *(str + 1) == XctDirection) {
		ret = XctRightToLeft;
		str += 2;
		*length -= (char *) str - *text;
		*text = (char *) str;
		return ret;
	    } else if (*str == XctDirectionEnd) {
		ret = XctDirectionEnd;
		str++;
		*length -= (char *) str - *text;
		*text = (char *) str;
		return ret;
	    }

	    SKIP_P(str)
	    SKIP_I(str)

	    if (*str < 0x40 && *str > 0x7e)
		break;

	    ret = XctCSISeq;
	    str++;
	    *length -= (char *) str - *text;
	    *text = (char *) str;
	    return ret;
    }

    if (ch & 0x80) {
	if (ch < 0xa0)
	    ret = XctC1;
	else
	    ret = XctGR;
    } else {
	if (ch == XctHT || ch == XctNL)
	    ret = ch;
	else if (ch < 0x20)
	    ret = XctC0;
	else
	    ret = XctGL;
    }

    return ret;
}

XlcCharSet
_fallcAddCT(char *name, char *encoding)
{
    CTInfo ct_info;
    XlcCharSet charset;
    CTParseRec parse;
    char *ct_ptr = encoding;
    int length;
    unsigned int type;

    length = strlen(encoding);

    switch (type = _XlcParseCT(&parse, &ct_ptr, &length)) {
	case XctExtSeg:
	case XctGL94:
	case XctGL94MB:
	case XctGR94:
	case XctGR94MB:
	case XctGR96:
	    if (parse.ct_info)		/* existed */
		return parse.ct_info->charset;
	    break;
	default:
	    return (XlcCharSet) NULL;
    }

    charset = _fallcCreateDefaultCharSet(name, encoding);
    if (charset == NULL)
	return (XlcCharSet) NULL;
    _fallcAddCharSet(charset);

    ct_info = (CTInfo) Xmalloc(sizeof(CTInfoRec));
    if (ct_info == NULL)
	return (XlcCharSet) NULL;

    ct_info->charset = charset;
    ct_info->encoding = charset->ct_sequence;
    ct_info->encoding_len = strlen(ct_info->encoding);
    if (type == XctExtSeg) {
	ct_info->ext_segment = ct_info->encoding + 6;
	ct_info->ext_segment_len = strlen(ct_info->ext_segment);
    } else {
	ct_info->ext_segment = NULL;
	ct_info->ext_segment_len = 0;
    }
    ct_info->next = ct_list;
    ct_list = ct_info;

    return charset;
}

static CTInfo
_XlcGetCTInfoFromCharSet(XlcCharSet charset)
{
    CTInfo ct_info;

    for (ct_info = ct_list; ct_info; ct_info = ct_info->next)
	if (ct_info->charset == charset)
	    return ct_info;

    return (CTInfo) NULL;
}

Bool
_fallcParseCharSet(XlcCharSet charset)
{
    CTParseRec parse;
    char *ptr, buf[BUFSIZ];
    unsigned int type;
    int length;

    if (charset->ct_sequence == NULL)
	return False;

    ptr = charset->ct_sequence;
    length = strlen(ptr);

    type = _XlcParseCT(&parse, &ptr, &length);

    if (charset->name) {
	charset->xrm_name = falrmStringToQuark(charset->name);

	snprintf(buf, sizeof(buf), "%s", charset->name);
	if (ptr = strchr(buf, ':'))
	    *ptr = '\0';
	charset->xrm_encoding_name = falrmStringToQuark(buf);
	charset->encoding_name = falrmQuarkToString(charset->xrm_encoding_name);
    } else {
	charset->xrm_name = 0;
	charset->encoding_name = NULL;
	charset->xrm_encoding_name = 0;
    }

    charset->side = parse.side;
    charset->char_size = parse.char_size;
    charset->set_size = parse.set_size;

    return True;
}

static void init_converter();

Bool
_fallcInitCTInfo(void)
{
    XlcCharSet charset;
    CTData ct_data;
    int num;

    if (ct_list == NULL) {
	num = sizeof(default_ct_data) / sizeof(CTDataRec);
	for (ct_data = default_ct_data; num-- > 0; ct_data++) {
	    charset = _fallcAddCT(ct_data->name, ct_data->encoding);
	    if (charset == NULL)
		continue;
	}
	init_converter();
    }

    return True;
}


static int
_XlcCheckCTSequence(State state, char **ctext, int *ctext_len)
{
    XlcCharSet charset;
    CTParseRec parse;
    CTInfo ct_info;
    int length;

    _XlcParseCT(&parse, ctext, ctext_len);

    ct_info = parse.ct_info;
    if (parse.ext_seg_length > 0) {	/* XctExtSeg or XctOtherSeg */
	if (ct_info) {
	    length = ct_info->ext_segment_len;
	    *ctext += length;
	    *ctext_len -= length;
	    state->ext_seg_left = parse.ext_seg_length - length;
	    state->ext_seg_charset = ct_info->charset;
	} else {
	    state->ext_seg_left = parse.ext_seg_length;
	    state->ext_seg_charset = NULL;
	}
    } else if (ct_info) {
	if (charset = ct_info->charset) {
	    if (charset->side == XlcGL)
		state->GL_charset = charset;
	    else if (charset->side == XlcGR)
		state->GR_charset = charset;
	}
    }

    return 0;
}


static void
init_state(XlcConv conv)
{
    State state = (State) conv->state;
    static XlcCharSet GL_charset = NULL;
    static XlcCharSet GR_charset = NULL;

    if (GL_charset == NULL) {
	GL_charset = _fallcGetCharSet("ISO8859-1:GL");
	GR_charset = _fallcGetCharSet("ISO8859-1:GR");
    }

    state->GL_charset = state->charset = GL_charset;
    state->GR_charset = GR_charset;
    state->ext_seg_charset = NULL;
    state->ext_seg_left = 0;
}

static int
cttocs(
    XlcConv conv,
    XPointer *from,
    int *from_left,
    XPointer *to,
    int *to_left,
    XPointer *args,
    int num_args)
{
    State state = (State) conv->state;
    unsigned char ch;
    int length;
    XlcCharSet charset = NULL;
    char *ctptr, *bufptr;
    int ctext_len, buf_len;

    ctptr = *((char **) from);
    bufptr = *((char **) to);
    ctext_len = *from_left;
    buf_len = *to_left;

    while (ctext_len > 0 && buf_len > 0) {
	if (state->ext_seg_left > 0) {
	    length = min(state->ext_seg_left, ctext_len);
	    length = min(length, buf_len);

	    ctext_len -= length;
	    state->ext_seg_left -= length;

	    if (state->ext_seg_charset) {
		charset = state->ext_seg_charset;
		buf_len -= length;
		if (charset->side == XlcGL) {
		    while (length-- > 0)
			*bufptr++ = *ctptr++ & 0x7f;
		} else if (charset->side == XlcGR) {
		    while (length-- > 0)
			*bufptr++ = *ctptr++ | 0x80;
		} else {
		    while (length-- > 0)
			*bufptr++ = *ctptr++;
		}

		if (state->ext_seg_left < 1)
		    state->ext_seg_charset = NULL;
	    }
	    break;
	}
	ch = *((unsigned char *) ctptr);
	if (ch == 0x1b || ch == 0x9b) {
	    length = _XlcCheckCTSequence(state, &ctptr, &ctext_len);
	    if (length < 0)
		return -1;
	    if (state->ext_seg_left > 0 && charset)
		break;
	} else {
	    if (charset) {
		if (charset != (ch & 0x80 ? state->GR_charset :
				state->GL_charset))
		    break;
	    } else
		charset = ch & 0x80 ? state->GR_charset : state->GL_charset;

	    if ((ch < 0x20 && ch != '\0' && ch != '\n' && ch != '\t') ||
		    (ch >= 0x80 && ch < 0xa0))
		return -1;

	    *bufptr++ = *ctptr++;
	    ctext_len--;
	    buf_len--;
	}
    }

    if (charset)
	state->charset = charset;
    if (num_args > 0)
	*((XlcCharSet *) args[0]) = state->charset;

    *from_left -= ctptr - *((char **) from);
    *from = (XPointer) ctptr;

    *to_left -= bufptr - *((char **) to);
    *to = (XPointer) bufptr;

    return 0;
}

static int
cstoct(
    XlcConv conv,
    XPointer *from,
    int *from_left,
    XPointer *to,
    int *to_left,
    XPointer *args,
    int num_args)
{
    State state = (State) conv->state;
    XlcSide side;
    unsigned char min_ch, max_ch, ch;
    int length;
    CTInfo ct_info;
    XlcCharSet charset;
    char *csptr, *ctptr;
    int csstr_len, ct_len;

    if (num_args < 1)
	return -1;

    csptr = *((char **) from);
    ctptr = *((char **) to);
    csstr_len = *from_left;
    ct_len = *to_left;

    charset = (XlcCharSet) args[0];

    ct_info = _XlcGetCTInfoFromCharSet(charset);
    if (ct_info == NULL)
	return -1;

    side = charset->side;

    if (ct_info->ext_segment) {
	if (charset != state->ext_seg_charset && state->ext_seg_left < 1) {
	    length = ct_info->encoding_len;
	    if (ct_len < length)
		return -1;
	    strcpy(ctptr, ct_info->encoding);
	    ctptr[4] = ((ct_info->ext_segment_len + csstr_len) / 128) | 0x80;
	    ctptr[5] = ((ct_info->ext_segment_len + csstr_len) % 128) | 0x80;
	    ctptr += length;
	    ct_len -= length;
	    state->ext_seg_left = csstr_len;
	}
	length = min(state->ext_seg_left, csstr_len);
	state->ext_seg_left -= length;

	if (side == XlcGL) {
	    while (length-- > 0)
		*ctptr++ = *csptr++ & 0x7f;
	} else if (side == XlcGR) {
	    while (length-- > 0)
		*ctptr++ = *csptr++ | 0x80;
	} else {
	    while (length-- > 0)
		*ctptr++ = *csptr++;
	}
	state->ext_seg_charset = (state->ext_seg_left > 0) ? charset : NULL;
    } else {
	if ((side == XlcGR && charset != state->GR_charset) ||
	    (side == XlcGL && charset != state->GL_charset)) {

	    ct_len -= ct_info->encoding_len;
	    if (ct_len < 0)
		return -1;
	    strcpy(ctptr, ct_info->encoding);
	    ctptr += ct_info->encoding_len;
	}

	min_ch = 0x20;
	max_ch = 0x7f;

	if (charset->set_size == 94) {
	    max_ch--;
	    if (charset->char_size > 1 || side == XlcGR)
		min_ch++;
	}

	while (csstr_len > 0 && ct_len > 0) {
	    ch = *((unsigned char *) csptr++) & 0x7f;
	    if (ch < min_ch || ch > max_ch)
		if (ch != 0x00 && ch != 0x09 && ch != 0x0a && ch != 0x1b)
		    continue;	/* XXX */
	    if (side == XlcGL)
		*ctptr++ = ch & 0x7f;
	    else if (side == XlcGR)
		*ctptr++ = ch | 0x80;
	    else
		*ctptr++ = ch;
	    csstr_len--;
	    ct_len--;
	}
	if (side == XlcGR)
	    state->GR_charset = charset;
	else if (side == XlcGL)
	    state->GL_charset = charset;
    }

    *from_left -= csptr - *((char **) from);
    *from = (XPointer) csptr;

    *to_left -= ctptr - *((char **) to);
    *to = (XPointer) ctptr;

    return 0;
}

static int
strtocs(
    XlcConv conv,
    XPointer *from,
    int *from_left,
    XPointer *to,
    int *to_left,
    XPointer *args,
    int num_args)
{
    State state = (State) conv->state;
    char *src, *dst;
    unsigned char side;
    int length;

    src = (char *) *from;
    dst = (char *) *to;

    length = min(*from_left, *to_left);
    side = *((unsigned char *) src) & 0x80;

    while (side == (*((unsigned char *) src) & 0x80) && length-- > 0)
	*dst++ = *src++;

    *from_left -= src - (char *) *from;
    *from = (XPointer) src;
    *to_left -= dst - (char *) *to;
    *to = (XPointer) dst;

    if (num_args > 0)
	*((XlcCharSet *)args[0]) = side ? state->GR_charset : state->GL_charset;

    return 0;
}

static int
cstostr(
    XlcConv conv,
    XPointer *from,
    int *from_left,
    XPointer *to,
    int *to_left,
    XPointer *args,
    int num_args)
{
    State state = (State) conv->state;
    char *csptr, *string_ptr;
    int csstr_len, str_len;
    unsigned char ch;
    int unconv_num = 0;

    if (num_args < 1 || (state->GL_charset != (XlcCharSet) args[0] &&
	state->GR_charset != (XlcCharSet) args[0]))
	return -1;

    csptr = *((char **) from);
    string_ptr = *((char **) to);
    csstr_len = *from_left;
    str_len = *to_left;

    while (csstr_len-- > 0 && str_len > 0) {
	ch = *((unsigned char *) csptr++);
	if ((ch < 0x20 && ch != 0x00 && ch != 0x09 && ch != 0x0a) ||
	    ch == 0x7f || ((ch & 0x80) && ch < 0xa0)) {
	    unconv_num++;
	    continue;
	}
	*((unsigned char *) string_ptr++) = ch;
	str_len--;
    }

    *from_left -= csptr - *((char **) from);
    *from = (XPointer) csptr;

    *to_left -= string_ptr - *((char **) to);
    *to = (XPointer) string_ptr;

    return unconv_num;
}


static void
close_converter(XlcConv conv)
{
    if (conv->state)
	Xfree((char *) conv->state);

    Xfree((char *) conv);
}

static XlcConv
create_conv(XlcConvMethods methods)
{
    XlcConv conv;

    conv = (XlcConv) Xmalloc(sizeof(XlcConvRec));
    if (conv == NULL)
	return (XlcConv) NULL;

    conv->state = (XPointer) Xmalloc(sizeof(StateRec));
    if (conv->state == NULL) {
	    close_converter(conv);
	    return (XlcConv) NULL;
    }

    conv->methods = methods;

    init_state(conv);

    return conv;
}

static XlcConvMethodsRec cttocs_methods = {
    close_converter,
    cttocs,
    init_state
} ;

static XlcConv
open_cttocs(XLCd from_lcd, char *from_type, XLCd to_lcd, char *to_type)
{
    return create_conv(&cttocs_methods);
}

static XlcConvMethodsRec cstoct_methods = {
    close_converter,
    cstoct,
    init_state
} ;

static XlcConv
open_cstoct(XLCd from_lcd, char *from_type, XLCd to_lcd, char *to_type)
{
    return create_conv(&cstoct_methods);
}

static XlcConvMethodsRec strtocs_methods = {
    close_converter,
    strtocs,
    init_state
} ;

static XlcConv
open_strtocs(XLCd from_lcd, char *from_type, XLCd to_lcd, char *to_type)
{
    return create_conv(&strtocs_methods);
}

static XlcConvMethodsRec cstostr_methods = {
    close_converter,
    cstostr,
    init_state
} ;

static XlcConv
open_cstostr(XLCd from_lcd, char *from_type, XLCd to_lcd, char *to_type)
{
    return create_conv(&cstostr_methods);
}

static void
init_converter(void)
{
    _fallcSetConverter((XLCd) NULL, XlcNCompoundText, (XLCd) NULL, XlcNCharSet,
		     open_cttocs);
    _fallcSetConverter((XLCd) NULL, XlcNString, (XLCd) NULL, XlcNCharSet,
		     open_strtocs);

    _fallcSetConverter((XLCd) NULL, XlcNCharSet, (XLCd) NULL, XlcNCompoundText,
		     open_cstoct);
    _fallcSetConverter((XLCd) NULL, XlcNCharSet, (XLCd) NULL, XlcNString,
		     open_cstostr);
}
