Logo Search packages:      
Sourcecode: lesstif1-1 version File versions  Download package

XmString.c

/*
 *
 * $Header: /cvsroot/lesstif/lesstif/lib/Xm/XmString.c,v 1.119 2003/09/13 18:48:39 dannybackx Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 * Copyright  1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 LessTif Development Team
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static const char rcsid[] = "$Header: /cvsroot/lesstif/lesstif/lib/Xm/XmString.c,v 1.119 2003/09/13 18:48:39 dannybackx Exp $";

/*
 * ** Danny had added a flag here, but I think the behavior he added is always
 *  desirable.  What this refers to is checking in XmStringGetLtoR for the
 *  value of XmSTRING_DEFAULT_CHARSET for the tag. - MLM
 *
 * Not defining this breaks certain things badly (Danny 11/8/1996).
 * As I can feel that this statement requires justification by examples,
 *  here' s one :
 *    The XmStringCreateSimple() routine uses tag XmFONTLIST_DEFAULT_TAG.
 * If you use its result with XmStringGetLtoR and use XmSTRING_DEFAULT_CHARSET
 *  for tag, (as pre-1.2 documentation says you should), then an invalid result
 *  would be obtained.
 *
 * ** There is an implicit assumption that charsets come before the text
 *  they are used by in the internal representation.  Don't change this
 *  if you want to live.
 */

#include <LTconfig.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* #include <locale.h> */
#include <limits.h>

#include <XmI/XmI.h>
#include <Xm/XmP.h>
#include <Xm/DisplayP.h>

#include <XmI/LTmisc.h>
#include <XmI/MacrosI.h>

#include <XmI/DebugUtil.h>


/*
 * for information on the external encoding, see doc/XmStrings.txt
 */
/*
 * 1 octect tag 1 octet (nominal) len
 */
#define ASN1_HEADER_SIZE      2
/*
 * ASN1 header + XmSTRING header
 */
#define XmSTRING_HEADER_SIZE  (ASN1_HEADER_SIZE * 2)

/* We don't need this in a production build */
#ifdef LESSTIF_PRODUCTION
#ifdef XMSTRING_DEBUG
#undef XMSTRING_DEBUG
#endif
#else
#ifndef XMSTRING_DEBUG
#define XMSTRING_DEBUG
#endif
#endif

#if   USE_XFT
#include <X11/Xft/Xft.h>
#include <X11/extensions/Xrender.h>
#endif

/**************************** PRIVATE FUNCTIONS ****************************/

#if 0
      /* These are never ever used. */
#ifdef XMSTRING_DEBUG
static int
asn1_dump(unsigned char *string)
{
    unsigned length, i, nlen, j;
    struct __XmStringExtRec *str = (struct __XmStringExtRec *)string;
    unsigned char *next;

    printf("STRING: TAG: %02x LEN: %02x\n", str->tag, str->len);
    fflush(stdout);

    if (str->tag != XmSTRING_TAG || str->len != XmSTRING_LENGTH)
    {
      printf("IS NOT AN XmSTRING\n");
      fflush(stdout);
      return 0;
    }

    next = str->data;
    str = (struct __XmStringExtRec *)next;

    if (str->tag != XmSTRING_COMPONENT_XMSTRING)
    {
      printf("IS NOT AN XmSTRING: %d\n", __LINE__);
      fflush(stdout);
      return 0;
    }

    length = 0;

    if (str->len > XmSTRING_LENGTH)
    {

      for (i = 0; i < (str->len & ~XmSTRING_LENGTH); i++)
      {
          length <<= 8;
          length |= str->data[i];
          if (i > sizeof(unsigned))
          {
            printf("Invalid XmString\n");
            fflush(stdout);
            return 0;
          }
      }
    }
    else
    {
      i = 0;
      length = str->len & ~XmSTRING_LENGTH;
    }

    next = &str->data[i];

    if (length < 0)
    {
      printf("String is malformed\n");
      fflush(stdout);
      return 0;
    }
    else if (length == 0)
    {
      return 0;
    }

    for (;;)
    {
      str = (struct __XmStringExtRec *)next;

      /* primitive type -- doesn't recurse */
      nlen = 0;

      if (str->len > XmSTRING_LENGTH)
      {

          for (i = 0; i < (str->len & ~XmSTRING_LENGTH); i++)
          {
            nlen <<= 8;
            nlen |= str->data[i];
            if (i > sizeof(unsigned))
            {
                printf("Invalid XmString\n");
                fflush(stdout);
                exit(0);
            }
          }
      }
      else
      {
          i = 0;
          nlen = str->len & ~XmSTRING_LENGTH;
      }

      switch (str->tag)
      {
      case XmSTRING_COMPONENT_UNKNOWN:
          printf("UNKNOWN COMPONENT: length %d\n", nlen);
          fflush(stdout);
          nlen++;
          break;

      case XmSTRING_COMPONENT_CHARSET:
          printf("CHARSET:\n");
          fflush(stdout);
          for (j = 0; j < nlen; j++)
            putchar(str->data[i + j]);
          putchar('\n');
          fflush(stdout);
          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_TEXT:
          printf("TEXT: %d\n", nlen);
          fflush(stdout);
          for (j = 0; j < nlen; j++)
            putchar(str->data[i + j]);
          putchar('\n');
          fflush(stdout);
          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          printf("DIRECTION: %d\n", nlen);
          fflush(stdout);
          for (j = 0; j < nlen; j++)
            printf("%d ", str->data[i + j]);
          putchar('\n');
          fflush(stdout);
          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_SEPARATOR:
          printf("SEPARATOR: %d\n", nlen);
          fflush(stdout);
          for (j = 0; j < nlen; j++)
            printf("%d ", str->data[i + j]);
          putchar('\n');
          fflush(stdout);
          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_LOCALE_TEXT:
          printf("LOCALE TEXT: %d\n", nlen);
          fflush(stdout);
          for (j = 0; j < nlen; j++)
            putchar(str->data[i + j]);
          putchar('\n');
          fflush(stdout);
          nlen += i + ASN1_HEADER_SIZE;
          break;
      default:
          printf("invalid tag: %02x\n", str->tag);
          fflush(stdout);
          nlen = 1;
      }

      next += nlen;
      length -= nlen;
      if (length < 0)
      {
          printf("String is malformed\n");
          fflush(stdout);
          return 0;
      }
      else if (length == 0)
      {
          printf("\n\n");
          fflush(stdout);
          return 0;
      }
    }
}

static void
_Xm_dump_fontlist(XmFontList fontlist)
{
    int i;

    printf("Fontlist: %p\n", fontlist);
    for (i = 0;
       fontlist && fontlist->renditions[i]->tag
            && strlen(fontlist->renditions[i]->tag) != 0;
       i++)
    {
      printf("Fontlist entry: %d : tag: %s : type: %d : font: %p\n",
             i, fontlist->renditions[i]->tag, fontlist->renditions[i]->type,
             fontlist->renditions[i]->font);
    }
    printf("\n");
}


static void
_Xm_dump_fontlist_cache(void)
{
}


static void
_Xm_dump_external(XmString str)
{
    asn1_dump(str);
    printf("\n");
}


static void
_Xm_dump_internal(_XmString str)
{
    int i;

    if (!str)
    {
      printf(" NULL internal string\n");
      return;
    }

    for (i = 0; i < str->number_of_components; i++)
    {
      switch (str->components[i]->type)
      {
      case XmSTRING_COMPONENT_UNKNOWN:
          printf(" %d: UNKNOWN component\n", i);
          break;

      case XmSTRING_COMPONENT_CHARSET:
          printf(" %d: CHARSET: %s\n", i, str->components[i]->data);
          break;

      case XmSTRING_COMPONENT_TEXT:
          printf(" %d: TEXT: %s, font: %d\n", i, str->components[i]->data,
               str->components[i]->font);
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          printf(" %d: DIRECTION: %d\n", i, (int)str->components[i]->data[0]);
          break;

      case XmSTRING_COMPONENT_SEPARATOR:
          printf(" %d: SEPARATOR\n", i);
          break;

      case XmSTRING_COMPONENT_LOCALE_TEXT:
          printf(" %d: LOCALE TEXT: %s, font: %d\n", i,
               str->components[i]->data, str->components[i]->font);
          break;

      default:
          break;
      }
    }
    printf("\n");
}
#endif /* XMSTRING_DEBUG */
#endif      /* Never ever used */

static _XmString
__XmAllocNewXmString(int number_of_components)
{
    _XmString newString = (_XmString)XtCalloc(1, sizeof(struct __XmStringRec));
    int i;

    newString->number_of_components = number_of_components;

    if (number_of_components)
    {
      newString->components =
          (_XmStringComponent *)XtMalloc(sizeof(_XmStringComponent)
                                 * newString->number_of_components);
    }

    for (i = 0; i < number_of_components; i++)
    {
      newString->components[i] =
          (_XmStringComponent)XtCalloc(1, sizeof(_XmStringComponentRec));
    }

    return newString;
}

static void
__XmGrowXmString(_XmString string)
{
    string->number_of_components++;

    if (string->number_of_components == 1)
    {
      string->components = (_XmStringComponent *)XtMalloc(sizeof(_XmStringComponent));
    }
    else
    {
      string->components =
          (_XmStringComponent *)XtRealloc((char *)string->components,
                                  sizeof(_XmStringComponent)
                                  * string->number_of_components);
    }

    string->components[string->number_of_components - 1] =
      (_XmStringComponent)XtCalloc(1, sizeof(_XmStringComponentRec));
}

static _XmStringComponent
__XmStringPeekNextComponent(_XmStringContext context)
{
    if (context == NULL)
    {
      return NULL;
    }

    if (context->current_component < (context->string->number_of_components - 1))
    {
      return context->string->components[context->current_component + 1];
    }
    else
    {
      return NULL;
    }
}

static _XmStringComponent
__XmStringGetNextComponent(_XmStringContext context)
{
    if (context == NULL)
      return NULL;

    context->current_component++;

    if (context->current_component < context->string->number_of_components)
    {
      return context->string->components[context->current_component];
    }
    else
    {
      return NULL;
    }
}

static void
__XmStringComponentCopy(struct __XmStringComponentRec *dest,
                  struct __XmStringComponentRec *src)
{
    dest->type = src->type;
    dest->length = src->length;
    dest->data = XtNewString(src->data);
}


static _XmString
__XmStringFromASN1(XmString string)
{
    unsigned i, nlen, length;
    struct __XmStringExtRec *str = (struct __XmStringExtRec *)string;
    unsigned char *next;
    char *charset;
    _XmString intern;

    if (!string)
    {
      return NULL;
    }

    if (str->tag != XmSTRING_TAG || str->len != XmSTRING_LENGTH)
    {
      return NULL;
    }

    next = str->data;
    str = (struct __XmStringExtRec *)next;

    if (str->tag != XmSTRING_COMPONENT_XMSTRING)
    {
      return NULL;
    }

    length = 0;

    if (str->len > XmSTRING_LENGTH)
    {

      for (i = 0; i < (str->len & ~XmSTRING_LENGTH); i++)
      {
          length <<= 8;
          length |= str->data[i];
          if (i > sizeof(unsigned))
          {
            _XmWarning(NULL, "Invalid XmString\n");
            return NULL;
          }
      }
    }
    else
    {
      i = 0;
      length = str->len & ~XmSTRING_LENGTH;
    }

    next = &str->data[i];

    if (length <= 0)
    {
      return NULL;
    }

    intern = __XmAllocNewXmString(0);

    for (;;)
    {
      str = (struct __XmStringExtRec *)next;

      /* primitive type -- doesn't recurse */
      nlen = 0;

      if (str->len > XmSTRING_LENGTH)
      {

          for (i = 0; i < (str->len & ~XmSTRING_LENGTH); i++)
          {
            nlen <<= 8;
            nlen |= str->data[i];
            if (i > sizeof(unsigned))
            {
                _XmWarning(NULL, "Invalid XmString\n");
                _XmStringFree(intern);
                return NULL;
            }
          }
      }
      else
      {
          i = 0;
          nlen = str->len & ~XmSTRING_LENGTH;
      }

      switch (str->tag)
      {
      case XmSTRING_COMPONENT_UNKNOWN:
          _XmWarning(NULL, "UNKNOWN COMPONENT IN EXTERNAL STRING (0x%x)\n",
            str->tag);
          nlen++;
          break;

      case XmSTRING_COMPONENT_CHARSET:
          __XmGrowXmString(intern);

          intern->components[intern->number_of_components - 1]->type =
            XmSTRING_COMPONENT_CHARSET;

          charset = XtMalloc(nlen + 1);
          memcpy(charset, &str->data[i], nlen);
          charset[nlen] = 0;
          if (_XmStringIsCurrentCharset(charset))
          {
            intern->components[intern->number_of_components - 1]->data =
                XtNewString(XmFONTLIST_DEFAULT_TAG);
            intern->components[intern->number_of_components - 1]->length =
                strlen(XmFONTLIST_DEFAULT_TAG);
            XtFree(charset);
          }
          else
          {
            intern->components[intern->number_of_components - 1]->data =
                charset;
            intern->components[intern->number_of_components - 1]->length =
                nlen;
          }

          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_TEXT:
          __XmGrowXmString(intern);

          intern->components[intern->number_of_components - 1]->type =
            XmSTRING_COMPONENT_TEXT;
          intern->components[intern->number_of_components - 1]->length =
            nlen;
          intern->components[intern->number_of_components - 1]->data =
            XtMalloc(nlen + 1);
          memcpy(intern->components[intern->number_of_components - 1]->data,
                 &str->data[i],
              nlen);
          intern->components[intern->number_of_components - 1]->data[nlen] = 0;

          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          __XmGrowXmString(intern);

          intern->components[intern->number_of_components - 1]->type =
            XmSTRING_COMPONENT_DIRECTION;
          intern->components[intern->number_of_components - 1]->length =
            1;
          intern->components[intern->number_of_components - 1]->data =
            XtMalloc(1);
          intern->components[intern->number_of_components - 1]->data[0] =
            str->data[0];

          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_SEPARATOR:
          __XmGrowXmString(intern);

          intern->components[intern->number_of_components - 1]->type =
            XmSTRING_COMPONENT_SEPARATOR;
          intern->components[intern->number_of_components - 1]->length =
            0;
          intern->components[intern->number_of_components - 1]->data = NULL;

          nlen += i + ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_LOCALE_TEXT:
          __XmGrowXmString(intern);

          intern->components[intern->number_of_components - 1]->type =
            XmSTRING_COMPONENT_LOCALE_TEXT;
          intern->components[intern->number_of_components - 1]->length =
            nlen;
          intern->components[intern->number_of_components - 1]->data =
            XtMalloc(nlen + 1);
          memcpy(intern->components[intern->number_of_components - 1]->data,
                &str->data[i],
              nlen);
          intern->components[intern->number_of_components - 1]->data[nlen] = 0;

          nlen += i + ASN1_HEADER_SIZE;
          break;

#if (XmVERSION > 1)
      case XmSTRING_COMPONENT_RENDITION_BEGIN:
      case XmSTRING_COMPONENT_RENDITION_END:
            __XmGrowXmString(intern);
            intern->components[intern->number_of_components - 1]->type =
                  str->tag;
            intern->components[intern->number_of_components - 1]->length =
                  nlen;
            intern->components[intern->number_of_components - 1]->data =
                  XtMalloc(nlen + 1);
            memcpy(intern->components[intern->number_of_components - 1]->data,
                  &str->data[i],
                  nlen);
            intern->components[intern->number_of_components - 1]->data[nlen] = 0;

            nlen += i + ASN1_HEADER_SIZE;
            break;
      case XmSTRING_COMPONENT_LOCALE:
      case XmSTRING_COMPONENT_WIDECHAR_TEXT:
      case XmSTRING_COMPONENT_LAYOUT_PUSH:
      case XmSTRING_COMPONENT_LAYOUT_POP:
            break;
      case XmSTRING_COMPONENT_TAB:
          __XmGrowXmString(intern);
          intern->components[intern->number_of_components - 1]->type = XmSTRING_COMPONENT_TAB;
          intern->components[intern->number_of_components - 1]->length = 0;
          intern->components[intern->number_of_components - 1]->data = NULL;

          nlen += i + ASN1_HEADER_SIZE;
          break;
#endif

      default:
          _XmWarning(NULL, "XmString has invalid tag: %02x\n", str->tag);
          nlen = 1;
      }

      next += nlen;
#if 0
/* length is unsigned, this could create a problem */
      length -= nlen;

      if (length < 0)
      {
          _XmWarning(NULL, "XmString is malformed\n");
          _XmStringFree(intern);
          return NULL;
      }
      else if (length == 0)
          return intern;
#else
      if (length < nlen)
      {
          _XmWarning(NULL, "XmString is malformed\n");
          _XmStringFree(intern);
          return NULL;
      }

      length -= nlen;
      if (length == 0)
      {
          return intern;
      }
#endif
    }
}

static XmString
__XmStringToASN1(_XmString string)
{
    XmString external;
    struct __XmStringExtRec *str;
    int totlen, i, j, nlen, added, tlen;
    char *tmp;

    if (!string)
    {
      return NULL;
    }

    totlen = 0;
    for (i = 0; i < string->number_of_components; i++)
    {
      switch (string->components[i]->type)
      {
      case XmSTRING_COMPONENT_CHARSET:
          if (strcmp(string->components[i]->data,
                   XmFONTLIST_DEFAULT_TAG) == 0)
          {
            nlen = strlen(_XmStringGetCurrentCharset());
          }
          else
          {
            nlen = string->components[i]->length;
          }

          totlen += nlen + ASN1_HEADER_SIZE;

          if (nlen >= XmSTRING_LENGTH)
          {
            nlen = string->components[i]->length;
            while (nlen)
            {
                totlen++;
                nlen >>= 8;
            }
          }
          break;

      case XmSTRING_COMPONENT_TEXT:
      case XmSTRING_COMPONENT_LOCALE_TEXT:
          totlen += string->components[i]->length + ASN1_HEADER_SIZE;
          if (string->components[i]->length >= XmSTRING_LENGTH)
          {
            nlen = string->components[i]->length;
            while (nlen)
            {
                totlen++;
                nlen >>= 8;
            }
          }
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          totlen += 3;
          break;

      case XmSTRING_COMPONENT_SEPARATOR:
          totlen += ASN1_HEADER_SIZE;
          break;

      case XmSTRING_COMPONENT_UNKNOWN:
          _XmWarning(NULL, "UNKNOWN COMPONENT IN INTERNAL STRING\n");
          break;

#if (XmVERSION > 1)
      case XmSTRING_COMPONENT_RENDITION_BEGIN:
      case XmSTRING_COMPONENT_RENDITION_END:
            totlen += string->components[i]->length + ASN1_HEADER_SIZE;
            if (string->components[i]->length >= XmSTRING_LENGTH) {
                  nlen = string->components[i]->length;
                  while (nlen) {
                        totlen++;
                        nlen >>= 8;
                  }
            }
            break;
      case XmSTRING_COMPONENT_LOCALE:
      case XmSTRING_COMPONENT_WIDECHAR_TEXT:
      case XmSTRING_COMPONENT_LAYOUT_PUSH:
      case XmSTRING_COMPONENT_LAYOUT_POP:
            break;
      case XmSTRING_COMPONENT_TAB:
            totlen += ASN1_HEADER_SIZE;
            break;
#endif

      }
    }

    added = 0;
    /* additional space for length data in extralong strings */
    if (totlen >= XmSTRING_LENGTH)
    {
      nlen = totlen;
      while (nlen)
      {
          added++;
          nlen >>= 8;
      }
    }

    /* add four bytes for the standard header */
    external = (unsigned char *)XtMalloc(totlen + XmSTRING_HEADER_SIZE + added);

    /* standard header and overall length */
    str = (struct __XmStringExtRec *)external;
    str->tag = XmSTRING_TAG;
    str->len = XmSTRING_LENGTH;
    str = (struct __XmStringExtRec *)str->data;

    str->tag = XmSTRING_COMPONENT_XMSTRING;
    if (totlen >= XmSTRING_LENGTH)
    {
      str->len = XmSTRING_LENGTH;
      nlen = totlen;
      j = 0;
      while (totlen)
      {
          totlen >>= 8;
          j++;
      }

      while (totlen)
      {
      }

      str->len += j;
      for (i = j - 1; i >= 0; i--)
      {
          str->data[i] = nlen & 0x00FFU;
          nlen >>= 8;
      }
      str = (struct __XmStringExtRec *)&str->data[j];
    }
    else
    {
      str->len = totlen;
      str = (struct __XmStringExtRec *)str->data;
    }

    for (i = 0; i < string->number_of_components; i++)
    {
      switch (string->components[i]->type)
      {
      case XmSTRING_COMPONENT_CHARSET:

          str->tag = XmSTRING_COMPONENT_CHARSET;

          if (strcmp(string->components[i]->data,
                   XmFONTLIST_DEFAULT_TAG) == 0)
          {
            tmp = _XmStringGetCurrentCharset();
            nlen = strlen(tmp);
          }
          else
          {
            tmp = string->components[i]->data;
            nlen = string->components[i]->length;
          }

          if (nlen >= XmSTRING_LENGTH)
          {
            tlen = nlen;
            totlen = 0;
            while (tlen)
            {
                totlen++;
                tlen >>= 8;
            }
            str->len = XmSTRING_LENGTH + totlen;
            tlen = nlen;
            for (j = totlen - 1; j >= 0; j--)
            {
                str->data[j] = tlen & 0x00FFU;
                tlen >>= 8;
            }
            memcpy(&str->data[totlen], tmp,
                  nlen);
            str = (struct __XmStringExtRec *)&str->data[totlen + nlen];
          }
          else
          {
            str->len = nlen;
            memcpy(str->data, tmp, nlen);
            str = (struct __XmStringExtRec *)&str->data[nlen];
          }
          break;

      case XmSTRING_COMPONENT_TEXT:
          str->tag = XmSTRING_COMPONENT_TEXT;
          if (string->components[i]->length >= XmSTRING_LENGTH)
          {
            nlen = string->components[i]->length;
            totlen = 0;
            while (nlen)
            {
                totlen++;
                nlen >>= 8;
            }
            str->len = XmSTRING_LENGTH + totlen;
            nlen = string->components[i]->length;
            for (j = totlen - 1; j >= 0; j--)
            {
                str->data[j] = nlen & 0x00FFU;
                nlen >>= 8;
            }
            memcpy(&str->data[totlen],
                  string->components[i]->data,
                  string->components[i]->length);
            str = (struct __XmStringExtRec *)&str->data[totlen +
                                    string->components[i]->length];
          }
          else
          {
            str->len = string->components[i]->length;
            memcpy(str->data,
                   string->components[i]->data,
                   string->components[i]->length);
            str = (struct __XmStringExtRec *)&str->data[string->components[i]->length];
          }
          break;

      case XmSTRING_COMPONENT_LOCALE_TEXT:
          str->tag = XmSTRING_COMPONENT_LOCALE_TEXT;

          if (string->components[i]->length >= XmSTRING_LENGTH)
          {
            nlen = string->components[i]->length;
            totlen = 0;
            while (nlen)
            {
                totlen++;
                nlen >>= 8;
            }
            str->len = XmSTRING_LENGTH + totlen;
            nlen = string->components[i]->length;
            for (j = totlen - 1; j >= 0; j--)
            {
                str->data[j] = nlen & 0x00FFU;
                nlen >>= 8;
            }
            memcpy(&str->data[totlen],
                   string->components[i]->data,
                   string->components[i]->length);
            str = (struct __XmStringExtRec *)&str->data[totlen +
                                    string->components[i]->length];
          }
          else
          {
            str->len = string->components[i]->length;
            memcpy(str->data,
                   string->components[i]->data,
                   string->components[i]->length);
            str = (struct __XmStringExtRec *)&str->data[string->components[i]->length];
          }
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          str->tag = XmSTRING_COMPONENT_DIRECTION;
          str->len = 1;
          str->data[0] = string->components[i]->data[0];
          str = (struct __XmStringExtRec *)&str->data[1];
          break;

      case XmSTRING_COMPONENT_SEPARATOR:
          str->tag = XmSTRING_COMPONENT_SEPARATOR;
          str->len = 0;
          str = (struct __XmStringExtRec *)str->data;
          break;

      case XmSTRING_COMPONENT_UNKNOWN:
      default:
          _XmWarning(NULL, "UNKNOWN COMPONENT IN INTERNAL STRING\n");
          break;
#if (XmVERSION > 1)
      case XmSTRING_COMPONENT_RENDITION_BEGIN:
#if 0
          str->tag = XmSTRING_COMPONENT_RENDITION_BEGIN;
          str->len = strlen(str->data);
          str = (struct __XmStringExtRec *)
            (((char *)str->data)+ str->len + 1);
          break;
#endif
      case XmSTRING_COMPONENT_RENDITION_END:
#if 0
          str->tag = XmSTRING_COMPONENT_RENDITION_END;
          str->len = strlen(str->data);
          str = (struct __XmStringExtRec *)
            (((char *)str->data)+ str->len + 1);
#endif
          str->tag = string->components[i]->type;
          if (string->components[i]->length >= XmSTRING_LENGTH)
          {
            nlen = string->components[i]->length;
            totlen = 0;
            while (nlen)
            {
                totlen++;
                nlen >>= 8;
            }
            str->len = XmSTRING_LENGTH + totlen;
            nlen = string->components[i]->length;
            for (j = totlen - 1; j >= 0; j--)
            {
                str->data[j] = nlen & 0x00FFU;
                nlen >>= 8;
            }
            memcpy(&str->data[totlen],
                  string->components[i]->data,
                  string->components[i]->length);
            str = (struct __XmStringExtRec *)&str->data[totlen +
                                    string->components[i]->length];
          }
          else
          {
            str->len = string->components[i]->length;
            memcpy(str->data,
                   string->components[i]->data,
                   string->components[i]->length);
            str = (struct __XmStringExtRec *)&str->data[string->components[i]->length];
          }
          break;
      case XmSTRING_COMPONENT_LOCALE:
      case XmSTRING_COMPONENT_WIDECHAR_TEXT:
      case XmSTRING_COMPONENT_LAYOUT_PUSH:
      case XmSTRING_COMPONENT_LAYOUT_POP:
            break;
      case XmSTRING_COMPONENT_TAB:
            str->tag = XmSTRING_COMPONENT_TAB;
            str->len = 0;
            str = (struct __XmStringExtRec *)str->data;
            break;
#endif
      }
    }

    return external;
}

static Boolean
__XmStringSegmentExtent(XmFontList flist, _XmStringComponent comp,
                  Dimension *width, Dimension *height,
                  Dimension *ascent, Dimension *descent)
{
    int dc, amax = 0, dmax = 0;
    XCharStruct ov;
    XRectangle ink, log;

    *height = 0;
    *width = 0;
    *ascent = 0;
    *descent = 0;

    if ((comp->type != XmSTRING_COMPONENT_TEXT &&
       comp->type != XmSTRING_COMPONENT_LOCALE_TEXT) ||
      comp->font == XmUNSPECIFIED)
    {
      DEBUGOUT(_LtDebug(__FILE__, NULL,
                    "__XmStringSegmentExtent: got NULL Font/bad text\n"));

      return False;
    }
    if (!flist)
    {
      _XmWarning(NULL, "__XmStringSegmentExtent: got NULL FontList");
      return False;
    }

    switch (flist->renditions[comp->font]->type) {
    case XmFONT_IS_FONT:
      DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringSegmentExtent(%p, XmFONT_IS_FONT)\n", flist));
      {
          XFontStruct *fontstruct = (XFontStruct *)flist->renditions[comp->font]->font;

          if (fontstruct->min_byte1 || fontstruct->max_byte1)
          {
            /*
             * This also happens in XTextExtents
             * *width = XTextWidth16(fontstruct,
             *                (XChar2b *)comp->data, comp->length / 2);
             */
            XTextExtents16(fontstruct,
                         (XChar2b *)comp->data, comp->length / 2,
                         &dc, &amax, &dmax, &ov);
            *width = ov.width;
          }
          else
          {
            /*
             * This also happens in XTextExtents
             * *width = XTextWidth(fontstruct, comp->data, comp->length);
             */

            XTextExtents(fontstruct,
                       comp->data, comp->length,
                       &dc, &amax, &dmax, &ov);
            *width = ov.width;

            DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringSegmentExtent: '%s' len %d\n",
                  comp->data, comp->length));
          }

          *height = amax + dmax;
          *ascent = amax;
          *descent = dmax;
      }
      break;

    case XmFONT_IS_FONTSET:
      DEBUGOUT(_LtDebug(__FILE__, NULL,
            "_XmStringSegmentExtent(%p,XmFONT_IS_FONTSET)\n", flist));

      XmbTextExtents((XFontSet)flist->renditions[comp->font]->font,
                   comp->data, comp->length, &ink, &log);

      *height = log.height;
      *width = log.width;
      *ascent = -log.y;
      *descent = log.height+log.y;
      break;

    case XmFONT_IS_XFT:
#if   USE_XFT
      DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringSegmentExtent(%p,XmFONT_IS_XFT)\n", flist));

      *ascent = flist->renditions[comp->font]->xft_font->ascent;
      *descent = flist->renditions[comp->font]->xft_font->descent;
      *height = flist->renditions[comp->font]->xft_font->height;
    {
      XGlyphInfo  info;
      XftTextExtents8(flist->dpy,
            flist->renditions[comp->font]->xft_font,
            comp->data, comp->length,
            &info);
      *width = info.width;

#if 1
/* Don't use this ! */
      *height = info.height;
#endif
    }
      *height = flist->renditions[comp->font]->font_average_height;
#endif
      break;
    case XmFONT_IS_XOC:
#if   USE_BIDI
      DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringSegmentExtent(%p,XmFONT_IS_XOC)\n", flist));

      /* FIX ME */
#endif
      break;
    }

    DEBUGOUT(_LtDebug(__FILE__, NULL,
      "_XmStringSegmentExtent => width %d height %d\n",
      *width, *height));

    return True;
}

/**************************** INTERNAL FUNCTIONS ***************************/

extern XFontStruct *
_XmGetFirstFont(XmFontListEntry entry)
{
    XFontStruct **flist;
    char **fnames;
    int num;

    if (entry == NULL)
    {
      return NULL;
    }

    if (entry->type == XmFONT_IS_FONT)
    {
      return (XFontStruct *)entry->font;
    }
    else if (entry->type == XmFONT_IS_FONTSET)
    {
      num = XFontsOfFontSet((XFontSet)entry->font, &flist, &fnames);
      if (num)
      {
          return flist[0];
      }
    }

    return NULL;
}


extern Boolean
_XmFontListGetDefaultFont(XmFontList fontlist,
                    XFontStruct **font_struct)
{
    int i;

    if (fontlist == NULL) {
      DEBUGOUT(_LtDebug(__FILE__, NULL,
                    "_XmFontListGetDefaultFont(NULL, ...)\n"));
      *font_struct = NULL;
      return False;
    }

    for (i = 0; fontlist->renditions[i]->tag != NULL; i++) {
      if (!strcmp(XmFONTLIST_DEFAULT_TAG, fontlist->renditions[i]->tag)) {
          break;
      }
    }

    /* Default to first entry */
    if (fontlist->renditions[i]->tag == NULL)
      i = 0;

    if (fontlist->renditions[i]->type == XmFONT_IS_FONT) {
      if ((*font_struct = (XFontStruct *)fontlist->renditions[i]->font))
            return True;
      else
            return False;
    } else if (fontlist->renditions[i]->type == XmFONT_IS_FONTSET) {
      if ((*font_struct = _XmGetFirstFont(fontlist->renditions[i])))
            return True;
      else
            return False;
    }

    *font_struct = NULL;
    return False;
}


extern Boolean
_XmFontListSearch(XmFontList fontlist,
              XmStringCharSet charset,
              short *indx,
              XFontStruct **font_struct)
{
    int i, p;

    if (fontlist == NULL || charset == NULL) {
      *indx = 0;
      *font_struct = NULL;
      return False;
    }

    p = INT_MAX;
    for (i = 0; i < fontlist->count; i++) {
      if (!strcmp(charset, fontlist->renditions[i]->tag))
      {
            p = i;
      }
    }
    if (p != INT_MAX) i = p;

/*  if (fontlist->renditions[i]->tag == NULL && */
    if (p == INT_MAX && strcmp(charset, XmFONTLIST_DEFAULT_TAG) != 0) {
      for (i = 0; i < fontlist->count; i++) {
          if (!strcmp(XmFONTLIST_DEFAULT_TAG, fontlist->renditions[i]->tag)) {
            p = i;
            break;
          }
      }
    }

    if (p == INT_MAX) {
      *indx = XmUNSPECIFIED;
      *font_struct = NULL;
      return False;
    } else
      i = p;

    *indx = i;
    if (fontlist->renditions[i]->type == XmFONT_IS_FONT) {
      if ((*font_struct = (XFontStruct *)fontlist->renditions[i]->font))
            return True;
      else
            return False;
    } else if (fontlist->renditions[i]->type == XmFONT_IS_FONTSET) {
      if ((*font_struct = _XmGetFirstFont(fontlist->renditions[i])))
            return True;
      else
            return False;
    }

    *font_struct = NULL;
    return False;
}

/*
 * See the difference between _XmString and XmString (internal vs.
 *      external representation).
 *
 * Returns True if the argument is external representation.
 */
extern Boolean
_XmStringIsXmString(XmString string)
{
    struct __XmStringExtRec *str = (struct __XmStringExtRec *)string;
    unsigned char *next;

    if (!string)
    {
      DEBUGOUT(_LtDebug(__FILE__, NULL,
                    "_XmStringIsXmString: string is NULL\n"));

      return False;
    }
    if (str->tag != XmSTRING_TAG || str->len != XmSTRING_LENGTH)
    {
      DEBUGOUT(_LtDebug(__FILE__, NULL,
                    "_XmStringIsXmString: tag or len is invalid\n"));

      return False;
    }
    next = str->data;
    str = (struct __XmStringExtRec *)next;

    if (str->tag != XmSTRING_COMPONENT_XMSTRING)
    {
      return False;
    }

    return True;
}


extern Boolean
_XmStringEmpty(_XmString string)
{
    int i;

    if (string == NULL || string->number_of_components == 0)
    {
      return True;
    }

    for (i = 0; i < string->number_of_components; i++)
    {
      if ((string->components[i]->type == XmSTRING_COMPONENT_TEXT ||
           string->components[i]->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
          string->components[i]->length != 0)
      {
          return False;
      }
    }

    return True;
}


#if XmVERSION >= 2

/*   Helper function for XmStringIsVoid().
 *   CheckMe !!
 */
static Boolean
_XmStringIsVoid(_XmString string)
{
    int i;

    if (string == NULL || string->number_of_components == 0)
    {
      return True;
    }

    for (i = 0; i < string->number_of_components; i++)
    {
      if ( (string->components[i]->type == XmSTRING_COMPONENT_TEXT ||
            string->components[i]->type == XmSTRING_COMPONENT_LOCALE_TEXT ||
            string->components[i]->type == XmSTRING_COMPONENT_SEPARATOR ||
            string->components[i]->type == XmSTRING_COMPONENT_TAB) &&
          string->components[i]->length != 0)
      {
          return False;
      }
    }

    return True;
}
#endif /* XmVERSION >= 2 */


extern _XmString
_XmStringCreate(XmString cs)
{
    if (!cs)
    {
      return NULL;
    }

    return __XmStringFromASN1(cs);
}


extern void
_XmStringFree(_XmString string)
{
    int i;

    if (string == NULL)
      return;

    for (i = 0; i < string->number_of_components; i++)
    {
       /* Peter Stein, 07/2000 */                                                       
       /* in some wired circumstances this may not be the case */                       
       /* leading to a crash */                                                         
      if (string->components[i] && string->components[i]->data /*&& string->components[i]->length*/)
      {
          /* rws 30 Aug 1998
             If we only free this if the length is greater than 0 we end
             up not freeing the space from the string "" which has a length
             of 0 but there has been space allocated for it.
           */
          XtFree((char *)string->components[i]->data);
      }

      XtFree((char *)string->components[i]);
    }
                                                                                                  
    if( string->number_of_components > 0 )                                                  
      {                                                                                       
       /* Peter Stein, 07/2000: */                                                      
       /* initialisation in __XmAllocNewXmString() */                                   
       /*      only done in this case */                                                
       /* so free should also only happen than*/                                        
         XtFree((char *)string->components);                                              
      }
    XtFree((char *)string);
}


extern char *
_XmStringGetCurrentCharset(void)
{
    char *lang, *p;

    if ((lang = getenv("LANG")) == NULL)
    {
      return XmFALLBACK_CHARSET;
    }

    if (strcmp(lang, "C") == 0 ||
      strcmp(lang, "POSIX") == 0 ||
      strcmp(lang, "ISO8859-1") == 0 ||
      strcmp(lang, "ISO-8859-1") == 0)
    {
      return XmSTRING_OS_CHARSET;
    }

    if ((p = strchr(lang, '.')) != NULL && *(p+1) != 0) {
      return p+1;
    }

    return XmFALLBACK_CHARSET;
}


extern char *
_XmCharsetCanonicalize(String charset)
{
    return NULL;
}

/* amai: we don't know whether this matches precisely the 
   Motif version of that private call, but it seems to be
   there, and so we export our version */
extern void
_XmStringUpdate(XmFontList fontlist, _XmString string)
{
    short i, where, def, tf;
    XFontStruct *dummy;

    if (!_XmFontListSearch(fontlist, XmFONTLIST_DEFAULT_TAG, &def, &dummy)) {
      tf = XmUNSPECIFIED;
    } else {
      tf = def;
    }

    if (def == XmUNSPECIFIED) {
      _XmFontListSearch(fontlist, fontlist->renditions[0]->tag, &def, &dummy);
    }

    for (i = 0; i < string->number_of_components; i++) {
      if (string->components[i]->type == XmSTRING_COMPONENT_CHARSET) {
          if (_XmFontListSearch(fontlist,
                          string->components[i]->data,
                          &where,
                          &dummy)) {
            tf = where;
          }
      }
      if (string->components[i]->type == XmSTRING_COMPONENT_LOCALE_TEXT) {
          string->components[i]->font = def;
      }

      if (string->components[i]->type == XmSTRING_COMPONENT_TEXT) {
          if (tf != XmUNSPECIFIED) {
            string->components[i]->font = tf;
          } else {
            string->components[i]->font = def;
          }
      }
    }
}


extern _XmString
_XmStringCopy(_XmString s)
{
    _XmString newString;
    int i;

    if (s == NULL)
    {
      return NULL;
    }

    newString = __XmAllocNewXmString(s->number_of_components);

    for (i = 0; i < newString->number_of_components; i++)
    {
      __XmStringComponentCopy(newString->components[i], s->components[i]);
    }

    return newString;
}


extern Boolean
_XmStringByteCompare(_XmString a, _XmString b)
{
    _XmStringContext context1 = NULL;
    _XmStringComponent foo1;
    _XmStringContext context2 = NULL;
    _XmStringComponent foo2;

    if (!_XmStringInitContext(&context1, a))
    {
      return False;
    }

    if (!_XmStringInitContext(&context2, b))
    {
      _XmStringFreeContext(context1);
      return False;
    }

    while ((foo1 = __XmStringGetNextComponent(context1)))
    {
      if (!(foo2 = __XmStringGetNextComponent(context2)))
      {
          _XmStringFreeContext(context1);
          _XmStringFreeContext(context2);
          return False;
      }

      if (foo1->type == XmSTRING_COMPONENT_SEPARATOR)
      {
          if (foo2->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            continue;
          }
          else
          {
            _XmStringFreeContext(context1);
            _XmStringFreeContext(context2);
            return False;
          }
      }
      else if (foo2->type == XmSTRING_COMPONENT_SEPARATOR)
      {
          _XmStringFreeContext(context1);
          _XmStringFreeContext(context2);
          return False;
      }

      if (strcmp(foo1->data, foo2->data))
      {
          _XmStringFreeContext(context1);
          _XmStringFreeContext(context2);
          return False;
      }
    }

    _XmStringFreeContext(context1);
    _XmStringFreeContext(context2);

    return True;
}


extern Boolean
_XmStringHasSubstring(_XmString string, _XmString substring)
{
    int i, j, k;
    Boolean match;

    /* if both empty, match */
    if (_XmStringEmpty(string) && _XmStringEmpty(substring))
    {
      return True;
    }

    /* if first string is empty, never match */
    if (_XmStringEmpty(string))
    {
      return False;
    }

    /* if second empty, match */
    if (_XmStringEmpty(substring))
    {
      return True;
    }

    for (i = 0; i < string->number_of_components; i++)
    {
      if (string->components[i]->type == substring->components[0]->type &&
          string->components[i]->length >= substring->components[0]->length &&
          ((string->components[i]->data != NULL &&
            substring->components[0]->data != NULL &&
               strstr( string->components[i]->data,
                       substring->components[0]->data ) != NULL) ||
           (string->components[i]->data == NULL &&
            substring->components[0] == NULL)))
      {
          match = True;

          for (j = i + 1, k = 1; k < substring->number_of_components; j++, k++)
          {
            if (string->components[j]->type !=
                substring->components[k]->type ||
                string->components[j]->length <
                substring->components[k]->length ||
                ((string->components[j]->data != NULL &&
                  substring->components[k]->data != NULL &&
                  strstr(string->components[j]->data,
                        substring->components[k]->data) == NULL) ||
                 (string->components[j]->data != NULL &&
                  substring->components[k] == NULL) ||
                 (string->components[j]->data == NULL &&
                  substring->components[k] != NULL)))
            {
                match = False;
                break;
            }
          }

          if (match)
          {
            return True;
          }
      }
    }

    return False;
}


extern XmString
_XmStringCreateExternal(XmFontList fontlist, _XmString cs)
{
    /* need to change the string maybe, so that if a font doesn't exist
     * it changes to default? */
    if (fontlist)
    {
      _XmStringUpdate(fontlist, cs);
    }

    return __XmStringToASN1(cs);
}


extern char *
_XmStringGetTextConcat(XmString string)
{
    int ilen = 0, len = 0;
    char *ret = NULL;
    _XmString str;
    _XmStringComponent comp;
    _XmStringContext context = NULL;

    str = _XmStringCreate(string);

    _XmStringInitContext(&context, str);

    while ((comp = __XmStringGetNextComponent(context)) != NULL)
    {
      if (comp->type == XmSTRING_COMPONENT_TEXT ||
          comp->type == XmSTRING_COMPONENT_LOCALE_TEXT)
      {
          if (len == 0)
          {
            len = comp->length;
            ret = XtMalloc(len + 1);
          }
          else
          {
            len = comp->length;
            ret = XtRealloc(ret, ilen + len + 1);
          }
          memcpy(&ret[ilen], comp->data, len);
          ret[ilen + len] = 0;
          ilen += len;
      }
    }

    _XmStringFreeContext(context);

    _XmStringFree(str);

    return ret;
}


extern Boolean
_XmStringIsCurrentCharset(XmStringCharSet c)
{
    if (!c)
    {
      return False;
    }

    /* returns true if this charset is in the current locale */
    if (strcmp(c, _XmStringGetCurrentCharset()) == 0)
       return True;
    else  
       return False;
}


extern Boolean
_XmStringSingleSegment(XmString str, char **pTextOut,
                   XmStringCharSet *pCharsetOut)
{
    XmStringContext context;
    Boolean ret = False;
    XmStringDirection ddir;
    Boolean dsep;

    if (!XmStringInitContext(&context, str))
    {
      return False;
    }

    ret = XmStringGetNextSegment(context, pTextOut, pCharsetOut, &ddir, &dsep);

    XmStringFreeContext(context);

    return ret;
}

/*
 * In Motif 2.x this becomes XmeSetWMShellTitle and even gets documented!
 * (this function, apparently, has also been name XmeStringUpdateWMShellTitle
 * as well, sigh).
 *
 * If "xmstr" consists of a single segment with the "ISO8859-1" tag, then
 * the title and icon name will be set to the text of that segment and
 * the encoding will be set to STRING; If "xmstr" consists of a single
 * segment with the default font tag, then we do the same except the
 * encoding is set to None.  Otherwise, we convert to compound text and
 * set the encoding to COMPOUND_TEXT.
 *
 * FIX ME - deal with the font tags as per the above spec.
 */

extern void
_XmStringUpdateWMShellTitle(XmString xmstr, Widget shell)
{
    String title = NULL;
    Atom encoding;
    Boolean freeit;
    Arg a[4];
    int n;

    if (XmStringGetLtoR(xmstr, XmFONTLIST_DEFAULT_TAG, &title)
    &&  title != NULL)
    {
      freeit   = True;
      encoding = None;
    }
    else
    {
      title    = "";
      freeit   = False;
      encoding = None;
    }

    n = 0;
    XtSetArg(a[n], XmNtitle,            title);    ++n;
    XtSetArg(a[n], XmNtitleEncoding,    encoding); ++n;
    XtSetArg(a[n], XmNiconName,         title);    ++n;
    XtSetArg(a[n], XmNiconNameEncoding, encoding); ++n;
    XtSetValues(shell, a, n);

    if (freeit)
    {
      XtFree(title);
    }
}


extern Boolean
_XmStringInitContext(_XmStringContext *context, _XmString string)
{
    _XmStringContext p;

    if (string && context)
    {
      p = (_XmStringContext)XtMalloc(sizeof(struct __XmStringContextRec));

      p->string = string;
      p->current_component = -1;

      *context = p;
      return True;
    }
    else
    {
      return False;
    }
}


extern Boolean
_XmStringGetNextSegment(_XmStringContext context,
                  XmStringCharSet *tag,
                  XmStringDirection *direction,
                  char **text,
                  short *char_count,
                  Boolean *separator)
{
    Boolean valid = False;
    XmStringComponentType type = XmSTRING_COMPONENT_UNKNOWN;
    char *ntext;
    XmStringCharSet ntag;
    XmStringDirection ndir;
    _XmStringComponent next;

    /*
     * note that tag is not zero'd.  That's because tag values need
     * to persist across calls to this func, unless a new tag is seen.
     */
    if (text)
    {
      *text = NULL;
    }
    if (direction)
    { /* like in original motif */
      *direction = XmSTRING_DIRECTION_L_TO_R;
    }
    if (separator)
    {
      *separator = False;
    }
    ntext = NULL;
    ntag = NULL;
    ndir = XmSTRING_DIRECTION_DEFAULT;

    while ((next = __XmStringGetNextComponent(context)) != NULL)
    {
      valid = True;
      type = next->type;

      switch (type)
      {
      case XmSTRING_COMPONENT_CHARSET:
          ntag = next->data;
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          ndir = next->data[0];
          break;

      case XmSTRING_COMPONENT_SEPARATOR:
          break;

      case XmSTRING_COMPONENT_TEXT:
      case XmSTRING_COMPONENT_LOCALE_TEXT:
          ntext = next->data;
          *char_count = next->length;
          break;

      case XmSTRING_COMPONENT_UNKNOWN:
      default:
          break;
      }

      if (ntext != NULL && text)
      {
          *text = ntext;
      }
      if (ntag != NULL && tag)
      {
          *tag = ntag;
      }
      if (ndir != XmSTRING_DIRECTION_DEFAULT && direction)
      {
          *direction = ndir;
      }

      next = __XmStringPeekNextComponent(context);
      if (next)
      {
          type = next->type;

          if (type == XmSTRING_COMPONENT_SEPARATOR)
          {
            *separator = True;
            break;
          }

          if ((type == XmSTRING_COMPONENT_LOCALE_TEXT ||
             type == XmSTRING_COMPONENT_TEXT) && text && *text)
          {
            break;
          }

          if (type == XmSTRING_COMPONENT_CHARSET && tag && *tag)
          {
            break;
          }

          if (type == XmSTRING_COMPONENT_DIRECTION && direction &&
            *direction != XmSTRING_DIRECTION_DEFAULT)
          {
            break;
          }
      }
      ntext = NULL;
      ntag = NULL;
      ndir = XmSTRING_DIRECTION_DEFAULT;
    }

    if (tag && !*tag)
    {
      *tag = XmFONTLIST_DEFAULT_TAG;
    }

    return valid;
}


extern void
_XmStringFreeContext(_XmStringContext context)
{
    XtFree((char *)context);
}

extern void
_XmStringExtent(XmFontList fontlist, _XmString string,
            Dimension *width, Dimension *height)
{
      _XmStringContext context = NULL;
      Boolean have_seg, have_line_height;
      Dimension current_height = 0, current_width = 0;
      Dimension line_height = 0, default_line_height;
      Dimension amax, dmax, wd, ht;
      int pending_newlines;
      _XmStringComponent comp;
#if (XmVERSION > 1)
      XmRendition r, rend;
      XmRenderTable     rt;

      rt = XmRenderTableCopy(fontlist, 0, 0);
      rend = 0;
#else
/*    Try to use the same variable "rt" in Motif 1.x */
      XmFontList  rt;
      
      rt = XmFontListCopy(fontlist);
#endif

      if (!string) {
            *width = *height = 0;
            return;
      }

      DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringExtent start\n"));

      _XmStringUpdate(rt, string);
      _XmStringInitContext(&context, string);

      *height = *width = 0;

      have_seg = False;
      have_line_height = False;
      line_height = 0;
      default_line_height = 0;
      pending_newlines = 0;

      while ((comp = __XmStringGetNextComponent(context)) != NULL) {
#if (XmVERSION > 1)
            /* Determine which rendition to use */
            r = rend ? rend : rt->renditions[0];
#endif
            if (comp->type == XmSTRING_COMPONENT_TEXT ||
                        comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) {
                  if (__XmStringSegmentExtent(rt, comp,
                              &wd, &ht, &amax, &dmax)) {
                        have_seg = True;
                        if (ht > line_height)
                              line_height = ht; /*amax + dmax; */
                  }
                  current_width += wd;

                  DEBUGOUT(_LtDebug(__FILE__, NULL,
                        "_XmStringExtent: text segment, line height %d\n", ht));
            } else if (comp->type == XmSTRING_COMPONENT_SEPARATOR) {
                  DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringExtent: separator\n"));

                  /* What if we don't have a height ... ? */
                  if (line_height == 0) {
                        if (default_line_height == 0) {
                              /* Find a line height ! */
                              _XmStringComponentRec   blank;
                              _XmStringComponent blankcomp = &blank;

                              blankcomp->type = XmSTRING_COMPONENT_TEXT;
                              blankcomp->data = " ";
                              blankcomp->length = 1;
                              blankcomp->font = comp->font;

                              __XmStringSegmentExtent(rt, blankcomp,
                                    &wd, &default_line_height, &amax, &dmax);

                              DEBUGOUT(_LtDebug(__FILE__, NULL,
                                    "_XmStringExtent: separator found height %d\n",
                                    default_line_height));
                        }
                        line_height = default_line_height;
                  }
                  if (default_line_height == 0)
                        default_line_height = line_height;
                  /* What if we don't have a height ... ? (end) */

                  if (*width < current_width) {
                        *width = current_width;
                  }

                  if (!have_seg && !have_line_height) {
                        pending_newlines++;
                  } else if (have_seg && !have_line_height) {
                        have_line_height = True;
                        default_line_height = line_height;
                        current_height += default_line_height * pending_newlines;
                        pending_newlines = 0;
                  } else if (have_seg) {
                        default_line_height = line_height;
                  }

                  if (!have_seg && have_line_height) {
                        current_height += default_line_height;

                        DEBUGOUT(_LtDebug(__FILE__, NULL,
                              "_XmStringExtent-separator: default_line_height %d added\n",
                              default_line_height));
                  } else {
                        current_height += line_height;

                        DEBUGOUT(_LtDebug(__FILE__, NULL,
                              "_XmStringExtent-separator: line_height %d added\n",
                              line_height));
                  }

                  current_width = 0;
                  line_height = 0;
                  have_seg = False;
#if (XmVERSION > 1)
            } else if (comp->type == XmSTRING_COMPONENT_TAB) {
                  XmTabList   tl;
                  int         i;
                  Position    tabx;
                  Position    prev;

                  /* Look for a usable tab list */
                  for (tl=NULL, i=0; i<rt->count; i++) {
                        XmRendition rr = rt->renditions[i];
                        if (rr->tab_list && rr->tab_list != (XmTabList)XmAS_IS) {
                              tl = rr->tab_list;
                              break;
                        }
                  }

                  for (tabx=0, i=0; tl && i<tl->count; i++) {
                        Position    x = 0;
                        Display           *d = r->dpy;      /* For code re-use */

                        switch (tl->tabs[i]->units) {
                        case XmINCHES:
                              x = tl->tabs[i]->value * 25.4
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0) ;
                              break;
                        case XmPIXELS:
                              x = tl->tabs[i]->value;
                              break;
                        case Xm100TH_MILLIMETERS:
                              x = tl->tabs[i]->value * 100
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0) ;
                              break;
                        case Xm1000TH_INCHES:
                              x = tl->tabs[i]->value * 1000 * 25.4
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0) ;
                              break;
                        case XmCENTIMETERS:
                              x = tl->tabs[i]->value
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0)
                                    / 10;
                              break;
                        case XmMILLIMETERS:
                              x = tl->tabs[i]->value
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0);
                              break;
                        case XmPOINTS:
                              x = tl->tabs[i]->value * 72
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0) ;
                              break;
                        case Xm100TH_POINTS:
                              x = tl->tabs[i]->value * 7200
                                    * DisplayWidth(d, 0)
                                    / DisplayWidthMM(d, 0) ;
                              break;
                        case XmFONT_UNITS:
                        case Xm100TH_FONT_UNITS:
                        default:
                              _XmWarning(NULL,
                                    "Tab untreated unit type %d\n",
                                    r->tab_list->tabs[i]->units);
                        }

                        prev = tabx;
                        if (tl->tabs[i]->offset_model == XmABSOLUTE) {
                              tabx = x;
                        } else {    /* XmRELATIVE */
                              tabx += x;
                        }
                        if (prev < current_width && current_width < tabx) {
                              current_width = tabx;
                              continue;
                        }
                  }
            } else if (comp->type == XmSTRING_COMPONENT_RENDITION_BEGIN) {
                  char  *tag;

                  /* Get the rendition tag */
                  tag = XtMalloc(comp->length + 1);
                  memcpy(tag, comp->data, comp->length);
                  tag[comp->length] = '\0';

                  /* Get a rendition that matches the tag */
                  rend = XmRenderTableGetRendition(rt, tag);

                  if (rend == NULL) {
                        _XmRenderTableFinaliseTag(NULL, rt, tag);
                        rend = XmRenderTableGetRendition(rt, tag);
                  }

                  if (rend == NULL) {
                        XmDisplayCallbackStruct cbs;
                        Widget      dw = XmGetXmDisplay(rt->dpy);
                        XtCallCallbackList(dw,
                              DisplayNoRenditionCB(dw),
                              (XtPointer)&cbs);
                  }

                  /* Push it in front of the private rendertable */
                  if (rend)
                        rt = _XmRenderTablePushRendition(rt, rend);

                  DEBUGOUT(_LtDebug(__FILE__, NULL,
                        "_XmStringDraw Rendition(%s) -> %p\n",
                        tag, rend));

                  XmRenditionFree(rend);
                  XtFree(tag);
                  rend = NULL;
            
            } else if (comp->type == XmSTRING_COMPONENT_RENDITION_END) {
                  char  *tag;

                  /* Get the rendition tag */
                  tag = XtMalloc(comp->length + 1);
                  memcpy(tag, comp->data, comp->length);
                  tag[comp->length] = '\0';

                  /* Get a rendition that matches the tag */
                  rend = XmRenderTableGetRendition(rt, tag);

                  /* Pop it from the private rendertable */
                  if (rend) {
                        rt = _XmRenderTablePopRendition(rt, rend);
                        XmRenditionFree(rend);
                  }
                  rend = 0;
                  XtFree(tag);
#endif
            } else {
                  DEBUGOUT(_LtDebug(__FILE__, NULL,
                        "_XmStringExtent: bad component type %d\n",
                        comp->type));
            }
      }

      if (have_seg) {
            current_height += line_height;
      } else {
            current_height += default_line_height;
      }

      if (*height < current_height) {
            *height = current_height;
      }

      if (*width < current_width) {
            *width = current_width;
      }

      _XmStringFreeContext(context);

#if (XmVERSION > 1)
      XmRenderTableFree(rt);
#else
      XmFontListFree(rt);
#endif
}

extern Dimension
_XmStringWidth(XmFontList fontlist, _XmString string)
{
    Dimension width = 0, dontcare;

    _XmStringExtent(fontlist, string, &width, &dontcare);

    return width;
}


extern Dimension
_XmStringHeight(XmFontList fontlist, _XmString string)
{
    Dimension height = 0, dontcare;

    _XmStringExtent(fontlist, string, &dontcare, &height);

    return height;
}


extern Dimension
_XmStringBaseline(XmFontList fontlist, _XmString string)
{
    _XmStringContext context = NULL;
    Dimension current_baseline = 0, wd, ht, asc, desc;
    _XmStringComponent comp;

    if (!_XmStringInitContext(&context, string))
    {
      return (Dimension)0;
    }

    _XmStringUpdate(fontlist, string);

    while ((comp = __XmStringGetNextComponent(context)) != NULL)
    {
      if ((comp->type == XmSTRING_COMPONENT_TEXT ||
           comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
          comp->font != XmUNSPECIFIED)
      {
          __XmStringSegmentExtent(fontlist, comp, &wd, &ht, &asc, &desc);

          if (asc > current_baseline)
          {
            current_baseline = asc;
          }
      }
      else if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
      {
          break;
      }
    }

    _XmStringFreeContext(context);

    return current_baseline;
}


extern int
_XmStringLineCount(_XmString string)
{
    _XmStringContext context = NULL;
    char *text;
    char *tag=NULL;
    short len;
    XmStringDirection dir;
    Boolean sep;
    int lc;

    lc = 0;

    if (!_XmStringInitContext(&context, string))
    {
      return 0;
    }

    while (_XmStringGetNextSegment(context, &tag, &dir, &text, &len, &sep))
    {
      if (sep)
      {
          lc++;
      }
    }
    lc++;

    _XmStringFreeContext(context);

    return lc;
}

#if (XmVERSION > 1)
/*
 * Motif 2.* version of _XmStringDraw().
 *
 * Two differences with the Motif 1.x case :
 *    fontlist is really a renderTable
 *    string can have rendition tags in it
 */
extern void
_XmStringDraw(Display *d, Window w,
            XmFontList fontlist,
            _XmString string,
            GC gc,
            Position x, Position y, Dimension width,
            unsigned char align, unsigned char lay_dir,
            XRectangle *clip)
{
      _XmStringContext  context = NULL;
      _XmStringComponent      comp;
      Position          current_y;
      Position          current_x = 0, tabx;
      Boolean                 have_seg, have_line_height, clipped;
      Dimension         default_line_height;
      XRectangle        ink, log;
      int               pending_newlines, i, inc;
      char              *tag;
      XmRenderTable           ort, rt = NULL;
      XmRendition       r, rend = NULL;
      XmTabList         tl;
      GC                mygc;

      if (w == 0 || string == 0) {
            return;
      }
      if (!fontlist) {
            _XmWarning(NULL, "_XmStringDraw: got NULL FontList");
            return;
      }

      if (clip) {
            DEBUGOUT(_LtDebug(__FILE__, XtWindowToWidget(d, w),
                  "_XmStringDraw x %d y %d wid %d clip x %d y %d wid %d ht %d\n",
                  x, y, width,
                  clip->x, clip->y, clip->width, clip->height));
      } else {
            DEBUGOUT(_LtDebug(__FILE__, XtWindowToWidget(d, w),
                  "_XmStringDraw x %d y %d wid %d no-clip\n",
                  x, y, width));
      }

      current_y = y;
      _XmStringUpdate(fontlist, string);
      _XmStringInitContext(&context, string);

      mygc = XCreateGC(d, w, 0, NULL);
      XCopyGC(d, gc, ~0, mygc);

      pending_newlines = 0;
      have_line_height = False;
      clipped = False;
      default_line_height = 0;

      ort = rt = XmRenderTableCopy(fontlist, 0, 0);
      inc = 0;    /* This is the number to increment comp->font with
                   * because of the push()/pop() operations.
                   */

      /* repeat while there's at least one thing in the string */
      while (__XmStringPeekNextComponent(context) != NULL) {
            Dimension line_height, line_width, line_ascent, line_descent;
            Dimension seg_height, seg_width, seg_ascent, seg_descent;
            int start;

            start = context->current_component;

            line_width = line_height = 0;
            line_ascent = line_descent = 0;
            have_seg = False;

            /*
             * first, calculate the line extents
             */
            while ((comp = __XmStringGetNextComponent(context)) != NULL) {
                  DEBUGOUT(_LtDebug(__FILE__, NULL, "_XmStringDraw[%d] type %d\n",
                        context->current_component, comp->type));

                  if ((comp->type == XmSTRING_COMPONENT_TEXT ||
                        comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
                        comp->font != XmUNSPECIFIED) {

                        /* This uses comp->font so deal with 'inc' */
                        comp->font += inc;
                        __XmStringSegmentExtent(rt, comp,
                              &seg_width, &seg_height,
                              &seg_ascent, &seg_descent);
                        comp->font -= inc;

                        line_width += seg_width;
                        if (seg_height > line_height) {
                              line_height = seg_height;
                        }
                        if (seg_ascent > line_ascent) {
                              line_ascent = seg_ascent;
                        }
                        if (seg_descent > line_descent) {
                              line_descent = seg_descent;
                        }

                        have_seg = True;
                  } else if (comp->type == XmSTRING_COMPONENT_SEPARATOR) {
                        if (!have_seg && !have_line_height) {
                              pending_newlines++;
                        } else if (have_seg && !have_line_height) {
                              default_line_height = line_ascent + line_descent;
                              have_line_height = True;
                              current_y += pending_newlines * default_line_height;
                        } else if (have_seg) {
                              default_line_height = line_ascent + line_descent;
                        }
                        break;
                  } else {
                        switch (comp->type) {
                        case XmSTRING_COMPONENT_CHARSET:
                              /*
                               * Treat charset as a rendition begin,
                               * which is mostly right except there's no end.
                               */
                        case XmSTRING_COMPONENT_RENDITION_BEGIN:
                              /* Get the rendition tag */
                              tag = XtMalloc(comp->length + 1);
                              memcpy(tag, comp->data, comp->length);
                              tag[comp->length] = '\0';

                              /* Get a rendition that matches the tag */
                              rend = XmRenderTableGetRendition(rt, tag);

                              if (rend == NULL) {
                                    _XmRenderTableFinaliseTag(NULL, rt, tag);
                                    rend = XmRenderTableGetRendition(rt, tag);
                              }

                              if (rend == NULL) {
                                    XmDisplayCallbackStruct cbs;
                                    Widget      wid = XtWindowToWidget(d, w),
                                          dw = XmGetXmDisplay(d);
                                    XtCallCallbackList(wid,
                                          DisplayNoRenditionCB(dw),
                                          (XtPointer)&cbs);
                              }

                              /* Push it in front of the private rendertable */
                              if (rend) {
                                    inc++;
                                    rt = _XmRenderTablePushRendition(rt, rend);
                              }

                              DEBUGOUT(_LtDebug(__FILE__, NULL,
                                    "_XmStringDraw Rendition(%s) -> %p\n",
                                    tag, rend));

                              XmRenditionFree(rend);
                              rend = NULL;
                              XtFree(tag);
                              tag = NULL;
                              break;
                        case XmSTRING_COMPONENT_RENDITION_END:
                              /* Get the rendition tag */
                              tag = XtMalloc(comp->length + 1);
                              memcpy(tag, comp->data, comp->length);
                              tag[comp->length] = '\0';

                              /* Get a rendition that matches the tag */
                              rend = XmRenderTableGetRendition(rt, tag);

                              /* Pop it from the private rendertable */
                              if (rend) {
                                    inc--;
                                    rt = _XmRenderTablePopRendition(rt, rend);
                                    XmRenditionFree(rend);
                              }
                              rend = 0;
                              XtFree(tag);
                              tag = NULL;
                              break;
                        case XmSTRING_COMPONENT_UNKNOWN:
                        case XmSTRING_COMPONENT_DIRECTION:
                        case XmSTRING_COMPONENT_LOCALE:
                        case XmSTRING_COMPONENT_WIDECHAR_TEXT:
                        case XmSTRING_COMPONENT_LAYOUT_PUSH:
                        case XmSTRING_COMPONENT_LAYOUT_POP:
                        case XmSTRING_COMPONENT_TAB:
                        default:
                              DEBUGOUT(_LtDebug(__FILE__, NULL,
                                    "_XmStringDraw component type %d\n",
                                    comp->type));
                        }
                  }
            }

            if (!have_seg && !have_line_height) {
                  continue;
            } else if (!have_seg && have_line_height) {
                  current_y += default_line_height;
                  continue;
            } else {
                  current_y += line_ascent;
            }

            /* Set clipping if necessary, or perhaps disregard the line entirely */
            if (clip) {
                  if (current_y + line_descent <= clip->y ||
                              current_y - line_ascent >= clip->y + clip->height) {
                        current_y += line_descent;
                        continue;
                  }
                  if (!clipped && (current_y - line_ascent < clip->y ||
                              current_y + line_descent > clip->y + clip->height)) {
                        XSetClipRectangles(d, mygc, 0, 0, clip, 1, YXBanded);
                        clipped = True;
                  }
            }

            context->current_component = start;

            /* now we handle the starting x of this line based on the alignment */
            switch (align) {
            case XmALIGNMENT_CENTER:
                  current_x = x + (width - line_width + 1) / 2;
                  break;
            case XmALIGNMENT_BEGINNING:
                  current_x = x;
                  break;
            case XmALIGNMENT_END:
                  current_x = x + width - line_width;
                  break;
            }

            while ((comp = __XmStringGetNextComponent(context)) != NULL) {
                  /* Determine which rendition to use */
                  r = rend ? rend : rt->renditions[0];

                  if (comp->type == XmSTRING_COMPONENT_SEPARATOR) {
                        break;
                  } else if (comp->type == XmSTRING_COMPONENT_RENDITION_BEGIN) {
                        /* Get the rendition tag */
                        tag = XtMalloc(comp->length + 1);
                        memcpy(tag, comp->data, comp->length);
                        tag[comp->length] = '\0';

                        /* Get a rendition that matches the tag */
                        rend = XmRenderTableGetRendition(rt, tag);

                        if (rend == NULL) {
                              _XmRenderTableFinaliseTag(NULL, rt, tag);
                              rend = XmRenderTableGetRendition(rt, tag);
                        }

                        if (rend == NULL) {
                              XmDisplayCallbackStruct cbs;
                              Widget      wid = XtWindowToWidget(d, w),
                                    dw = XmGetXmDisplay(d);
                              XtCallCallbackList(wid,
                                    DisplayNoRenditionCB(dw),
                                    (XtPointer)&cbs);
                        }

                        /* Push it in front of the private rendertable */
                        if (rend) {
                              inc++;
                              rt = _XmRenderTablePushRendition(rt, rend);
                        }

                        XmRenditionFree(rend);
                        rend = NULL;
                        continue;
                  } else if (comp->type == XmSTRING_COMPONENT_RENDITION_END) {
                        /* Get the rendition tag */
                        tag = XtMalloc(comp->length + 1);
                        memcpy(tag, comp->data, comp->length);
                        tag[comp->length] = '\0';

                        /* Get a rendition that matches the tag */
                        rend = XmRenderTableGetRendition(rt, tag);

                        /* Pop it from the private rendertable */
                        if (rend) {
                              rt = _XmRenderTablePopRendition(rt, rend);
                              inc--;
                              XmRenditionFree(rend);
                        }
                        rend = 0;
                        continue;
                  } else if (comp->type == XmSTRING_COMPONENT_TAB) {
                        Position    prev;

                        /* Look for a usable tab list */
                        for (tl=NULL, i=0; i<rt->count; i++) {
                              XmRendition rr = rt->renditions[i];
                              if (rr->tab_list && rr->tab_list != (XmTabList)XmAS_IS) {
                                    tl = rr->tab_list;
                                    break;
                              }
                        }

                        for (tabx=0, i=0; tl && i<tl->count; i++) {
                              Position    x = 0;

                              switch (tl->tabs[i]->units) {
                              /* The statements below are copy/pasted from
                               * a sequence higher in this file
                               */
                              case XmINCHES:
                                    x = tl->tabs[i]->value * 25.4
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0) ;
                                    break;
                              case XmPIXELS:
                                    x = tl->tabs[i]->value;
                                    break;
                              case Xm100TH_MILLIMETERS:
                                    x = tl->tabs[i]->value * 100
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0) ;
                                    break;
                              case Xm1000TH_INCHES:
                                    x = tl->tabs[i]->value * 1000 * 25.4
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0) ;
                                    break;
                              case XmCENTIMETERS:
                                    x = tl->tabs[i]->value
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0)
                                          / 10;
                                    break;
                              case XmMILLIMETERS:
                                    x = tl->tabs[i]->value
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0);
                                    break;
                              case XmPOINTS:
                                    x = tl->tabs[i]->value * 72
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0) ;
                                    break;
                              case Xm100TH_POINTS:
                                    x = tl->tabs[i]->value * 7200
                                          * DisplayWidth(d, 0)
                                          / DisplayWidthMM(d, 0) ;
                                    break;
                              case XmFONT_UNITS:
                              case Xm100TH_FONT_UNITS:
                              default:
                                    _XmWarning(NULL,
                                          "Tab untreated unit type %d\n",
                                          r->tab_list->tabs[i]->units);
                              }

                              prev = tabx;
                              if (tl->tabs[i]->offset_model == XmABSOLUTE) {
                                    tabx = x;
                              } else {    /* XmRELATIVE */
                                    tabx += x;
                              }

                              if (prev < current_x && current_x < tabx) {
                                    current_x = tabx;
                                    continue;
                              }
                        }     /* end for */
                        continue;
                  } else if ((comp->type != XmSTRING_COMPONENT_TEXT &&
                              comp->type != XmSTRING_COMPONENT_LOCALE_TEXT) ||
                              comp->font == XmUNSPECIFIED) {
                        DEBUGOUT(_LtDebug(__FILE__, NULL,
                              "_XmStringDraw untreated case 0x%2x\n", comp->type));
                        continue;
                  }

                  switch (r->type) {
                  case XmFONT_IS_FONT:
                        comp->font += inc;
                        seg_width = (((XFontStruct *)r->font)->min_byte1 |
                             ((XFontStruct *)r->font)->max_byte1)
                            ? XTextWidth16(((XFontStruct *)r->font),
                                       (XChar2b *)comp->data, comp->length >> 1)
                            : XTextWidth(((XFontStruct *)r->font),
                                     comp->data, comp->length);
                        comp->font -= inc;
                        break;

                  case XmFONT_IS_FONTSET:
                        comp->font += inc;
                        XmbTextExtents((XFontSet)r->font,
                              comp->data, comp->length, &ink, &log);
                        /* need to do something with the extents of the font set here. */
                        seg_width = log.width;
                        comp->font -= inc;
                        break;
#if 0
                  case XmAS_IS:
/*                      fprintf(stderr, "Yaw\n");     */
#endif
                  case XmFONT_IS_XFT:
#if   USE_XFT
                  {
                        XGlyphInfo  ext;

                        if (fontlist->renditions[comp->font]->xft_font) {
                              XftTextExtents8(d,
                                    fontlist->renditions[comp->font]->xft_font,
                                    comp->data,
                                    comp->length,
                                    &ext);
                              seg_width = ext.width;
                        } else {
                              seg_width = (((XFontStruct *)r->font)->min_byte1 |
                                    ((XFontStruct *)r->font)->max_byte1)
                                    ? XTextWidth16(((XFontStruct *)r->font),
                                    (XChar2b *)comp->data, comp->length >> 1)
                                    : XTextWidth(((XFontStruct *)r->font),
                                    comp->data, comp->length);
                        }
                  }
                        break;
#endif
                  case XmFONT_IS_XOC:
#if   USE_BIDI
#endif
                        break;
                  }
#if 0
                  /* Set clipping if necessary, or perhaps skip this segment */
                  if (clip) {
                        if (current_x + seg_width <= clip->x ||
                              current_x >= clip->x + clip->width) {
                              current_x += seg_width;
                              continue;
                        }
                        if (!clipped && (current_x < clip->x ||
                              current_x + seg_width > clip->x + clip->width)) {
                              XSetClipRectangles(d, mygc, 0, 0, clip, 1, YXBanded);
                              clipped = True;
                        }
                  }
#endif
                  /* Set the new parameters from the rendition */
                  if (r->rendition_foreground != (Pixel)XmAS_IS) {
                        XSetForeground(d, mygc, r->rendition_foreground);
                  }
                  if (r->rendition_background != (Pixel)XmAS_IS) {
                        XSetBackground(d, mygc, r->rendition_background);     
                  }

                  switch (r->type) {
                  case XmFONT_IS_FONT:
                        XSetFont(d, mygc, ((XFontStruct *)r->font)->fid);

                        DEBUGOUT(_LtDebug(__FILE__, NULL,
                              "_XmStringDraw fnt(%s) x %d y %d\n",
                              r->font_name, current_x, current_y));

                        if (((XFontStruct *)r->font)->min_byte1 |
                              ((XFontStruct *)r->font)->max_byte1)
                        {
                              XDrawString16(d, w, mygc,
                                    current_x, current_y,
                                    (XChar2b *)comp->data,
                                    comp->length >> 1);
                        } else {
                              XDrawString(d, w, mygc,
                                    current_x, current_y,
                                    comp->data,
                                    comp->length);
                        }
                        break;

                  case XmFONT_IS_FONTSET:
                        XmbDrawString(d, w, (XFontSet)r->font, mygc,
                              current_x, current_y,
                              comp->data, comp->length);
                        break;
#if   USE_XFT
                  case XmFONT_IS_XFT:
                        _XmXftDrawString(d, w, r, 1, 
                              current_x, current_y,
                              comp->data, comp->length);
                        break;
                  case XmFONT_IS_XOC:
                        break;
#endif
                  }
                  current_x += seg_width;
            }

            current_y += line_descent;
      }

      _XmStringFreeContext(context);
      if (clipped) {
            XSetClipMask(d, mygc, None);
      }

      if (rend)
            XmRenditionFree(rend);
      XmRenderTableFree(ort);
      XFreeGC(d, mygc);
}
#else
/*
 * Motif 1.2 version of _XmStringDraw().
 */
extern void
_XmStringDraw(Display *d, Window w, XmFontList fontlist, _XmString string,
            GC gc, Position x, Position y, Dimension width,
            unsigned char align, unsigned char lay_dir,
            XRectangle *clip)
{
    _XmStringContext context = NULL;
    _XmStringComponent comp;
    Position current_y;
    Position current_x = 0;
    Boolean have_seg, have_line_height, clipped;
    Dimension default_line_height;
    XRectangle ink, log;
    int pending_newlines;

    if (w == 0 || string == 0)
    {
      return;
    }
    if (!fontlist)
    {
      _XmWarning(NULL, "_XmStringDraw: got NULL FontList");
      return;
    }

    DEBUGOUT(_LtDebug(__FILE__, XtWindowToWidget(d, w),
                  "_XmStringDraw x %d y %d wid %d\n",
                  x, y, width));

    current_y = y;

    _XmStringUpdate(fontlist, string);
    _XmStringInitContext(&context, string);

    pending_newlines = 0;
    have_line_height = False;
    clipped = False;
    default_line_height = 0;

    /* repeat while there's at least one thing in the string */
    while (__XmStringPeekNextComponent(context) != NULL)
    {
      Dimension line_height, line_width, line_ascent, line_descent;
      Dimension seg_height, seg_width, seg_ascent, seg_descent;
      int start;

      start = context->current_component;

      line_width = line_height = 0;
      line_ascent = line_descent = 0;
      have_seg = False;

      /*
       * first, calculate the line extents
       */
      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if ((comp->type == XmSTRING_COMPONENT_TEXT ||
             comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
            comp->font != XmUNSPECIFIED)
          {
            __XmStringSegmentExtent(fontlist, comp,
                              &seg_width, &seg_height,
                              &seg_ascent, &seg_descent);

            line_width += seg_width;
            if (seg_height > line_height)
            {
                line_height = seg_height;
            }
            if (seg_ascent > line_ascent)
            {
                line_ascent = seg_ascent;
            }
            if (seg_descent > line_descent)
            {
                line_descent = seg_descent;
            }

            have_seg = True;
          }
          else if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            if (!have_seg && !have_line_height)
            {
                pending_newlines++;
            }
            else if (have_seg && !have_line_height)
            {
                default_line_height = line_ascent + line_descent;
                have_line_height = True;
                current_y += pending_newlines * default_line_height;
            }
            else if (have_seg)
            {
                default_line_height = line_ascent + line_descent;
            }
            break;
          }
      }

      if (!have_seg && !have_line_height)
      {
          continue;
      }
      else if (!have_seg && have_line_height)
      {
          current_y += default_line_height;
          continue;
      }
      else
      {
          current_y += line_ascent;
      }

      /* Set clipping if necessary, or perhaps disregard the line entirely */
      if (clip)
      {
          if (current_y + line_descent <= clip->y ||
            current_y - line_ascent >= clip->y + clip->height)
          {
            current_y += line_descent;
            continue;
          }
          if (!clipped && (current_y - line_ascent < clip->y ||
            current_y + line_descent > clip->y + clip->height))
          {
            XSetClipRectangles(d, gc, 0, 0, clip, 1, YXBanded);
            clipped = True;
          }
      }

      context->current_component = start;

      /* now we handle the starting x of this line based on the alignment */
      switch (align)
      {
      case XmALIGNMENT_CENTER:
          current_x = x + (width - line_width + 1) / 2;
          break;

      case XmALIGNMENT_BEGINNING:
          current_x = x;
          break;

      case XmALIGNMENT_END:
          current_x = x + width - line_width;
          break;
      }

      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            break;
          }
          else if ((comp->type != XmSTRING_COMPONENT_TEXT &&
                  comp->type != XmSTRING_COMPONENT_LOCALE_TEXT) ||
                 comp->font == XmUNSPECIFIED)
          {
            continue;
          }

          switch (fontlist->renditions[comp->font]->type)
          {
          case XmFONT_IS_FONT:
            seg_width =
                (((XFontStruct *)fontlist->renditions[comp->font]->font)->min_byte1 |
                 ((XFontStruct *)fontlist->renditions[comp->font]->font)->max_byte1)
                ? XTextWidth16(((XFontStruct *)fontlist->renditions[comp->font]->font),
                           (XChar2b *)comp->data, comp->length >> 1)
                : XTextWidth(((XFontStruct *)fontlist->renditions[comp->font]->font),
                         comp->data, comp->length);
            break;

          case XmFONT_IS_FONTSET:
            XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                         comp->data, comp->length, &ink, &log);
            /* need to do something with the extents of the font set here. */
            seg_width = log.width;
            break;
#if   USE_XFT
          /* This won't happen - this is the Motif 1.2 version. */
          case XmFONT_IS_XFT:
            break;
#endif
#if   USE_BIDI
          case XmFONT_IS_XOC:
            break;
#endif
          }

          /* Set clipping if necessary, or perhaps skip this segment */
          if (clip)
          {
            if (current_x + seg_width <= clip->x ||
                current_x >= clip->x + clip->width)
            {
                current_x += seg_width;
                continue;
            }
            if (!clipped && (current_x < clip->x ||
                current_x + seg_width > clip->x + clip->width))
            {
                XSetClipRectangles(d, gc, 0, 0, clip, 1, YXBanded);
                clipped = True;
            }
          }

          switch (fontlist->renditions[comp->font]->type)
          {
          case XmFONT_IS_FONT:
            XSetFont(d, gc,
                   ((XFontStruct *)fontlist->renditions[comp->font]->font)->fid);
            if (((XFontStruct *)fontlist->renditions[comp->font]->font)->min_byte1 |
                ((XFontStruct *)fontlist->renditions[comp->font]->font)->max_byte1)
            {
                XDrawString16(d, w, gc, current_x, current_y,
                          (XChar2b *)comp->data, comp->length >> 1);
            }
            else
            {
                XDrawString(d, w, gc, current_x, current_y,
                        comp->data, comp->length);
            }
            break;

          case XmFONT_IS_FONTSET:
            XmbDrawString(d, w, (XFontSet)fontlist->renditions[comp->font]->font, gc,
                        current_x, current_y, comp->data, comp->length);
            break;
          }

          current_x += seg_width;
      }

      current_y += line_descent;
    }
    _XmStringFreeContext(context);
    if (clipped)
    {
      XSetClipMask(d, gc, None);
    }
}
#endif

extern void
_XmStringDrawImage(Display *d, Window w,
               XmFontList fontlist, _XmString string,
               GC gc, Position x, Position y, Dimension width,
               unsigned char align, unsigned char lay_dir,
               XRectangle *clip)
{
    _XmStringContext context = NULL;
    _XmStringComponent comp;
    Position current_y;
    Position current_x = 0;
    Boolean have_seg, have_line_height, clipped;
    Dimension default_line_height;
    XRectangle ink, log;
    int pending_newlines;

    current_y = y;

    _XmStringUpdate(fontlist, string);
    _XmStringInitContext(&context, string);

    pending_newlines = 0;
    have_line_height = False;
    clipped = False;
    default_line_height = 0;

    /* repeat while there's at least one thing in the string */
    while (__XmStringPeekNextComponent(context) != NULL)
    {
      Dimension line_height, line_width, line_ascent, line_descent;
      Dimension seg_height, seg_width, seg_ascent, seg_descent;
      int start;

      start = context->current_component;

      line_width = line_height = 0;
      line_ascent = line_descent = 0;
      have_seg = False;

      /*
       * first, calculate the line extents
       */
      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if ((comp->type == XmSTRING_COMPONENT_TEXT ||
             comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
            comp->font != XmUNSPECIFIED)
          {
            __XmStringSegmentExtent(fontlist, comp,
                              &seg_width, &seg_height,
                              &seg_ascent, &seg_descent);

            line_width += seg_width;
            if (seg_height > line_height)
            {
                line_height = seg_height;
            }
            if (seg_ascent > line_ascent)
            {
                line_ascent = seg_ascent;
            }
            if (seg_descent > line_descent)
            {
                line_descent = seg_descent;
            }

            have_seg = True;
          }

          if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            if (!have_seg && !have_line_height)
            {
                pending_newlines++;
            }
            else if (have_seg && !have_line_height)
            {
                default_line_height = line_ascent + line_descent;
                have_line_height = True;
                current_y += pending_newlines * default_line_height;
            }
            else if (have_seg)
            {
                default_line_height = line_ascent + line_descent;
            }

            break;
          }
      }

      if (!have_seg && !have_line_height)
      {
          continue;
      }
      else if (!have_seg && have_line_height)
      {
          current_y += default_line_height;
          continue;
      }
      else
      {
          current_y += line_ascent;
      }

      /* Set clipping if necessary, or perhaps disregard the line entirely */
      if (clip)
      {
          if (current_y + line_descent <= clip->y ||
            current_y - line_ascent >= clip->y + clip->height)
          {
            current_y += line_descent;
            continue;
          }
          if (!clipped && (current_y - line_ascent < clip->y ||
            current_y + line_descent > clip->y + clip->height))
          {
            XSetClipRectangles(d, gc, 0, 0, clip, 1, YXBanded);
            clipped = True;
          }
      }

      context->current_component = start;

      /* now we handle the starting x of this line based on the alignment */
      switch (align)
      {
      case XmALIGNMENT_CENTER:
          current_x = x + (width - line_width + 1) / 2;
          break;

      case XmALIGNMENT_BEGINNING:
          current_x = x;
          break;

      case XmALIGNMENT_END:
          current_x = x + width - line_width;
          break;
      }

      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            break;
          }
          else if ((comp->type != XmSTRING_COMPONENT_TEXT &&
                  comp->type != XmSTRING_COMPONENT_LOCALE_TEXT) ||
                 comp->font == XmUNSPECIFIED)
          {
            continue;
          }

          switch (fontlist->renditions[comp->font]->type)
          {
          case XmFONT_IS_FONT:
            seg_width =
                (((XFontStruct *)fontlist->renditions[comp->font]->font)->min_byte1 |
                 ((XFontStruct *)fontlist->renditions[comp->font]->font)->max_byte1)
                ? XTextWidth16(((XFontStruct *)fontlist->renditions[comp->font]->font),
                           (XChar2b *)comp->data, comp->length >> 1)
                : XTextWidth(((XFontStruct *)fontlist->renditions[comp->font]->font),
                         comp->data, comp->length);
            break;

          case XmFONT_IS_FONTSET:
            XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                         comp->data, comp->length, &ink, &log);
            /* need to do something with the extents of the font set here. */
            seg_width = log.width;
            break;
#if   USE_XFT
          case XmFONT_IS_XFT:
            {
                  XGlyphInfo  ext;

                  XftTextExtents8(d,
                        fontlist->renditions[comp->font]->xft_font,
                        comp->data,
                        comp->length,
                        &ext);
                  seg_width = ext.width;
            }
            break;
#endif
          case XmFONT_IS_XOC:
#if   USE_BIDI
#endif
            break;
          }

          /* Set clipping if necessary, or perhaps skip this segment */
          if (clip)
          {
            if (current_x + seg_width <= clip->x ||
                current_x >= clip->x + clip->width)
            {
                current_x += seg_width;
                continue;
            }
            if (!clipped && (current_x < clip->x ||
                current_x + seg_width > clip->x + clip->width))
            {
                XSetClipRectangles(d, gc, 0, 0, clip, 1, YXBanded);
                clipped = True;
            }
          }

          switch (fontlist->renditions[comp->font]->type)
          {
          case XmFONT_IS_FONT:
            XSetFont(d, gc,
                   ((XFontStruct *)fontlist->renditions[comp->font]->font)->fid);
            if (((XFontStruct *)fontlist->renditions[comp->font]->font)->min_byte1 |
                ((XFontStruct *)fontlist->renditions[comp->font]->font)->max_byte1)
            {
                XDrawImageString16(d, w, gc, current_x, current_y,
                          (XChar2b *)comp->data, comp->length >> 1);
            }
            else
            {
                XDrawImageString(d, w, gc, current_x, current_y,
                             comp->data, comp->length);
            }
            break;

          case XmFONT_IS_FONTSET:
            XmbDrawImageString(d, w, (XFontSet)fontlist->renditions[comp->font]->font,
                           gc, current_x, current_y,
                           comp->data, comp->length);
            break;
#if   USE_XFT
          case XmFONT_IS_XFT:
            _XmXftDrawString(d, w, fontlist->renditions[comp->font], 1, 
                  current_x, current_y,
                  comp->data, comp->length);
            break;
#endif
          case XmFONT_IS_XOC:
#if   USE_BIDI
#endif
            break;
          }

          current_x += seg_width;
      }

      current_y += line_descent;
    }
    _XmStringFreeContext(context);
    if (clipped)
    {
      XSetClipMask(d, gc, None);
    }
}


extern void
_XmStringDrawUnderline(Display *d, Window w, XmFontList fontlist,
                   _XmString string, GC gc,
                   Position x, Position y, Dimension width,
                   unsigned char align, unsigned char lay_dir,
                   XRectangle *clip, _XmString underline)
{
    _XmStringContext context = NULL, und_c = NULL;
    _XmStringComponent comp, und;
    Position current_y;
    Position current_x = 0;
    int xo;
    char *p, *u;
    Boolean have_seg, have_line_height, clipped;
    Dimension default_line_height;
    XRectangle ink, log;
    int pending_newlines;

    if (w == 0)
    {
      return;
    }

    DEBUGOUT(_LtDebug(__FILE__, XtWindowToWidget(d, w),
                  "_XmStringDrawUnderline x %d y %d wid %d\n",
                  x, y, width));
    if (!underline)
    {
      DEBUGOUT(_LtDebug(__FILE__, XtWindowToWidget(d, w),
                   "_XmStringDrawUnderline, underlined string is NULL\n"));
    }

    /* Currently only works if "underline" is a single-segment LtoR string */
    _XmStringInitContext(&und_c, underline);
    while ((und = __XmStringGetNextComponent(und_c)) != NULL)
    {
      if ((und->type == XmSTRING_COMPONENT_TEXT ||
           und->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
          und->font != XmUNSPECIFIED)
      {
          break;
      }
    }

    if (und->type == XmSTRING_COMPONENT_TEXT ||
      und->type == XmSTRING_COMPONENT_LOCALE_TEXT)
    {
      u = und->data;
    }
    else
    {
      u = NULL;
    }

    current_y = y;

    _XmStringUpdate(fontlist, string);
    _XmStringInitContext(&context, string);

    pending_newlines = 0;
    have_line_height = False;
    clipped = False;
    default_line_height = 0;

    /* repeat while there's at least one thing in the string */
    while (__XmStringPeekNextComponent(context) != NULL)
    {
      Dimension line_height, line_width, line_ascent, line_descent;
      Dimension seg_height, seg_width, seg_ascent, seg_descent;
      int start;

      start = context->current_component;

      line_width = line_height = 0;
      line_ascent = line_descent = 0;
      have_seg = False;

      /*
       * first, calculate the line extents
       */
      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if ((comp->type == XmSTRING_COMPONENT_TEXT ||
             comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
            comp->font != XmUNSPECIFIED)
          {
            __XmStringSegmentExtent(fontlist, comp,
                              &seg_width, &seg_height,
                              &seg_ascent, &seg_descent);

            line_width += seg_width;
            if (seg_height > line_height)
            {
                line_height = seg_height;
            }
            if (seg_ascent > line_ascent)
            {
                line_ascent = seg_ascent;
            }
            if (seg_descent > line_descent)
            {
                line_descent = seg_descent;
            }

            have_seg = True;
          }

          if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            if (!have_seg && !have_line_height)
            {
                pending_newlines++;
            }
            else if (have_seg && !have_line_height)
            {
                default_line_height = line_ascent + line_descent;
                have_line_height = True;
                current_y += pending_newlines * default_line_height;
            }
            else if (have_seg)
            {
                default_line_height = line_ascent + line_descent;
            }

            break;
          }
      }

      if (!have_seg && !have_line_height)
      {
          continue;
      }
      else if (!have_seg && have_line_height)
      {
          current_y += default_line_height;
          continue;
      }
      else
      {
          current_y += line_ascent;
      }

      /* Set clipping if necessary, or perhaps disregard the line entirely */
      if (clip)
      {
          if (current_y + line_descent < clip->y ||
            current_y - line_ascent >= clip->y + clip->height)
          {
            current_y += line_descent;
            continue;
          }
          if (!clipped && (current_y - line_ascent < clip->y ||
            current_y + line_descent >= clip->y + clip->height))
          {
            XSetClipRectangles(d, gc, 0, 0, clip, 1, YXBanded);
            clipped = True;
          }
      }

      context->current_component = start;

      /* now we handle the starting x of this line based on the alignment */
      switch (align)
      {
      case XmALIGNMENT_CENTER:
          current_x = x + (width - line_width + 1) / 2;
          break;

      case XmALIGNMENT_BEGINNING:
          current_x = x;
          break;

      case XmALIGNMENT_END:
          current_x = x + width - line_width;
          break;
      }

      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            break;
          }
          else if ((comp->type != XmSTRING_COMPONENT_TEXT &&
                  comp->type != XmSTRING_COMPONENT_LOCALE_TEXT) ||
                 comp->font == XmUNSPECIFIED)
          {
            continue;
          }

          switch (fontlist->renditions[comp->font]->type)
          {
          case XmFONT_IS_FONT:
            seg_width =
                (((XFontStruct *)fontlist->renditions[comp->font]->font)->min_byte1 |
                 ((XFontStruct *)fontlist->renditions[comp->font]->font)->max_byte1)
                ? XTextWidth16(((XFontStruct *)fontlist->renditions[comp->font]->font),
                           (XChar2b *)comp->data, comp->length >> 1)
                : XTextWidth(((XFontStruct *)fontlist->renditions[comp->font]->font),
                         comp->data, comp->length);
            break;

          case XmFONT_IS_FONTSET:
            XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                         comp->data, comp->length, &ink, &log);
            /* need to do something with the extents of the font set here. */
            seg_width = log.width;
            break;
#if   USE_XFT
          case XmFONT_IS_XFT:
            {
                  XGlyphInfo  ext;

                  XftTextExtents8(d,
                        fontlist->renditions[comp->font]->xft_font,
                        comp->data,
                        comp->length,
                        &ext);
                  seg_width = ext.width;
            }
            break;
#endif
          case XmFONT_IS_XOC:
#if   USE_BIDI
#endif
            break;
          }

          /* Set clipping if necessary, or perhaps skip this segment */
          if (clip)
          {
            if (current_x + seg_width <= clip->x ||
                current_x >= clip->x + clip->width)
            {
                current_x += seg_width;
                continue;
            }
            if (!clipped && (current_x < clip->x ||
                current_x + seg_width > clip->x + clip->width))
            {
                XSetClipRectangles(d, gc, 0, 0, clip, 1, YXBanded);
                clipped = True;
            }
          }

          switch (fontlist->renditions[comp->font]->type)
          {
          case XmFONT_IS_FONT:
            {
                XFontStruct *fontstruct =
                (XFontStruct *)fontlist->renditions[comp->font]->font;

               Boolean is_wide;

               if (!fontstruct) break;
                /* modified by Codematic 11.01.1998 */

               is_wide =
                fontstruct->min_byte1 || fontstruct->max_byte1;

                XSetFont(d, gc, fontstruct->fid);

                if (is_wide)
                {

                  XDrawString16(d, w, gc, current_x, current_y,
                              (XChar2b *)comp->data, comp->length / 2);
                }
                else
                {
                  XDrawString(d, w, gc, current_x, current_y,
                            comp->data, comp->length);
                }

                /* Now *first* underline, then update current_x */

                if (u && (p = strstr(comp->data, u)))
                {       /* Underline */
                  int ww, llen = p - comp->data;
                  char *f = XtMalloc(llen + 1);

                  strncpy(f, comp->data, llen);
                  f[llen] = '\0';
                  if (is_wide)
                  {
                      xo = XTextWidth16(fontstruct, (XChar2b *)f,
                                    llen / 2);
                  }
                  else
                  {
                      xo = XTextWidth(fontstruct, f, llen);
                  }
                  XtFree(f);

                  /* ww = width of underlined text */
                  if (is_wide)
                  {
                      ww = XTextWidth16(fontstruct, (XChar2b *)u,
                                    strlen(u) / 2);
                  }
                  else
                  {
                      ww = XTextWidth(fontstruct, u, strlen(u));
                  }

                  /*
                   * Subtracted 1 from the width to take the space
                   * between characters into account. Is there a
                   * decent way to do this ??  Danny 17/4/96
                   */
                  XDrawLine(d, w, gc,
                          current_x + xo,
                          current_y + line_descent,
                          current_x + xo + ww - 1,
                          current_y + line_descent);
                }
            }
            break;

          case XmFONT_IS_FONTSET:
            XmbDrawImageString(d, w, (XFontSet)fontlist->renditions[comp->font]->font,
                           gc, current_x, current_y,
                           comp->data, comp->length);

            /* Added by SG 08/08/1998
             * Now *first* underline, then update current_x
             */

            if (u && (p = strstr(comp->data, u))) {   /* Underline */
                int ww, llen = p - comp->data;

                XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                           comp->data, llen, &ink, &log);
                xo = log.width;

                /* ww = width of underlined text */
                XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                         u, strlen(u), &ink, &log);
                ww = log.width;

                /*
                 * Subtracted 1 from the width to take the space
                 * between characters into account. Is there a
                 * decent way to do this ??  Danny 17/4/96
                 */
                XDrawLine(d, w, gc,
                        current_x + xo,
                        current_y + line_descent,
                        current_x + xo + ww - 1,
                        current_y + line_descent);
            }
#if   USE_XFT
          case XmFONT_IS_XFT:
            /* FIX ME need to underline */
            _XmXftDrawString(d, w, fontlist->renditions[comp->font], 1, 
                  current_x, current_y,
                  comp->data, comp->length);
            if (u && (p = strstr(comp->data, u))) {   /* Underline */
                  int ww, llen = p - comp->data;
                  XGlyphInfo  ext;

                  XftTextExtents8(d,
                        fontlist->renditions[comp->font]->xft_font,
                        comp->data, llen,
                        &ext);
                  seg_width = ext.width;
#if 0
                  XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                        comp->data, llen, &ink, &log);
#endif
                  xo = ext.width;

                  /* ww = width of underlined text */
#if 0
                  XmbTextExtents((XFontSet)fontlist->renditions[comp->font]->font,
                        u, strlen(u), &ink, &log);
#endif
                  XftTextExtents8(d,
                        fontlist->renditions[comp->font]->xft_font,
                        u, strlen(u), &ext);
                  ww = ext.width;

                  /*
                   * Subtracted 1 from the width to take the space
                   * between characters into account. Is there a
                   * decent way to do this ??  Danny 17/4/96
                   */
                  XDrawLine(d, w, gc,
                        current_x + xo,
                        current_y + line_descent,
                        current_x + xo + ww - 1,
                        current_y + line_descent);
            }
            break;
#endif
          case XmFONT_IS_XOC:
#if   USE_BIDI
#endif
            break;
          }

          current_x += seg_width;
      }

      current_y += line_descent;
    }
    _XmStringFreeContext(und_c);
    _XmStringFreeContext(context);
    if (clipped)
    {
      XSetClipMask(d, gc, None);
    }
}


extern void
_XmStringDrawMnemonic(Display *d,
                  Window w,
                  XmFontList fontlist,
                  _XmString string,
                  GC gc,
                  Position x,
                  Position y,
                  Dimension width,
                  unsigned char alignment,
                  unsigned char layout_direction,
                  XRectangle *clip,
                  String mnemonic,
                  XmStringCharSet charset)
{
    XmString und;
    _XmString _und;

    DEBUGOUT(_LtDebug(__FILE__, XtWindowToWidget(d, w),
                  "_XmStringDrawMnemonic(%s, %s)\n",
                  _LtDebugXmString2String((XmString)string), mnemonic));

    if (mnemonic == NULL)
    {
      _XmStringDraw(d, w, fontlist, string, gc, x, y, width, alignment,
                  layout_direction, clip);
      return;
    }

    und = XmStringCreate(mnemonic, charset);
    _und = _XmStringCreate(und);

    _XmStringDrawUnderline(d, w, fontlist, string,
                     gc, x, y, width, alignment,
                     layout_direction, clip, _und);

    XmStringFree(und);
    _XmStringFree(_und);
}

/*
 * This is essentially _XmStringDraw without the drawing part.
 * It expects baselines to point to memory already allocated.
 */
extern void
_XmStringBaselines(XmFontList fontlist, _XmString string, Position y,
               Dimension *baselines)
{
    _XmStringContext context = NULL;
    _XmStringComponent comp = NULL;
    Boolean have_seg, have_line_height;
    Dimension default_line_height, default_line_ascent;
    int pending_newlines;

    _XmStringUpdate(fontlist, string);
    _XmStringInitContext(&context, string);

    pending_newlines = 0;
    have_line_height = False;
    default_line_height = 0;
    default_line_ascent = 0;

    while (__XmStringPeekNextComponent(context) != NULL)
    {
      Dimension line_height, line_ascent, line_descent;
      Dimension seg_height, seg_width, seg_ascent, seg_descent;
      int start;

      start = context->current_component;

      line_height = 0;
      line_ascent = line_descent = 0;
      have_seg = False;

      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if ((comp->type == XmSTRING_COMPONENT_TEXT ||
             comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
            comp->font != XmUNSPECIFIED)
          {
            __XmStringSegmentExtent(fontlist, comp,
                              &seg_width, &seg_height,
                              &seg_ascent, &seg_descent);

            if (seg_height > line_height)
            {
                line_height = seg_height;
            }
            if (seg_ascent > line_ascent)
            {
                line_ascent = seg_ascent;
            }
            if (seg_descent > line_descent)
            {
                line_descent = seg_descent;
            }

            have_seg = True;
          }
          else if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
            if (!have_seg && !have_line_height)
            {
                pending_newlines++;
            }
            else if (have_seg && !have_line_height)
            {
                default_line_height = line_ascent + line_descent;
                default_line_ascent = line_ascent;
                have_line_height = True;
                while (pending_newlines--)
                {
                  *baselines++ = y + default_line_ascent;
                  y += default_line_height;
                }
            }
            else if (have_seg)
            {
                default_line_height = line_ascent + line_descent;
                default_line_ascent = line_ascent;
            }
            break;
          }
      }

      if (!have_seg && !have_line_height)
      {
          continue;
      }
      else if (!have_seg && have_line_height)
      {
          *baselines++ = y + default_line_ascent;
          y += default_line_height;
          continue;
      }
      else
      {
          y += line_ascent;
      }

      *baselines++ = y;
      y += line_descent;
    }
    if (comp)
    {
      *baselines = have_line_height
          ? y + default_line_ascent
          : y;
    }
    _XmStringFreeContext(context);
}

/************************* PUBLIC FUNCTIONS *****************************/

extern Dimension
XmStringBaseline(XmFontList fontlist,
             XmString string)
{
    Dimension baseline;
    _XmString str;

    if (!_XmStringIsXmString(string))
    {
      return 0;
    }

    str = _XmStringCreate(string);
    baseline = _XmStringBaseline(fontlist, str);
    _XmStringFree(str);

    return baseline;
}


extern Boolean
XmStringByteCompare(XmString s1,
                XmString s2)
{
    _XmString str1, str2;
    Boolean ret;

    if (!_XmStringIsXmString(s1) || !_XmStringIsXmString(s2))
    {
      return False;
    }

    str1 = _XmStringCreate(s1);
    str2 = _XmStringCreate(s2);

    ret = _XmStringByteCompare(str1, str2);

    _XmStringFree(str1);
    _XmStringFree(str2);

    return ret;
}


extern Boolean
XmStringCompare(XmString s1, XmString s2)
{
 XmStringContext context1 = NULL, context2 = NULL;
 char *text1, *text2, *tag1, *tag2;
 Boolean separator1, separator2, compare_tags;
 XmStringDirection direction1, direction2;

 if (!_XmStringIsXmString(s1) || !_XmStringIsXmString(s2))
      return False;

 if (!XmStringInitContext(&context1, s1))
      return False;
 if (!XmStringInitContext(&context2, s2))
      { XmStringFreeContext(context1); return False; }

 while (XmStringGetNextSegment(context1,
       &text1, &tag1, &direction1, &separator1)) {
      if (!XmStringGetNextSegment(context2,
            &text2, &tag2, &direction2, &separator2)) {
            XtFree(text1);
            XtFree(tag1);
            XmStringFreeContext(context1);
            XmStringFreeContext(context2);
            return False;
      }
      compare_tags = (strcmp(tag1, XmFONTLIST_DEFAULT_TAG) != 0 &&
                  strcmp(tag2, XmFONTLIST_DEFAULT_TAG) != 0);
       /* Don't compare tags when one of them = XmFONTLIST_DEFAULT_TAG */
      /*
      printf("%p %p\n>%s< >%s<\n%i %i\n%i %i\n", text1, text2, text1, text2, direction1, direction2, separator1, separator2);
      */
      if (
          ((text1 == NULL || text2 == NULL) && text1 != text2) 
          || (text1 != text2 && strcmp(text1, text2) != 0) /* diferent text */
          || direction1 != direction2
          || (compare_tags && strcmp(tag1, tag2) != 0)
          || separator1 != separator2
         )
      {
            /*
            printf("Different\n");
            */
            XtFree(text1);
            XtFree(text2);
            XtFree(tag1);
            XtFree(tag2);
            XmStringFreeContext(context1);
            XmStringFreeContext(context2);
            return False;
      }
            /*
            printf("Equal\n");
            */
 }

 XtFree(text1);
 XtFree(text2);
 XtFree(tag1);
 XtFree(tag2);
 XmStringFreeContext(context1);
 XmStringFreeContext(context2);
 return True;
}


extern XmString
XmStringConcat(XmString str1, XmString str2)
{
    _XmString s1, s2;
    _XmString newString;
    int i, base, total;
    XmString ret;

#if 0
    DEBUGOUT(_LtDebug(__FILE__, NULL, "XmStringConcat() entering\n"));
#endif
    if (!_XmStringIsXmString(str1) && !_XmStringIsXmString(str2))
    {
      return NULL;
    }
    else if (!_XmStringIsXmString(str1) && _XmStringIsXmString(str2))
    {
      return XmStringCopy(str2);
    }
    else if (_XmStringIsXmString(str1) && !_XmStringIsXmString(str2))
    {
      return XmStringCopy(str1);
    }

    s1 = _XmStringCreate(str1);
    s2 = _XmStringCreate(str2);

    if (s1)
    {
      total = s1->number_of_components;
    }
    else
    {
      total = 0;
    }
    base = total;
    total += s2->number_of_components;

    newString = __XmAllocNewXmString(total);

    if (s1)
    {
      for (i = 0; i < base; i++)
      {
          __XmStringComponentCopy(newString->components[i],
                            s1->components[i]);
      }
    }

    for (i = 0; i < s2->number_of_components; i++)
    {
      __XmStringComponentCopy(newString->components[base + i],
                        s2->components[i]);
    }

    _XmStringFree(s1);
    _XmStringFree(s2);

    ret = _XmStringCreateExternal(NULL, newString);

    _XmStringFree(newString);
#if 0
    DEBUGOUT(_LtDebug(__FILE__, NULL, "XmStringConcat() leaving\n"));
#endif
    return ret;
}


extern XmString
XmStringCopy(XmString s)
{
    _XmString s1;
    XmString ret;

    if (!_XmStringIsXmString(s))
    {
      return NULL;
    }

    s1 = _XmStringCreate(s);
    ret = _XmStringCreateExternal(NULL, s1);

    _XmStringFree(s1);

    return ret;
}


extern XmString
XmStringNConcat(XmString s1, XmString s2, int num_bytes)
{
    _XmString str1, str2;
    XmString ret;
    int i, nlen, tmp;

    if (!_XmStringIsXmString(s1) || !_XmStringIsXmString(s2))
    {
      return NULL;
    }

    str1 = _XmStringCreate(s1);
    str2 = _XmStringCreate(s2);

    i = 0;
    while (num_bytes && i < str2->number_of_components)
    {
      num_bytes -= ASN1_HEADER_SIZE;      /* header */
      if (num_bytes < 0)
      {
          break;
      }

      if (num_bytes == 0)
      {
          __XmGrowXmString(str1);
          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = 0;
          break;
      }

      if (str2->components[i]->type == XmSTRING_COMPONENT_SEPARATOR)
      {
          __XmGrowXmString(str1);

          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = 0;

          i++;
          continue;
      }

      if (str2->components[i]->type == XmSTRING_COMPONENT_DIRECTION)
      {
          num_bytes--;

          __XmGrowXmString(str1);

          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = 1;
          str1->components[str1->number_of_components - 1]->data =
            XtMalloc(1);
          str1->components[str1->number_of_components - 1]->data[0] =
            str2->components[i]->data[0];

          i++;
          continue;
      }

      __XmGrowXmString(str1);
      nlen = str2->components[i]->length;

      if (num_bytes < XmSTRING_LENGTH && nlen >= XmSTRING_LENGTH)
      {
          nlen = num_bytes;
          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = nlen;
          str1->components[str1->number_of_components - 1]->data =
            XtMalloc(nlen + 1);
          memcpy(str1->components[str1->number_of_components - 1]->data,
                 str2->components[i]->data,
               nlen);
          str1->components[str1->number_of_components - 1]->data[nlen] = 0;
      }
      else
      {
          if (nlen > num_bytes)
          {
            nlen = num_bytes;
          }

          if (nlen >= XmSTRING_LENGTH)
          {
            for (tmp = nlen; tmp >= XmSTRING_LENGTH; tmp >>= 8)
                num_bytes--;
          }

          if (nlen > num_bytes)
          {
            nlen = num_bytes;
          }

          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = nlen;
          str1->components[str1->number_of_components - 1]->data =
            XtMalloc(nlen + 1);
          memcpy(str1->components[str1->number_of_components - 1]->data,
                 str2->components[i]->data,
               nlen);
          str1->components[str1->number_of_components - 1]->data[nlen] = 0;
      }

      num_bytes -= nlen;
    }

    ret = _XmStringCreateExternal(NULL, str1);

    _XmStringFree(str1);
    _XmStringFree(str2);

    return ret;
}


extern XmString
XmStringNCopy(XmString s1, int num_bytes)
{
    _XmString str1, str2;
    XmString ret;
    int i, nlen, tmp;

    if (!_XmStringIsXmString(s1))
    {
      return NULL;
    }

    str1 = __XmAllocNewXmString(0);
    str2 = _XmStringCreate(s1);

    i = 0;
    while (num_bytes && i < str2->number_of_components)
    {
      num_bytes -= ASN1_HEADER_SIZE;      /* header */
      if (num_bytes < 0)
      {
          break;
      }

      if (num_bytes == 0)
      {
          __XmGrowXmString(str1);
          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = 0;
          break;
      }

      if (str2->components[i]->type == XmSTRING_COMPONENT_SEPARATOR)
      {
          __XmGrowXmString(str1);

          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = 0;

          i++;
          continue;
      }

      if (str2->components[i]->type == XmSTRING_COMPONENT_DIRECTION)
      {
          num_bytes--;

          __XmGrowXmString(str1);

          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = 1;
          str1->components[str1->number_of_components - 1]->data =
            XtMalloc(1);
          str1->components[str1->number_of_components - 1]->data[0] =
            str2->components[i]->data[0];

          i++;
          continue;
      }

      __XmGrowXmString(str1);
      nlen = str2->components[i]->length;

      if (num_bytes < XmSTRING_LENGTH && nlen >= XmSTRING_LENGTH)
      {
          nlen = num_bytes;
          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = nlen;
          str1->components[str1->number_of_components - 1]->data =
            XtMalloc(nlen + 1);
          memcpy(str1->components[str1->number_of_components - 1]->data,
                 str2->components[i]->data,
               nlen);
          str1->components[str1->number_of_components - 1]->data[nlen] = 0;
      }
      else
      {
          if (nlen > num_bytes)
          {
            nlen = num_bytes;
          }

          if (nlen >= XmSTRING_LENGTH)
          {
            for (tmp = nlen; tmp >= XmSTRING_LENGTH; tmp >>= 8)
            {
                num_bytes--;
            }
          }

          if (nlen > num_bytes)
          {
            nlen = num_bytes;
          }

          str1->components[str1->number_of_components - 1]->type =
            str2->components[i]->type;
          str1->components[str1->number_of_components - 1]->length = nlen;
          str1->components[str1->number_of_components - 1]->data =
            XtMalloc(nlen + 1);
          memcpy(str1->components[str1->number_of_components - 1]->data,
                 str2->components[i]->data,
               nlen);
          str1->components[str1->number_of_components - 1]->data[nlen] = 0;
      }

      num_bytes -= nlen;
    }

    ret = _XmStringCreateExternal(NULL, str1);

    _XmStringFree(str1);
    _XmStringFree(str2);

    return ret;
}


extern XmString
XmStringSegmentCreate(char *text,
                  char *tag,
                  XmStringDirection direction,
                  Boolean separator)
{
    _XmString str = NULL;
    XmString ret;

    if (text && tag)
    {
      if (strcmp(tag, XmFONTLIST_DEFAULT_TAG) == 0 ||
          strcmp(tag, XmSTRING_DEFAULT_CHARSET) == 0)
      {
          str = __XmAllocNewXmString(2);

          str->components[0]->type = XmSTRING_COMPONENT_DIRECTION;
          str->components[0]->length = strlen(text);
          str->components[0]->data = XtMalloc(1);
          str->components[0]->data[0] = direction;

          str->components[1]->type = XmSTRING_COMPONENT_LOCALE_TEXT;
          str->components[1]->length = strlen(text);
          str->components[1]->data = XtNewString(text);
      }
      else
      {
          str = __XmAllocNewXmString(3);

          str->components[0]->type = XmSTRING_COMPONENT_DIRECTION;
          str->components[0]->length = strlen(text);
          str->components[0]->data = XtMalloc(1);
          str->components[0]->data[0] = direction;

          str->components[1]->type = XmSTRING_COMPONENT_CHARSET;
          str->components[1]->length = strlen(tag);
          str->components[1]->data = XtNewString(tag);

          str->components[2]->type = XmSTRING_COMPONENT_TEXT;
          str->components[2]->length = strlen(text);
          str->components[2]->data = XtNewString(text);
      }
    }
    else if (text)
    {
      str = __XmAllocNewXmString(2);

      str->components[0]->type = XmSTRING_COMPONENT_DIRECTION;
      str->components[0]->length = 0;
      str->components[0]->data = XtMalloc(1);
      str->components[0]->data[0] = direction;

      str->components[1]->type = XmSTRING_COMPONENT_LOCALE_TEXT;
      str->components[1]->length = strlen(text);
      str->components[1]->data = XtNewString(text);
    }
    else
    {
      str = __XmAllocNewXmString(1);

      str->components[0]->type = XmSTRING_COMPONENT_DIRECTION;
      str->components[0]->length = 0;
      str->components[0]->data = XtMalloc(1);
      str->components[0]->data[0] = direction;
    }

    if (separator)
    {
      __XmGrowXmString(str);
      str->components[str->number_of_components - 1]->type =
          XmSTRING_COMPONENT_SEPARATOR;
      str->components[str->number_of_components - 1]->length = 0;
      str->components[str->number_of_components - 1]->data = 0;
    }

    ret = _XmStringCreateExternal(NULL, str);
    _XmStringFree(str);

    return ret;
}


extern XmString
XmStringSeparatorCreate(void)
{
    _XmString newString = __XmAllocNewXmString(1);
    XmString ret;

    newString->components[0]->type = XmSTRING_COMPONENT_SEPARATOR;
    newString->components[0]->length = 0;
    newString->components[0]->data = NULL;

    ret = _XmStringCreateExternal(NULL, newString);
    _XmStringFree(newString);

    return ret;
}


extern XmString
XmStringDirectionCreate(XmStringDirection direction)
{
    _XmString newString = __XmAllocNewXmString(1);
    XmString ret;

    newString->components[0]->type = XmSTRING_COMPONENT_DIRECTION;
    newString->components[0]->length = 0;
    newString->components[0]->data = XtMalloc(1);
    newString->components[0]->data[0] = direction;

    ret = _XmStringCreateExternal(NULL, newString);
    _XmStringFree(newString);

    return ret;
}


extern XmString
XmStringCreate(char *text, char *tag)
{
    _XmString str;
    XmString ret;

    if (text && tag && strcmp(tag, XmFONTLIST_DEFAULT_TAG) != 0)
    {
      str = __XmAllocNewXmString(2);

      str->components[0]->type = XmSTRING_COMPONENT_CHARSET;
      str->components[0]->length = strlen(tag);
      str->components[0]->data = XtNewString(tag);

      str->components[1]->type = XmSTRING_COMPONENT_TEXT;
      str->components[1]->length = strlen(text);
      str->components[1]->data = XtNewString(text);
    }
    else if (text)
    {
      str = __XmAllocNewXmString(1);

      str->components[0]->type = XmSTRING_COMPONENT_LOCALE_TEXT;
      str->components[0]->length = strlen(text);
      str->components[0]->data = XtNewString(text);
    }
    else
    {
      return NULL;
    }

    ret = _XmStringCreateExternal(NULL, str);
    _XmStringFree(str);

    return ret;
}


extern XmString
XmStringCreateLtoR(char *text, char *tag)
{
    XmString ret;
    _XmString str;
    char *t;
    char *p, *b;

    if (text == NULL)
    {
      return NULL;
    }

    t = XtNewString(text);
    p = strstr(t, "\n");

    if (p)              /* if there was actually a return character in the string. */
    {
      Boolean at_end;

      if (*(p + 1))
      {
          at_end = False;
      }
      else
      {
          at_end = True;
      }

      *p = 0;

      ret = XmStringSegmentCreate(t, tag,
                            XmSTRING_DIRECTION_L_TO_R, True);

      str = _XmStringCreate(ret);

      XmStringFree(ret);

      while (!at_end)
      {
          Boolean need_text;
          Boolean need_sep;

          p++;
          b = p;

          if ((p = strstr(b, "\n")) == NULL)
          {
            at_end = True;
            need_sep = False;
          }
          else
          {
            *p = 0;
            need_sep = True;
            if (*(p + 1))
            {
                at_end = False;
            }
            else
            {
                at_end = True;
            }
          }

          if ((p != NULL && p != b) || strlen(b) != 0)
          {
            need_text = True;
          }
          else
          {
            need_text = False;
          }

          if (need_text)
          {

            __XmGrowXmString(str);

            if (strcmp(tag, XmFONTLIST_DEFAULT_TAG) == 0)
            {
                str->components[str->number_of_components - 1]->type =
                  XmSTRING_COMPONENT_LOCALE_TEXT;
            }
            else
            {
                str->components[str->number_of_components - 1]->type =
                  XmSTRING_COMPONENT_TEXT;
            }
            str->components[str->number_of_components - 1]->length =
                strlen(b);
            str->components[str->number_of_components - 1]->data = XtNewString(b);
          }

          if (need_sep)
          {
            __XmGrowXmString(str);

            str->components[str->number_of_components - 1]->type =
                XmSTRING_COMPONENT_SEPARATOR;
            str->components[str->number_of_components - 1]->length = 0;
            str->components[str->number_of_components - 1]->data = NULL;
          }
      }

      ret = _XmStringCreateExternal(NULL, str);
      _XmStringFree(str);
    }
    else
    {
      ret = XmStringSegmentCreate(text, tag,
                            XmSTRING_DIRECTION_L_TO_R, False);
    }

    XtFree(t);

    return ret;
}


extern XmString
XmStringLtoRCreate(char *text, char *tag)
{
    return XmStringCreateLtoR(text, tag);
}


extern XmString
XmStringCreateLocalized(char *text)
{
      DEBUGOUT(_LtDebug(__FILE__, NULL, "XmStringCreateLocalized(%s)\n", text));
      return XmStringCreate(text, XmFONTLIST_DEFAULT_TAG);
}


extern XmString
XmStringCreateSimple(char *text)
{
    _XmString str;
    XmString ret;

    if (!text)
    {
      return NULL;
    }

    str = __XmAllocNewXmString(2);

    str->components[0]->type = XmSTRING_COMPONENT_CHARSET;
    str->components[0]->length = strlen(XmFONTLIST_DEFAULT_TAG);
    str->components[0]->data = XtNewString(XmFONTLIST_DEFAULT_TAG);

    str->components[1]->type = XmSTRING_COMPONENT_TEXT;
    str->components[1]->length = strlen(text);
    str->components[1]->data = XtNewString(text);

    ret = _XmStringCreateExternal(NULL, str);

    _XmStringFree(str);

    return ret;
}

/*
 * The Motif 2.1 documentation says that the GC can be left in an
 * "undefined" state when the XmString contains a font.
 */
extern void
XmStringDraw(Display *d,
           Window w,
           XmFontList fontlist,
           XmString string,
           GC gc,
           Position x,
           Position y,
           Dimension width,
           unsigned char alignment,
           unsigned char layout_direction,
           XRectangle *clip)
{
    _XmString str;

    if (!_XmStringIsXmString(string))
    {
      return;
    }

    str = _XmStringCreate(string);

    _XmStringDraw(d, w, fontlist, str, gc, x, y, width,
              alignment, layout_direction, clip);

    _XmStringFree(str);
}


extern void
XmStringDrawImage(Display *d,
              Window w,
              XmFontList fontlist,
              XmString string,
              GC gc,
              Position x,
              Position y,
              Dimension width,
              unsigned char alignment,
              unsigned char layout_direction,
              XRectangle *clip)
{
    _XmString str;

    if (!_XmStringIsXmString(string))
    {
      return;
    }

    str = _XmStringCreate(string);

    _XmStringDrawImage(d, w, fontlist, str, gc, x, y, width,
                   alignment, layout_direction, clip);

    _XmStringFree(str);
}


/*
 * Similar to XmStringDraw, but with an additional parameter
 *
 * If the underline string is contained in string, then that part
 * of string is underlined.
 */
extern void
XmStringDrawUnderline(Display *d,
                  Window w,
                  XmFontList fontlist,
                  XmString string,
                  GC gc,
                  Position x,
                  Position y,
                  Dimension width,
                  unsigned char alignment,
                  unsigned char layout_direction,
                  XRectangle *clip,
                  XmString underline)
{
    _XmString str, und;

    if (!_XmStringIsXmString(string))
    {
      return;
    }


    str = _XmStringCreate(string);

    if (underline != NULL)
    {

      und = _XmStringCreate(underline);

      _XmStringDrawUnderline(d, w, fontlist, str, gc, x, y, width,
                         alignment, layout_direction, clip, und);

      /*  _XmStringFree(und); FIX ME Why is this commented? MLM */
    }
    else
    {
      _XmStringDraw(d, w, fontlist, str, gc, x, y, width,
                  alignment, layout_direction, clip);
    }
    _XmStringFree(str);
}


extern Boolean
XmStringEmpty(XmString s1)
{
    _XmString str;
    Boolean ret;

    if (!_XmStringIsXmString(s1))
    {
      return True;
    }

    str = _XmStringCreate(s1);

    ret = _XmStringEmpty(str);

    _XmStringFree(str);

    return ret;
}


extern void
XmStringExtent(XmFontList fontlist,
             XmString string,
             Dimension *width,
             Dimension *height)
{
    _XmString str;

    *width = *height = 0;

    if (!_XmStringIsXmString(string))
    {
      return;
    }

    str = _XmStringCreate(string);

    _XmStringUpdate(fontlist, str);
    _XmStringExtent(fontlist, str, width, height);
    _XmStringFree(str);

    if (!string)
    {
      DEBUGOUT(_LtDebug(__FILE__, NULL,
                    "XmStringExtent() string is NULL\n"));
    }

}


/*
 * according to Motif, this does only return the first segment in a
 * multi-segment string
 */
extern Boolean
XmStringGetLtoR(XmString string,
            XmStringCharSet tag,
            char **text)
{
    Boolean Found = False;
    XmStringContext context = NULL;

    *text = NULL;
    if (!_XmStringIsXmString(string))
    {
      return False;
    }

    XmStringInitContext(&context, string);

    while (XmStringGetNextSegment(context, NULL, NULL, NULL, NULL))
    {
      if (context->text && context->charset && tag &&
          strcmp(context->charset, tag) == 0)
      {
          *text = XtNewString(context->text);
          Found = True;
          break;
      }

      /*
       * Provide some kind of compatibility between 1.1's
       * XmSTRING_DEFAULT_CHARSET and 1.2's XmFONTLIST_DEFAULT_TAG
       */
      if (context->charset &&
          strcmp(context->charset, XmFONTLIST_DEFAULT_TAG) == 0 &&
          strcmp(tag, XmSTRING_DEFAULT_CHARSET) == 0)
      {
          *text = XtNewString(context->text);
          Found = True;
          break;
      }

      if (context->charset &&
          strcmp(tag, XmFONTLIST_DEFAULT_TAG) == 0 &&
          strcmp(context->charset, XmSTRING_DEFAULT_CHARSET) == 0)
      {
          *text = XtNewString(context->text);
          Found = True;
          break;
      }
    }

    XmStringFreeContext(context);
    return Found;
}


extern void
XmStringFree(XmString string)
{
    if (!_XmStringIsSpecified(string))
    {
      return;
    }

    if (!_XmStringIsXmString(string))
    {
      return;
    }

    XtFree((char *)string);
}


extern Boolean
XmStringInitContext(XmStringContext *context,
                XmString string)
{
    if (!_XmStringIsXmString(string))
    {
      return False;
    }

    *context = (XmStringContext)XtCalloc(1, sizeof(struct _XmtStringContextRec));
    (*context)->string = _XmStringCreate(string);
    (*context)->current_component = -1;

    return True;
}


extern XmStringComponentType
XmStringGetNextComponent(XmStringContext context,
                   char **text,
                   XmStringCharSet *tag,
                   XmStringDirection *direction,
                   XmStringComponentType *unknown_tag,
                   unsigned short *unknown_length,
                   unsigned char **unknown_value)
{
    _XmStringComponent r;

    if (context == NULL)
    {
      return XmSTRING_COMPONENT_UNKNOWN;
    }

    context->current_component++;

    if (context->current_component < context->string->number_of_components)
    {
      r = context->string->components[context->current_component];

      switch (r->type)
      {
      case XmSTRING_COMPONENT_CHARSET:
          if (r->data && tag)
          {
            *tag = XtNewString(r->data);
          }
          break;

      case XmSTRING_COMPONENT_TEXT:
      case XmSTRING_COMPONENT_LOCALE_TEXT:
          if (r->data && text)
          {
            *text = XtNewString(r->data);
          }
          break;

      case XmSTRING_COMPONENT_DIRECTION:
          if (direction)
            *direction = r->data[0];
          break;

      case XmSTRING_COMPONENT_UNKNOWN:
      case XmSTRING_COMPONENT_END:
      case XmSTRING_COMPONENT_USER_BEGIN:
      case XmSTRING_COMPONENT_USER_END:
          if (unknown_tag)
            *unknown_tag = r->type;
          if (unknown_length)
            *unknown_length = r->length;
          if (unknown_value) {
            *unknown_value = (unsigned char *)XtMalloc(r->length);
            memcpy(*unknown_value, r->data, r->length);
          }
          break;

#if (XmVERSION > 1)
      case XmSTRING_COMPONENT_RENDITION_BEGIN:
      case XmSTRING_COMPONENT_RENDITION_END:
          if (r->data && tag) {
            *tag = XtNewString(r->data);
          }
          break;
      case XmSTRING_COMPONENT_LOCALE:
      case XmSTRING_COMPONENT_WIDECHAR_TEXT:
      case XmSTRING_COMPONENT_LAYOUT_PUSH:
      case XmSTRING_COMPONENT_LAYOUT_POP:
      case XmSTRING_COMPONENT_TAB:
            break;

#endif
      default:
          _XmWarning(NULL,
                   "XmStringGetNextComponent: unknown type %d\n", r);
      }
      return r->type;
    }
    else
    {
      return XmSTRING_COMPONENT_END;
    }
}


extern XmStringComponentType
XmStringPeekNextComponent(XmStringContext context)
{
    if (context == NULL)
    {
      return XmSTRING_COMPONENT_UNKNOWN;
    }

    if (context->current_component < (context->string->number_of_components - 1))
    {
      return context->string->components[context->current_component + 1]->type;
    }
    else
    {
      return XmSTRING_COMPONENT_END;
    }
}


/*
 * Assumption: if you change charsets or directions before a separator,
 * then that is another segment.  I don't know if this is right, but it
 * makes sense? MLM
 */
extern Boolean
XmStringGetNextSegment(XmStringContext context,
                   char **text,
                   XmStringCharSet *tag,
                   XmStringDirection *direction,
                   Boolean *separator)
{
    Boolean ret;

    ret = _XmStringGetNextSegment((_XmStringContext)context,
                          &context->charset, &context->direction,
                          &context->text, &context->textlen,
                          &context->separator);

    if (!ret)
    {
      return False;
    }

    if (text)
    {
      *text = XtNewString(context->text);
    }

    if (tag)
    {
      *tag = XtNewString(context->charset);
    }

    if (direction)
    {
      *direction = context->direction;
    }

    if (separator)
    {
      *separator = context->separator;
    }

    return True;
}


extern void
XmStringFreeContext(XmStringContext context)
{
    _XmStringFree(context->string);

    XtFree((char *)context);
}


extern Boolean
XmStringHasSubstring(XmString string,
                 XmString substring)
{
    _XmString str, substr;
    Boolean ret;

    if (!_XmStringIsXmString(string) || !_XmStringIsXmString(substring))
    {
      return False;
    }

    str = _XmStringCreate(string);
    substr = _XmStringCreate(substring);

    ret = _XmStringHasSubstring(str, substr);

    _XmStringFree(str);
    _XmStringFree(substr);

    return ret;
}


extern Dimension
XmStringWidth(XmFontList fontlist,
            XmString string)
{
    Dimension width, height;

    XmStringExtent(fontlist, string, &width, &height);

    return width;
}


extern Dimension
XmStringHeight(XmFontList fontlist, XmString string)
{
    Dimension width, height;

    XmStringExtent(fontlist, string, &width, &height);

    return height;
}


extern int
XmStringLength(XmString s1)
{
    unsigned length, i;
    struct __XmStringExtRec *str = (struct __XmStringExtRec *)s1;
    unsigned char *next;

    if (!_XmStringIsXmString(s1))
    {
      return 0;
    }

    /* skip header */
    next = str->data;
    str = (struct __XmStringExtRec *)next;
    length = 0;

    if (str->len > XmSTRING_LENGTH)
    {

      for (i = 0; i < (str->len & ~XmSTRING_LENGTH); i++)
      {
          length <<= 8;
          length |= str->data[i];
          if (i > sizeof(unsigned))
          {
            return 0;
          }
      }
    }
    else
    {
      length = str->len & ~XmSTRING_LENGTH;
    }

    return length + XmSTRING_HEADER_SIZE;
}


extern int
XmStringLineCount(XmString string)
{
    _XmString str;
    int lc;

    if (!_XmStringIsXmString(string))
    {
      return 0;
    }

    str = _XmStringCreate(string);

    lc = _XmStringLineCount(str);

    _XmStringFree(str);

    return lc;
}


/*
 * This sucker appears to be undocumented.
 * Literal search in OM docs brings up a thread-safe variant
 * only 'XmStringCreateFontList_r' !?!
 */
extern XmFontList
XmStringCreateFontList( XFontStruct *font, XmStringCharSet charset)
{
      return XmFontListCreate(font, charset);
}


#if XmVERSION >= 2

extern XmString
XmStringConcatAndFree(XmString str1, XmString str2)
{
  XmString tmp;
  tmp = XmStringConcat(str1, str2);
  XmStringFree(str1);
  XmStringFree(str2);
  return tmp;
}


/*
 * MxFTP uses this as
 *    item = (char *)XmStringUnparse(tempstr,NULL,XmCHARSET_TEXT,
 *          XmCHARSET_TEXT,NULL,0,XmOUTPUT_ALL);
 */
extern XtPointer
XmStringUnparse(XmString string, XmStringTag tag, XmTextType tag_type,
      XmTextType output_type, XmParseTable parse_table,
      Cardinal parse_count, XmParseModel parse_model)
{
    _XmStringContext    context = NULL;
    _XmStringComponent  comp = NULL;
    _XmString           _string;
    Boolean       have_seg, have_line_height;
    Dimension           default_line_height, default_line_ascent;
    int                 pending_newlines;
    char          *res = NULL;

    if (tag != NULL) {
      _XmWarning(NULL, "XmStringUnparse currently only handles tag == NULL case");
    }

    _string = _XmStringCreate(string);
    _XmStringInitContext(&context, _string);

    pending_newlines = 0;
    have_line_height = False;
    default_line_height = 0;
    default_line_ascent = 0;

    DEBUGOUT(_LtDebug(__FILE__, NULL,
      "XmStringUnparse[internal] _string %p context %p\n",
      _string, context));

    while (__XmStringPeekNextComponent(context) != NULL)
    {
      Dimension line_height, line_ascent, line_descent;
      int start;

      start = context->current_component;

      line_height = 0;
      line_ascent = line_descent = 0;
      have_seg = False;

      while ((comp = __XmStringGetNextComponent(context)) != NULL)
      {
          if ((comp->type == XmSTRING_COMPONENT_TEXT ||
             comp->type == XmSTRING_COMPONENT_LOCALE_TEXT) &&
            comp->font != XmUNSPECIFIED)
          {
            DEBUGOUT(_LtDebug(__FILE__, NULL,
                  "XmStringUnparse[internal] len %d '%s' font %d\n",
                  comp->length, comp->data ? comp->data : "(null)",
                  comp->font));
            if (comp->data) {
                  if (res) {
                        res = (char *)realloc(res, strlen(res) + 1 + strlen(comp->data));
                        strcat(res, comp->data);
                  } else {
                        res = (char *)malloc(strlen(comp->data)+1);
                        strcpy(res, comp->data);
                  }
            }
                  
          }
          else if (comp->type == XmSTRING_COMPONENT_SEPARATOR)
          {
          }
      }

    }
    _XmStringFreeContext(context);
    _XmStringFree(_string);

    return res;
}


extern XmString
XmStringParseText(XtPointer text,
      XtPointer *text_end,
      XmStringTag tag,
      XmTextType type,
      XmParseTable parse_table,
      Cardinal parse_count,
      XtPointer call_data)
{
      char  *the_end, *p,
            *txt = (char *)text;

      DEBUGOUT(_LtDebug(__FILE__, NULL, "XmStringParseText(%s, %s)\n",
            txt, tag));

      if (text_end == NULL) {
            /* Find the end of the string
             * so we have only one check further along */
            for (p=txt; *p; p++) ;
            the_end = p;
      } else
            the_end = *(char **)text_end;

      /* FIX ME simplistic implementation */
      return XmStringCreate(txt, tag);
}


extern Cardinal
XmStringToXmStringTable(XmString string, XmString break_comp, XmStringTable *table)
{
      
      _XmWarning(NULL, "XmStringToXmStringTable() is not implemented yet!\n");
      return (Cardinal)0;
}


extern XmString
XmStringTableToXmString(XmStringTable table, Cardinal count, XmString break_component)
{
      _XmWarning(NULL, "XmStringTableToXmString() is not implemented yet!\n");
      return (XmString)NULL;
}


extern XtPointer *
XmStringTableUnparse(XmStringTable table, Cardinal count, XmStringTag tag,
      XmTextType tag_type, XmTextType output_type, XmParseTable parse,
      Cardinal parse_count, XmParseModel parse_model)
{
      _XmWarning(NULL, "XmStringTableUnparse() is not implemented yet!\n");
      return (XtPointer *)NULL;
}


extern XmStringTable
XmStringTableParseStringArray(XtPointer *strings, Cardinal count,
      XmStringTag tag, XmTextType type, XmParseTable parse,
      Cardinal parse_count, XtPointer call_data)
{
      _XmWarning(NULL, "XmStringTableParseStringArray() is not implemented yet!\n");
      return (XmStringTable)NULL;
}


extern XmString
XmStringPutRendition(XmString string, XmStringTag rendition)
{
      _XmWarning(NULL, "XmStringPutRendition() is not implemented yet!\n");
      return (XmString)NULL;
}


extern XmParseMapping
XmParseMappingCreate(ArgList  arg_list, Cardinal arg_count)
{
      _XmWarning(NULL, "XmParseMappingCreate() is not implemented yet!\n");
      return (XmParseMapping)NULL;
}


extern void
XmParseMappingSetValues(XmParseMapping parse_mapping, ArgList arg_list, Cardinal arg_count)
{
      _XmWarning(NULL, "XmParseMappingSetValues() is not implemented yet!\n");
}


extern void
XmParseMappingGetValues(XmParseMapping parse_mapping, ArgList arg_list, Cardinal arg_count)
{
      _XmWarning(NULL, "XmParseMappingGetValues() is not implemented yet!\n");
}


extern void
XmParseMappingFree(XmParseMapping parse_mapping)
{
      _XmWarning(NULL, "XmParseMappingFree() is not implemented yet!\n");
}


extern void
XmParseTableFree(XmParseTable parse_table, Cardinal parse_count)
{
      _XmWarning(NULL, "XmParseTableFree() is not implemented yet!\n");
}


extern XmStringComponentType
XmStringGetNextTriple(XmStringContext context,
           unsigned int *length,
         XtPointer *value)
{
      _XmWarning(NULL, "XmStringGetNextTriple() is not implemented yet!\n");
      return (XmStringComponentType)NULL;
}


extern XmString
XmStringComponentCreate(XmStringComponentType ctype,
                  unsigned int length,
                  XtPointer value)
{
      XmString    r;
      _XmString   str = NULL;

      switch (ctype) {
      case XmSTRING_COMPONENT_SEPARATOR:
            return XmStringSeparatorCreate();
      case XmSTRING_COMPONENT_TAB:
            str = __XmAllocNewXmString(1);

            str->components[0]->type = ctype;
            str->components[0]->length = 0;
            str->components[0]->data = NULL;
            r = _XmStringCreateExternal(NULL, str);
            _XmStringFree(str);

            return r;
      case XmSTRING_COMPONENT_END:
            /* These all have value NULL and length 0 */
            break;
      case XmSTRING_COMPONENT_LAYOUT_POP:
            /* These all have value NULL and length 0 */
            break;
      case XmSTRING_COMPONENT_DIRECTION:
            str = __XmAllocNewXmString(1);
            str->components[0]->type = XmSTRING_COMPONENT_DIRECTION;
            str->components[0]->length = 0;
            str->components[0]->data = XtMalloc(sizeof(int));
            str->components[0]->data[0] = (int)value;
            r = _XmStringCreateExternal(NULL, str);
            _XmStringFree(str);
            return r;
      case XmSTRING_COMPONENT_RENDITION_BEGIN:
      case XmSTRING_COMPONENT_RENDITION_END:
            str = __XmAllocNewXmString(1);
            str->components[0]->type = ctype;
            str->components[0]->length = length;
            str->components[0]->data = XtMalloc(length);
            memcpy(str->components[0]->data, value, length);
            r = _XmStringCreateExternal(NULL, str);
            _XmStringFree(str);
            return r;
      case XmSTRING_COMPONENT_LAYOUT_PUSH:
      case XmSTRING_COMPONENT_LOCALE_TEXT:
      case XmSTRING_COMPONENT_WIDECHAR_TEXT:
      case XmSTRING_COMPONENT_TEXT:
      case XmSTRING_COMPONENT_UNKNOWN:
      case XmSTRING_COMPONENT_LOCALE:
      case XmSTRING_COMPONENT_TAG:
#if 0
      case XmSTRING_COMPONENT_CHARSET:
      case XmSTRING_COMPONENT_FONTLIST_ELEMENT_TAG:
#endif
            break;
      default:
            _XmWarning(NULL, "XmStringComponentCreate() is not implemented yet!\n");
            }
      return (XmString)NULL;
}


extern XmIncludeStatus
XmeGetNextCharacter(XtPointer *text_in_out,
                    XtPointer text_end,
                    XmTextType type,
                    XmStringTag tag,
                    XmParseMapping entry,
                    int pattern_length,
                    XmString *str_include,
                    XtPointer call_data)
{
       _XmWarning(NULL, "XmeGetNextCharacter() is not implemented yet!\n");
       return XmINSERT; /* this is not sufficient to indicate a failure ... */
}


extern XmIncludeStatus
XmeGetDirection(XtPointer *text_in_out,
                XtPointer text_end,
                XmTextType type,
                XmStringTag tag,
                XmParseMapping entry,
                int pattern_length,
                XmString *str_include,
                XtPointer call_data)
{
       _XmWarning(NULL, "XmeGetDirection() is not implemented yet!\n");
       return XmINSERT; /* this is not sufficient to indicate a failure ... */
}


extern XmStringComponentType
XmStringPeekNextTriple(XmStringContext context)
{
       _XmWarning(NULL, "XmStringPeekNextTriple() is not implemented yet!\n");
       return XmSTRING_COMPONENT_UNKNOWN;
}


/* similar structure to XmStringEmpty() */
extern Boolean
XmStringIsVoid(XmString s1)
{
    _XmString str;
    Boolean ret;

    if (!_XmStringIsXmString(s1))
    {
      return True;
    }

    str = _XmStringCreate(s1);

    ret = _XmStringIsVoid(str);

    _XmStringFree(str);

    return ret;
}

#endif /* #if XmVERSION >= 2 */

Generated by  Doxygen 1.6.0   Back to index