/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include <asnstrng.h>
#include <string.h>
#include <chardefs.h>
#define DEFINE_CODESET_TABLES
#include <codesets.h>

// CHECK_VALUE_PRESENT verifies that the object contains a value - that is, it 
// either contains an explicit value, or has a default value.
#define CHECK_VALUE_PRESENT \
  if (!is_present() && !is_defaultable()) return ASN_VALUE_NOT_SET

// CHECK_VALUE_VALID verifies that the object has an explicit value.
#define CHECK_VALUE_VALID \
  if (!is_present()) return ASN_VALUE_NOT_SET


bool asn_directoryString::check_type(uint32 tagRead, int classRead) const {
  if (classRead != asn_class) return false;
  if (tagRead == PRINTABLE_STRING_TAG) return true;
  if (tagRead == TELETEX_STRING_TAG) return true; // teletexString / T.61 string
  if (tagRead == BMP_STRING_TAG) return true; 
  if (tagRead == UNIVERSAL_STRING_TAG) return true; 
  if (tagRead == UTF8_STRING_TAG) return true; 
  if (tagRead == IA5_STRING_TAG) return true; 
// IA5 isn't really permitted in a directoryString.  However, there are enough 
// certs out there that use IA5String within an X.500 name that we accept
// it on input (although we should never generate it ourselves).
  return false;
}

int asn_directoryString::normalize(void) {
  CHECK_VALUE_PRESENT;
  if (convert2printable() == 0) return 0;
  if (convert2T61() == 0) return 0;
  if (convert2BMP() == 0) return 0;
  return convert2Univ();
}

int asn_charstring::set_value_uninterpreted(unsigned char * s, uint32 len) {
  int result;
  invalidate_value();
  str_val.clear();
  if ((result = str_val.append(s, len)) != 0) return result;
  set_value_valid();
  return 0;
}

int asn_charstring::set_value_uninterpreted(r_buffer_t & value) {
  int result;
  invalidate_value();
  str_val.clear();
  if ((result = str_val.append(value)) != 0) return result;
  set_value_valid();
  return 0;
}

void asn_charstring::set_tag(uint32 tag) {
  set_codeset(tag);
}

int asn_charstring::set_codeset(uint32 tag) {
  if (!check_codeset(tag)) return ASN_CODESET_NOT_PERMITTED;
  invalidate_encoding();
  codeset_tag = tag;
  asn_octetstring::set_tag(tag);
  return 0;
}

uint32 asn_charstring::get_codeset(void) const {
  return codeset_tag;
}


int U2UTF8(r_buffer_t src, buffer_t & dest) {
  uint32 ch;
  unsigned i;
  for (i=0;i<src.data_len;i+=4) {
    ch = (((((src[i] << 8) + src[i+1]) << 8) + src[i+2]) << 8) + src[i+3];
    if (ch <= 0x7flu) {
      dest.append((unsigned char)ch);
    } else if (ch <= 0x7fflu) {
      dest.append( (unsigned char) ( ( (ch & ~0x3flu) >> 6 ) + 0xc0lu ) );
      dest.append( (unsigned char)   ( (ch & 0x3flu) + 0x80lu) );
    } else if (ch <= 0xfffflu) {
      dest.append((unsigned char)(((ch & 0xf000lu) >> 12) + 0xe0lu));
      dest.append((unsigned char)(((ch & 0x0fc0lu) >> 6) + 0x80lu));
      dest.append((unsigned char)(( ch & 0x003flu) + 0x80lu));
    } else if (ch <= 0x1ffffflu) {
      dest.append((unsigned char)(((ch & 0xfc000lu) >> 18) + 0xf0lu));
      dest.append((unsigned char)(((ch & 0x03f000lu) >> 12) + 0x80lu));
      dest.append((unsigned char)(((ch & 0x000fc0lu) >> 6) + 0x80lu));
      dest.append((unsigned char)(( ch & 0x00003flu) + 0x80lu));
    } else if (ch <= 0x3fffffflu) {
      dest.append((unsigned char)(((ch & 0x3f000000lu) >> 24) + 0xf8lu));
      dest.append((unsigned char)(((ch & 0x00fc0000lu) >> 18) + 0x80lu));
      dest.append((unsigned char)(((ch & 0x0003f000lu) >> 12) + 0x80lu));
      dest.append((unsigned char)(((ch & 0x00000fc0lu) >> 6) + 0x80lu));
      dest.append((unsigned char)(( ch & 0x0000003flu) + 0x80lu));
    } else if (ch <= 0x7ffffffflu) {
      dest.append((unsigned char)(((ch & 0xc0000000lu) >> 30) + 0xfclu));
      dest.append((unsigned char)(((ch & 0x3f000000lu) >> 24) + 0x80lu));
      dest.append((unsigned char)(((ch & 0x00fc0000lu) >> 18) + 0x80lu));
      dest.append((unsigned char)(((ch & 0x0003f000lu) >> 12) + 0x80lu));
      dest.append((unsigned char)(((ch & 0x00000fc0lu) >> 6) + 0x80lu));
      dest.append((unsigned char)(( ch & 0x0000003flu) + 0x80lu));
    };
  };
  return 0;
}

int BMP2UTF8(r_buffer_t src, buffer_t & dest) {
  uint32 ch;
  unsigned i;
  for (i=0;i<src.data_len;i+=2) {
    ch = (src[i] << 8) + src[i+1];
    if (ch <= 0x7flu) {
      dest.append((unsigned char)ch);
    } else if (ch <= 0x7fflu) {
      dest.append((unsigned char)(((ch & ~0x3flu) >> 6) + 0xc0lu));
      dest.append((unsigned char)((ch & 0x3flu) + 0x80lu));
    } else {
      dest.append((unsigned char)(((ch & 0xf000lu) >> 12) + 0xe0lu));
      dest.append((unsigned char)(((ch & 0x0fc0lu) >> 6) + 0x80lu));
      dest.append((unsigned char)(( ch & 0x003flu) + 0x80lu));
    };
  };
  return 0;
}

int IA52UTF8(r_buffer_t src, buffer_t & dest) {
  uint32 ch;
  unsigned i;
  for (i=0;i<src.data_len;i++) {
    ch = src[i];
    if (ch <= 0x7flu) {
      dest.append((unsigned char)ch);
    } else {
      dest.append((unsigned char)(((ch & ~0x3flu) >> 6) + 0xc0lu));
      dest.append((unsigned char)((ch & 0x3flu) + 0x80lu));
    };
  };
  return 0;
}

int UTF82U(r_buffer_t src, buffer_t & dest) {
  unsigned i, j;
  uint32 ch;
  for (i=0,j=0;i<src.data_len;i++) {
    if (src[i] <= 0x7ful) {
      ch = src[i];
    } else if (src[i] < 0xc0ul) {
      return ASN_CANT_CONVERT; // Error in UTF8 encoding
    } else if (src[i] <= 0xdf) {
      ch = ((uint32)(src[i]) - 0xc0lu) << 6 
           + ((uint32)(src[i+1]) - 0x80lu);
      i+=1;
    } else if (src[i] <= 0xeful) {
      ch = ((uint32)(src[i]) - 0xe0lu) << 12 
           + ((uint32)(src[i+1]) - 0x80lu) << 6
           + ((uint32)(src[i+2]) - 0x80lu);
      i+=2;
    } else if (src[i] <= 0xf7ul) {
      ch = ((uint32)(src[i]) - 0xf0lu) << 18 
           + ((uint32)(src[i+1]) - 0x80lu) << 12
           + ((uint32)(src[i+2]) - 0x80lu) << 6;
           + ((uint32)(src[i+3]) - 0x80lu);
      i+=3;
    } else if (src[i] <= 0xfbul) {
      ch = ((uint32)(src[i]) - 0xf8lu) << 24 
           + ((uint32)(src[i+1]) - 0x80lu) << 18
           + ((uint32)(src[i+2]) - 0x80lu) << 12
           + ((uint32)(src[i+3]) - 0x80lu) << 6;
           + ((uint32)(src[i+4]) - 0x80lu);
      i+=4;
    } else if (src[i] <= 0xfdul) {
      ch = ((uint32)(src[i]) - 0xfclu) << 30 
           + ((uint32)(src[i+1]) - 0x80lu) << 24
           + ((uint32)(src[i+2]) - 0x80lu) << 18
           + ((uint32)(src[i+3]) - 0x80lu) << 12
           + ((uint32)(src[i+4]) - 0x80lu) << 6;
           + ((uint32)(src[i+5]) - 0x80lu);
      i+=5;
    } else return ASN_CANT_CONVERT;
    dest.append((unsigned char )((ch & 0xff000000ul) >> 24));
    dest.append((unsigned char )((ch & 0x00ff0000ul) >> 16));
    dest.append((unsigned char )((ch & 0x0000ff00ul) >> 8));
    dest.append((unsigned char )(ch & 0x000000fful));
  };
  return 0;
}

int UTF82BMP(r_buffer_t src, buffer_t & dest) {
  unsigned i,j;
  uint32 ch;
  for (i=0,j=0;i<src.data_len;i++) {
    if (src[i] <= 0x7ful) {
      ch = src[i];
    } else if (src[i] < 0xc0ul) {
      return ASN_CANT_CONVERT; // Error in UTF8 encoding
    } else if (src[i] <= 0xdf) {
      ch = ((uint32)(src[i]) - 0xc0lu) << 6 
           + ((uint32)(src[i+1]) - 0x80lu);
      i+=1;
    } else if (src[i] <= 0xeful) {
      ch = ((uint32)(src[i]) - 0xe0lu) << 12 
           + ((uint32)(src[i+1]) - 0x80lu) << 6
           + ((uint32)(src[i+2]) - 0x80lu);
      i+=2;
    } else if (src[i] <= 0xf7ul) {
      ch = ((uint32)(src[i]) - 0xf0lu) << 18 
           + ((uint32)(src[i+1]) - 0x80lu) << 12
           + ((uint32)(src[i+2]) - 0x80lu) << 6;
           + ((uint32)(src[i+3]) - 0x80lu);
      i+=3;
    } else if (src[i] <= 0xfbul) {
      ch = ((uint32)(src[i]) - 0xf8lu) << 24 
           + ((uint32)(src[i+1]) - 0x80lu) << 18
           + ((uint32)(src[i+2]) - 0x80lu) << 12
           + ((uint32)(src[i+3]) - 0x80lu) << 6;
           + ((uint32)(src[i+4]) - 0x80lu);
      i+=4;
    } else if (src[i] <= 0xfdul) {
      ch = ((uint32)(src[i]) - 0xfclu) << 30 
           + ((uint32)(src[i+1]) - 0x80lu) << 24
           + ((uint32)(src[i+2]) - 0x80lu) << 18
           + ((uint32)(src[i+3]) - 0x80lu) << 12
           + ((uint32)(src[i+4]) - 0x80lu) << 6;
           + ((uint32)(src[i+5]) - 0x80lu);
      i+=5;
    } else return ASN_CANT_CONVERT;
    if (ch > 0xfffflu) return ASN_CANT_CONVERT;
    dest.append((unsigned char )((ch & 0x0000ff00ul) >> 8));
    dest.append((unsigned char )(ch & 0x000000fful));
  };
  return 0;
}

int UTF82IA5(r_buffer_t src, buffer_t & dest) {
  unsigned i,j;
  uint32 ch;
  for (i=0,j=0;i<src.data_len;i++) {
    if (src[i] <= 0x7ful) {
      ch = src[i];
    } else if (src[i] < 0xc0ul) {
      return ASN_CANT_CONVERT; // Error in UTF8 encoding
    } else if (src[i] <= 0xdf) {
      ch = ((uint32)(src[i]) - 0xc0lu) << 6 
           + ((uint32)(src[i+1]) - 0x80lu);
      i+=1;
    } else if (src[i] <= 0xeful) {
      ch = ((uint32)(src[i]) - 0xe0lu) << 12 
           + ((uint32)(src[i+1]) - 0x80lu) << 6
           + ((uint32)(src[i+2]) - 0x80lu);
      i+=2;
    } else if (src[i] <= 0xf7ul) {
      ch = ((uint32)(src[i]) - 0xf0lu) << 18 
           + ((uint32)(src[i+1]) - 0x80lu) << 12
           + ((uint32)(src[i+2]) - 0x80lu) << 6;
           + ((uint32)(src[i+3]) - 0x80lu);
      i+=3;
    } else if (src[i] <= 0xfbul) {
      ch = ((uint32)(src[i]) - 0xf8lu) << 24 
           + ((uint32)(src[i+1]) - 0x80lu) << 18
           + ((uint32)(src[i+2]) - 0x80lu) << 12
           + ((uint32)(src[i+3]) - 0x80lu) << 6;
           + ((uint32)(src[i+4]) - 0x80lu);
      i+=4;
    } else if (src[i] <= 0xfdul) {
      ch = ((uint32)(src[i]) - 0xfclu) << 30 
           + ((uint32)(src[i+1]) - 0x80lu) << 24
           + ((uint32)(src[i+2]) - 0x80lu) << 18
           + ((uint32)(src[i+3]) - 0x80lu) << 12
           + ((uint32)(src[i+4]) - 0x80lu) << 6;
           + ((uint32)(src[i+5]) - 0x80lu);
      i+=5;
    } else return ASN_CANT_CONVERT;
    if (ch > 0xfflu) return ASN_CANT_CONVERT;
    dest.append((unsigned char )(ch & 0x000000fful));
  };
  return 0;
}



int U2BMP(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;
  uint32 saved_l = dest.data_len;

    for (i=0; i<src.data_len; i+=4) {
    if (src.data[i] != 0) break;
    if (src.data[i+1] != 0) break;
    dest.append(src.data[i+2]);
    dest.append(src.data[i+3]);
  };
  if (i >= src.data_len) return 0;
  dest.data_len = saved_l;
  return ASN_CANT_CONVERT;
}

int U2IA5(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;
  uint32 saved_l = dest.data_len;

  for (i=0; i<src.data_len; i+=4) {
    if (src.data[i] != 0) break;
    if (src.data[i+1] != 0) break;
    if (src.data[i+2] != 0) break;
    dest.append(src.data[i+3]);
  };
  if (i >= src.data_len) return 0;
  dest.data_len = saved_l;
  return ASN_CANT_CONVERT;
}

int BMP2U(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;

  for (i=0; i<src.data_len; i+=2) {
    dest.append((unsigned char)0);
    dest.append((unsigned char)0);
    dest.append(src.data[i]);
    dest.append(src.data[i+1]);
  };
  return 0;
}

int IA52BMP(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;

  for (i=0; i<src.data_len; i++) {
    dest.append((unsigned char)0);
    dest.append(src.data[i]);
  };
  return 0;
}

int IA52U(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;

  for (i=0; i<src.data_len; i++) {
    dest.append((unsigned char)0);
    dest.append((unsigned char)0);
    dest.append((unsigned char)0);
    dest.append(src.data[i]);
  };
  return 0;
}

int BMP2IA5(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;
  uint32 saved_l = dest.data_len;

  for (i=0; i<src.data_len; i+=2) {
    if (src.data[i] != 0) break;
    dest.append(src.data[i+1]);
  };
  if (i >= src.data_len) return 0;
  dest.data_len = saved_l;
  return ASN_CANT_CONVERT;
}


#define GRAVE 1
#define ACUTE 2
#define CIRCUMFLEX 3
#define TILDE 4
#define MACRON 5
#define BREVE 6
#define DOT 7
#define DIAERESIS 8
#define CIRCLE 10
#define CEDILLA 11
#define UNDERLINE 12
#define DOUBLE_ACUTE 13
#define OGONEK 14
#define CARON 15

int T612IA5(const r_buffer_t & src, buffer_t & dest) {
  buffer_t temp;
  if (T612BMP(src, temp) != 0) return ASN_CANT_CONVERT;
  return BMP2IA5(temp, dest);
}

int T612BMP(const r_buffer_t & src, buffer_t & dest) {
  T61codepage_t codepage = {0,0};
  uint32 i;
  uint32 saved_l = dest.data_len;
  unsigned accent_pending = 0;

  for (i=0; i<src.data_len; i++) {
    if ((src.data[i] == T61_BS) ||
        (src.data[i] == T61_LF) ||
        (src.data[i] == T61_FF) ||
        (src.data[i] == T61_CR) ||
        (src.data[i] == T61_SUB)) {
      dest.append((unsigned char)0);
      dest.append(src.data[i]);
      accent_pending = 0;
    } else if ((src.data[i] == 0x24u) &&
               (codepage.primary == 0)) {
      dest.append((unsigned char)0);
      dest.append(IA5_CURRENCY);
      accent_pending = 0;
    } else if ((codepage.supplementary == 0) &&
               (src.data[i] >= 0xc0u) &&
               (src.data[i] <= 0xcfu)) {
      if (accent_pending != 0) break;
      else if (src.data[i] == T61_ACCENT_GRAVE) accent_pending = GRAVE;
      else if (src.data[i] == T61_ACCENT_ACUTE) accent_pending = ACUTE;
      else if (src.data[i] == T61_ACCENT_CIRCUMFLEX) accent_pending = CIRCUMFLEX;
      else if (src.data[i] == T61_ACCENT_TILDE) accent_pending = TILDE;
      else if (src.data[i] == T61_ACCENT_MACRON) accent_pending = MACRON;
      else if (src.data[i] == T61_ACCENT_BREVE) accent_pending = BREVE;
      else if (src.data[i] == T61_ACCENT_DOT) accent_pending = DOT;
      else if (src.data[i] == T61_ACCENT_DIAERESIS) accent_pending = DIAERESIS;
      else if (src.data[i] == T61_ACCENT_OLDUMLAUT) accent_pending = DIAERESIS;  // Old Umlaut
      else if (src.data[i] == T61_ACCENT_CIRCLE) accent_pending = CIRCLE;
      else if (src.data[i] == T61_ACCENT_CEDILLA) accent_pending = CEDILLA;
      else if (src.data[i] == T61_NONSPACE_UNDERLINE) accent_pending = UNDERLINE;
      else if (src.data[i] == T61_ACCENT_DOUBLE_ACUTE) accent_pending = DOUBLE_ACUTE;
      else if (src.data[i] == T61_ACCENT_OGONEK) accent_pending = OGONEK;
      else if (src.data[i] == T61_ACCENT_CARON) accent_pending = CARON;
      else break;
    } else if ((codepage.primary == 0) && (accent_pending)) {
      if (src.data[i] == T61_CAP_A) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc0);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc1);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc2);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc3);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc4);
        } else if (accent_pending == CIRCLE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc5);
        } else break;
      } else if (src.data[i] == T61_CAP_C) {
        if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0);
          dest.append(IA5_CAP_C_CEDILLA);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x06);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x08);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x0a);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x0c);
        } else break;
      } else if (src.data[i] == T61_CAP_D) {
        if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x0e);
        } else break;
      } else if (src.data[i] == T61_CAP_E) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc8);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xc9);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xca);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xcb);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x12);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x14);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x16);
        } else if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x18);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x1a);
        } else break;
      } else if (src.data[i] == T61_CAP_G) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x1c);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x1e);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x20);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x22);
        } else break;
      } else if (src.data[i] == T61_CAP_H) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x24);
        } else break;
      } else if (src.data[i] == T61_CAP_I) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xcc);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xcd);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xce);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xcf);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x28);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x2a);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x2c);
        } else if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x2e);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x30);
        } else break;
      } else if (src.data[i] == T61_CAP_J) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x34);
        } else break;
      } else if (src.data[i] == T61_CAP_K) {
        if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x36);
        } else break;
      } else if (src.data[i] == T61_CAP_L) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x39);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x3b);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x3d);
        } else break;
      } else if (src.data[i] == T61_CAP_N) {
        if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x0);
          dest.append((unsigned char) 0xd1);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x43);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x45);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x47);
        } else break;
      } else if (src.data[i] == T61_CAP_O) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xd2);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xd3);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xd4);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xd5);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xd6);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x4c);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x4e);
        } else if (accent_pending == DOUBLE_ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x50);
        } else break;
      } else if (src.data[i] == T61_CAP_R) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x54);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x56);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x58);
        } else break;
      } else if (src.data[i] == T61_CAP_S) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x5a);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x5c);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x5e);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x60);
        } else break;
      } else if (src.data[i] == T61_CAP_T) {
        if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x62);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x64);
        } else break;
      } else if (src.data[i] == T61_CAP_U) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xd9);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xda);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xdb);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xdc);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x68);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x6a);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x6c);
        } else if (accent_pending == CIRCLE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x6e);
        } else if (accent_pending == DOUBLE_ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x70);
        } else if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x72);
        } else break;
      } else if (src.data[i] == T61_CAP_W) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x74);
        } else break;
      } else if (src.data[i] == T61_CAP_Y) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0);
          dest.append((unsigned char) 0xdd);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x1);
          dest.append((unsigned char) 0x76);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x1);
          dest.append((unsigned char) 0x78);
        } else break;
      } else if (src.data[i] == T61_CAP_Z) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x79);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x1);
          dest.append((unsigned char) 0x7b);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x1);
          dest.append((unsigned char) 0x7d);
        } else break;
      } else if (src.data[i] == T61_LOWER_A) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe0);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe1);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe2);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe3);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe4);
        } else if (accent_pending == CIRCLE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe5);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x01);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x03);
        } else if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x05);
        } else break;
      } else if (src.data[i] == T61_LOWER_C) {
        if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe7);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x07);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x09);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x0b);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x0d);
        } else break;
      } else if (src.data[i] == T61_LOWER_D) {
        if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x0f);
        } else break;
      } else if (src.data[i] == T61_LOWER_E) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe8);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xe9);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xea);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xeb);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x13);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x15);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x17);
        } else if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x19);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x1b);
        } else break;
      } else if (src.data[i] == T61_LOWER_G) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x1d);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x1f);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x21);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x23);
        } else break;
      } else if (src.data[i] == T61_LOWER_H) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x25);
        } else break;
      } else if (src.data[i] == T61_LOWER_I) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xec);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xed);
        } if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xee);
        } if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xef);
        } if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x29);
        } if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x2b);
        } if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x2d);
        } if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x2f);
        } else break;
      } else if (src.data[i] == T61_LOWER_J) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x35);
        } else break;
      } else if (src.data[i] == T61_LOWER_K) {
        if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x37);
        } else break;
      } else if (src.data[i] == T61_LOWER_L) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x3a);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x3c);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x3e);
        } else break;
      } else if (src.data[i] == T61_LOWER_N) {
        if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf1);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x44);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x46);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0x48);
        } else break;
      } else if (src.data[i] == T61_LOWER_O) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf2);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf3);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf4);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf5);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf6);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x4d);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x4f);
        } else if (accent_pending == DOUBLE_ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x51);
        } else break;
      } else if (src.data[i] == T61_LOWER_R) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x55);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x57);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x59);
        } else break;
      } else if (src.data[i] == T61_LOWER_S) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x5b);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x5d);
        } else if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x5f);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x61);
        } else break;
      } else if (src.data[i] == T61_LOWER_T) {
        if (accent_pending == CEDILLA) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x63);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x65);
        } else break;
      } else if (src.data[i] == T61_LOWER_U) {
        if (accent_pending == GRAVE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xf9);
        } else if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xfa);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xfb);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xfc);
        } else if (accent_pending == TILDE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x69);
        } else if (accent_pending == MACRON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x6b);
        } else if (accent_pending == BREVE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x6d);
        } else if (accent_pending == CIRCLE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x6f);
        } else if (accent_pending == DOUBLE_ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x71);
        } else if (accent_pending == OGONEK) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x73);
        } else break;
      } else if (src.data[i] == T61_LOWER_W) {
        if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x75);
        } else break;
      } else if (src.data[i] == T61_LOWER_Y) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xfd);
        } else if (accent_pending == DIAERESIS) {
          dest.append((unsigned char) 0x00);
          dest.append((unsigned char) 0xff);
        } else if (accent_pending == CIRCUMFLEX) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x77);
        } else break;
      } else if (src.data[i] == T61_LOWER_Z) {
        if (accent_pending == ACUTE) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x7a);
        } else if (accent_pending == DOT) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x7c);
        } else if (accent_pending == CARON) {
          dest.append((unsigned char) 0x01);
          dest.append((unsigned char) 0x7e);
        } else break;
      } else break;
      accent_pending = 0;
    } else if ((codepage.primary == 0) &&
               (src.data[i] >= T61_SP) && 
               (src.data[i] <= 0x7eu)) {
        dest.append((unsigned char) 0);
        dest.append(src.data[i]);
    } else if (codepage.supplementary == 0) {
      if ((src.data[i] >= T61_DOWNBANG) &&
          (src.data[i] <= T61_POUND)) {
        dest.append((unsigned char) 0);
        dest.append(src.data[i]);
      } else if (src.data[i] == T61_DOLLAR) {
        dest.append((unsigned char) 0);
        dest.append(IA5_DOLLAR);
      } else if (src.data[i] == T61_YEN) {
        dest.append((unsigned char) 0);
        dest.append(IA5_YEN);
      } else if (src.data[i] == T61_HASH) {
        dest.append((unsigned char) 0);
        dest.append(IA5_HASH);
      } else if (src.data[i] == T61_SECTION) {
        dest.append((unsigned char) 0);
        dest.append(IA5_SECTION);
      } else if (src.data[i] == T61_CURRENCY) {
        dest.append((unsigned char) 0);
        dest.append(IA5_CURRENCY);
      } else if (src.data[i] == T61_LEFTCHEVRON) {
        dest.append((unsigned char) 0);
        dest.append(IA5_LEFTCHEVRON);
      } else if ((src.data[i] >= T61_DEGREE) &&
                 (src.data[i] <= T61_SUPER3)) {
        dest.append((unsigned char) 0);
        dest.append(src.data[i]);
      } else if (src.data[i] == T61_CROSS) {
        dest.append((unsigned char) 0);
        dest.append(IA5_CROSS);
      } else if ((src.data[i] >= T61_MU) &&
                 (src.data[i] <= T61_CENTERDOT)) {
        dest.append((unsigned char) 0);
        dest.append(src.data[i]);
      } else if (src.data[i] == T61_DIVISION) {
        dest.append((unsigned char) 0);
        dest.append(IA5_DIVISION);
      } else if ((src.data[i] >= T61_RIGHTCHEVRON) &&
                 (src.data[i] <= T61_DOWNQMARK)) {
        dest.append((unsigned char) 0);
        dest.append(src.data[i]);
      } else if (src.data[i] == T61_PI) {
        dest.append((unsigned char) 0x03);
        dest.append((unsigned char) 0xa9);
      } else if (src.data[i] == T61_CAP_AE) {
        dest.append((unsigned char) 0x00);
        dest.append((unsigned char) IA5_CAP_AE);
      } else if (src.data[i] == T61_CAP_D_BAR) {
        dest.append((unsigned char) 0x00);
        dest.append((unsigned char) IA5_CAP_D_BAR);
      } else if (src.data[i] == T61_A_BAR) {
        dest.append((unsigned char) 0x00);
        dest.append((unsigned char) IA5_A_BAR);
      } else if (src.data[i] == T61_CAP_H_BAR) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x26);
      } else if (src.data[i] == T61_CAP_IJ) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x32);
      } else if (src.data[i] == T61_CAP_L_DOT) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x3f);
      } else if (src.data[i] == T61_CAP_L_BAR) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x41);
      } else if (src.data[i] == T61_CAP_O_BAR) {
        dest.append((unsigned char) 0x00);
        dest.append(IA5_CAP_O_BAR);
      } else if (src.data[i] == T61_CAP_OE) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x52);
      } else if (src.data[i] == T61_O_BAR) {
        dest.append((unsigned char) 0);
        dest.append(IA5_O_BAR);
      } else if (src.data[i] == T61_CAP_P_BAR) {
        dest.append((unsigned char)0);
        dest.append(IA5_CAP_P_BAR);
      } else if (src.data[i] == T61_CAP_T_BAR) {
        dest.append((unsigned char)0x01);
        dest.append((unsigned char)0x66);
      } else if (src.data[i] == T61_CAP_N_HOOK) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x4a);
      } else if (src.data[i] == T61_LOWER_APOST_N) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x49);

      } else if (src.data[i] == T61_KAPPA) {
        dest.append((unsigned char) 0x03);
        dest.append((unsigned char) 0x9a);
      } else if (src.data[i] == T61_LOWER_AE) {
        dest.append((unsigned char) 0x00);
        dest.append(IA5_LOWER_AE);
      } else if (src.data[i] == T61_LOWER_D_BAR) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x11);
      } else if (src.data[i] == T61_LOWER_DELTA_BAR) {
        dest.append((unsigned char) 0x03);
        dest.append((unsigned char) 0xb4);
      } else if (src.data[i] == T61_LOWER_H_BAR) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x27);
      } else if (src.data[i] == T61_LOWER_IH) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x31);
      } else if (src.data[i] == T61_LOWER_IJ) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x33);
      } else if (src.data[i] == T61_LOWER_L_DOT) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x40);
      } else if (src.data[i] == T61_LOWER_L_BAR) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x42);
      } else if (src.data[i] == T61_LOWER_O_BAR) {
        dest.append((unsigned char) 0x00);
        dest.append(IA5_LOWER_O_BAR);
      } else if (src.data[i] == T61_LOWER_OE) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x53);
      } else if (src.data[i] == T61_BETA) {
        dest.append((unsigned char) 0x03);
        dest.append((unsigned char) 0xb2);
      } else if (src.data[i] == T61_LOWER_P_BAR) {
        dest.append((unsigned char) 0x00);
        dest.append(IA5_LOWER_P_BAR);
      } else if (src.data[i] == T61_LOWER_T_BAR) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x67);
      } else if (src.data[i] == T61_LOWER_N_HOOK) {
        dest.append((unsigned char) 0x01);
        dest.append((unsigned char) 0x4b);
      } else break;
    } else break;
  };
  if (i<src.data_len) {
    dest.data_len = saved_l;
    return ASN_CANT_CONVERT;
  };
  return 0;
}


int IA52T61(const r_buffer_t & src, buffer_t & dest) {
  uint32 i;
  uint32 saved_l = dest.data_len;

  for (i=0; i<src.data_len; i++) {
    if ((src.data[i] == IA5_BS) ||
        (src.data[i] == IA5_LF) ||
        (src.data[i] == IA5_FF) ||
        (src.data[i] == IA5_CR) ||
        (src.data[i] == IA5_SUB)) {
      dest.append(src.data[i]);
    } else if (src.data[i] == IA5_HASH) {
      dest.append((unsigned char)T61_HASH);
    } else if (src.data[i] == IA5_DOLLAR) {
      dest.append((unsigned char)T61_DOLLAR);
    } else if ((src.data[i] >= IA5_SP) && (src.data[i] <= IA5_TILDE)) {
      dest.append(src.data[i]);
    } else if ((src.data[i] >= IA5_DOWNBANG) &&
               (src.data[i] <= IA5_POUND)) {
      dest.append(src.data[i]);
    } else if (src.data[i] == IA5_CURRENCY) {
      dest.append(T61_CURRENCY);
    } else if (src.data[i] == IA5_YEN) {
      dest.append(T61_YEN);
    } else if (src.data[i] == IA5_SECTION) {
      dest.append(T61_SECTION);
    } else if (src.data[i] == IA5_LEFTCHEVRON) {
      dest.append(T61_LEFTCHEVRON);
    } else if (src.data[i] == IA5_RIGHTCHEVRON) {
      dest.append(T61_RIGHTCHEVRON);
    } else if ((src.data[i] >= IA5_DEGREE) && (src.data[i] <= IA5_SUPER3)) {
      dest.append(src.data[i]);
    } else if ((src.data[i] >= IA5_MU) && (src.data[i] <= IA5_CENTERDOT)) {
      dest.append(src.data[i]);
    } else if (src.data[i] == IA5_O_BAR) {
      dest.append(T61_O_BAR);
    } else if ((src.data[i] >= IA5_QUARTER) && (src.data[i] <= IA5_DOWNQMARK)) {
      dest.append(src.data[i]);
    } else if (src.data[i] == IA5_CAP_A_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_CAP_A);
    } else if (src.data[i] == IA5_CAP_A_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_CAP_A);
    } else if (src.data[i] == IA5_CAP_A_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_CAP_A);
    } else if (src.data[i] == IA5_CAP_A_TILDE) {
      dest.append(T61_ACCENT_TILDE);
      dest.append(T61_CAP_A);
    } else if (src.data[i] == IA5_CAP_A_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_CAP_A);
    } else if (src.data[i] == IA5_CAP_A_CIRCLE) {
      dest.append(T61_ACCENT_CIRCLE);
      dest.append(T61_CAP_A);
    } else if (src.data[i] == IA5_CAP_AE) {
      dest.append(T61_CAP_AE);
    } else if (src.data[i] == IA5_CAP_C_CEDILLA) {
      dest.append(T61_ACCENT_CEDILLA);
      dest.append(T61_CAP_C);
    } else if (src.data[i] == IA5_CAP_E_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_CAP_E);
    } else if (src.data[i] == IA5_CAP_E_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_CAP_E);
    } else if (src.data[i] == IA5_CAP_E_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_CAP_E);
    } else if (src.data[i] == IA5_CAP_E_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_CAP_E);
    } else if (src.data[i] == IA5_CAP_I_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_CAP_I);
    } else if (src.data[i] == IA5_CAP_I_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_CAP_I);
    } else if (src.data[i] == IA5_CAP_I_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_CAP_I);
    } else if (src.data[i] == IA5_CAP_I_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_CAP_I);
    } else if (src.data[i] == IA5_CAP_D_BAR) {
      dest.append(T61_CAP_D_BAR);
    } else if (src.data[i] == IA5_CAP_N_TILDE) {
      dest.append(T61_ACCENT_TILDE);
      dest.append(T61_CAP_N);
    } else if (src.data[i] == IA5_CAP_O_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_CAP_O);
    } else if (src.data[i] == IA5_CAP_O_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_CAP_O);
    } else if (src.data[i] == IA5_CAP_O_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_CAP_O);
    } else if (src.data[i] == IA5_CAP_O_TILDE) {
      dest.append(T61_ACCENT_TILDE);
      dest.append(T61_CAP_O);
    } else if (src.data[i] == IA5_CAP_O_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_CAP_O);
    } else if (src.data[i] == IA5_CROSS) {
      dest.append(T61_CROSS);
    } else if (src.data[i] == IA5_CAP_O_BAR) {
      dest.append(T61_CAP_O_BAR);
    } else if (src.data[i] == IA5_CAP_U_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_CAP_U);
    } else if (src.data[i] == IA5_CAP_U_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_CAP_U);
    } else if (src.data[i] == IA5_CAP_U_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_CAP_U);
    } else if (src.data[i] == IA5_CAP_U_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_CAP_U);
    } else if (src.data[i] == IA5_CAP_Y_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_CAP_Y);
    } else if (src.data[i] == IA5_CAP_P_BAR) {
      dest.append(T61_CAP_P_BAR);
    } else if (src.data[i] == IA5_BETA) {
      dest.append(T61_BETA);
      } else if (src.data[i] == IA5_LOWER_A_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_LOWER_A);
    } else if (src.data[i] == IA5_LOWER_A_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_LOWER_A);
    } else if (src.data[i] == IA5_LOWER_A_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_LOWER_A);
    } else if (src.data[i] == IA5_LOWER_A_TILDE) {
      dest.append(T61_ACCENT_TILDE);
      dest.append(T61_LOWER_A);
    } else if (src.data[i] == IA5_LOWER_A_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_LOWER_A);
    } else if (src.data[i] == IA5_LOWER_A_CIRCLE) {
      dest.append(T61_ACCENT_CIRCLE);
      dest.append(T61_LOWER_A);
    } else if (src.data[i] == IA5_LOWER_AE) {
      dest.append(T61_LOWER_AE);
    } else if (src.data[i] == IA5_LOWER_C_CEDILLA) {
      dest.append(T61_ACCENT_CEDILLA);
      dest.append(T61_LOWER_C);
    } else if (src.data[i] == IA5_LOWER_E_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_LOWER_E);
    } else if (src.data[i] == IA5_LOWER_E_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_LOWER_E);
    } else if (src.data[i] == IA5_LOWER_E_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_LOWER_E);
    } else if (src.data[i] == IA5_LOWER_E_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_LOWER_E);
    } else if (src.data[i] == IA5_LOWER_I_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_LOWER_I);
    } else if (src.data[i] == IA5_LOWER_I_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_LOWER_I);
    } else if (src.data[i] == IA5_LOWER_I_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_LOWER_I);
    } else if (src.data[i] == IA5_LOWER_I_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_LOWER_I);
    } else if (src.data[i] == IA5_LOWER_D_BAR) {
      dest.append(T61_LOWER_D_BAR);
    } else if (src.data[i] == IA5_LOWER_N_TILDE) {
      dest.append(T61_ACCENT_TILDE);
      dest.append(T61_LOWER_N);
    } else if (src.data[i] == IA5_LOWER_O_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_LOWER_O);
    } else if (src.data[i] == IA5_LOWER_O_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_LOWER_O);
    } else if (src.data[i] == IA5_LOWER_O_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_LOWER_O);
    } else if (src.data[i] == IA5_LOWER_O_TILDE) {
      dest.append(T61_ACCENT_TILDE);
      dest.append(T61_LOWER_O);
    } else if (src.data[i] == IA5_LOWER_O_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_LOWER_O);
    } else if (src.data[i] == IA5_DIVISION) {
      dest.append(T61_DIVISION);
    } else if (src.data[i] == IA5_LOWER_O_BAR) {
      dest.append(T61_LOWER_O_BAR);
    } else if (src.data[i] == IA5_LOWER_U_GRAVE) {
      dest.append(T61_ACCENT_GRAVE);
      dest.append(T61_LOWER_U);
    } else if (src.data[i] == IA5_LOWER_U_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_LOWER_U);
    } else if (src.data[i] == IA5_LOWER_U_CIRCUMFLEX) {
      dest.append(T61_ACCENT_CIRCUMFLEX);
      dest.append(T61_LOWER_U);
    } else if (src.data[i] == IA5_LOWER_U_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_LOWER_U);
    } else if (src.data[i] == IA5_LOWER_Y_ACUTE) {
      dest.append(T61_ACCENT_ACUTE);
      dest.append(T61_LOWER_Y);
    } else if (src.data[i] == IA5_LOWER_P_BAR) {
      dest.append(T61_LOWER_P_BAR);
    } else if (src.data[i] == IA5_LOWER_Y_DIAERESIS) {
      dest.append(T61_ACCENT_DIAERESIS);
      dest.append(T61_LOWER_Y);
    } else break;
  };
  if (i>=src.data_len) return 0;
  dest.data_len = saved_l;
  return ASN_CANT_CONVERT;
}



int asn_charstring::convert2IA5(void) {
  uint32 theCodeset;
  buffer_t temp;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == IA5_STRING_TAG) return 0;
  if (!check_codeset(IA5_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;

// We are allowed to do the conversion

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
// IA5 is a superset of printableString, so we can convert trivially
    break;
  case UNIVERSAL_STRING_TAG:
    if (U2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case UTF8_STRING_TAG:
    if (UTF82IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case BMP_STRING_TAG:
    if (BMP2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  default:
    return ASN_CANT_CONVERT;
  };
  set_codeset(IA5_STRING_TAG);
  return 0;
}

int asn_charstring::convert2printable(void) {
  uint32 theCodeset;
  buffer_t temp;
  unsigned i;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == PRINTABLE_STRING_TAG) return 0;
  if (!check_codeset(PRINTABLE_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;

  switch (theCodeset) {
  case IA5_STRING_TAG:
  case UTF8_STRING_TAG:
// Check that all the IA5 characters are within the printableString codeset 
    for (i=0;i<str_val.data_len;i++) 
      if (!printableStringCharacterIA5(str_val.data[i])) return ASN_CANT_CONVERT;
    break;
  case UNIVERSAL_STRING_TAG:
    if (U2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    for (i=0;i<temp.data_len;i++)
      if (!printableStringCharacterIA5(temp.data[i])) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case BMP_STRING_TAG:
    if (BMP2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    for (i=0;i<temp.data_len;i++)
      if (!printableStringCharacterIA5(temp.data[i])) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    for (i=0;i<temp.data_len;i++)
      if (!printableStringCharacterIA5(temp.data[i])) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  default:
    return ASN_CANT_CONVERT;
  };
  set_codeset(PRINTABLE_STRING_TAG);
  return 0;
}


int asn_charstring::convert2T61(void) {
  buffer_t temp;
  int result;

  CHECK_VALUE_PRESENT;
  if (get_codeset() == TELETEX_STRING_TAG) return 0;
  if (!check_codeset(TELETEX_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  
  if ((result = convert2T61(temp)) != 0) return result;
  set_value_uninterpreted(temp);
  set_codeset(TELETEX_STRING_TAG);
  return 0;
}

int asn_charstring::convert2BMP(void) {
  uint32 theCodeset;
  buffer_t temp;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == BMP_STRING_TAG) return 0;
  if (!check_codeset(BMP_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  
  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
  case IA5_STRING_TAG:
    if (IA52BMP(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case UTF8_STRING_TAG:
    if (UTF82BMP(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case UNIVERSAL_STRING_TAG:
    if (U2BMP(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case TELETEX_STRING_TAG:
  default:
    return ASN_CANT_CONVERT;
  };
  set_codeset(BMP_STRING_TAG);
  return 0;
}

int asn_charstring::convert2Univ(void) {
  uint32 theCodeset;
  buffer_t temp;
  buffer_t temp1;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == UNIVERSAL_STRING_TAG) return 0;
  if (!check_codeset(UNIVERSAL_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
  case IA5_STRING_TAG:
    if (IA52U(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case UTF8_STRING_TAG:
    if (BMP2U(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case BMP_STRING_TAG:
    if (BMP2U(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp1) != 0) return ASN_CANT_CONVERT;
    if (IA52U(temp1, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  default:
    return ASN_CANT_CONVERT;
  };
  set_codeset(UNIVERSAL_STRING_TAG);
  return 0;
}

int asn_charstring::convert2UTF8(void) {
  uint32 theCodeset;
  buffer_t temp;
  buffer_t temp1;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == UTF8_STRING_TAG) return 0;
  if (!check_codeset(UTF8_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
    break;
  case IA5_STRING_TAG:
    if (IA52UTF8(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case BMP_STRING_TAG:
    if (BMP2UTF8(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case UNIVERSAL_STRING_TAG:
    if (U2UTF8(str_val, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp1) != 0) return ASN_CANT_CONVERT;
    if (IA52UTF8(temp1, temp) != 0) return ASN_CANT_CONVERT;
    str_val.clear();
    str_val.append(temp);
    break;
  default:
    return ASN_CANT_CONVERT;
  };
  set_codeset(UNIVERSAL_STRING_TAG);
  return 0;
}



int asn_charstring::convert2IA5(buffer_t &res) const {
  uint32 theCodeset;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == IA5_STRING_TAG) {
    res.append(str_val);
    return 0;
  };

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
// IA5 is a superset of printableString, so we can convert trivially
    res.append(str_val);
    return 0;
  case UNIVERSAL_STRING_TAG:
    return U2IA5(str_val, res);
  case UTF8_STRING_TAG:
    return UTF82IA5(str_val, res);
  case BMP_STRING_TAG:
    return BMP2IA5(str_val, res);
  case TELETEX_STRING_TAG:
    return T612IA5(str_val, res);
  default:
    break;
  };
  return ASN_CANT_CONVERT;
}

int asn_charstring::convert2printable(buffer_t & res) const {
  uint32 theCodeset;
  unsigned i;
  buffer_t temp;

  CHECK_VALUE_PRESENT;

  if ((theCodeset = get_codeset()) == PRINTABLE_STRING_TAG) {
    res.append(str_val);
    return 0;
  };

  switch (theCodeset) {
  case IA5_STRING_TAG:
  case UTF8_STRING_TAG:
// Check that all the IA5/UTF8 characters are within the printableString codeset 
    for (i=0;i<str_val.data_len;i++) 
      if (!printableStringCharacterIA5(str_val.data[i])) break;
    if (i<str_val.data_len) break;
    res.append(str_val);
    return 0;
  case UNIVERSAL_STRING_TAG:
    if (U2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    for (i=0;i<temp.data_len;i++)
      if (!printableStringCharacterIA5(temp.data[i+3])) return ASN_CANT_CONVERT;
    res.append(temp);
    return 0;
  case BMP_STRING_TAG:
    if (BMP2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    for (i=0;i<temp.data_len;i++)
      if (!printableStringCharacterIA5(temp.data[i+3])) return ASN_CANT_CONVERT;
    res.append(temp);
    return 0;
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    for (i=0;i<temp.data_len;i++)
      if (!printableStringCharacterIA5(temp.data[i+3])) return ASN_CANT_CONVERT;
    res.append(temp);
    return 0;
  default:
    break;
  };
  return ASN_CANT_CONVERT;
}

int asn_charstring::convert2T61(buffer_t & res) const {
  uint32 theCodeset;
  buffer_t temp;

  CHECK_VALUE_PRESENT;

  if ((theCodeset = get_codeset()) == TELETEX_STRING_TAG) {
    res.append(str_val);
    return 0;
  };

  switch (theCodeset) {

  case PRINTABLE_STRING_TAG:
// the printableString codeset is a proper subset of T61, so we don't need to convert
    res.append(str_val);
    return 0;
  case UTF8_STRING_TAG:
    if (UTF82IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    return IA52T61(temp, res);
  case IA5_STRING_TAG:
    return IA52T61(str_val, res);
  case UNIVERSAL_STRING_TAG:
    if (U2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    return IA52T61(temp, res);
  case BMP_STRING_TAG:
    if (BMP2IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    return IA52T61(temp, res);
  default:
    break;
  };
  return ASN_CANT_CONVERT;
}

int asn_charstring::convert2BMP(buffer_t & res) const {
  uint32 theCodeset;
  buffer_t temp;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == BMP_STRING_TAG) {
    res.append(str_val);
    return 0;
  };

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
  case IA5_STRING_TAG:
    return IA52BMP(str_val, res);
  case UNIVERSAL_STRING_TAG:
    return U2BMP(str_val, res);
  case UTF8_STRING_TAG:
    return UTF82BMP(str_val, res);
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    return IA52BMP(temp, res);
  default:
    break;
  };
  return ASN_CANT_CONVERT;
}

int asn_charstring::convert2Univ(buffer_t & res) const {
  uint32 theCodeset;
  buffer_t temp;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == UNIVERSAL_STRING_TAG) {
    res.append(str_val);
    return 0;
  };

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
  case IA5_STRING_TAG:
    return IA52U(str_val, res);
  case UTF8_STRING_TAG:
    return UTF82U(str_val, res);
  case BMP_STRING_TAG:
    return BMP2U(str_val, res);
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    return IA52U(temp, res);
  default:
    break;
  };
  return ASN_CANT_CONVERT;
}



int asn_charstring::convert2UTF8(buffer_t & res) const {
  uint32 theCodeset;
  buffer_t temp;

  CHECK_VALUE_PRESENT;
  if ((theCodeset = get_codeset()) == UTF8_STRING_TAG) {
    res.append(str_val);
    return 0;
  };

  switch (theCodeset) {
  case PRINTABLE_STRING_TAG:
    res.append(str_val);
    return 0;
  case IA5_STRING_TAG:
    return IA52UTF8(str_val, res);
  case BMP_STRING_TAG:
    return BMP2UTF8(str_val, res);
  case TELETEX_STRING_TAG:
    if (T612IA5(str_val, temp) != 0) return ASN_CANT_CONVERT;
    return IA52UTF8(temp, res);
  case UNIVERSAL_STRING_TAG:
    return U2UTF8(str_val, res);
  default:
    break;
  };
  return ASN_CANT_CONVERT;
}



int asn_charstring::set_value_C(char * s) {
  buffer_t temp;
  buffer_t temp1;
  unsigned i;
  int c;

  for (i=0; s[i] != 0; i++) {
    if ((c = Local2IA5(s[i])) == -1) return ASN_CANT_CONVERT;
    temp.append((unsigned char)c);  
  };
// IA5 is a little special, in that we're treating it pretty much a default codeset.
// If it isn't allowed in the current string object, coerce it to one that it.
// Try in the order: printableString, UTF8String, BMPstring, UniversalString

  if (check_codeset(IA5_STRING_TAG)) {
    set_value_uninterpreted(temp);
    set_codeset(IA5_STRING_TAG);
    set_value_valid();
    return 0;
  } else if (check_codeset(TELETEX_STRING_TAG)) {
    IA52T61(temp,temp1);
    set_value_uninterpreted(temp1);
    set_codeset(TELETEX_STRING_TAG);
    set_value_valid();
    return 0;
  } else if (check_codeset(UTF8_STRING_TAG)) {
    IA52UTF8(temp,temp1);
    set_value_uninterpreted(temp1);
    set_codeset(UTF8_STRING_TAG);
    set_value_valid();
    return 0;
  } else if (check_codeset(BMP_STRING_TAG)) {
    IA52BMP(temp,temp1);
    set_value_uninterpreted(temp1);
    set_codeset(BMP_STRING_TAG);
    set_value_valid();
    return 0;
  } else if (check_codeset(UNIVERSAL_STRING_TAG)) {
    IA52U(temp,temp1);
    set_value_uninterpreted(temp1);
    set_codeset(UNIVERSAL_STRING_TAG);
    set_value_valid();
    return 0;
  } else return ASN_CANT_CONVERT;

}

int asn_charstring::get_value_C(buffer_t &s, char splat) const {
  int result;
  uint32 i;
  uint32 saved_l;
  int c;

  if ((result = convert2IA5(s)) != 0) return result;
  for (i=saved_l; i<s.data_len; i++) {
    if ((c = IA52Local(s.data[i])) >= 0) s.data[i] = c;
    else if (splat) s.data[i] = splat;
    else {
      s.data_len = saved_l;
      return ASN_CANT_CONVERT;
    };
  };
  
  return 0;
}


int asn_charstring::get_value_IA5(buffer_t &s) const {
  return convert2IA5(s);
}

int asn_charstring::set_value_IA5(char * s) {
  r_buffer_t temp;
  temp.data= (unsigned char *)s;
  temp.data_len = strlen(s);
  return set_value_IA5(temp);
};
  
int asn_charstring::set_value_IA5(r_buffer_t &s) {

  unsigned i;
  buffer_t dst;
  if (check_codeset(IA5_STRING_TAG)) {
    set_value_uninterpreted(s);
    set_codeset(IA5_STRING_TAG);
    set_value_valid();
    return 0;
// IA5 is a little special, in that we're treating it pretty much a default codeset.
// If it isn't allowed in the current string object, coerce it to one that it.
// Try in the order: printableString, TeletexString, UTF8string, BMPstring, UniversalString
  } else {
    if (check_codeset(PRINTABLE_STRING_TAG)) {
      for (i=0; i< s.data_len != 0;i++) 
        if (!printableStringCharacterIA5(s[i])) goto try_teletex;
      return set_value_printable(s);
    };
try_teletex:
    if (check_codeset(TELETEX_STRING_TAG)) {
      if (IA52T61(s, dst)) goto try_UTF8;
      return set_value_T61(dst);
    };
try_UTF8:
    if (check_codeset(UTF8_STRING_TAG)) {
      if (IA52UTF8(s, dst)) goto try_BMP;
      return set_value_UTF8(dst);
    };
try_BMP:
    if (check_codeset(BMP_STRING_TAG)) {
      if (IA52BMP(s, dst)) goto try_UNIV;
      return set_value_BMP(dst);
    };
try_UNIV:
    if (check_codeset(UNIVERSAL_STRING_TAG)) {
      if (IA52U(s, dst)) goto try_UNIV;
      return set_value_Univ(dst);
    };
  };
  return ASN_CODESET_NOT_PERMITTED;
}

int asn_charstring::get_value_printable(buffer_t &s) const {
  return convert2printable(s);
}

int asn_charstring::set_value_printable(char * s) {
  if (!check_codeset(PRINTABLE_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted((unsigned char *)s, strlen(s));
  set_codeset(PRINTABLE_STRING_TAG);
  return 0;
}

int asn_charstring::set_value_printable(r_buffer_t & s) {
  if (!check_codeset(PRINTABLE_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted(s);
  set_codeset(PRINTABLE_STRING_TAG);
  return 0;
}

int asn_charstring::get_value_T61(buffer_t &s) const {
  return convert2T61(s);
}

int asn_charstring::set_value_T61(char * s) {
  if (!check_codeset(TELETEX_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted((unsigned char *)s, strlen(s));
  set_codeset(TELETEX_STRING_TAG);
  return 0;
}

int asn_charstring::set_value_T61(r_buffer_t &s) {
  if (!check_codeset(TELETEX_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted(s);
  set_codeset(TELETEX_STRING_TAG);
  return 0;
}

int asn_charstring::get_value_BMP(buffer_t &s) const {
  return convert2BMP(s);
}

int asn_charstring::set_value_BMP(uint16 * s) {
  uint32 len = 0;
  if (!check_codeset(BMP_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  while (s[len] != 0) len++;
  set_value_uninterpreted((unsigned char *)s, 2 * len);
  set_codeset(BMP_STRING_TAG);
  return 0;
}

int asn_charstring::set_value_BMP(r_buffer_t &s) {
  uint32 len = 0;
  if (!check_codeset(BMP_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted(s);
  set_codeset(BMP_STRING_TAG);
  return 0;
}

int asn_charstring::get_value_Univ(buffer_t &s) const {
  return convert2Univ(s);
}

int asn_charstring::set_value_Univ(uint32 * s) {
  uint32 len = 0;
  if (!check_codeset(UNIVERSAL_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted((unsigned char *)s, 4 * len);
  set_codeset(UNIVERSAL_STRING_TAG);
  return 0;
}


int asn_charstring::set_value_Univ(r_buffer_t &s) {
  uint32 len = 0;
  if (!check_codeset(UNIVERSAL_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted(s);
  set_codeset(UNIVERSAL_STRING_TAG);
  return 0;
}


int asn_charstring::get_value_UTF8(buffer_t &s) const {
  return convert2UTF8(s);
}

int asn_charstring::set_value_UTF8(char * s) {
  uint32 len = 0;
  if (!check_codeset(UTF8_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted((unsigned char *)s, strlen(s));
  set_codeset(UTF8_STRING_TAG);
  return 0;
}


int asn_charstring::set_value_UTF8(r_buffer_t &s) {
  uint32 len = 0;
  if (!check_codeset(UTF8_STRING_TAG)) return ASN_CODESET_NOT_PERMITTED;
  set_value_uninterpreted(s);
  set_codeset(UTF8_STRING_TAG);
  return 0;
}


bool asn_directoryString::check_codeset(uint32 codeset) const {
  return (codeset == PRINTABLE_STRING_TAG) ||
         (codeset == TELETEX_STRING_TAG) ||
         (codeset == UNIVERSAL_STRING_TAG) ||
         (codeset == BMP_STRING_TAG) || 
         (codeset == UTF8_STRING_TAG);
}

bool asn_ia5String::check_codeset(uint32 codeset) const {
  return (codeset == IA5_STRING_TAG);
}

bool asn_printableString::check_codeset(uint32 codeset) const {
  return (codeset == PRINTABLE_STRING_TAG);
}

bool asn_teletexString::check_codeset(uint32 codeset) const {
  return (codeset == TELETEX_STRING_TAG);
}

bool asn_bmpString::check_codeset(uint32 codeset) const {
  return (codeset == BMP_STRING_TAG);
}

bool asn_universalString::check_codeset(uint32 codeset) const {
  return (codeset == UNIVERSAL_STRING_TAG);
}

bool asn_UTF8String::check_codeset(uint32 codeset) const {
  return (codeset == UTF8_STRING_TAG);
}

unsigned char toupper_IA5(unsigned char c) {
  if ((c >= 0x61u) && (c <= 0x7au)) return c - 0x20;
  else if ((c >= 0xe0u) && (c <= 0xf6u)) return c - 0x20;
  else if ((c >= 0xf8u) && (c <= 0xfeu)) return c - 0x20;
  return c;
}

unsigned toupper_BMP(unsigned c) {
  if (c == 0xffu) return 0x178u;
  if (c < 0x100u) return toupper_IA5((unsigned) c);
  if ((c >= 0x100u) && (c <= 0x137u)) return c & 0xfffeu;
  if ((c >= 0x139u) && (c <= 0x148u)) return ((c + 1) & 0xfffeu) - 1;
  if ((c >= 0x14au) && (c <= 0x177u)) return c & 0xfffeu;
  if ((c >= 0x179u) && (c <= 0x17eu)) return ((c + 1) & 0xfffeu) - 1;
  return c;
}

uint32 toupper_UNIV(uint32 c) {
  if (c <= 0xffffu) return toupper_BMP((unsigned)c);
  return c;
}

int BMP2Array(r_buffer_t & src, uint16 * dst) {
  unsigned i; 

  if ((src.data_len % 2) != 0) return ASN_CANT_CONVERT;

  for (i=0; i<src.data_len-1; i+=2) {
    *dst++ = (uint16)(src[i+1]) + ((uint16)(src[i]) << 8);
  };
  *dst++ = 0;
  return 0;
}

int Univ2Array(r_buffer_t & src, uint32 * dst) {
  unsigned i; 

  if ((src.data_len % 4) != 0) return ASN_CANT_CONVERT;
  
  for (i=0; i<src.data_len-3; i+=4) {
    *dst++ = (uint32)(src[i+3]) + (((uint32)(src[i+2]) + (((uint32)(src[i+1]) + (((uint32)(src[i])) << 8)) << 8)) << 8);
  };
  *dst++ = 0;
  return 0;

}

int Array2BMP(uint16 src[], buffer_t & dst) {
  unsigned i;
  while (src[i] != 0) {
    dst.append((unsigned char)((src[i] & 0xff00lu) >> 8));
    dst.append((unsigned char)(src[i] & 0x00fflu));
    i++;
  };
  return 0;
}

int Array2Univ(uint32 src[], buffer_t & dst) {
  unsigned i;
  while (src[i] != 0) {
    dst.append((unsigned char)((src[i] & 0xff000000lu) >> 24));
    dst.append((unsigned char)((src[i] & 0xff0000lu) >> 16));
    dst.append((unsigned char)((src[i] & 0xff00lu) >> 8));
    dst.append((unsigned char)(src[i] & 0x00fflu));
    i++;
  };
  return 0;
}
