#include <stdio.h>
#include <stdlib.h>

// conversion tables are based on
// http://w...content-available-to-author-only...e.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT

const unsigned short cp437hi_utf16[128] =
{
  0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
  0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
  0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
  0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
  0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
  0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
  0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
  0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
  0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
  0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
  0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
  0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
  0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
  0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
  0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
  0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
};

const unsigned long utf16_cp437hi[128] =
{
  0xff00a0, 0xad00a1, 0x9b00a2, 0x9c00a3,
  0x9d00a5, 0xa600aa, 0xae00ab, 0xaa00ac,
  0xf800b0, 0xf100b1, 0xfd00b2, 0xe600b5,
  0xfa00b7, 0xa700ba, 0xaf00bb, 0xac00bc,
  0xab00bd, 0xa800bf, 0x8e00c4, 0x8f00c5,
  0x9200c6, 0x8000c7, 0x9000c9, 0xa500d1,
  0x9900d6, 0x9a00dc, 0xe100df, 0x8500e0,
  0xa000e1, 0x8300e2, 0x8400e4, 0x8600e5,
  0x9100e6, 0x8700e7, 0x8a00e8, 0x8200e9,
  0x8800ea, 0x8900eb, 0x8d00ec, 0xa100ed,
  0x8c00ee, 0x8b00ef, 0xa400f1, 0x9500f2,
  0xa200f3, 0x9300f4, 0x9400f6, 0xf600f7,
  0x9700f9, 0xa300fa, 0x9600fb, 0x8100fc,
  0x9800ff, 0x9f0192, 0xe20393, 0xe90398,
  0xe403a3, 0xe803a6, 0xea03a9, 0xe003b1,
  0xeb03b4, 0xee03b5, 0xe303c0, 0xe503c3,
  0xe703c4, 0xed03c6, 0xfc207f, 0x9e20a7,
  0xf92219, 0xfb221a, 0xec221e, 0xef2229,
  0xf72248, 0xf02261, 0xf32264, 0xf22265,
  0xa92310, 0xf42320, 0xf52321, 0xc42500,
  0xb32502, 0xda250c, 0xbf2510, 0xc02514,
  0xd92518, 0xc3251c, 0xb42524, 0xc2252c,
  0xc12534, 0xc5253c, 0xcd2550, 0xba2551,
  0xd52552, 0xd62553, 0xc92554, 0xb82555,
  0xb72556, 0xbb2557, 0xd42558, 0xd32559,
  0xc8255a, 0xbe255b, 0xbd255c, 0xbc255d,
  0xc6255e, 0xc7255f, 0xcc2560, 0xb52561,
  0xb62562, 0xb92563, 0xd12564, 0xd22565,
  0xcb2566, 0xcf2567, 0xd02568, 0xca2569,
  0xd8256a, 0xd7256b, 0xce256c, 0xdf2580,
  0xdc2584, 0xdb2588, 0xdd258c, 0xde2590,
  0xb02591, 0xb12592, 0xb22593, 0xfe25a0
};

unsigned short Cp437toUtf16(unsigned char c)
{
  if (c < 0x80)
    return c;
  return cp437hi_utf16[c - 0x80];
}

static int CompareUtf16toCp437(const void* pv1, const void* pv2)
{
  const unsigned long *pl1 = pv1, *pl2 = pv2;
  unsigned long v1 = *pl1 & 0xFFFF, v2 = *pl2 & 0xFFFF;

  if (v1 > v2)
    return +1;

  if (v1 < v2)
    return -1;

  return 0;
}

int Utf16toCp437(unsigned long cp)
{
  unsigned long* p;

  if (cp < 0x80)
    return cp;

  if (cp > 0xFFFF)
    return -1;
    
  p = bsearch(&cp,
              utf16_cp437hi,
              sizeof(utf16_cp437hi) / sizeof(utf16_cp437hi[0]),
              sizeof(utf16_cp437hi[0]),
              &CompareUtf16toCp437);

  if (p == NULL)
    return -1;

  return *p >> 16;
}

int main(void)
{
  unsigned c;

  for (c = 0; c <= 0xFF; c++)
  {
    unsigned long cp = Cp437toUtf16(c);
    int c2 = Utf16toCp437(cp);

    printf("0x%02X -> 0x%04lX -> 0x%02X\n", c, cp, c2);

    if (c != c2)
    {
      puts("Failure!");
      return EXIT_FAILURE;
    }
  }

  puts("Success!");
  return EXIT_SUCCESS;
}
