/* $XConsortium: ResEncod.c /main/8 1996/11/12 05:37:04 pascale $ */
/*
 * Motif
 *
 * Copyright (c) 1987-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 librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 * 
 */
/*
 * HISTORY
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif


#include <stdio.h>
#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#endif
#include <string.h>
#include <ctype.h>
#ifdef UTF8_SUPPORTED
#include <iconv.h>
#endif
#include <errno.h>
#include <Xm/XmosP.h>
#include "MessagesI.h"
#include "ResEncodI.h"
#include "XmI.h"
#include "XmosI.h"
#include "XmStringI.h"
#include "ResConverI.h"

#define MSG8    _XmMMsgResConvert_0007
#define MSG9    _XmMMsgResConvert_0008
#define MSG10   _XmMMsgResConvert_0009
#define MSG11   _XmMMsgResConvert_0010
#define MSG13   _XmMMsgResConvert_0012
#define MSG14   _XmMMsgResConvert_0013

typedef unsigned char Octet;
typedef Octet *OctetPtr;
typedef XmConst Octet *const_OctetPtr;

typedef enum {
    ct_Dir_StackEmpty,
    ct_Dir_Undefined,
    ct_Dir_LeftToRight,
    ct_Dir_RightToLeft
} ct_Direction;

/*
** ct_Charset is used in the xmstring_to_text conversion to keep track
** of the prevous character set.  The order is not important.
*/
typedef enum {
    cs_none,
    cs_Hanzi,
    cs_JapaneseGCS,
    cs_Katakana,
    cs_KoreanGCS,
    cs_Latin1,
    cs_Latin2,
    cs_Latin3,
    cs_Latin4,
    cs_Latin5,
    cs_LatinArabic,
    cs_LatinCyrillic,
    cs_LatinGreek,
    cs_LatinHebrew,
    cs_NonStandard,
    cs_ir111
} ct_Charset; 

/* Internal context block */
typedef struct _ct_context {
    OctetPtr	    octet;	 /* octet ptr into compound text stream */
    OctetPtr	    lastoctet;		/* ptr to last octet in stream */
    struct {				/* flags */
	unsigned    dircs	: 1;	/* direction control seq encountered */
	unsigned    gchar	: 1;	/*   graphic characters encountered */
	unsigned    ignext	: 1;	/*   ignore extensions */
	unsigned    gl		: 1;	/*   text is for gl */
	unsigned    text	: 1;	/*   current item is a text seq */
    } flags;
    ct_Direction    *dirstack;		/* direction stack pointer */
    unsigned int    dirsp;		/* current dir stack index */
    unsigned int    dirstacksize;	/* size of direction stack */
    OctetPtr        encoding;           /* ptr to current encoding sequence */
    unsigned int    encodinglen;        /* length of encoding sequence */
    OctetPtr	    item;		/* ptr to current item */
    unsigned int    itemlen;		/* length of current item */
    unsigned int    version;		/* version of compound text */
    XmConst char*   gl_charset;		/* ptr to GL character set */
    unsigned char   gl_charset_size;	/* # of chars in GL charset */
    unsigned char   gl_octets_per_char;	/* # of octets per GL char */
    XmConst char*   gr_charset;		/* ptr to GR character set */
    unsigned char   gr_charset_size;	/* # of chars in GR charset */
    unsigned char   gr_octets_per_char;	/* # of octets per GR char */
    XmString	    xmstring;		/* compound string to be returned */
    XmString	    xmsep;		/* compound string separator segment */
    XmString	    xmtab;		/* compound string tab segment */
} ct_context;

/*
 *    Segment Encoding Registry datatype and macros
 */

typedef struct _EncodingRegistry {
  char                        *fontlist_tag;
  char                        *ct_encoding;
  struct _EncodingRegistry    *next;
} SegmentEncoding;

#define EncodingRegistryTag(er)       ((SegmentEncoding *)(er))->fontlist_tag
#define EncodingRegistryEncoding(er)  ((SegmentEncoding *)(er))->ct_encoding
#define EncodingRegistryNext(er)      ((SegmentEncoding *)(er))->next

/*
** Define standard character set strings
*/

static XmConst char CS_ISO8859_1[] = "ISO8859-1" ;
static XmConst char CS_ISO8859_2[] = "ISO8859-2" ;
static XmConst char CS_ISO8859_3[] = "ISO8859-3" ;
static XmConst char CS_ISO8859_4[] = "ISO8859-4" ;
static XmConst char CS_ISO8859_5[] = "ISO8859-5" ;
static XmConst char CS_ISO8859_6[] = "ISO8859-6" ;
static XmConst char CS_ISO8859_7[] = "ISO8859-7" ;
static XmConst char CS_ISO8859_8[] = "ISO8859-8" ;
static XmConst char CS_ISO8859_9[] = "ISO8859-9" ;
static XmConst char CS_JISX0201[] = "JISX0201.1976-0" ;
static XmConst char CS_GB2312_0[] = "GB2312.1980-0" ;
static XmConst char CS_GB2312_1[] = "GB2312.1980-1" ;
static XmConst char CS_JISX0208_0[] = "JISX0208.1983-0" ;
static XmConst char CS_JISX0208_1[] = "JISX0208.1983-1" ;
static XmConst char CS_KSC5601_0[] = "KSC5601.1987-0" ;
static XmConst char CS_KSC5601_1[] = "KSC5601.1987-1" ;
static XmConst char CS_UTF_8[] = "UTF-8" ;
static XmConst char CS_ISO_IR_111[] = "ISO-IR-111" ;



/* Define handy macros (note: these constants are in OCTAL) */
#define EOS	00
#define STX	02
#define HT	011
#define NL	012
#define ESC	033
#define CSI	0233

static XmConst Octet NEWLINESTRING[] = "\012";
#define NEWLINESTRING_LEN		sizeof(NEWLINESTRING)-1

static XmConst Octet TABSTRING[] = "\011";
#define TABSTRING_LEN		sizeof(TABSTRING)-1

static XmConst Octet CTEXT_L_TO_R[] = "\233\061\135";
#define CTEXT_L_TO_R_LEN		sizeof(CTEXT_L_TO_R)-1

static XmConst Octet CTEXT_R_TO_L[] = "\233\062\135";
#define CTEXT_R_TO_L_LEN		sizeof(CTEXT_R_TO_L)-1

static XmConst Octet CTEXT_SET_ISO8859_1[] = "\033\050\102\033\055\101";
#define CTEXT_SET_ISO8859_1_LEN		sizeof(CTEXT_SET_ISO8859_1)-1

static XmConst Octet CTEXT_SET_ISO8859_2[] = "\033\050\102\033\055\102";
#define CTEXT_SET_ISO8859_2_LEN		sizeof(CTEXT_SET_ISO8859_2)-1

static XmConst Octet CTEXT_SET_ISO8859_3[] = "\033\050\102\033\055\103";
#define CTEXT_SET_ISO8859_3_LEN		sizeof(CTEXT_SET_ISO8859_3)-1

static XmConst Octet CTEXT_SET_ISO8859_4[] = "\033\050\102\033\055\104";
#define CTEXT_SET_ISO8859_4_LEN		sizeof(CTEXT_SET_ISO8859_4)-1

static XmConst Octet CTEXT_SET_ISO8859_5[] = "\033\050\102\033\055\114";
#define CTEXT_SET_ISO8859_5_LEN		sizeof(CTEXT_SET_ISO8859_5)-1

static XmConst Octet CTEXT_SET_ISO8859_6[] = "\033\050\102\033\055\107";
#define CTEXT_SET_ISO8859_6_LEN		sizeof(CTEXT_SET_ISO8859_6)-1

static XmConst Octet CTEXT_SET_ISO8859_7[] = "\033\050\102\033\055\106";
#define CTEXT_SET_ISO8859_7_LEN		sizeof(CTEXT_SET_ISO8859_7)-1

static XmConst Octet CTEXT_SET_ISO8859_8[] = "\033\050\102\033\055\110";
#define CTEXT_SET_ISO8859_8_LEN		sizeof(CTEXT_SET_ISO8859_8)-1

static XmConst Octet CTEXT_SET_ISO8859_9[] = "\033\050\102\033\055\115";
#define CTEXT_SET_ISO8859_9_LEN		sizeof(CTEXT_SET_ISO8859_9)-1

static XmConst Octet CTEXT_SET_JISX0201[] = "\033\050\112\033\051\111";
#define CTEXT_SET_JISX0201_LEN		sizeof(CTEXT_SET_JISX0201)-1

static XmConst Octet CTEXT_SET_GB2312_0[] = "\033\044\050\101\033\044\051\101";
#define CTEXT_SET_GB2312_0_LEN		sizeof(CTEXT_SET_GB2312_0)-1

static XmConst Octet CTEXT_SET_JISX0208_0[] = "\033\044\050\102\033\044\051\102";
#define CTEXT_SET_JISX0208_0_LEN	sizeof(CTEXT_SET_JISX0208_0)-1

static XmConst Octet CTEXT_SET_KSC5601_0[] = "\033\044\050\103\033\044\051\103";
#define CTEXT_SET_KSC5601_0_LEN		sizeof(CTEXT_SET_KSC5601_0)-1

static XmConst Octet CTEXT_SET_IR_111[] = "\033\050\102\033\055\100";
#define CTEXT_SET_IR_111_LEN		sizeof(CTEXT_SET_IR_111)-1

#ifdef UTF8_SUPPORTED
static XmConst char UTF8_NEWLINESTRING[] = "\012";
#define UTF8_NEWLINESTRING_LEN		sizeof(UTF8_NEWLINESTRING)-1

static XmConst char UTF8_TABSTRING[] = "\011";
#define UTF8_TABSTRING_LEN		sizeof(UTF8_TABSTRING)-1

static XmConst char UTF8_L_TO_R[] = "\342\200\216";
#define UTF8_L_TO_R_LEN		sizeof(UTF8_L_TO_R)-1

static XmConst char UTF8_R_TO_L[] = "\342\200\217";
#define UTF8_R_TO_L_LEN		sizeof(UTF8_R_TO_L)-1
#endif /* UTF8_SUPPORTED */

#define CTVERSION 1
#define _IsValidC0(ctx, c)	(((c) == HT) || ((c) == NL) || ((ctx)->version > CTVERSION)) 
#define _IsValidC1(ctx, c)	((ctx)->version > CTVERSION)
 
#define _IsValidESCFinal(c)	(((c) >= 0x30) && ((c) <= 0x7e))
#define _IsValidCSIFinal(c)	(((c) >= 0x40) && ((c) <= 0x7e))

#define _IsInC0Set(c)		((c) <= 0x1f)
#define _IsInC1Set(c)		(((c) >= 0x80) && ((c) <= 0x9f))
#define _IsInGLSet(c)		(((c) >= 0x20) && ((c) <= 0x7f))
#define _IsInGRSet(c)		((c) >= 0xa0)
#define _IsInColumn2(c)		(((c) >= 0x20) && ((c) <= 0x2f))
#define _IsInColumn3(c)		(((c) >= 0x30) && ((c) <= 0x3f))
#define _IsInColumn4(c)		(((c) >= 0x40) && ((c) <= 0x4f))
#define _IsInColumn5(c)		(((c) >= 0x50) && ((c) <= 0x5f))
#define _IsInColumn6(c)		(((c) >= 0x60) && ((c) <= 0x6f))
#define _IsInColumn7(c)		(((c) >= 0x70) && ((c) <= 0x7f))
#define _IsInColumn4or5(c)	(((c) >= 0x40) && ((c) <= 0x5f))


#define _SetGL(ctx, charset, size, octets)\
    (ctx)->flags.gl = True;\
    (ctx)->gl_charset = (charset);\
    (ctx)->gl_charset_size = (size);\
    (ctx)->gl_octets_per_char = (octets)

#define _SetGR(ctx, charset, size, octets)\
    (ctx)->flags.gl = False;\
    (ctx)->gr_charset = (charset);\
    (ctx)->gr_charset_size = (size);\
    (ctx)->gr_octets_per_char = (octets)

#define _PushDir(ctx, dir)\
    if ( (ctx)->dirsp == ((ctx)->dirstacksize - 1) ) {\
	(ctx)->dirstacksize += 8;\
	(ctx)->dirstack = \
	    (ct_Direction *)XtRealloc((char *)(ctx)->dirstack,\
				(ctx)->dirstacksize * sizeof(ct_Direction));\
    }\
    (ctx)->dirstack[++((ctx)->dirsp)] = dir;\
    (ctx)->flags.dircs = True

#define _PopDir(ctx)	((ctx)->dirsp)--

#define _CurDir(ctx)	(ctx)->dirstack[(ctx)->dirsp]

/* this should probably be the other way around, (XmFONTLIST_DEFAULT_TAG map to
   _MOTIF_DEFAULT_LOCALE) but this is the smallest code change, and the code
   will not work any differently */

/* Define the MIT registered charset */

static SegmentEncoding _mit_ISO_IR_1111_registry = 
{ "ISO-IR-111", "ISO-IR-111", NULL};
static SegmentEncoding _mit_UTF_8_registry = 
{ "UTF-8", "UTF-8", &_mit_ISO_IR_1111_registry};
static SegmentEncoding _mit_KSC5601_1987_1_registry = 
{ "KSC5601.1987-1", "KSC5601.1987-1", &_mit_UTF_8_registry};
static SegmentEncoding _mit_KSC5601_1987_0_registry = 
{ "KSC5601.1987-0", "KSC5601.1987-0", &_mit_KSC5601_1987_1_registry};
static SegmentEncoding _mit_JISX0208_1983_1_registry = 
{ "JISX0208.1983-1", "JISX0208.1983-1", &_mit_KSC5601_1987_0_registry};
static SegmentEncoding _mit_JISX0208_1983_0_registry = 
{ "JISX0208.1983-0", "JISX0208.1983-0", &_mit_JISX0208_1983_1_registry};
static SegmentEncoding _mit_GB2312_1980_1_registry = 
{ "GB2312.1980-1", "GB2312.1980-1", &_mit_JISX0208_1983_0_registry};
static SegmentEncoding _mit_GB2312_1980_0_registry = 
{ "GB2312.1980-0", "GB2312.1980-0", &_mit_GB2312_1980_1_registry};
static SegmentEncoding _mit_JISX0201_1976_0_registry = 
{ "JISX0201.1976-0", "JISX0201.1976-0", &_mit_GB2312_1980_0_registry};
static SegmentEncoding _mit_ISO8859_9_registry = 
{ "ISO8859-9", "ISO8859-9", &_mit_JISX0201_1976_0_registry};
static SegmentEncoding _mit_ISO8859_8_registry = 
{ "ISO8859-8", "ISO8859-8", &_mit_ISO8859_9_registry};
static SegmentEncoding _mit_ISO8859_7_registry = 
{ "ISO8859-7", "ISO8859-7", &_mit_ISO8859_8_registry};
static SegmentEncoding _mit_ISO8859_6_registry = 
{ "ISO8859-6", "ISO8859-6", &_mit_ISO8859_7_registry};
static SegmentEncoding _mit_ISO8859_5_registry = 
{ "ISO8859-5", "ISO8859-5", &_mit_ISO8859_6_registry};
static SegmentEncoding _mit_ISO8859_4_registry = 
{ "ISO8859-4", "ISO8859-4", &_mit_ISO8859_5_registry};
static SegmentEncoding _mit_ISO8859_3_registry = 
{ "ISO8859-3", "ISO8859-3", &_mit_ISO8859_4_registry};
static SegmentEncoding _mit_ISO8859_2_registry = 
{ "ISO8859-2", "ISO8859-2", &_mit_ISO8859_3_registry};
static SegmentEncoding _mit_ISO8859_1_registry = 
{ "ISO8859-1", "ISO8859-1", &_mit_ISO8859_2_registry};

static SegmentEncoding _loc_encoding_registry = 
{ _MOTIF_DEFAULT_LOCALE, XmFONTLIST_DEFAULT_TAG, &_mit_ISO8859_1_registry};
static SegmentEncoding _encoding_registry = 
{ XmFONTLIST_DEFAULT_TAG, XmFONTLIST_DEFAULT_TAG, &_loc_encoding_registry};
static SegmentEncoding *_encoding_registry_ptr = &_encoding_registry;


/********    Static Function Declarations    ********/

static SegmentEncoding * FindEncoding( 
                        char *fontlist_tag) ;
static Boolean processCharsetAndText(XmStringCharSet tag,
				     OctetPtr	ctext,
#if NeedWidePrototypes
				     int 	separator,
#else
				     Boolean	separator,
#endif /* NeedWidePrototypes */
				     OctetPtr	*outc,
				     unsigned int	*outlen,
				     ct_Charset	*prev);
#ifdef UTF8_SUPPORTED
static Boolean processCharsetAndTextUtf8(XmStringCharSet tag,
				     OctetPtr	ctext,
#if NeedWidePrototypes
				     int 	separator,
#else
				     Boolean	separator,
#endif /* NeedWidePrototypes */
				     OctetPtr	*outc,
				     unsigned int	*outlen,
				     ct_Charset	*prev);
#endif

static Boolean processESCHack( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean processExtendedSegmentsHack( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean cvtTextToXmString( 
                        XrmValue *from,
                        XrmValue *to) ;
static void outputXmString( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int separator) ;
#else
                        Boolean separator) ;
#endif /* NeedWidePrototypes */
static XmString concatStringToXmString( 
                        XmString compoundstring,
                        char *textstring,
			int textlen,
                        char *charset,
#if NeedWidePrototypes
                        int direction,
                        int separator) ;
#else
                        XmStringDirection direction,
                        Boolean separator) ;
#endif /* NeedWidePrototypes */
static Boolean processESC( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean processCSI( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean processExtendedSegments( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean process94n( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean process94GL( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean process94GR( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean process96GR( 
                        ct_context *ctx,
#if NeedWidePrototypes
                        int final) ;
#else
                        Octet final) ;
#endif /* NeedWidePrototypes */
static Boolean cvtXmStringToText( 
                        XrmValue *from,
                        XrmValue *to) ;
static OctetPtr ctextConcat( 
                        OctetPtr str1,
                        unsigned int str1len,
                        const_OctetPtr str2,
                        unsigned int str2len) ;

#ifdef UTF8_SUPPORTED
static Boolean  cvtXmStringToUTF8String(
        XrmValue *from,
        XrmValue *to ) ;

static char* Convert(
        const char     *str,
        unsigned int    len,  
        const char      *to_codeset,
        const char      *from_codeset);

static char* ConvertWithIconv(
        const char      *str,
        unsigned int    len,
        iconv_t converter);
#endif

/********    End Static Function Declarations    ********/
 
/************************************************************************
 *
 *  FindEncoding
 *    Find the SegmentEncoding with fontlist_tag.  Return NULL if no
 *    such SegmentEncoding exists.  As a side effect, free any encodings
 *    encountered that have been unregistered.
 *
 ************************************************************************/
static SegmentEncoding *
FindEncoding(char *fontlist_tag)
{
  SegmentEncoding     *prevPtr, *encodingPtr = _encoding_registry_ptr;
  String              encoding = NULL;

  if (encodingPtr)
    {
      if (strcmp(fontlist_tag, EncodingRegistryTag(encodingPtr)) == 0)
      {
        encoding = EncodingRegistryEncoding(encodingPtr);
        
        /* Free unregistered encodings. */
        if (encoding == NULL)
          {
            _encoding_registry_ptr = EncodingRegistryNext(encodingPtr);
            XtFree( (char *) encodingPtr);
            encodingPtr = NULL;
          }
        
        return(encodingPtr);
      }
    }
  else return(encodingPtr);
  
  for (prevPtr = encodingPtr, encodingPtr = EncodingRegistryNext(encodingPtr);
       encodingPtr != NULL;
       prevPtr = encodingPtr, encodingPtr = EncodingRegistryNext(encodingPtr))
    {
      if (strcmp(fontlist_tag, EncodingRegistryTag(encodingPtr)) == 0)
      {
        encoding = EncodingRegistryEncoding(encodingPtr);
        
        /* Free unregistered encodings. */
        if (encoding == NULL)
          {
            EncodingRegistryNext(prevPtr) = 
              EncodingRegistryNext(encodingPtr);
            XtFree( (char *) encodingPtr);
            encodingPtr = NULL;
          }
        
        return(encodingPtr);
      }
      /* Free unregistered encodings. */
      else if (EncodingRegistryEncoding(encodingPtr) == NULL)
      {
        EncodingRegistryNext(prevPtr) = EncodingRegistryNext(encodingPtr);
        XtFree( (char *) encodingPtr);
      }
    }
  
  return(NULL);
}


/************************************************************************
 *
 *  XmRegisterSegmentEncoding
 *    Register a compound text encoding format for a specified font list
 *    element tag.  Returns NULL for a new tag or a copy of the old encoding
 *    for an already registered tag.
 *
 ************************************************************************/
char *
XmRegisterSegmentEncoding(
     char     *fontlist_tag,
     char     *ct_encoding)
{
  SegmentEncoding     *encodingPtr = NULL;
  String              ret_val = NULL;
  
  _XmProcessLock();
  encodingPtr = FindEncoding(fontlist_tag);

  if (encodingPtr)
    {
      ret_val = XtNewString(EncodingRegistryEncoding(encodingPtr));
      EncodingRegistryEncoding(encodingPtr) = 
      ct_encoding ? XtNewString(ct_encoding) : (String)NULL;
    }
  else if (ct_encoding != NULL)
    {
      encodingPtr = 
      (SegmentEncoding *)XtMalloc((Cardinal)sizeof(SegmentEncoding));
      EncodingRegistryTag(encodingPtr) = XtNewString(fontlist_tag);
      EncodingRegistryEncoding(encodingPtr) = XtNewString(ct_encoding);
      
      EncodingRegistryNext(encodingPtr) = _encoding_registry_ptr;
      _encoding_registry_ptr = encodingPtr;
    }
  
  _XmProcessUnlock();
  return(ret_val);
}



/************************************************************************
 *
 * _XmGetEncodingRegistryTarget returns the current encoding registry
 * as a list of NULL separated items.  The length is returned through
 * the passed length pointer.
 *
 ************************************************************************/
XtPointer 
_XmGetEncodingRegistryTarget(int *length)
{
  int i, count, total_size;
  SegmentEncoding *current;
  char *rval;
  
  total_size = 0;

  _XmProcessLock();
  current = _encoding_registry_ptr;
  while(current != NULL) {
    total_size += strlen(EncodingRegistryTag(current)) +
      strlen(EncodingRegistryEncoding(current)) + 2;
    current = EncodingRegistryNext(current);
  }

  *length = total_size;

  /* Create output buffer large enough for all the
     pairs of tags and encodings */
  rval = XtMalloc(sizeof(char) * total_size);

  i = 0;
  current = _encoding_registry_ptr;
  while(current != NULL) {
    count = strlen(EncodingRegistryTag(current));
    strcpy(&rval[i], EncodingRegistryTag(current));
    i += count;
    rval[i] = 0;
    i++;
    count = strlen(EncodingRegistryEncoding(current));
    strcpy(&rval[i], EncodingRegistryEncoding(current));
    i += count;
    rval[i] = 0;
    i++;
    current = EncodingRegistryNext(current);
  }
  
  _XmProcessUnlock();
  return((XtPointer) rval);
}

/************************************************************************
 *
 *  XmMapSegmentEncoding
 *    Returns the compound text encoding format associated with the
 *    specified font list element tag.  Returns NULL if not found.
 *
 ************************************************************************/
char *
XmMapSegmentEncoding(char        *fontlist_tag)
{
  SegmentEncoding     *encodingPtr = NULL;
  String              ret_val = NULL;

  _XmProcessLock();
  encodingPtr = FindEncoding(fontlist_tag);

  if (encodingPtr) 
    ret_val = XtNewString(EncodingRegistryEncoding(encodingPtr));
  
  _XmProcessUnlock();
  return(ret_val);
}


/************************************************************************
 *
 *  XmCvtCTToXmString
 *	Convert a compound text string to a XmString.  This is the public
 *	version which takes only a compound text string as an argument.
 *	Note: processESC and processExtendedSegments have to be hacked
 *	for this to work.
 *
 ************************************************************************/
XmString 
XmCvtCTToXmString(
        char *text )
{
    ct_context	    *ctx;		/* compound text context block */
    Boolean	    ok = True;
    Octet	    c;
    XmString	    xmsReturned;	/* returned Xm string */

    ctx = (ct_context *) XtMalloc(sizeof(ct_context));

/* initialize the context block */
    ctx->octet = (OctetPtr)text;
    ctx->flags.dircs = False;
    ctx->flags.gchar = False;
    ctx->flags.ignext = False;
    ctx->flags.gl = False;
    ctx->flags.text = False;
    ctx->dirstacksize = 8;
    ctx->dirstack = (ct_Direction *)
            XtMalloc(ctx->dirstacksize*sizeof(ct_Direction));
/*
 * Define XLIB_HANDLES_DIRECTION if vendor's X library knows how
 * to deal with direction control sequences in CT. Otherwise
 * no such ones will be output if there are no Rtol segments
 * in the XmString. Note that the MIT sample implementation 
 * does not deal with direction control sequences...
 */
#ifdef XLIB_HANDLES_DIRECTION
    ctx->dirstack[0] = ct_Dir_StackEmpty;
    ctx->dirsp = 0;
#else
    ctx->dirstack[0] = ct_Dir_StackEmpty;
    ctx->dirstack[1] = ct_Dir_LeftToRight;
    ctx->dirsp = 1;
#endif
    ctx->encoding = NULL;
    ctx->encodinglen = 0;
    ctx->item = NULL;
    ctx->itemlen = 0;
    ctx->version = CTVERSION;
    ctx->gl_charset = CS_ISO8859_1;
    ctx->gl_charset_size = 94;
    ctx->gl_octets_per_char = 1;
    ctx->gr_charset = CS_ISO8859_1;
    ctx->gr_charset_size = 96;
    ctx->gr_octets_per_char = 1;
    ctx->xmstring = NULL;
    ctx->xmsep = NULL;
    ctx->xmtab = NULL;

/*
** check for version/ignore extensions sequence (must be first if present)
**  Format is:	ESC 02/03 V 03/00   ignoring extensions is OK
**		ESC 02/03 V 03/01   ignoring extensions is not OK
**  where V is in the range 02/00 thru 02/15 and represents versions 1 thru 16
*/
    if (    (ctx->octet[0] == ESC)
	&&  (ctx->octet[1] == 0x23)
	&&  (_IsInColumn2(ctx->octet[2])
	&&  ((ctx->octet[3] == 0x30) || ctx->octet[3] == 0x31))
       ) {
	ctx->version = ctx->octet[2] - 0x1f;	/* 0x20-0x2f => version 1-16 */
	if (ctx->octet[3] == 0x30)		/* 0x30 == can ignore extensions */
	    ctx->flags.ignext = True;
	ctx->octet += 4;			/* advance ptr to next seq */
    }


    while (ctx->octet[0] != 0) {
    switch (*ctx->octet) {			/* look at next octet in seq */
	case ESC:
	    /* %%% TEMP
	    ** if we have any text to output, do it
	    ** this section needs to be optimized so that it handles
	    ** paired character sets without outputting a new segment.
	    */
	    if (ctx->flags.text) {
		outputXmString(ctx, False);	/* with no separator */
	    }
	    ctx->flags.text = False;
	    ctx->item = ctx->octet;		/* remember start of this item */
	    ctx->itemlen = 0;

	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */

	    /* scan for final char */
	    while (	(ctx->octet[0] != 0)
		     && (_IsInColumn2(*ctx->octet)) ) {
		ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    }

	    if (ctx->octet[0] == 0) {	/* if nothing after this, it's an error */
		ok = False;
		break;
	    }

	    c = *ctx->octet;			/* get next char in seq */
	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    if (_IsValidESCFinal(c)) {
		/* we have a valid ESC sequence - handle it */
		ok = processESCHack(ctx, c);
	    } else {
		ok = False;
	    }
	    if (ok) {
	      ctx->encoding = ctx->item;
	      ctx->encodinglen = ctx->itemlen;
	    }
	    break;

	case CSI:
	    /*
	    ** CSI format is:	CSI P I F   where
	    **	    03/00 <= P <= 03/15
	    **	    02/00 <= I <= 02/15
	    **	    04/00 <= F <= 07/14
	    */
	    /* %%% TEMP
	    ** if we have any text to output, do it
	    ** This may need optimization.
	    */
	    if (ctx->flags.text) {
		/* check whether we have a specific direction set */
                if (((ctx->octet[1] == 0x31) && (ctx->octet[2] == 0x5d))||
                    ((ctx->octet[1] == 0x32) && (ctx->octet[2] == 0x5d))||
                    (ctx->octet[1] == 0x5d))
                        outputXmString(ctx, False);    /* without a separator*/
                else
			outputXmString(ctx, True);	/* with a separator */
	    }
	    ctx->flags.text = False;
	    ctx->item = ctx->octet;		/* remember start of this item */
	    ctx->itemlen = 0;

	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */

	    /* scan for final char */
	    while (	(ctx->octet[0] != 0)
		    &&	_IsInColumn3(*ctx->octet)  ) {
		ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    }
	    while (	(ctx->octet[0] != 0)
		    && _IsInColumn2(*ctx->octet)   ) {
		ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    }

	    /* if nothing after this, it's an error */
	    if (ctx->octet[0] == 0) {
		ok = False;
		break;
	    }

	    c = *ctx->octet;			/* get next char in seq */
	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    if (_IsValidCSIFinal(c)) {
		/* we have a valid CSI sequence - handle it */
		ok = processCSI(ctx, c);
	    } else {
		ok = False;
	    }
	    break;

	case NL:			    /* new line */
	    /* if we have any text to output, do it */
	    if (ctx->flags.text) {
	      outputXmString(ctx, True);	/* with a separator */
	      ctx->flags.text = False;
	    } else {
	      if (ctx->xmsep == NULL) {
		ctx->xmsep = XmStringSeparatorCreate();
	      }
	      ctx->xmstring = XmStringConcatAndFree(ctx->xmstring, 
						    XmStringCopy(ctx->xmsep));
	    }
	    ctx->octet++;			/* advance ptr to next char */
	    break;

	case HT:
	    /* if we have any text to output, do it */
	    if (ctx->flags.text) {
	      outputXmString(ctx, False);
	      ctx->flags.text = False;
	    } 
	    if (ctx->xmtab == NULL) {
	      ctx->xmtab = XmStringComponentCreate(XmSTRING_COMPONENT_TAB, 0, 
						   NULL);
	    }
	    ctx->xmstring = XmStringConcatAndFree(ctx->xmstring, 
						  XmStringCopy(ctx->xmtab));
	    ctx->octet++;			/* advance ptr to next char */
	    break;

	default:			    /* just 'normal' text */
	    ctx->item = ctx->octet;		/* remember start of this item */
	    ctx->itemlen = 0;
	    ctx->flags.text = True;
	    while (ctx->octet[0] != 0) {
		c = *ctx->octet;
		if ((c == ESC) || (c == CSI) || (c == NL) || (c == HT)) {
		    break;
		}
		if (	(_IsInC0Set(c) && (!_IsValidC0(ctx, c)))
		    ||	(_IsInC1Set(c) && (!_IsValidC1(ctx, c))) ) {
		    ok = False;
		    break;
		}
		ctx->flags.gchar = True;	/* We have a character! */

                /*
                 *  We should look at the actual character to
                 *  decide whether it's a gl or gr character.
                 *
                 *  We'll hit the problem if we get a CT that
                 *  isn't generated by Motif.
                 */
                if (isascii((unsigned char)c)) {
		    ctx->itemlen += ctx->gl_octets_per_char;
		    ctx->octet += ctx->gl_octets_per_char;
		} else {
		    ctx->octet += ctx->gr_octets_per_char;
		    ctx->itemlen += ctx->gr_octets_per_char;
		}
	    } /* end while */
	    break;
	} /* end switch */
    if (!ok) break;
    } /* end while */

/* if we have any text left to output, do it */
    if (ctx->flags.text) {
	outputXmString(ctx, False);		/* with no separator */
    }

    XtFree((char *) ctx->dirstack);
    if (ctx->xmsep != NULL) XmStringFree(ctx->xmsep);
    if (ctx->xmtab != NULL) XmStringFree(ctx->xmtab);
    xmsReturned = (XmString)ctx->xmstring;
    XtFree((char *) ctx);

    if (ok)
      return ( xmsReturned );
    else 
	return ( (XmString)NULL );
    
}


/***********************************************************************
 *
 * Hacked procedures to work with XmCvtCTToXmString.
 *
 ***********************************************************************/

/* processESCHack - handle valid ESC sequences */
static Boolean 
processESCHack(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    Boolean	    ok;

    switch (ctx->item[1]) {
    case 0x24:			/* 02/04 - invoke 94(n) charset into GL or GR */
	ok = process94n(ctx, final);
	break;
    case 0x25:			/* 02/05 - extended segments */
	/* if we have any text to output, do it */
	if (ctx->flags.text) {
	    outputXmString(ctx, False);	/* with no separator */
	    ctx->flags.text = False;
	}
	ok = processExtendedSegmentsHack(ctx, final);
	break;
    case 0x28:			/* 02/08 - invoke 94 charset into GL */
	ok = process94GL(ctx, final);
	break;
    case 0x29:			/* 02/09 - invoke 94 charset into GR */
	ok = process94GR(ctx, final);
	break;
    case 0x2d:			/* 02/13 - invoke 96 charset into GR */
	ok =  process96GR(ctx, final);
	break;
    default:
	ok = False;
	break;
    }
    return(ok);
}


static Boolean 
processExtendedSegmentsHack(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    OctetPtr	    esptr;			/* ptr into ext seg */
    unsigned int    seglen;			/* length of ext seg */
    unsigned int    len;			/* length */
    String	    charset_copy;		/* ptr to NULL-terminated copy of ext seg charset */
    OctetPtr	    text_copy;			/* ptr to NULL-terminated copy of ext seg text */
    XmString	    tempxm1;
    Boolean	    ok = True;

    /* Extended segments
    **  01/11 02/05 02/15 03/00 M L	    variable # of octets/char
    **  01/11 02/05 02/15 03/01 M L	    1 octet/char
    **  01/11 02/05 02/15 03/02 M L	    2 octets/char
    **  01/11 02/05 02/15 03/03 M L	    3 octets/char
    **  01/11 02/05 02/15 03/04 M L	    4 octets/char
    */
    if (	(ctx->itemlen == 4)
	&&	(ctx->item[2] == 0x2f)
	&&	(_IsInColumn3(final))
       ) 
      {
	if (    (ctx->octet[0] < 0x80)
	    ||  (ctx->octet[1] < 0x80)
	   )	
	  {
	    return(False);
	  }

	/*
	** The most significant bit of M and L are always set to 1
	** The number is computed as ((M-128)*128)+(L-128)
	*/
	seglen = *ctx->octet - 0x80;
	ctx->octet++; ctx->itemlen++;		/* advance pointer */
	seglen = (seglen << 7) + (*ctx->octet - 0x80);
	ctx->octet++; ctx->itemlen++;		/* advance pointer */
	
	/* Check for premature end. */
	for (esptr = ctx->octet; esptr < (ctx->octet + seglen); esptr++) 
	  {
	    if (*esptr == 0) 
	      {
		return(False);
	      }
	  }	

        esptr = ctx->octet;			/* point to charset */
	ctx->itemlen += seglen;			/* advance pointer over segment */
	ctx->octet += seglen;

	switch (final) {
	case 0x30:				/* variable # of octets per char */
	case 0x31:				/* 1 octet per char */
	case 0x32:				/* 2 octets per char */
	    /* scan for STX separator between charset and text */
	    len = 0;
	    while (esptr[len] != STX)
		len++;
	    if (len > ctx->itemlen) {		/* if we ran off the end, error */
		ok = False;
		break;
	    }
	    charset_copy = XtMalloc(len + 1);
	    strncpy(charset_copy, (char *) esptr, len);
	    charset_copy[len] = EOS;
	    esptr += len + 1;			/* point to text part */
	    len = seglen - len - 1;		/* calc length of text part */

	    /* For two-octets charsets, make sure the text
	     * contains an integral number of characters. */
            if (final == 0x32 && len % 2) {
	      XtFree(charset_copy);
	      return (False);
            }
	    
	    text_copy = (unsigned char *) XtMalloc(len + 1);
	    memcpy( text_copy, esptr, len);
	    text_copy[len] = EOS;
	    tempxm1 = XmStringConcatAndFree(
		         XmStringDirectionCreate((_CurDir(ctx) ==
						  ct_Dir_LeftToRight ?
						  XmSTRING_DIRECTION_L_TO_R :
						  (_CurDir(ctx) ==
						   ct_Dir_RightToLeft ? 
						   XmSTRING_DIRECTION_R_TO_L :
						   XmSTRING_DIRECTION_UNSET))),
			 XmStringCreate((char *)text_copy, charset_copy));
	    ctx->xmstring = XmStringConcatAndFree(ctx->xmstring, tempxm1);
	    XtFree((char *) text_copy);
	    XtFree((char *) charset_copy);
	    ok = True;
	    break;
	    
	case 0x33:				/* 3 octets per char */
	case 0x34:				/* 4 octets per char */
	    /* not supported */
	    ok = False;
	    break;

	default:
	    /* reserved for future use */
	    ok = False;
	    break;
	} /* end switch */
    } /* end if */

    return(ok);
}
  

/************************************************************************
 *
 *  XmCvtTextToXmString
 *	Convert a compound text string to a XmString.
 *
 ************************************************************************/
/*ARGSUSED*/
Boolean 
XmCvtTextToXmString(
        Display *display,
        XrmValuePtr args,	/* unused */
        Cardinal *num_args,	/* unused */
        XrmValue *from_val,
        XrmValue *to_val,
        XtPointer *converter_data ) /* unused */
{
    Boolean		ok;

    if (from_val->addr == NULL)
	return( FALSE);

    ok = cvtTextToXmString(from_val, to_val);

    if (!ok)
    {
	to_val->addr = NULL;
	to_val->size = 0;
	XtAppWarningMsg(XtDisplayToApplicationContext(display),
			"conversionError","compoundText", "XtToolkitError",
			MSG13, (String *)NULL, (Cardinal *)NULL);
    }
    return(ok);
}


static Boolean 
cvtTextToXmString(
        XrmValue *from,
        XrmValue *to )
{
    ct_context	    *ctx;		/* compound text context block */
    Boolean	    ok = True;
    Octet	    c;

    ctx = (ct_context *) XtMalloc(sizeof(ct_context));

/* initialize the context block */
    ctx->octet = (OctetPtr)from->addr;
    ctx->lastoctet = ctx->octet + from->size;
    ctx->flags.dircs = False;
    ctx->flags.gchar = False;
    ctx->flags.ignext = False;
    ctx->flags.gl = False;
    ctx->flags.text = False;
    ctx->dirstacksize = 8;
    ctx->dirstack = (ct_Direction *)
            XtMalloc(ctx->dirstacksize*sizeof(ct_Direction));
/*
 * Define XLIB_HANDLES_DIRECTION if vendor's X library knows how
 * to deal with direction control sequences in CT. Otherwise
 * no such ones will be output if there are no Rtol segments
 * in the XmString. Note that the MIT sample implementation 
 * does not deal with direction control sequences...
 */
#ifdef XLIB_HANDLES_DIRECTION
    ctx->dirstack[0] = ct_Dir_StackEmpty;
    ctx->dirsp = 0;
#else
    ctx->dirstack[0] = ct_Dir_StackEmpty;
    ctx->dirstack[1] = ct_Dir_LeftToRight;
    ctx->dirsp = 1;
#endif
    ctx->encoding = NULL;
    ctx->encodinglen = 0;
    ctx->item = NULL;
    ctx->itemlen = 0;
    ctx->version = CTVERSION;
    ctx->gl_charset = CS_ISO8859_1;
    ctx->gl_charset_size = 94;
    ctx->gl_octets_per_char = 1;
    ctx->gr_charset = CS_ISO8859_1;
    ctx->gr_charset_size = 96;
    ctx->gr_octets_per_char = 1;
    ctx->xmstring = NULL;
    ctx->xmsep = NULL;
    ctx->xmtab = NULL;

/*
** check for version/ignore extensions sequence (must be first if present)
**  Format is:	ESC 02/03 V 03/00   ignoring extensions is OK
**		ESC 02/03 V 03/01   ignoring extensions is not OK
**  where V is in the range 02/00 thru 02/15 and represents versions 1 thru 16
*/
    if (    (from->size >= 4)
	&&  (ctx->octet[0] == ESC)
	&&  (ctx->octet[1] == 0x23)
	&&  (_IsInColumn2(ctx->octet[2])
	&&  ((ctx->octet[3] == 0x30) || ctx->octet[3] == 0x31))
       ) {
	ctx->version = ctx->octet[2] - 0x1f;	/* 0x20-0x2f => version 1-16 */
	if (ctx->octet[3] == 0x30)		/* 0x30 == can ignore extensions */
	    ctx->flags.ignext = True;
	ctx->octet += 4;			/* advance ptr to next seq */
    }


    while (ctx->octet < ctx->lastoctet) {
    switch (*ctx->octet) {			/* look at next octet in seq */
	case ESC:
	    /* %%% TEMP
	    ** if we have any text to output, do it
	    ** this section needs to be optimized so that it handles
	    ** paired character sets without outputting a new segment.
	    */
	    if (ctx->flags.text) {
		outputXmString(ctx, False);	/* with no separator */
	    }
	    ctx->flags.text = False;
	    ctx->item = ctx->octet;		/* remember start of this item */
	    ctx->itemlen = 0;

	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */

	    /* scan for final char */
	    while (	(ctx->octet != ctx->lastoctet)
		     && (_IsInColumn2(*ctx->octet)) ) {
		ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    }

	    if (ctx->octet == ctx->lastoctet) {	/* if nothing after this, it's an error */
		ok = False;
		break;
	    }

	    c = *ctx->octet;			/* get next char in seq */
	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    if (_IsValidESCFinal(c)) {
		/* we have a valid ESC sequence - handle it */
		ok = processESC(ctx, c);
	    } else {
		ok = False;
	    }
	    if (ok) {
	      ctx->encoding = ctx->item;
	      ctx->encodinglen = ctx->itemlen;
	    }
	    break;

	case CSI:
	    /*
	    ** CSI format is:	CSI P I F   where
	    **	    03/00 <= P <= 03/15
	    **	    02/00 <= I <= 02/15
	    **	    04/00 <= F <= 07/14
	    */
	    /* %%% TEMP
	    ** if we have any text to output, do it
	    ** This may need optimization.
	    */
	    if (ctx->flags.text) {
		/* check whether we have a specific direction set */
                if (((ctx->octet[1] == 0x31) && (ctx->octet[2] == 0x5d))||
                    ((ctx->octet[1] == 0x32) && (ctx->octet[2] == 0x5d))||
                    (ctx->octet[1] == 0x5d))
                        outputXmString(ctx, False);    /* without a separator*/
                else
			outputXmString(ctx, True);	/* with a separator */
	    }
	    ctx->flags.text = False;
	    ctx->item = ctx->octet;		/* remember start of this item */
	    ctx->itemlen = 0;

	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */

	    /* scan for final char */
	    while (	(ctx->octet != ctx->lastoctet)
		    &&	_IsInColumn3(*ctx->octet)  ) {
		ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    }
	    while (	(ctx->octet != ctx->lastoctet)
		    && _IsInColumn2(*ctx->octet)   ) {
		ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    }

	    /* if nothing after this, it's an error */
	    if (ctx->octet == ctx->lastoctet) {
		ok = False;
		break;
	    }

	    c = *ctx->octet;			/* get next char in seq */
	    ctx->octet++; ctx->itemlen++;	/* advance ptr to next char */
	    if (_IsValidCSIFinal(c)) {
		/* we have a valid CSI sequence - handle it */
		ok = processCSI(ctx, c);
	    } else {
		ok = False;
	    }
	    break;

	case NL:			    /* new line */
	    /* if we have any text to output, do it */
	    if (ctx->flags.text) {
	      outputXmString(ctx, True);	/* with a separator */
	      ctx->flags.text = False;
	    } else {
	      if (ctx->xmsep == NULL) {
		ctx->xmsep = XmStringSeparatorCreate();
	      }
	      ctx->xmstring = XmStringConcatAndFree(ctx->xmstring,
						    XmStringCopy(ctx->xmsep));
	    }
	    ctx->octet++;			/* advance ptr to next char */
	    break;

	case HT:
	    /* if we have any text to output, do it */
	    if (ctx->flags.text) {
	      outputXmString(ctx, False);
	      ctx->flags.text = False;
	    } 
	    if (ctx->xmtab == NULL) {
	      ctx->xmtab = XmStringComponentCreate(XmSTRING_COMPONENT_TAB, 0, 
						   NULL);
	    }
	    ctx->xmstring = XmStringConcatAndFree(ctx->xmstring, 
						  XmStringCopy(ctx->xmtab));
	    ctx->octet++;			/* advance ptr to next char */
	    break;

	default:			    /* just 'normal' text */
	    ctx->item = ctx->octet;		/* remember start of this item */
	    ctx->itemlen = 0;
	    ctx->flags.text = True;
	    while (ctx->octet < ctx->lastoctet) {
		c = *ctx->octet;
		if ((c == ESC) || (c == CSI) || (c == NL) || (c == HT)) {
		    break;
		}
		if (	(_IsInC0Set(c) && (!_IsValidC0(ctx, c)))
		    ||	(_IsInC1Set(c) && (!_IsValidC1(ctx, c))) ) {
		    ok = False;
		    break;
		}
		ctx->flags.gchar = True;	/* We have a character! */

                /*
                 *  We should look at the actual character to
                 *  decide whether it's a gl or gr character.
                 *
                 *  We'll hit the problem if we get a CT that
                 *  isn't generated by Motif.
                 */
                if (isascii((unsigned char)c)) {
		    ctx->octet += ctx->gl_octets_per_char;
		    ctx->itemlen += ctx->gl_octets_per_char;
		} else {
		    ctx->octet += ctx->gr_octets_per_char;
		    ctx->itemlen += ctx->gr_octets_per_char;
		}
		if (ctx->octet > ctx->lastoctet) {
		    ok = False;
		    break;
		}
	    } /* end while */
	    break;
	} /* end switch */
    if (!ok) break;
    } /* end while */

/* if we have any text left to output, do it */
    if (ctx->flags.text) {
	outputXmString(ctx, False);		/* with no separator */
    }

    XtFree((char *) ctx->dirstack);
    if (ctx->xmstring != NULL) {
	to->addr = (char *) ctx->xmstring;
	to->size = sizeof(XmString);
    }
    if (ctx->xmsep != NULL) XmStringFree(ctx->xmsep);
    if (ctx->xmtab != NULL) XmStringFree(ctx->xmtab);
    XtFree((char *) ctx);

    return (ok);
}


static char **
cvtCTsegment(ct_context *ctx,
	     OctetPtr item,
	     unsigned int length)
{
  XTextProperty tmp_prop;
  OctetPtr octets;
  Boolean free_octets = False;
  int count;
  int ret_val;
  char **strings = NULL;
  
  if (ctx->encoding) {
    if (ctx->encoding + ctx->encodinglen != item) {
      octets = 
	(OctetPtr)XtMalloc((ctx->encodinglen + length) * sizeof(Octet));
      memcpy((char *)octets, (char *)ctx->encoding, ctx->encodinglen);
      memcpy((char *)(octets + ctx->encodinglen), (char *)item, length);
      free_octets = True;
    } else {
      octets = ctx->encoding;
    }
  } else {
    octets = ctx->item;
  }

  tmp_prop.value = octets;
  tmp_prop.encoding = XInternAtom(_XmGetDefaultDisplay(), 
				  XmSCOMPOUND_TEXT, False);
  tmp_prop.format = 8;
  tmp_prop.nitems = ctx->encodinglen + length;
  ret_val = XmbTextPropertyToTextList(_XmGetDefaultDisplay(), 
				      &tmp_prop,
				      &strings, 
				      &count);
  if (ret_val > 0) {
    XFreeStringList(strings);
    strings = NULL;
  }
  if (free_octets)
    XtFree((char *)octets);

  return strings;
}


/* outputXmString */
static void 
outputXmString(
        ct_context *ctx,
#if NeedWidePrototypes
        int separator )
#else
        Boolean separator )
#endif /* NeedWidePrototypes */
{
  char **strings = NULL;

  /* 
   * CR # 8544: XmbTextListToTextProperty converts from locale encoding
   * to MIT registered encodings. We have to convert back - UNLESS the
   * CT was not created with XmbTextListToTextProperty. However, there
   * is no way to tell how we got the CT, but in most cases, the
   * tag will be FONTLIST_DEFAULT_TAG, and then we want to do this. So 
   * we try this, and only use the encoding tags if we could not convert.
   */
  strings = cvtCTsegment(ctx, ctx->item, ctx->itemlen);
  if (strings) {
    ctx->xmstring = concatStringToXmString
    (ctx->xmstring,
     strings[0],
     strlen(strings[0]),
     XmFONTLIST_DEFAULT_TAG,
     (XmStringDirection) ((_CurDir(ctx) == ct_Dir_LeftToRight) ?
			  XmSTRING_DIRECTION_L_TO_R :
			  ((_CurDir(ctx) == ct_Dir_RightToLeft) ?
			   XmSTRING_DIRECTION_R_TO_L :
			   XmSTRING_DIRECTION_UNSET)),
     separator );
    
    XFreeStringList(strings);
    return;
  }

  /* If we couldn't convert to locale encoding... */
  /* This is not really right. We can probably never draw this string and 
     get something that looks right out of this */

  /*
   *  If the GL charset is ISO8859-1, and the GR charset is any ISO8859
   *  charset, then they're a pair, so we can create a single segment using
   *  just the GR charset.
   *
   *  If GL and GR are multibyte charsets and they match (both GB2312 or both
   *  KSC5601) except for JISX0208, then we can create a single segment using
   *  just the GR charset. If GL and GR are multibyte charsets and they DON'T
   *  match, or if GL or GR is multibyte and the other is singlebyte, then
   *  there's no way to tell which characters belong to GL and which to GR,
   *  so treat it like a non-Latin1 in GL - 7 bit characters go to GL, 8 bit
   *  characters to to GR.  *** THIS APPEARS TO BE A HOLE IN THE COMPOUND
   *  TEXT SPEC ***.
   *
   *  Otherwise the charsets are not a pair and we will switch between GL
   *  and GR segments each time the high bit changes.
   */
  if (((ctx->gl_charset == CS_ISO8859_1)
       &&	
       ((ctx->gr_charset == CS_ISO8859_1) ||
	(ctx->gr_charset == CS_ISO8859_2) ||
	(ctx->gr_charset == CS_ISO8859_3) ||
	(ctx->gr_charset == CS_ISO8859_4) ||
	(ctx->gr_charset == CS_ISO8859_5) || 
	(ctx->gr_charset == CS_ISO8859_6) || 
	(ctx->gr_charset == CS_ISO8859_7) || 
	(ctx->gr_charset == CS_ISO8859_8) || 
	(ctx->gr_charset == CS_ISO8859_9)))
      ||
      ((ctx->gl_charset == CS_GB2312_0) && 
       (ctx->gr_charset == CS_GB2312_1))
      ||
      ((ctx->gl_charset == CS_KSC5601_0) && 
       (ctx->gr_charset == CS_KSC5601_1)))
    {
      /* OK to do single segment output but always use GR charset */
      ctx->xmstring = concatStringToXmString
	(ctx->xmstring,
	 (char *) ctx->item,
	 ctx->itemlen,
	 (char *) ctx->gr_charset,
	 (XmStringDirection) ((_CurDir(ctx) == ct_Dir_LeftToRight) ?
			      XmSTRING_DIRECTION_L_TO_R :
			      ((_CurDir(ctx) == ct_Dir_RightToLeft) ?
			       XmSTRING_DIRECTION_R_TO_L :
			       XmSTRING_DIRECTION_UNSET)),
	 separator );
      
    }
  else
    {
      /* have to create a new segment everytime the highbit changes */
      unsigned int	j = 0;
      unsigned int	start = 0;
      Octet		c;
      Boolean		curseg_is_gl;
      
      curseg_is_gl = isascii((unsigned char)ctx->item[0]);
      
      while (j < ctx->itemlen)
	{
	  c = ctx->item[j];
	  if (isascii((unsigned char)c))
	    {
	      if (!curseg_is_gl)
		{
		  /* output gr string */
		  assert(j > start);
		  ctx->xmstring = concatStringToXmString
		    (ctx->xmstring,
		     (char *)ctx->item + start,
		     j - start,
		     (char *) ctx->gr_charset,
		     (XmStringDirection)
		     ((_CurDir(ctx) == ct_Dir_LeftToRight) ?
		      XmSTRING_DIRECTION_L_TO_R :
		      ((_CurDir(ctx) == ct_Dir_RightToLeft) ?
		       XmSTRING_DIRECTION_R_TO_L :
		       XmSTRING_DIRECTION_UNSET)),
		     False );
		  start = j;
		  curseg_is_gl = True;	/* start gl segment */
		};
	      j++;
	    }
	  else
	    {
	      if (curseg_is_gl)
		{
		  /* output gl string */
		  assert(j > start);
		  ctx->xmstring = concatStringToXmString
		    (ctx->xmstring,
		     (char *)ctx->item + start,
		     j - start,
		     (char *) ctx->gl_charset,
		     (XmStringDirection)
		     ((_CurDir(ctx) == ct_Dir_LeftToRight) ?
		      XmSTRING_DIRECTION_L_TO_R :
		      ((_CurDir(ctx) == ct_Dir_RightToLeft) ?
		       XmSTRING_DIRECTION_R_TO_L :
		       XmSTRING_DIRECTION_UNSET)),
		     False );
		  start = j;
		    curseg_is_gl = False;	/* start gr segment */
		};
	      j++;
	    }; /* end if */
	}; /* end while */
      
      /* output last segment */
      ctx->xmstring = concatStringToXmString
	(ctx->xmstring,
	(char *)ctx->item + start,
	 ctx->itemlen - start,
	 (char *) ((curseg_is_gl) ?
		   ctx->gl_charset :
		   ctx->gr_charset ),
	 (XmStringDirection) ((_CurDir(ctx) == ct_Dir_LeftToRight) ?
			      XmSTRING_DIRECTION_L_TO_R :
			      ((_CurDir(ctx) == ct_Dir_RightToLeft) ?
			       XmSTRING_DIRECTION_R_TO_L :
			       XmSTRING_DIRECTION_UNSET)),
	 False );
      

      if (separator)
	{
	  if (ctx->xmsep == NULL)
	    {
	      ctx->xmsep = XmStringSeparatorCreate();
	    };
	  ctx->xmstring = XmStringConcatAndFree(ctx->xmstring, 
						XmStringCopy(ctx->xmsep));
	};
    }; /* end if paired */
}

static XmString 
concatStringToXmString(
        XmString compoundstring,
        char *textstring,
	int textlen,
        char *charset,
#if NeedWidePrototypes
        int direction,
        int separator )
#else
        XmStringDirection direction,
        Boolean separator )
#endif /* NeedWidePrototypes */
{
    XmString	tempxm1;

    tempxm1 =
      XmStringConcatAndFree(XmStringDirectionCreate(direction),
			    _XmStringNCreate(textstring, charset, textlen));
    
    if (separator)
      tempxm1 = XmStringConcatAndFree(tempxm1,
				      XmStringSeparatorCreate());

    compoundstring = XmStringConcatAndFree(compoundstring, tempxm1);
    return (compoundstring);
}


/* processESC - handle valid ESC sequences */
static Boolean 
processESC(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    Boolean	    ok;

    switch (ctx->item[1]) {
    case 0x24:			/* 02/04 - invoke 94(n) charset into GL or GR */
	ok = process94n(ctx, final);
	break;
    case 0x25:			/* 02/05 - extended segments */
	/* if we have any text to output, do it */
	if (ctx->flags.text) {
	    outputXmString(ctx, False);	/* with no separator */
	    ctx->flags.text = False;
	}
	ok = processExtendedSegments(ctx, final);
	break;
    case 0x28:			/* 02/08 - invoke 94 charset into GL */
	ok = process94GL(ctx, final);
	break;
    case 0x29:			/* 02/09 - invoke 94 charset into GR */
	ok = process94GR(ctx, final);
	break;
    case 0x2d:			/* 02/13 - invoke 96 charset into GR */
	ok =  process96GR(ctx, final);
	break;
    default:
	ok = False;
	break;
    }
    return(ok);
}


/*
**  processCSI - handle valid CSI sequences
**	CSI sequences
**	09/11 03/01 05/13   begin left-to-right text
**	09/11 03/02 05/13   begin right-to-left text
**	09/11 05/13	    end of string
**	09/11 P I F	    reserved for use in future extensions
*/
static Boolean 
processCSI(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    Boolean	    ok = True;

    switch (final) {
    case 0x5d:				/* end of direction sequence */
	switch (ctx->item[1]) {
	case 0x31:			/* start left to right */
	    if (ctx->flags.gchar && ctx->dirsp == 0) {
		ok = False;
	    } else {
		_PushDir(ctx, ct_Dir_LeftToRight);
	    }
	    break;
	case 0x32:			/* start right to left */
	    if (ctx->flags.gchar && ctx->dirsp == 0) {
		ok = False;
	    } else {
		_PushDir(ctx, ct_Dir_RightToLeft);
	    }
	    break;
	case 0x5d:			/* Just CSI EOS - revert */
	    if (ctx->dirsp > 0) {
		_PopDir(ctx);
		
	    } else {
		ok = False;
	    }
	    break;
	default:			/* anything else is an error */
	    ok = False;
	}
	break;

    default:				/* reserved for future extensions */
	ok = False;
	break;
    }
    return(ok);
}



static Boolean 
processExtendedSegments(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    OctetPtr	    esptr;			/* ptr into ext seg */
    unsigned int    seglen;			/* length of ext seg */
    unsigned int    len;			/* length */
    String	    charset_copy;		/* ptr to NULL-terminated copy of ext seg charset */
    OctetPtr	    text_copy;			/* ptr to NULL-terminated copy of ext seg text */
    XmString	    tempxm1;
    Boolean	    ok = True;

    /* Extended segments
    **  01/11 02/05 02/15 03/00 M L	    variable # of octets/char
    **  01/11 02/05 02/15 03/01 M L	    1 octet/char
    **  01/11 02/05 02/15 03/02 M L	    2 octets/char
    **  01/11 02/05 02/15 03/03 M L	    3 octets/char
    **  01/11 02/05 02/15 03/04 M L	    4 octets/char
    */
    if (	(ctx->itemlen == 4)
	&&	(ctx->item[2] == 0x2f)
	&&	(_IsInColumn3(final))
       ) {
	if (    ((ctx->lastoctet - ctx->octet) < 2)
	    ||  (ctx->octet[0] < 0x80)
	    ||  (ctx->octet[1] < 0x80)
	   ) {
	    return(False);
	}

	/*
	** The most significant bit of M and L are always set to 1
	** The number is computed as ((M-128)*128)+(L-128)
	*/
	seglen = *ctx->octet - 0x80;
	ctx->octet++; ctx->itemlen++;		/* advance pointer */
	seglen = (seglen << 7) + (*ctx->octet - 0x80);
	ctx->octet++; ctx->itemlen++;		/* advance pointer */
	if ((ctx->lastoctet - ctx->octet) < seglen) {
	    return(False);
	}
	esptr = ctx->octet;			/* point to charset */
	ctx->itemlen += seglen;			/* advance pointer over segment */
	ctx->octet += seglen;

	switch (final) {
	case 0x30:				/* variable # of octets per char */
	case 0x31:				/* 1 octet per char */
	case 0x32:				/* 2 octets per char */
	    /* scan for STX separator between charset and text */
	    len = 0;
	    while (esptr[len] != STX)
		len++;
	    if (len > ctx->itemlen) {		/* if we ran off the end, error */
		ok = False;
		break;
	    }
	    charset_copy = XtMalloc(len + 1);
	    strncpy(charset_copy, (char *) esptr, len);
	    charset_copy[len] = EOS;
	    esptr += len + 1;			/* point to text part */
	    len = seglen - len - 1;		/* calc length of text part */
	    text_copy = (unsigned char *) XtMalloc(len + 1);
	    memcpy( text_copy, esptr, len);
	    text_copy[len] = EOS;
	    tempxm1 = XmStringConcatAndFree(
		         XmStringDirectionCreate((_CurDir(ctx) ==
						  ct_Dir_LeftToRight ?
						  XmSTRING_DIRECTION_L_TO_R :
						  (_CurDir(ctx) ==
						   ct_Dir_RightToLeft ? 
						   XmSTRING_DIRECTION_R_TO_L :
						   XmSTRING_DIRECTION_UNSET))),
			 XmStringCreate((char *)text_copy, charset_copy));
	    ctx->xmstring = XmStringConcatAndFree(ctx->xmstring, tempxm1);
	    XtFree((char *) text_copy);
	    XtFree(charset_copy);
	    ok = True;
	    break;
	    
	case 0x33:				/* 3 octets per char */
	case 0x34:				/* 4 octets per char */
	    /* not supported */
	    ok = False;
	    break;

	default:
	    /* reserved for future use */
	    ok = False;
	    break;
	} /* end switch */
    } /* end if */

    return(ok);
}


static Boolean 
process94n(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    if (ctx->itemlen > 3) {
	switch (ctx->item[2]) {
	case 0x28:				/* into GL */
	    switch (final) {
	    case 0x41:				/* 04/01 - China (PRC) Hanzi */
		_SetGL(ctx, CS_GB2312_0, 94, 2);
		break;
	    case 0x42:				/* 04/02 - Japanese GCS, level 2 */
		_SetGL(ctx, CS_JISX0208_0, 94, 2);
		break;
	    case 0x43:				/* 04/03 - Korean GCS */
		_SetGL(ctx, CS_KSC5601_0, 94, 2);
		break;
	    default:
		/* other character sets are not supported */
		return False;
	    } /* end switch (final) */
	    break;

	case 0x29:				/* into GR */
	    switch (final) {
	    case 0x41:				/* 04/01 - China (PRC) Hanzi */
		_SetGR(ctx, CS_GB2312_1, 94, 2);
		break;
	    case 0x42:				/* 04/02 - Japanese GCS, level 2 */
		_SetGR(ctx, CS_JISX0208_1, 94, 2);
		break;
	    case 0x43:				/* 04/03 - Korean GCS */
		_SetGR(ctx, CS_KSC5601_1, 94, 2);
		break;
	    default:
		/* other character sets are not supported */
		return False;
	    } /* end switch (final) */
	    break;

	default:
	    /* error */
	    return False;
	} /* end switch item[2] */
    }
    else {
	/* error */
	return False;
    } /* end if */
    return True;
}



static Boolean 
process94GL(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    switch (final) {
    case 0x42:				/* 04/02 - Left half, ISO8859* (ASCII) */
	_SetGL(ctx, CS_ISO8859_1,  94, 1);
	break;
    case 0x4a:				/* 04/10 - Left half, Katakana */
	_SetGL(ctx, CS_JISX0201, 94, 1);
	break;
    default:
	return False;
    }

    return(True);
}


static Boolean 
process94GR(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    switch (final) {
    case 0x49:				/* 04/09 - Right half, Katakana */
	_SetGR(ctx, CS_JISX0201, 94, 1);
	break;
    default:
	return False;
    }

    return(True);
}



static Boolean 
process96GR(
        ct_context *ctx,
#if NeedWidePrototypes
        int final )
#else
        Octet final )
#endif /* NeedWidePrototypes */
{
    switch (final) {
    case 0x40:				/* 04/00 - Right half, IR-111 */
	_SetGR(ctx, CS_ISO_IR_111, 96, 1);
	break;
    case 0x41:				/* 04/01 - Right half, Latin 1 */
	_SetGR(ctx, CS_ISO8859_1, 96, 1);
	break;
    case 0x42:				/* 04/02 - Right half, Latin 2 */
	_SetGR(ctx, CS_ISO8859_2, 96, 1);
	break;
    case 0x43:				/* 04/03 - Right half, Latin 3 */
	_SetGR(ctx, CS_ISO8859_3, 96, 1);
	break;
    case 0x44:				/* 04/04 - Right half, Latin 4 */
	_SetGR(ctx, CS_ISO8859_4, 96, 1);
	break;
    case 0x46:				/* 04/06 - Right half, Latin/Greek */
	_SetGR(ctx, CS_ISO8859_7, 96, 1);
	break;
    case 0x47:				/* 04/07 - Right half, Latin/Arabic */
	_SetGR(ctx, CS_ISO8859_6, 96, 1);
	break;
    case 0x48:				/* 04/08 - Right half, Latin/Hebrew */
	_SetGR(ctx, CS_ISO8859_8, 96, 1);
	break;
    case 0x4c:				/* 04/12 - Right half, Latin/Cyrillic */
	_SetGR(ctx, CS_ISO8859_5, 96, 1);
	break;
    case 0x4d:				/* 04/13 - Right half, Latin 5 */
	_SetGR(ctx, CS_ISO8859_9, 96, 1);
	break;
    default:
	return False;
    }

    return(True);
}


/************************************************************************
 *
 *  XmCvtXmStringToCT
 *	Convert an XmString to a compound text string directly.
 *	This is the public version of the resource converter and only
 *	requires the XmString as an argument.
 *
 ************************************************************************/
char * 
XmCvtXmStringToCT(
        XmString string )
{
  Boolean	ok;
  /* Dummy up some XrmValues to pass to cvtXmStringToText. */
  XrmValue	from_val;
  XrmValue	to_val;
  
  if (string == NULL)
    return ( (char *) NULL );
  
  from_val.addr = (char *) string;
  
  ok = cvtXmStringToText(&from_val, &to_val);
  
  if (!ok)
  {
    XtWarningMsg( "conversionError","compoundText", "XtToolkitError",
		 MSG8, NULL, NULL) ;
    return( (char *) NULL ) ;
    }
  return( (char *) to_val.addr) ;
  }

#ifdef UTF8_SUPPORTED
/************************************************************************
 *
 *  XmCvtXmStringToUTF8String
 *	Convert an XmString to a compound utf8 string directly.
 *	This is the public version of the resource converter and only
 *	requires the XmString as an argument.
 *
 ************************************************************************/
char * 
XmCvtXmStringToUTF8String(
        XmString string )
{
  Boolean	ok;
  /* Dummy up some XrmValues to pass to cvtXmStringToText. */
  XrmValue	from_val;
  XrmValue	to_val;
  
  if (string == NULL)
    return ( (char *) NULL );
  
  from_val.addr = (char *) string;
  
  ok = cvtXmStringToUTF8String(&from_val, &to_val);
  
  if (!ok)
  {
    XtWarningMsg( "conversionError","compoundText", "XtToolkitError",
		 MSG8, NULL, NULL) ;
    return( (char *) NULL ) ;
    }
  return( (char *) to_val.addr) ;
  }
#endif

/***************************************************************************
 *                                                                       *
 * _XmConvertCSToString - Converts compound string to corresponding      * 
 *   STRING if it can be fully converted.  Otherwise returns NULL.       *
 *                                                                       *
 ***************************************************************************/
/*ARGSUSED*/
char *
_XmConvertCSToString(XmString cs) /* unused */
{
  return((char *)NULL);
  
}


/***************************************************************************
 *									   *
 * _XmCvtXmStringToCT - public wrapper for the widgets to use.	  	   *
 *   This returns the length info as well - critical for the list widget   *
 * 									   *
 ***************************************************************************/
Boolean 
_XmCvtXmStringToCT(
        XrmValue *from,
        XrmValue *to )
{
    return (cvtXmStringToText( from, to ));
}

#ifdef UTF8_SUPPORTED
/***************************************************************************
 *									   *
 * _XmCvtXmStringToUTF8String - public wrapper for the widgets to use.	   *
 *   This returns the length info as well - critical for the list widget   *
 * 									   *
 ***************************************************************************/
Boolean 
_XmCvtXmStringToUTF8String(
        XrmValue *from,
        XrmValue *to )
{
    return (cvtXmStringToUTF8String( from, to ));
}
#endif

/************************************************************************
 *
 *  XmCvtXmStringToText
 *	Convert an XmString to an ASCII string.
 *
 ************************************************************************/
/*ARGSUSED*/
Boolean 
XmCvtXmStringToText(
        Display *display,
        XrmValuePtr args,	/* unused */
        Cardinal *num_args,	/* unused */
        XrmValue *from_val,
        XrmValue *to_val,
        XtPointer *converter_data ) /* unused */
{
    Boolean		ok;

    if (from_val->addr == NULL)
	return( FALSE) ;

    ok = cvtXmStringToText(from_val, to_val);

    if (!ok)
    {
	XtAppWarningMsg(XtDisplayToApplicationContext(display),
			"conversionError","compoundText", "XtToolkitError",
			MSG14, (String *)NULL, (Cardinal *)NULL);
    }
    return(ok);
}

#ifdef UTF8_SUPPORTED
/************************************************************************
 *
 *  cvtXmStringToUTF8String
 *    Convert an XmString to a compound text string.  This is the 
 *    underlying conversion routine for XmCvtXmStringToUTF8String,
 *    _XmCvtXmStringToUTF8String.
 *
 ************************************************************************/
static Boolean 
cvtXmStringToUTF8String(
        XrmValue *from,
        XrmValue *to )
{
  Boolean		ok;
  OctetPtr		outc = NULL;
  unsigned int		outlen = 0;
  _XmStringContextRec	stack_context;
  XmStringCharSet	ct_encoding = NULL, cset_save = NULL;
  ct_Direction		prev_direction = ct_Dir_LeftToRight;
  ct_Charset		prev_charset = cs_Latin1;
  XmStringComponentType	comp;
  unsigned int		len;
  XtPointer		val = NULL;
  Octet			tmp_buf[256];
  OctetPtr		tmp;
  
  /* Initialize the return parameters. */
  to->addr = (XPointer) NULL;
  to->size = 0;

  if (!from->addr) 
    return(False);

  _XmStringContextReInit(&stack_context, (XmString) from->addr);

/* BEGIN OSF Fix CR 7403 */
  while ((comp = XmeStringGetComponent(&stack_context, True, False, 
				       &len, &val)) !=
	 XmSTRING_COMPONENT_END)
    {
      switch (comp)
	{
	case XmSTRING_COMPONENT_LOCALE_TEXT:
	  cset_save = XmFONTLIST_DEFAULT_TAG;
	  /* Fall through */
	case XmSTRING_COMPONENT_TEXT:
	  if (cset_save != NULL) {
	    /* Check Registry */
	    if (ct_encoding)
	      XtFree((char *)ct_encoding);
	    ct_encoding = XmMapSegmentEncoding(cset_save);
	  }

	  /* We must duplicate val because the routines we want to */
	  /*	call don't take a length parameter. */
	  tmp = (OctetPtr) XmStackAlloc(len + 1, tmp_buf);
	  memcpy((char*)tmp, val, len);
	  tmp[len] = EOS;

	  if (ct_encoding != NULL) {
	    /* We have a mapping. */
	    ok = processCharsetAndTextUtf8(ct_encoding, tmp, FALSE, 
				       &outc, &outlen, &prev_charset);
	  } else {
	    /* No mapping.  Vendor dependent. */
	    ok = processCharsetAndTextUtf8(cset_save, tmp, FALSE, 
						    &outc, &outlen, 
						    &prev_charset);
	  }

	  /* Free our local copy of val. */
	  XmStackFree((char*) tmp, tmp_buf);

	  if (!ok)
	    {
	      _XmStringContextFree(&stack_context);
	      return(False);
	    }
	  break;
	
	case XmSTRING_COMPONENT_CHARSET:
	  cset_save = (XmStringCharSet)val;
	  break;
	
	case XmSTRING_COMPONENT_DIRECTION:
	  /* Output the direction, if changed */
	  if (*(XmStringDirection *)val == XmSTRING_DIRECTION_L_TO_R) {
	    if (prev_direction != ct_Dir_LeftToRight) {
	      outc = ctextConcat(outc, outlen, 
				 (unsigned char *) UTF8_L_TO_R,
				 (unsigned int)UTF8_L_TO_R_LEN);
	      outlen += UTF8_L_TO_R_LEN;
	      prev_direction = ct_Dir_LeftToRight;
	    }
	  } else {
	    if (prev_direction != ct_Dir_RightToLeft) {
	      outc = ctextConcat(outc, outlen, 
				 (unsigned char *) UTF8_R_TO_L,
				 (unsigned int)UTF8_R_TO_L_LEN);
	      outlen += UTF8_R_TO_L_LEN;
	      prev_direction = ct_Dir_RightToLeft;
	    }
	  };
	  break;
	
	case XmSTRING_COMPONENT_SEPARATOR:
	  if (ct_encoding != NULL) {		  /* We have a mapping. */
	    ok = processCharsetAndTextUtf8(ct_encoding, NULL, TRUE, 
				       &outc, &outlen, &prev_charset);
	  }
	  else 
	    {
	      /* No mapping.  Vendor dependent. */
	      ok = processCharsetAndTextUtf8(cset_save, NULL, TRUE,
                                    &outc, &outlen, &prev_charset);
	    }

	  if (!ok)
	    {
	      _XmStringContextFree(&stack_context);
	      return(False);
	    }
	  break;
	
	case XmSTRING_COMPONENT_TAB:
	  outc = ctextConcat(outc, outlen, 
			     (unsigned char *)UTF8_TABSTRING, 
			     (unsigned int)UTF8_TABSTRING_LEN);
	  outlen++;
	  break;

	default:
	  /* Just ignore it. */
	  break;
	}
  } /* end while */

  if (ct_encoding)
    XtFree((char *)ct_encoding);

/* END OSF Fix CR 7403 */
  if (outc != NULL) {
    to->addr = (char *) outc;
    to->size = outlen;
  }

  _XmStringContextFree(&stack_context);

  return(True);
}
#endif

/************************************************************************
 *
 *  cvtXmStringToText
 *    Convert an XmString to a compound text string.  This is the 
 *    underlying conversion routine for XmCvtXmStringToCT, 
 *    _XmCvtXmStringToCT, and XmCvtXmStringToText.
 *
 ************************************************************************/
static Boolean 
cvtXmStringToText(
        XrmValue *from,
        XrmValue *to )
{
  Boolean		ok;
  OctetPtr		outc = NULL;
  unsigned int		outlen = 0;
  _XmStringContextRec	stack_context;
  XmStringCharSet	ct_encoding = NULL, cset_save = NULL;
/*
 * Define XLIB_HANDLES_DIRECTION if vendor's X library knows how
 * to deal with direction control sequences in CT. Otherwise
 * no such beasts will be output if there are no Rtol segments
 * in the XmString. Note that the MIT sample implementation 
 * does not deal with direction control sequences... 
 */
#ifdef XLIB_HANDLES_DIRECTION
  ct_Direction		prev_direction = ct_Dir_Undefined;
#else
  ct_Direction		prev_direction = ct_Dir_LeftToRight;
#endif
  ct_Charset		prev_charset = cs_Latin1;
  XmStringComponentType	comp;
  unsigned int		len;
  XtPointer		val = NULL;
  Octet			tmp_buf[256];
  OctetPtr		tmp;
  
  /* Initialize the return parameters. */
  to->addr = (XPointer) NULL;
  to->size = 0;

  if (!from->addr) 
    return(False);

  _XmStringContextReInit(&stack_context, (XmString) from->addr);

/* BEGIN OSF Fix CR 7403 */
  while ((comp = XmeStringGetComponent(&stack_context, True, False, 
				       &len, &val)) !=
	 XmSTRING_COMPONENT_END)
    {
      switch (comp)
	{
	case XmSTRING_COMPONENT_LOCALE_TEXT:
	  cset_save = XmFONTLIST_DEFAULT_TAG;
	  /* Fall through */
	case XmSTRING_COMPONENT_TEXT:
	  if (cset_save != NULL) {
	    /* Check Registry */
	    if (ct_encoding)
	      XtFree((char *)ct_encoding);
	    ct_encoding = XmMapSegmentEncoding(cset_save);
	  }

	  /* We must duplicate val because the routines we want to */
	  /*	call don't take a length parameter. */
	  tmp = (OctetPtr) XmStackAlloc(len + 1, tmp_buf);
	  memcpy((char*)tmp, val, len);
	  tmp[len] = EOS;

	  if (ct_encoding != NULL) {
	    /* We have a mapping. */
	    ok = processCharsetAndText(ct_encoding, tmp, FALSE, 
				       &outc, &outlen, &prev_charset);
	  } else {
	    /* No mapping.  Vendor dependent. */
	    ok = _XmOSProcessUnmappedCharsetAndText(cset_save, tmp, FALSE, 
						    &outc, &outlen, 
						    &prev_charset);
	  }

	  /* Free our local copy of val. */
	  XmStackFree((char*) tmp, tmp_buf);

	  if (!ok)
	    {
	      _XmStringContextFree(&stack_context);
	      return(False);
	    }
	  break;
	
	case XmSTRING_COMPONENT_CHARSET:
	  cset_save = (XmStringCharSet)val;
	  break;
	
	case XmSTRING_COMPONENT_DIRECTION:
	  /* Output the direction, if changed */
	  if (*(XmStringDirection *)val == XmSTRING_DIRECTION_L_TO_R) {
	    if (prev_direction != ct_Dir_LeftToRight) {
	      outc = ctextConcat(outc, outlen, 
				 (unsigned char *) CTEXT_L_TO_R,
				 (unsigned int)CTEXT_L_TO_R_LEN);
	      outlen += CTEXT_L_TO_R_LEN;
	      prev_direction = ct_Dir_LeftToRight;
	    }
	  } else {
	    if (prev_direction != ct_Dir_RightToLeft) {
	      outc = ctextConcat(outc, outlen, 
				 (unsigned char *) CTEXT_R_TO_L,
				 (unsigned int)CTEXT_R_TO_L_LEN);
	      outlen += CTEXT_R_TO_L_LEN;
	      prev_direction = ct_Dir_RightToLeft;
	    }
	  };
	  break;
	
	case XmSTRING_COMPONENT_SEPARATOR:
	  if (ct_encoding != NULL) {		  /* We have a mapping. */
	    ok = processCharsetAndText(ct_encoding, NULL, TRUE, 
				       &outc, &outlen, &prev_charset);
	  }
	  else 
	    {
	      /* No mapping.  Vendor dependent. */
	      ok = _XmOSProcessUnmappedCharsetAndText(cset_save, NULL, TRUE, 
						      &outc, &outlen, &prev_charset);
	    }

	  if (!ok)
	    {
	      _XmStringContextFree(&stack_context);
	      return(False);
	    }
	  break;
	
	case XmSTRING_COMPONENT_TAB:
	  outc = ctextConcat(outc, outlen, 
			     (unsigned char *)TABSTRING, 
			     (unsigned int)TABSTRING_LEN);
	  outlen++;
	  break;

	default:
	  /* Just ignore it. */
	  break;
	}
  } /* end while */

  if (ct_encoding)
    XtFree((char *)ct_encoding);

/* END OSF Fix CR 7403 */
  if (outc != NULL) {
    to->addr = (char *) outc;
    to->size = outlen;
  }

  _XmStringContextFree(&stack_context);

  return(True);
}

#ifdef UTF8_SUPPORTED
static Boolean
processCharsetAndTextUtf8(XmStringCharSet tag,
		      OctetPtr		ctext,
#if NeedWidePrototypes
		      int		separator,
#else
		      Boolean		separator,
#endif /* NeedWidePrototypes */
		      OctetPtr		*outc,
		      unsigned int	*outlen,
		      ct_Charset	*prev)
{
  unsigned int		ctlen = 0, len;

  if (strcmp(tag, XmFONTLIST_DEFAULT_TAG) == 0) {
      if (_XmStringIsCurrentCharset("UTF-8")) {
    	  if (ctext)
    		  ctlen = strlen((char *)ctext);

          if (ctlen > 0) {
              *outc = ctextConcat(*outc, *outlen, ctext, ctlen);
              *outlen += ctlen;
          };
      } else {
          XTextProperty	prop_rtn;
          int		ret_val;
          String	msg;
     
         /* Call XmbTextListToTextProperty */
          ret_val = 
              XmbTextListToTextProperty(_XmGetDefaultDisplay(),
                      (char **)&ctext,
                      1, XUTF8StringStyle, &prop_rtn);

          if (ret_val) {
              switch (ret_val) {
                  case XNoMemory:
                      msg = MSG9;
                      break;
                  case XLocaleNotSupported:
                      msg = MSG10;
                      break;
                  default:
                      msg = MSG11;
                      break;
              }

              XtWarningMsg("conversionError", "textProperty",
                      "XtToolkitError", msg, NULL, 0);

              return(False);
          }
	
          ctlen = strlen((char *)prop_rtn.value);

          /* Now copy in the text */
          if (ctlen > 0) {
              *outc = ctextConcat(*outc, *outlen, prop_rtn.value, ctlen);
              *outlen += ctlen;
          };

          XFree(prop_rtn.value);
      }

      /* Finally, add the separator if any */
      if (separator) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)UTF8_NEWLINESTRING, 
			    (unsigned int)UTF8_NEWLINESTRING_LEN);
	(*outlen)++;
      };

      *prev = cs_none;

      return(True);
  }
	
  if (ctext)
    ctlen = strlen((char *)ctext);

  /* Now copy in the text */
  if (ctlen > 0) {
    char *text = Convert((char *)ctext, ctlen, "UTF-8", tag);
    if (text == NULL) return(False);
    *outc = ctextConcat(*outc, *outlen, (const_OctetPtr)text, strlen(text));
    *outlen += ctlen;
    XtFree(text);
  };

  /* Finally, add the separator if any */
  if (separator) {
    *outc = ctextConcat(*outc, *outlen, 
			(unsigned char *)UTF8_NEWLINESTRING, 
			(unsigned int)UTF8_NEWLINESTRING_LEN);
    (*outlen)++;
  }
  return(True);
}
#endif


static Boolean
processCharsetAndText(XmStringCharSet tag,
		      OctetPtr		ctext,
#if NeedWidePrototypes
		      int		separator,
#else
		      Boolean		separator,
#endif /* NeedWidePrototypes */
		      OctetPtr		*outc,
		      unsigned int	*outlen,
		      ct_Charset	*prev)
{
  unsigned int		ctlen = 0, len;
  
  if (strcmp(tag, CS_UTF_8) == 0)
    tag = XmFONTLIST_DEFAULT_TAG;

  if (strcmp(tag, XmFONTLIST_DEFAULT_TAG) == 0)
    {
      XTextProperty	prop_rtn;
      int		ret_val;
      String		msg;
      
      /* Call XmbTextListToTextProperty */
      ret_val = 
	XmbTextListToTextProperty(_XmGetDefaultDisplay(), (char **)&ctext,
				  1, XCompoundTextStyle, &prop_rtn);
      
      if (ret_val)
	{
	  switch (ret_val)
	    {
	    case XNoMemory:
	      msg = MSG9;
	      break;
	    case XLocaleNotSupported:
	      msg = MSG10;
	      break;
	    default:
	      msg = MSG11;
	      break;
	    }
	  
	  XtWarningMsg("conversionError", "textProperty", "XtToolkitError",
		       msg, NULL, 0);

	  return(False);
	}
	
      /* Now copy in the text */
      if (prop_rtn.value) {
        ctlen = strlen((char *)prop_rtn.value);
	*outc = ctextConcat(*outc, *outlen, prop_rtn.value, ctlen);
	*outlen += ctlen;
      };

      XFree(prop_rtn.value);

      /* Finally, add the separator if any */
      if (separator) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)NEWLINESTRING, 
			    (unsigned int)NEWLINESTRING_LEN);
	(*outlen)++;
      };

      *prev = cs_none;

      return(True);
    }
	
  if (ctext)
    ctlen = strlen((char *)ctext);

  /* Next output the charset */
  if (strcmp(tag, CS_ISO8859_1) == 0) {
    if (*prev != cs_Latin1) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_1, 
			  (unsigned int)CTEXT_SET_ISO8859_1_LEN);
      *outlen += CTEXT_SET_ISO8859_1_LEN;
      *prev = cs_Latin1;
    };
  }
  else if (strcmp(tag, CS_ISO8859_2) == 0) {
    if (*prev != cs_Latin2) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_2, 
			  (unsigned int)CTEXT_SET_ISO8859_2_LEN);
      *outlen += CTEXT_SET_ISO8859_2_LEN;
      *prev = cs_Latin2;
    };
  }
  else if (strcmp(tag, CS_ISO8859_3) == 0) {
    if (*prev != cs_Latin3) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_3, 
			  (unsigned int)CTEXT_SET_ISO8859_3_LEN);
      *outlen += CTEXT_SET_ISO8859_3_LEN;
      *prev = cs_Latin3;
    };
  }
  else if (strcmp(tag, CS_ISO8859_4) == 0) {
    if (*prev != cs_Latin4) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_4, 
			  (unsigned int)CTEXT_SET_ISO8859_4_LEN);
      *outlen += CTEXT_SET_ISO8859_4_LEN;
      *prev = cs_Latin4;
    };
  }
  else if (strcmp(tag, CS_ISO8859_5) == 0) {
    if (*prev != cs_LatinCyrillic) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_5, 
			  (unsigned int)CTEXT_SET_ISO8859_5_LEN);
      *outlen += CTEXT_SET_ISO8859_5_LEN;
      *prev = cs_LatinCyrillic;
    };
  }
  else if (strcmp(tag, CS_ISO8859_6) == 0) {
    if (*prev != cs_LatinArabic) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_6, 
			  (unsigned int)CTEXT_SET_ISO8859_6_LEN);
      *outlen += CTEXT_SET_ISO8859_6_LEN;
      *prev = cs_LatinArabic;
    };
  }
  else if (strcmp(tag, CS_ISO8859_7) == 0) {
    if (*prev != cs_LatinGreek) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_7, 
			  (unsigned int)CTEXT_SET_ISO8859_7_LEN);
      *outlen += CTEXT_SET_ISO8859_7_LEN;
      *prev = cs_LatinGreek;
    };
  }
  else if (strcmp(tag, CS_ISO8859_8) == 0) {
    if (*prev != cs_LatinHebrew) {
      *outc = ctextConcat(*outc, *outlen, 
			  (unsigned char *)CTEXT_SET_ISO8859_8, 
			  (unsigned int)CTEXT_SET_ISO8859_8_LEN);
      *outlen += CTEXT_SET_ISO8859_8_LEN;
      *prev = cs_LatinHebrew;
    };
  }
  else
    if (strcmp(tag, CS_ISO8859_9) == 0) {
      if (*prev != cs_Latin5) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)CTEXT_SET_ISO8859_9, 
			    (unsigned int)CTEXT_SET_ISO8859_9_LEN);
	*outlen += CTEXT_SET_ISO8859_9_LEN;
	*prev = cs_Latin5;
      };
    }
    else if (strcmp(tag, CS_JISX0201) == 0) {
      if (*prev != cs_Katakana) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)CTEXT_SET_JISX0201, 
			    (unsigned int)CTEXT_SET_JISX0201_LEN);
	*outlen += CTEXT_SET_JISX0201_LEN;
	*prev = cs_Katakana;
      };
    }
    else if ((strcmp(tag, CS_GB2312_0) == 0) ||
	     (strcmp(tag, CS_GB2312_1) == 0)) {
      if (*prev != cs_Hanzi) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)CTEXT_SET_GB2312_0, 
			    (unsigned int)CTEXT_SET_GB2312_0_LEN);
	*outlen += CTEXT_SET_GB2312_0_LEN;
	*prev = cs_Hanzi;
      };
    }
    else if ((strcmp(tag, CS_JISX0208_0) == 0) ||
	     (strcmp(tag, CS_JISX0208_1) == 0)) {
      if (*prev != cs_JapaneseGCS) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)CTEXT_SET_JISX0208_0, 
			    (unsigned int)CTEXT_SET_JISX0208_0_LEN);
	*outlen += CTEXT_SET_JISX0208_0_LEN;
	*prev = cs_JapaneseGCS;
      };
    }
    else if ((strcmp(tag, CS_KSC5601_0) == 0) ||  
	     (strcmp(tag, CS_KSC5601_1) == 0)) {
      if (*prev != cs_KoreanGCS) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)CTEXT_SET_KSC5601_0, 
			    (unsigned int)CTEXT_SET_KSC5601_0_LEN);
	*outlen += CTEXT_SET_KSC5601_0_LEN;
	*prev = cs_KoreanGCS;
      };
    }
    else if (strcmp(tag, CS_ISO_IR_111) == 0) {
      if (*prev != cs_ir111) {
	*outc = ctextConcat(*outc, *outlen, 
			    (unsigned char *)CTEXT_SET_IR_111, 
			    (unsigned int)CTEXT_SET_IR_111_LEN);
	*outlen += CTEXT_SET_IR_111_LEN;
	*prev = cs_ir111;
      };
    }
    else {
      /* Must be a non-standard character set! */
      OctetPtr        temp;

      len = strlen(tag);
      temp = (unsigned char *) XtMalloc(*outlen + 6 + len + 2);
      /* orig + header + tag + STX + EOS */
      memcpy( temp, *outc, *outlen);
      XtFree((char *) *outc);
      *outc = temp;
      temp = &((*outc)[*outlen]);
      /*
       ** Format is:
       **     01/11 02/05 02/15 03/nn M L tag 00/02 text
       */
      *temp++ = 0x1b;
      *temp++ = 0x25;
      *temp++ = 0x2f;
      /*
       ** HACK!  The next octet in the sequence is the # of octets/char.
       ** XmStrings don't have this information, so just set it to be
       ** variable # of octets/char, and hope the caller knows what to do.
       */
      *temp++ = 0x30;
      /* encode len in next 2 octets */
      *temp++ = 0x80 + (len+ctlen+1)/128; 
      *temp++ = 0x80 + (len+ctlen+1)%128;
      strcpy((char *) temp, tag);
      temp += len;
      *temp++ = STX;
      *temp = EOS;		/* make sure there's a \0 on the end */
      *prev = cs_NonStandard;
      *outlen += 6 + len + 1;
    };
      
  /* Now copy in the text */
  if (ctlen > 0) {
    *outc = ctextConcat(*outc, *outlen, ctext, ctlen);
    *outlen += ctlen;
  };

  /* Finally, add the separator if any */
  if (separator) {
    *outc = ctextConcat(*outc, *outlen, 
			(unsigned char *)NEWLINESTRING, 
			(unsigned int)NEWLINESTRING_LEN);
    (*outlen)++;
  }
  return(True);
}
  

static OctetPtr 
ctextConcat(
        OctetPtr str1,
        unsigned int str1len,
        const_OctetPtr str2,
        unsigned int str2len )
{
	str1 = (OctetPtr)XtRealloc((char *)str1, (str1len + str2len + 1));
	memcpy( &str1[str1len], str2, str2len);
	str1[str1len+str2len] = EOS;
	return(str1);
}

#ifdef UTF8_SUPPORTED
char*
Convert(const char     *str,
        unsigned int    len,  
        const char      *to_codeset,
        const char      *from_codeset)
{
    char *res;
    iconv_t cd;

    if (str == NULL || to_codeset == NULL || from_codeset == NULL)
        return NULL;
  
    cd = iconv_open(to_codeset, from_codeset);
    if (cd == (iconv_t) -1) {
        char msg[255];
      
        snprintf(msg, sizeof(msg),
                "Could not open converter from '%s' to '%s'",
                from_codeset, to_codeset);
        
        XmeWarning(NULL, msg);
        
        return NULL;
    }

    res = ConvertWithIconv(str, len, cd);
  
    iconv_close(cd);

    return res;
}

char*
ConvertWithIconv(const char  *str,
        unsigned int    len,
        iconv_t converter)
{
    char *dest;
    char *outp;
    const char *p;
    size_t inbytes_remaining;
    size_t outbytes_remaining;
    size_t err;
    size_t outbuf_size;
    Boolean have_error = FALSE;
  
    if (str == NULL || converter == (iconv_t) -1)
        return NULL;
    
   if (len < 0)
       len = strlen(str);

   p = str;
   inbytes_remaining = len;
   outbuf_size = len + 1; /* + 1 for nul in case len == 1 */

   outbytes_remaining = outbuf_size - 1; /* -1 for nul */
   outp = dest = XtMalloc(outbuf_size);

again:
  
   err = iconv(converter, (char **)&p, &inbytes_remaining, &outp,
           &outbytes_remaining);

   if (err == (size_t) -1) {
      switch (errno) {
	case EINVAL:
            /* Incomplete text, do not report an error */
            break;
	case E2BIG:
            {
                size_t used = outp - dest;

                outbuf_size *= 2;
                dest = XtRealloc(dest, outbuf_size);
		
                outp = dest + used;
                outbytes_remaining = outbuf_size - used - 1; /* -1 for nul */

                goto again;
            }
        case EILSEQ:
            XmeWarning(NULL, "Invalid byte sequence in conversion input");
            have_error = TRUE;
            break;
        default:
            {
                char msg[255];
                snprintf(msg, sizeof(msg), "Error during conversion: %s",
		       strerror(errno));
                XmeWarning(NULL, msg);
                have_error = TRUE;
                break;
            }
      }
   }

   *outp = '\0';
  
   if (have_error) {
      XtFree(dest);
      dest = NULL;
   }

   return dest;
}
#endif
