RDB's Unicode patch. Fonts are now used in Unicode mode where
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 10 May 2001 08:34:20 +0000 (08:34 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 10 May 2001 08:34:20 +0000 (08:34 +0000)
possible and we have a single unified means of trying to display any
Unicode code point. Instead of the various ad-hoc translation modes
we had before, we now have a single `codepage' option which allows
us to treat the incoming (and outgoing) text as any given character
set, and locally we map that to Unicode and back.

git-svn-id: svn://svn.tartarus.org/sgt/putty@1110 cda61777-01e9-0310-a592-d414129be87e

Makefile
ldisc.c
putty.h
settings.c
terminal.c
unicode.c [new file with mode: 0644]
wcwidth.c [new file with mode: 0644]
windlg.c
window.c
xlat.c [deleted file]

index 07135ff..7b2122f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -69,7 +69,7 @@ RES=res
 
 ##-- objects putty puttytel
 GOBJS1 = window.$(OBJ) windlg.$(OBJ) winctrls.$(OBJ) terminal.$(OBJ)
-GOBJS2 = xlat.$(OBJ) sizetip.$(OBJ)
+GOBJS2 = sizetip.$(OBJ) wcwidth.$(OBJ) unicode.$(OBJ)
 ##-- objects putty puttytel plink
 LOBJS1 = telnet.$(OBJ) raw.$(OBJ) rlogin.$(OBJ) ldisc.$(OBJ) winnet.$(OBJ)
 ##-- objects putty plink
diff --git a/ldisc.c b/ldisc.c
index b307d4e..48fbe17 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
@@ -28,7 +28,7 @@ static int term_buflen = 0, term_bufsiz = 0, term_quotenext = 0;
 
 static int plen(unsigned char c)
 {
-    if ((c >= 32 && c <= 126) || (c >= 160))
+    if ((c >= 32 && c <= 126) || (c >= 160 && !utf))
        return 1;
     else if (c < 128)
        return 2;                      /* ^x for some x */
@@ -38,7 +38,7 @@ static int plen(unsigned char c)
 
 static void pwrite(unsigned char c)
 {
-    if ((c >= 32 && c <= 126) || (c >= 160)) {
+    if ((c >= 32 && c <= 126) || (c >= 160 && !utf)) {
        c_write(&c, 1);
     } else if (c < 128) {
        char cc[2];
diff --git a/putty.h b/putty.h
index ee6a2b7..f665f31 100644 (file)
--- a/putty.h
+++ b/putty.h
 #define GLOBAL extern
 #endif
 
-#define ATTR_ACTCURS 0x80000000UL      /* active cursor (block) */
-#define ATTR_PASCURS 0x40000000UL      /* passive cursor (box) */
-#define ATTR_INVALID 0x20000000UL
-#define ATTR_WRAPPED 0x10000000UL
-#define ATTR_RIGHTCURS 0x10000000UL    /* doubles as cursor-on-RHS indicator */
+/* Three attribute types: 
+ * The ATTRs (normal attributes) are stored with the characters in the main
+ * display arrays
+ *
+ * The TATTRs (temporary attributes) are generated on the fly, they can overlap
+ * with characters but not with normal attributes.
+ *
+ * The LATTRs (line attributes) conflict with no others and only have one
+ * value per line. But on area clears the LATTR cells are set to the erase_char
+ * (or DEFAULT_ATTR + 'E')
+ *
+ * ATTR_INVALID is an illegal colour combination.
+ */
+
+#define TATTR_ACTCURS      0x4UL      /* active cursor (block) */
+#define TATTR_PASCURS      0x2UL      /* passive cursor (box) */
+#define TATTR_RIGHTCURS            0x1UL      /* cursor-on-RHS */
 
 #define LATTR_NORM   0x00000000UL
 #define LATTR_WIDE   0x01000000UL
 #define LATTR_TOP    0x02000000UL
 #define LATTR_BOT    0x03000000UL
 #define LATTR_MODE   0x03000000UL
-
-#define ATTR_ASCII   0x00000000UL      /* normal ASCII charset ESC ( B */
-#define ATTR_GBCHR   0x00100000UL      /* UK variant   charset ESC ( A */
-#define ATTR_LINEDRW 0x00200000UL      /* line drawing charset ESC ( 0 */
-
-#define ATTR_BOLD    0x00000100UL
-#define ATTR_UNDER   0x00000200UL
-#define ATTR_REVERSE 0x00000400UL
-#define ATTR_BLINK   0x00000800UL
-#define ATTR_FGMASK  0x0000F000UL
-#define ATTR_BGMASK  0x000F0000UL
-#define ATTR_FGSHIFT 12
-#define ATTR_BGSHIFT 16
-
-#define ATTR_DEFAULT 0x00098000UL
-#define ATTR_DEFFG   0x00008000UL
-#define ATTR_DEFBG   0x00090000UL
-#define ATTR_CUR_XOR 0x000BA000UL
+#define LATTR_WRAPPED 0x10000000UL
+
+#define ATTR_INVALID 0x00FF0000UL
+
+/* Like Linux use the F000 page for direct to font. */
+#define ATTR_OEMCP   0x0000F000UL      /* OEM Codepage DTF */
+#define ATTR_ACP     0x0000F100UL      /* Ansi Codepage DTF */
+
+/* These are internal use overlapping with the UTF-16 surrogates */
+#define ATTR_ASCII   0x0000D800UL      /* normal ASCII charset ESC ( B */
+#define ATTR_LINEDRW 0x0000D900UL      /* line drawing charset ESC ( 0 */
+#define ATTR_GBCHR   0x0000DB00UL      /* UK variant   charset ESC ( A */
+#define CSET_MASK    0x0000FF00UL      /* Character set mask; MUST be 0xFF00 */
+
+#define DIRECT_CHAR(c) ((c&0xFC00)==0xD800)
+#define DIRECT_FONT(c) ((c&0xFE00)==0xF000)
+
+#define UCSERR      (ATTR_LINEDRW|'a') /* UCS Format error character. */
+#define UCSWIDE             0x303F
+
+#define ATTR_WIDE    0x10000000UL
+#define ATTR_BOLD    0x01000000UL
+#define ATTR_UNDER   0x02000000UL
+#define ATTR_REVERSE 0x04000000UL
+#define ATTR_BLINK   0x08000000UL
+#define ATTR_FGMASK  0x000F0000UL
+#define ATTR_BGMASK  0x00F00000UL
+#define ATTR_COLOURS 0x00FF0000UL
+#define ATTR_FGSHIFT 16
+#define ATTR_BGSHIFT 20
+
+#define ATTR_DEFAULT 0x00980000UL
+#define ATTR_DEFFG   0x00080000UL
+#define ATTR_DEFBG   0x00900000UL
 #define ERASE_CHAR   (ATTR_DEFAULT | ' ')
 #define ATTR_MASK    0xFFFFFF00UL
 #define CHAR_MASK    0x000000FFUL
-#define CSET_MASK    0x00F00000UL      /* mask for character set */
+
+#define ATTR_CUR_AND (~(ATTR_BOLD|ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS))
+#define ATTR_CUR_XOR 0x00BA0000UL
 
 typedef HDC Context;
 #define SEL_NL { 13, 10 }
@@ -83,6 +113,19 @@ GLOBAL int seen_disp_event;
 
 GLOBAL int session_closed;
 
+GLOBAL int big_cursor;
+
+GLOBAL int utf;
+GLOBAL int dbcs_screenfont;
+GLOBAL int font_codepage;
+GLOBAL int kbd_codepage;
+GLOBAL int line_codepage;
+GLOBAL WCHAR unitab_line[256];
+GLOBAL WCHAR unitab_font[256];
+GLOBAL WCHAR unitab_xterm[256];
+GLOBAL WCHAR unitab_oemcp[256];
+GLOBAL unsigned char unitab_ctrl[256];
+
 #define LGXF_OVR  1                   /* existing logfile overwrite */
 #define LGXF_APN  0                   /* existing logfile append */
 #define LGXF_ASK -1                   /* existing logfile ask */
@@ -123,7 +166,7 @@ typedef enum {
 } Mouse_Action;
 
 typedef enum {
-    VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN
+    VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN, VT_UNICODE
 } VT_Mode;
 
 enum {
@@ -260,10 +303,7 @@ typedef struct {
     short wordness[256];
     /* translations */
     VT_Mode vtmode;
-    int xlat_enablekoiwin;
-    int xlat_88592w1250;
-    int xlat_88592cp852;
-    int xlat_capslockcyr;
+    char line_codepage[32];
     /* X11 forwarding */
     int x11_forward;
     char x11_display[128];
@@ -310,6 +350,7 @@ struct RSAKey;                             /* be a little careful of scope */
  */
 void request_resize(int, int, int);
 void do_text(Context, int, int, char *, int, unsigned long, int);
+void do_cursor(Context, int, int, char *, int, unsigned long, int);
 void set_title(char *);
 void set_icon(char *);
 void set_sbar(int, int, int);
@@ -317,8 +358,9 @@ Context get_ctx(void);
 void free_ctx(Context);
 void palette_set(int, int, int, int);
 void palette_reset(void);
-void write_clip(void *, int, int);
-void get_clip(void **, int *);
+void write_aclip(char *, int, int);
+void write_clip(wchar_t *, int, int);
+void get_clip(wchar_t **, int *);
 void optimised_move(int, int, int);
 void set_raw_mouse_mode(int);
 Mouse_Button translate_button(Mouse_Button b);
@@ -448,11 +490,17 @@ void UpdateSizeTip(HWND src, int cx, int cy);
 void EnableSizeTip(int bEnable);
 
 /*
- * Exports from xlat.c.
+ * Exports from unicode.c.
  */
-unsigned char xlat_kbd2tty(unsigned char c);
-unsigned char xlat_tty2scr(unsigned char c);
-unsigned char xlat_latkbd2win(unsigned char c);
+#ifndef CP_UTF8
+#define CP_UTF8 65001
+#endif
+void init_ucs_tables(void);
+void lpage_send(int codepage, char *buf, int len);
+void luni_send(wchar_t * widebuf, int len);
+int check_compose(int first, int second);
+int decode_codepage(char *cp_name);
+char *cp_name(int codepage);
 
 /*
  * Exports from mscrypto.c
index 4493966..35fe436 100644 (file)
@@ -150,10 +150,7 @@ void save_settings(char *section, int do_host, Config * cfg)
        }
        write_setting_s(sesskey, buf, buf2);
     }
-    write_setting_i(sesskey, "KoiWinXlat", cfg->xlat_enablekoiwin);
-    write_setting_i(sesskey, "88592Xlat", cfg->xlat_88592w1250);
-    write_setting_i(sesskey, "88592-CP852", cfg->xlat_88592cp852);
-    write_setting_i(sesskey, "CapsLockCyr", cfg->xlat_capslockcyr);
+    write_setting_s(sesskey, "LineCodePage", cfg->line_codepage);
     write_setting_i(sesskey, "ScrollBar", cfg->scrollbar);
     write_setting_i(sesskey, "ScrollOnKey", cfg->scroll_on_key);
     write_setting_i(sesskey, "ScrollOnDisp", cfg->scroll_on_disp);
@@ -353,10 +350,8 @@ void load_settings(char *section, int do_host, Config * cfg)
            cfg->wordness[j] = atoi(q);
        }
     }
-    gppi(sesskey, "KoiWinXlat", 0, &cfg->xlat_enablekoiwin);
-    gppi(sesskey, "88592Xlat", 0, &cfg->xlat_88592w1250);
-    gppi(sesskey, "88592-CP852", 0, &cfg->xlat_88592cp852);
-    gppi(sesskey, "CapsLockCyr", 0, &cfg->xlat_capslockcyr);
+    gpps(sesskey, "LineCodePage", "", cfg->line_codepage,
+        sizeof(cfg->line_codepage));
     gppi(sesskey, "ScrollBar", 1, &cfg->scrollbar);
     gppi(sesskey, "ScrollOnKey", 0, &cfg->scroll_on_key);
     gppi(sesskey, "ScrollOnDisp", 1, &cfg->scroll_on_disp);
index 1fb7fce..9abb848 100644 (file)
@@ -9,6 +9,8 @@
 #include "putty.h"
 #include "tree234.h"
 
+#define VT52_PLUS
+
 #define CL_ANSIMIN     0x0001         /* Codes in all ANSI like terminals. */
 #define CL_VT100       0x0002         /* VT100 */
 #define CL_VT100AVO    0x0004         /* VT100 +AVO; 132x24 (not 132x14) & attrs */
@@ -54,7 +56,8 @@ static int disptop;                  /* distance scrolled back (0 or -ve) */
 static unsigned long *cpos;           /* cursor position (convenience) */
 
 static unsigned long *disptext;               /* buffer of text on real screen */
-static unsigned long *wanttext;               /* buffer of text we want on screen */
+static unsigned long *dispcurs;               /* location of cursor on real screen */
+static unsigned long curstype;        /* type of cursor on real screen */
 
 #define VBELL_TIMEOUT 100             /* millisecond len of visual bell */
 
@@ -67,8 +70,6 @@ int nbeeps;
 int beep_overloaded;
 long lastbeep;
 
-static unsigned char *selspace;               /* buffer for building selections in */
-
 #define TSIZE (sizeof(unsigned long))
 #define fix_cpos do { cpos = lineptr(curs.y) + curs.x; } while(0)
 
@@ -103,6 +104,10 @@ static int tblinker;                      /* When the blinking text is on */
 static int blink_is_real;             /* Actually blink blinking text */
 static int term_echoing;              /* Does terminal want local echo? */
 static int term_editing;              /* Does terminal want local edit? */
+static int vt52_bold;                 /* Force bold on non-bold colours */
+static int utf_state;                 /* Is there a pending UTF-8 character */
+static int utf_char;                  /* and what is it so far. */
+static int utf_size;                  /* The size of the UTF character. */
 
 static int xterm_mouse;                       /* send mouse messages to app */
 
@@ -142,14 +147,13 @@ static enum {
 
     DO_CTRLS,
 
-    IGNORE_NEXT,
-    SET_GL, SET_GR,
     SEEN_OSC_P,
     OSC_STRING, OSC_MAYBE_ST,
-    SEEN_ESCHASH,
     VT52_ESC,
     VT52_Y1,
-    VT52_Y2
+    VT52_Y2,
+    VT52_FG,
+    VT52_BG
 } termstate;
 
 static enum {
@@ -179,8 +183,9 @@ static short wordness[256] = {
     2, 2, 2, 2, 2, 2, 2, 2,           /* EF */
 };
 
-static unsigned char sel_nl[] = SEL_NL;
-static char *paste_buffer = 0;
+#define sel_nl_sz  (sizeof(sel_nl)/sizeof(wchar_t))
+static wchar_t sel_nl[] = SEL_NL;
+static wchar_t *paste_buffer = 0;
 static int paste_len, paste_pos, paste_hold;
 
 /*
@@ -262,6 +267,7 @@ static void power_on(void)
     rvideo = 0;
     in_vbell = FALSE;
     cursor_on = 1;
+    big_cursor = 0;
     save_attr = curr_attr = ATTR_DEFAULT;
     term_editing = term_echoing = FALSE;
     ldisc_send(NULL, 0);              /* cause ldisc to notice changes */
@@ -336,9 +342,8 @@ void term_init(void)
 {
     screen = alt_screen = scrollback = NULL;
     disptop = 0;
-    disptext = wanttext = NULL;
+    disptext = dispcurs = NULL;
     tabs = NULL;
-    selspace = NULL;
     deselect();
     rows = cols = -1;
     power_on();
@@ -354,7 +359,7 @@ void term_init(void)
 void term_size(int newrows, int newcols, int newsavelines)
 {
     tree234 *newsb, *newscreen, *newalt;
-    unsigned long *newdisp, *newwant, *oldline, *line;
+    unsigned long *newdisp, *oldline, *line;
     int i, j, ccols;
     int sblen;
     int save_alt_which = alt_which;
@@ -426,12 +431,7 @@ void term_size(int newrows, int newcols, int newsavelines)
        newdisp[i] = ATTR_INVALID;
     sfree(disptext);
     disptext = newdisp;
-
-    newwant = smalloc(newrows * (newcols + 1) * TSIZE);
-    for (i = 0; i < newrows * (newcols + 1); i++)
-       newwant[i] = ATTR_INVALID;
-    sfree(wanttext);
-    wanttext = newwant;
+    dispcurs = NULL;
 
     newalt = newtree234(NULL);
     for (i = 0; i < newrows; i++) {
@@ -448,10 +448,6 @@ void term_size(int newrows, int newcols, int newsavelines)
     }
     alt_screen = newalt;
 
-    sfree(selspace);
-    selspace =
-       smalloc((newrows + newsavelines) * (newcols + sizeof(sel_nl)));
-
     tabs = srealloc(tabs, newcols * sizeof(*tabs));
     {
        int i;
@@ -731,7 +727,7 @@ static void erase_lots(int line_only, int from_begin, int to_end)
     ldata = lineptr(start.y);
     while (poslt(start, end)) {
        if (start.y == cols && !erase_lattr)
-           ldata[start.x] &= ~ATTR_WRAPPED;
+           ldata[start.x] &= ~LATTR_WRAPPED;
        else
            ldata[start.x] = erase_char;
        if (incpos(start) && start.y < rows)
@@ -784,6 +780,12 @@ static void toggle_mode(int mode, int query, int state)
            break;
          case 2:                      /* VT52 mode */
            vt52_mode = !state;
+           if (vt52_mode) {
+               blink_is_real = FALSE;
+               vt52_bold = FALSE;
+           } else {
+               blink_is_real = cfg.blinktext;
+           }
            break;
          case 3:                      /* 80/132 columns */
            deselect();
@@ -857,6 +859,9 @@ static void toggle_mode(int mode, int query, int state)
          case 20:                     /* Return sends ... */
            cr_lf_return = state;
            break;
+         case 34:                     /* Make cursor BIG */
+           compatibility2(OTHER, VT220);
+           big_cursor = !state;
        }
 }
 
@@ -907,8 +912,149 @@ void term_out(void)
         * be able to display 8-bit characters, but I'll let that go 'cause
         * of i18n.
         */
-       if (((c & 0x60) == 0 || c == '\177') &&
-           termstate < DO_CTRLS && ((c & 0x80) == 0 || has_compat(VT220))) {
+
+       /* First see about all those translations. */
+       if (termstate == TOPLEVEL) {
+           if (utf)
+               switch (utf_state) {
+                 case 0:
+                   if (c < 0x80) {
+                       /* I know; gotos are evil. This one is really bad!
+                        * But before you try removing it follow the path of the
+                        * sequence "0x5F 0xC0 0x71" with UTF and VTGraphics on.
+                        */
+                       /*
+                          if (cfg.no_vt_graph_with_utf8) break;
+                        */
+                       goto evil_jump;
+                   } else if ((c & 0xe0) == 0xc0) {
+                       utf_size = utf_state = 1;
+                       utf_char = (c & 0x1f);
+                   } else if ((c & 0xf0) == 0xe0) {
+                       utf_size = utf_state = 2;
+                       utf_char = (c & 0x0f);
+                   } else if ((c & 0xf8) == 0xf0) {
+                       utf_size = utf_state = 3;
+                       utf_char = (c & 0x07);
+                   } else if ((c & 0xfc) == 0xf8) {
+                       utf_size = utf_state = 4;
+                       utf_char = (c & 0x03);
+                   } else if ((c & 0xfe) == 0xfc) {
+                       utf_size = utf_state = 5;
+                       utf_char = (c & 0x01);
+                   } else {
+                       c = UCSERR;
+                       break;
+                   }
+                   continue;
+                 case 1:
+                 case 2:
+                 case 3:
+                 case 4:
+                 case 5:
+                   if ((c & 0xC0) != 0x80) {
+                       inbuf_reap--;  /* This causes the faulting character */
+                       c = UCSERR;    /* to be logged twice - not really a */
+                       utf_state = 0; /* serious problem. */
+                       break;
+                   }
+                   utf_char = (utf_char << 6) | (c & 0x3f);
+                   if (--utf_state)
+                       continue;
+
+                   c = utf_char;
+
+                   /* Is somebody trying to be evil! */
+                   if (c < 0x80 ||
+                       (c < 0x800 && utf_size >= 2) ||
+                       (c < 0x10000 && utf_size >= 3) ||
+                       (c < 0x200000 && utf_size >= 4) ||
+                       (c < 0x4000000 && utf_size >= 5))
+                       c = UCSERR;
+
+                   /* Unicode line separator and paragraph separator are CR-LF */
+                   if (c == 0x2028 || c == 0x2029)
+                       c = 0x85;
+
+                   /* High controls are probably a Baaad idea too. */
+                   if (c < 0xA0)
+                       c = 0xFFFD;
+
+                   /* The UTF-16 surrogates are not nice either. */
+                   /*       The standard give the option of decoding these: 
+                    *       I don't want to! */
+                   if (c >= 0xD800 && c < 0xE000)
+                       c = UCSERR;
+
+                   /* ISO 10646 characters now limited to UTF-16 range. */
+                   if (c > 0x10FFFF)
+                       c = UCSERR;
+
+                   /* This is currently a TagPhobic application.. */
+                   if (c >= 0xE0000 && c <= 0xE007F)
+                       continue;
+
+                   /* U+FEFF is best seen as a null. */
+                   if (c == 0xFEFF)
+                       continue;
+                   /* But U+FFFE is an error. */
+                   if (c == 0xFFFE || c == 0xFFFF)
+                       c = UCSERR;
+
+                   /* Oops this is a 16bit implementation */
+                   if (c >= 0x10000)
+                       c = 0xFFFD;
+                   break;
+           } else {
+             evil_jump:;
+               switch (cset_attr[cset]) {
+                   /* 
+                    * Linedraw characters are different from 'ESC ( B'
+                    * only for a small range. For ones outside that
+                    * range, make sure we use the same font as well as
+                    * the same encoding.
+                    */
+                 case ATTR_LINEDRW:
+                   if (unitab_ctrl[c] != 0xFF)
+                       c = unitab_ctrl[c];
+                   else
+                       c = ((unsigned char) c) | ATTR_LINEDRW;
+                   break;
+
+                 case ATTR_GBCHR:
+                   /* If UK-ASCII, make the '#' a LineDraw Pound */
+                   if (c == '#') {
+                       c = '}' | ATTR_LINEDRW;
+                       break;
+                   }
+                 /*FALLTHROUGH*/ case ATTR_ASCII:
+                   if (unitab_ctrl[c] != 0xFF)
+                       c = unitab_ctrl[c];
+                   else
+                       c = ((unsigned char) c) | ATTR_ASCII;
+                   break;
+               }
+           }
+       }
+
+       /* How about C1 controls ? */
+       if ((c & -32) == 0x80 && termstate < DO_CTRLS && !vt52_mode &&
+           has_compat(VT220)) {
+           termstate = SEEN_ESC;
+           esc_query = FALSE;
+           c = '@' + (c & 0x1F);
+       }
+
+       /* Or the GL control. */
+       if (c == '\177' && termstate < DO_CTRLS && has_compat(OTHER)) {
+           if (curs.x && !wrapnext)
+               curs.x--;
+           wrapnext = FALSE;
+           fix_cpos;
+           *cpos = (' ' | curr_attr | ATTR_ASCII);
+       } else
+           /* Or normal C0 controls. */
+       if ((c & -32) == 0 && termstate < DO_CTRLS) {
            switch (c) {
              case '\005':             /* terminal type query */
                /* Strictly speaking this is VT100 but a VT100 defaults to
@@ -936,9 +1082,9 @@ void term_out(void)
                        } else if (*s == '^') {
                            state = 1;
                        } else
-                           *d++ = xlat_kbd2tty((unsigned char) *s);
+                           *d++ = *s;
                    }
-                   ldisc_send(abuf, d - abuf);
+                   lpage_send(CP_ACP, abuf, d - abuf);
                }
                break;
              case '\007':
@@ -1034,20 +1180,9 @@ void term_out(void)
                else {
                    compatibility(ANSIMIN);
                    termstate = SEEN_ESC;
+                   esc_query = FALSE;
                }
                break;
-             case 0233:
-               compatibility(VT220);
-               termstate = SEEN_CSI;
-               esc_nargs = 1;
-               esc_args[0] = ARG_DEFAULT;
-               esc_query = FALSE;
-               break;
-             case 0235:
-               compatibility(VT220);
-               termstate = SEEN_OSC;
-               esc_args[0] = 0;
-               break;
              case '\r':
                curs.x = 0;
                wrapnext = FALSE;
@@ -1102,22 +1237,13 @@ void term_out(void)
                }
                seen_disp_event = TRUE;
                break;
-             case '\177':             /* Destructive backspace
-                                         This does nothing on a real VT100 */
-               compatibility(OTHER);
-               if (curs.x && !wrapnext)
-                   curs.x--;
-               wrapnext = FALSE;
-               fix_cpos;
-               *cpos = (' ' | curr_attr | ATTR_ASCII);
-               break;
            }
        } else
            switch (termstate) {
              case TOPLEVEL:
                /* Only graphic characters get this far, ctrls are stripped above */
                if (wrapnext && wrap) {
-                   cpos[1] |= ATTR_WRAPPED;
+                   cpos[1] |= LATTR_WRAPPED;
                    if (curs.y == marg_b)
                        scroll(marg_t, marg_b, 1, TRUE);
                    else if (curs.y < rows - 1)
@@ -1133,49 +1259,49 @@ void term_out(void)
                    incpos(cursplus);
                    check_selection(curs, cursplus);
                }
-               switch (cset_attr[cset]) {
-                   /*
-                    * Linedraw characters are different from 'ESC ( B'
-                    * only for a small range. For ones outside that
-                    * range, make sure we use the same font as well as
-                    * the same encoding.
-                    */
-                 case ATTR_LINEDRW:
-                   if (c < 0x5f || c > 0x7F)
-                       *cpos++ =
-                           xlat_tty2scr((unsigned char) c) | curr_attr |
-                           ATTR_ASCII;
-                   else if (c == 0x5F)
-                       *cpos++ = ' ' | curr_attr | ATTR_ASCII;
-                   else
-                       *cpos++ =
-                           ((unsigned char) c) | curr_attr | ATTR_LINEDRW;
-                   break;
-                 case ATTR_GBCHR:
-                   /* If UK-ASCII, make the '#' a LineDraw Pound */
-                   if (c == '#') {
-                       *cpos++ = '}' | curr_attr | ATTR_LINEDRW;
+               if ((c & CSET_MASK) == ATTR_ASCII || (c & CSET_MASK) == 0)
+                   logtraffic((unsigned char) c, LGTYP_ASCII);
+               {
+                   extern int wcwidth(wchar_t ucs);
+                   int width = 0;
+                   if (DIRECT_CHAR(c))
+                       width = 1;
+                   if (!width)
+                       width = wcwidth((wchar_t) c);
+                   switch (width) {
+                     case 2:
+                       if (curs.x + 1 != cols) {
+                           *cpos++ = c | ATTR_WIDE | curr_attr;
+                           *cpos++ = UCSWIDE | curr_attr;
+                           curs.x++;
+                           break;
+                       }
+                     case 1:
+                       *cpos++ = c | curr_attr;
                        break;
+                     default:
+                       continue;
                    }
-                 /*FALLTHROUGH*/ default:
-                   *cpos = xlat_tty2scr((unsigned char) c) | curr_attr |
-                       (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
-                   logtraffic((unsigned char) c, LGTYP_ASCII);
-                   cpos++;
-                   break;
                }
                curs.x++;
                if (curs.x == cols) {
                    cpos--;
                    curs.x--;
                    wrapnext = TRUE;
+                   if (wrap && vt52_mode) {
+                       cpos[1] |= LATTR_WRAPPED;
+                       if (curs.y == marg_b)
+                           scroll(marg_t, marg_b, 1, TRUE);
+                       else if (curs.y < rows - 1)
+                           curs.y++;
+                       curs.x = 0;
+                       fix_cpos;
+                       wrapnext = FALSE;
+                   }
                }
                seen_disp_event = 1;
                break;
 
-             case IGNORE_NEXT:
-               termstate = TOPLEVEL;
-               break;
              case OSC_MAYBE_ST:
                /*
                 * This state is virtually identical to SEEN_ESC, with the
@@ -1189,12 +1315,15 @@ void term_out(void)
                }
                /* else fall through */
              case SEEN_ESC:
-               termstate = TOPLEVEL;
-               switch (c) {
-                 case ' ':            /* some weird sequence? */
-                   compatibility(VT220);
-                   termstate = IGNORE_NEXT;
+               if (c >= ' ' && c <= '/') {
+                   if (esc_query)
+                       esc_query = -1;
+                   else
+                       esc_query = c;
                    break;
+               }
+               termstate = TOPLEVEL;
+               switch (ANSI(c, esc_query)) {
                  case '[':            /* enter CSI mode */
                    termstate = SEEN_CSI;
                    esc_nargs = 1;
@@ -1207,14 +1336,6 @@ void term_out(void)
                    termstate = SEEN_OSC;
                    esc_args[0] = 0;
                    break;
-                 case '(':            /* should set GL */
-                   compatibility(VT100);
-                   termstate = SET_GL;
-                   break;
-                 case ')':            /* should set GR */
-                   compatibility(VT100);
-                   termstate = SET_GR;
-                   break;
                  case '7':            /* save cursor */
                    compatibility(VT100);
                    save_cursor(TRUE);
@@ -1278,14 +1399,97 @@ void term_out(void)
                    disptop = 0;
                    seen_disp_event = TRUE;
                    break;
-                 case '#':            /* ESC # 8 fills screen with Es :-) */
-                   compatibility(VT100);
-                   termstate = SEEN_ESCHASH;
-                   break;
                  case 'H':            /* set a tab */
                    compatibility(VT100);
                    tabs[curs.x] = TRUE;
                    break;
+
+                 case ANSI('8', '#'):  /* ESC # 8 fills screen with Es :-) */
+                   compatibility(VT100);
+                   {
+                       unsigned long *ldata;
+                       int i, j;
+                       pos scrtop, scrbot;
+
+                       for (i = 0; i < rows; i++) {
+                           ldata = lineptr(i);
+                           for (j = 0; j < cols; j++)
+                               ldata[j] = ATTR_DEFAULT | 'E';
+                           ldata[cols] = 0;
+                       }
+                       disptop = 0;
+                       seen_disp_event = TRUE;
+                       scrtop.x = scrtop.y = 0;
+                       scrbot.x = 0;
+                       scrbot.y = rows;
+                       check_selection(scrtop, scrbot);
+                   }
+                   break;
+
+                 case ANSI('3', '#'):
+                 case ANSI('4', '#'):
+                 case ANSI('5', '#'):
+                 case ANSI('6', '#'):
+                   compatibility(VT100);
+                   {
+                       unsigned long nlattr;
+                       unsigned long *ldata;
+                       switch (ANSI(c, esc_query)) {
+                         case ANSI('3', '#'):
+                           nlattr = LATTR_TOP;
+                           break;
+                         case ANSI('4', '#'):
+                           nlattr = LATTR_BOT;
+                           break;
+                         case ANSI('5', '#'):
+                           nlattr = LATTR_NORM;
+                           break;
+                         case ANSI('6', '#'):
+                           nlattr = LATTR_WIDE;
+                           break;
+                       }
+                       ldata = lineptr(curs.y);
+                       ldata[cols] &= ~LATTR_MODE;
+                       ldata[cols] |= nlattr;
+                   }
+                   break;
+
+                 case ANSI('A', '('):
+                   compatibility(VT100);
+                   cset_attr[0] = ATTR_GBCHR;
+                   break;
+                 case ANSI('B', '('):
+                   compatibility(VT100);
+                   cset_attr[0] = ATTR_ASCII;
+                   break;
+                 case ANSI('0', '('):
+                   compatibility(VT100);
+                   cset_attr[0] = ATTR_LINEDRW;
+                   break;
+
+                 case ANSI('A', ')'):
+                   compatibility(VT100);
+                   cset_attr[1] = ATTR_GBCHR;
+                   break;
+                 case ANSI('B', ')'):
+                   compatibility(VT100);
+                   cset_attr[1] = ATTR_ASCII;
+                   break;
+                 case ANSI('0', ')'):
+                   compatibility(VT100);
+                   cset_attr[1] = ATTR_LINEDRW;
+                   break;
+
+                 case ANSI('8', '%'):  /* Old Linux code */
+                 case ANSI('G', '%'):
+                   compatibility(OTHER);
+                   utf = 1;
+                   break;
+                 case ANSI('@', '%'):
+                   compatibility(OTHER);
+                   if (line_codepage != CP_UTF8)
+                       utf = 0;
+                   break;
                }
                break;
              case SEEN_CSI:
@@ -1502,11 +1706,9 @@ void term_out(void)
                             * this was selected by CSI 7m.
                             *
                             * case 2:
-                            *  This is DIM on the VT100-AVO and VT102
-                            * case 5:
-                            *  This is BLINK on the VT100-AVO and VT102+
+                            *  This is sometimes DIM, eg on the GIGI and Linux
                             * case 8:
-                            *  This is INVIS on the VT100-AVO and VT102
+                            *  This is sometimes INVIS various ANSI.
                             * case 21:
                             *  This like 22 disables BOLD, DIM and INVIS
                             *
@@ -1711,7 +1913,7 @@ void term_out(void)
                         * This first appeared in the VT220, but we do need to get 
                         * back to PuTTY mode so I won't check it.
                         *
-                        * The arg in 40..42 are a PuTTY extension.
+                        * The arg in 40..42,50 are a PuTTY extension.
                         * The 2nd arg, 8bit vs 7bit is not checked.
                         *
                         * Setting VT102 mode should also change the Fkeys to
@@ -1781,24 +1983,6 @@ void term_out(void)
                        break;
                    }
                break;
-             case SET_GL:
-             case SET_GR:
-               /* VT100 only here, checked above */
-               switch (c) {
-                 case 'A':
-                   cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
-                   break;
-                 case '0':
-                   cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
-                   break;
-                 case 'B':
-                 default:             /* specifically, 'B' */
-                   cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
-                   break;
-               }
-               if (!has_compat(VT220) || c != '%')
-                   termstate = TOPLEVEL;
-               break;
              case SEEN_OSC:
                osc_w = FALSE;
                switch (c) {
@@ -1912,55 +2096,6 @@ void term_out(void)
                    osc_strlen = 0;
                }
                break;
-             case SEEN_ESCHASH:
-               {
-                   unsigned long nlattr;
-                   unsigned long *ldata;
-                   int i, j;
-                   pos scrtop, scrbot;
-
-                   switch (c) {
-                     case '8':
-                       for (i = 0; i < rows; i++) {
-                           ldata = lineptr(i);
-                           for (j = 0; j < cols; j++)
-                               ldata[j] = ATTR_DEFAULT | 'E';
-                           ldata[cols] = 0;
-                       }
-                       disptop = 0;
-                       seen_disp_event = TRUE;
-                       scrtop.x = scrtop.y = 0;
-                       scrbot.x = 0;
-                       scrbot.y = rows;
-                       check_selection(scrtop, scrbot);
-                       break;
-
-                     case '3':
-                     case '4':
-                     case '5':
-                     case '6':
-                       switch (c) {
-                         case '3':
-                           nlattr = LATTR_TOP;
-                           break;
-                         case '4':
-                           nlattr = LATTR_BOT;
-                           break;
-                         case '5':
-                           nlattr = LATTR_NORM;
-                           break;
-                         case '6':
-                           nlattr = LATTR_WIDE;
-                           break;
-                       }
-
-                       ldata = lineptr(curs.y);
-                       ldata[cols] &= ~LATTR_MODE;
-                       ldata[cols] |= nlattr;
-                   }
-               }
-               termstate = TOPLEVEL;
-               break;
              case VT52_ESC:
                termstate = TOPLEVEL;
                seen_disp_event = TRUE;
@@ -1977,6 +2112,46 @@ void term_out(void)
                  case 'D':
                    move(curs.x - 1, curs.y, 1);
                    break;
+                   /*
+                    * From the VT100 Manual
+                    * NOTE: The special graphics characters in the VT100
+                    *       are different from those in the VT52
+                    *
+                    * From VT102 manual:
+                    *       137 _  Blank             - Same
+                    *       140 `  Reserved          - Humm.
+                    *       141 a  Solid rectangle   - Similar
+                    *       142 b  1/                - Top half of fraction for the
+                    *       143 c  3/                - subscript numbers below.
+                    *       144 d  5/
+                    *       145 e  7/
+                    *       146 f  Degrees           - Same
+                    *       147 g  Plus or minus     - Same
+                    *       150 h  Right arrow
+                    *       151 i  Ellipsis (dots)
+                    *       152 j  Divide by
+                    *       153 k  Down arrow
+                    *       154 l  Bar at scan 0
+                    *       155 m  Bar at scan 1
+                    *       156 n  Bar at scan 2
+                    *       157 o  Bar at scan 3     - Similar
+                    *       160 p  Bar at scan 4     - Similar
+                    *       161 q  Bar at scan 5     - Similar
+                    *       162 r  Bar at scan 6     - Same
+                    *       163 s  Bar at scan 7     - Similar
+                    *       164 t  Subscript 0
+                    *       165 u  Subscript 1
+                    *       166 v  Subscript 2
+                    *       167 w  Subscript 3
+                    *       170 x  Subscript 4
+                    *       171 y  Subscript 5
+                    *       172 z  Subscript 6
+                    *       173 {  Subscript 7
+                    *       174 |  Subscript 8
+                    *       175 }  Subscript 9
+                    *       176 ~  Paragraph
+                    *
+                    */
                  case 'F':
                    cset_attr[cset = 0] = ATTR_LINEDRW;
                    break;
@@ -2001,6 +2176,7 @@ void term_out(void)
                  case 'K':
                    erase_lots(TRUE, FALSE, TRUE);
                    break;
+#if 0
                  case 'V':
                    /* XXX Print cursor line */
                    break;
@@ -2010,6 +2186,7 @@ void term_out(void)
                  case 'X':
                    /* XXX Stop controller mode */
                    break;
+#endif
                  case 'Y':
                    termstate = VT52_Y1;
                    break;
@@ -2028,7 +2205,9 @@ void term_out(void)
                     *     emulation.
                     */
                    vt52_mode = FALSE;
+                   blink_is_real = cfg.blinktext;
                    break;
+#if 0
                  case '^':
                    /* XXX Enter auto print mode */
                    break;
@@ -2038,6 +2217,105 @@ void term_out(void)
                  case ']':
                    /* XXX Print screen */
                    break;
+#endif
+
+#ifdef VT52_PLUS
+                 case 'E':
+                   /* compatibility(ATARI) */
+                   move(0, 0, 0);
+                   erase_lots(FALSE, FALSE, TRUE);
+                   disptop = 0;
+                   break;
+                 case 'L':
+                   /* compatibility(ATARI) */
+                   if (curs.y <= marg_b)
+                       scroll(curs.y, marg_b, -1, FALSE);
+                   break;
+                 case 'M':
+                   /* compatibility(ATARI) */
+                   if (curs.y <= marg_b)
+                       scroll(curs.y, marg_b, 1, TRUE);
+                   break;
+                 case 'b':
+                   /* compatibility(ATARI) */
+                   termstate = VT52_FG;
+                   break;
+                 case 'c':
+                   /* compatibility(ATARI) */
+                   termstate = VT52_BG;
+                   break;
+                 case 'd':
+                   /* compatibility(ATARI) */
+                   erase_lots(FALSE, TRUE, FALSE);
+                   disptop = 0;
+                   break;
+                 case 'e':
+                   /* compatibility(ATARI) */
+                   cursor_on = TRUE;
+                   break;
+                 case 'f':
+                   /* compatibility(ATARI) */
+                   cursor_on = FALSE;
+                   break;
+                   /* case 'j': Save cursor position - broken on ST */
+                   /* case 'k': Restore cursor position */
+                 case 'l':
+                   /* compatibility(ATARI) */
+                   erase_lots(TRUE, TRUE, TRUE);
+                   curs.x = 0;
+                   wrapnext = FALSE;
+                   fix_cpos;
+                   break;
+                 case 'o':
+                   /* compatibility(ATARI) */
+                   erase_lots(TRUE, TRUE, FALSE);
+                   break;
+                 case 'p':
+                   /* compatibility(ATARI) */
+                   curr_attr |= ATTR_REVERSE;
+                   break;
+                 case 'q':
+                   /* compatibility(ATARI) */
+                   curr_attr &= ~ATTR_REVERSE;
+                   break;
+                 case 'v':            /* wrap Autowrap on - Wyse style */
+                   /* compatibility(ATARI) */
+                   wrap = 1;
+                   break;
+                 case 'w':            /* Autowrap off */
+                   /* compatibility(ATARI) */
+                   wrap = 0;
+                   break;
+
+                 case 'R':
+                   /* compatibility(OTHER) */
+                   vt52_bold = FALSE;
+                   curr_attr = ATTR_DEFAULT;
+                   if (use_bce)
+                       erase_char = (' ' |
+                                     (curr_attr &
+                                      (ATTR_FGMASK | ATTR_BGMASK |
+                                       ATTR_BLINK)));
+                   break;
+                 case 'S':
+                   /* compatibility(VI50) */
+                   curr_attr |= ATTR_UNDER;
+                   break;
+                 case 'W':
+                   /* compatibility(VI50) */
+                   curr_attr &= ~ATTR_UNDER;
+                   break;
+                 case 'U':
+                   /* compatibility(VI50) */
+                   vt52_bold = TRUE;
+                   curr_attr |= ATTR_BOLD;
+                   break;
+                 case 'T':
+                   /* compatibility(VI50) */
+                   vt52_bold = FALSE;
+                   curr_attr &= ~ATTR_BOLD;
+                   break;
+#endif
                }
                break;
              case VT52_Y1:
@@ -2048,6 +2326,39 @@ void term_out(void)
                termstate = TOPLEVEL;
                move(c - ' ', curs.y, 0);
                break;
+
+#ifdef VT52_PLUS
+             case VT52_FG:
+               termstate = TOPLEVEL;
+               curr_attr &= ~ATTR_FGMASK;
+               curr_attr &= ~ATTR_BOLD;
+               curr_attr |= (c & 0x7) << ATTR_FGSHIFT;
+               if ((c & 0x8) || vt52_bold)
+                   curr_attr |= ATTR_BOLD;
+
+               if (use_bce)
+                   erase_char = (' ' |
+                                 (curr_attr &
+                                  (ATTR_FGMASK | ATTR_BGMASK |
+                                   ATTR_BLINK)));
+               break;
+             case VT52_BG:
+               termstate = TOPLEVEL;
+               curr_attr &= ~ATTR_BGMASK;
+               curr_attr &= ~ATTR_BLINK;
+               curr_attr |= (c & 0x7) << ATTR_BGSHIFT;
+
+               /* Note: bold background */
+               if (c & 0x8)
+                   curr_attr |= ATTR_BLINK;
+
+               if (use_bce)
+                   erase_char = (' ' |
+                                 (curr_attr &
+                                  (ATTR_FGMASK | ATTR_BGMASK |
+                                   ATTR_BLINK)));
+               break;
+#endif
            }
        if (selstate != NO_SELECTION) {
            pos cursplus = curs;
@@ -2058,6 +2369,7 @@ void term_out(void)
     inbuf_head = 0;
 }
 
+#if 0
 /*
  * Compare two lines to determine whether they are sufficiently
  * alike to scroll-optimise one to the other. Return the degree of
@@ -2071,6 +2383,7 @@ static int linecmp(unsigned long *a, unsigned long *b)
        n += (*a++ == *b++);
     return n;
 }
+#endif
 
 /*
  * Given a context, update the window. Out of paranoia, we don't
@@ -2078,10 +2391,11 @@ static int linecmp(unsigned long *a, unsigned long *b)
  */
 static void do_paint(Context ctx, int may_optimise)
 {
-    int i, j, start, our_curs_y;
-    unsigned long attr, rv, cursor;
+    int i, j, our_curs_y;
+    unsigned long rv, cursor;
     pos scrpos;
     char ch[1024];
+    long cursor_background = ERASE_CHAR;
     long ticks;
 
     /*
@@ -2093,83 +2407,162 @@ static void do_paint(Context ctx, int may_optimise)
            in_vbell = FALSE;
     }
 
+    rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
+
     /* Depends on:
      * screen array, disptop, scrtop,
      * selection, rv, 
      * cfg.blinkpc, blink_is_real, tblinker, 
-     * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus
+     * curs.y, curs.x, blinker, cfg.blink_cur, cursor_on, has_focus, wrapnext
      */
+
+    /* Has the cursor position or type changed ? */
     if (cursor_on) {
        if (has_focus) {
            if (blinker || !cfg.blink_cur)
-               cursor = ATTR_ACTCURS;
+               cursor = TATTR_ACTCURS;
            else
                cursor = 0;
        } else
-           cursor = ATTR_PASCURS;
+           cursor = TATTR_PASCURS;
        if (wrapnext)
-           cursor |= ATTR_RIGHTCURS;
+           cursor |= TATTR_RIGHTCURS;
     } else
        cursor = 0;
-    rv = (!rvideo ^ !in_vbell ? ATTR_REVERSE : 0);
     our_curs_y = curs.y - disptop;
 
+    if (dispcurs && (curstype != cursor ||
+                    dispcurs !=
+                    disptext + our_curs_y * (cols + 1) + curs.x)) {
+       if (dispcurs > disptext && (dispcurs[-1] & ATTR_WIDE))
+           dispcurs[-1] |= ATTR_INVALID;
+       if ((*dispcurs & ATTR_WIDE))
+           dispcurs[1] |= ATTR_INVALID;
+       *dispcurs |= ATTR_INVALID;
+       curstype = 0;
+    }
+    dispcurs = NULL;
+
+    /* The normal screen data */
     for (i = 0; i < rows; i++) {
        unsigned long *ldata;
        int lattr;
+       int idx, dirty_line, dirty_run;
+       unsigned long attr = 0;
+       int updated_line = 0;
+       int start = 0;
+       int ccount = 0;
+       int last_run_dirty = 0;
+
        scrpos.y = i + disptop;
        ldata = lineptr(scrpos.y);
        lattr = (ldata[cols] & LATTR_MODE);
-       for (j = 0; j <= cols; j++) {
-           unsigned long d = ldata[j];
-           int idx = i * (cols + 1) + j;
+
+       idx = i * (cols + 1);
+       dirty_run = dirty_line = (ldata[cols] != disptext[idx + cols]);
+       disptext[idx + cols] = ldata[cols];
+
+       for (j = 0; j < cols; j++, idx++) {
+           unsigned long tattr, tchar;
+           unsigned long *d = ldata + j;
+           int break_run;
            scrpos.x = j;
 
-           wanttext[idx] = lattr | (((d & ~ATTR_WRAPPED) ^ rv
-                                     ^ (posle(selstart, scrpos) &&
-                                        poslt(scrpos, selend) ?
-                                        ATTR_REVERSE : 0)) |
-                                    (i == our_curs_y
-                                     && j == curs.x ? cursor : 0));
-           if (blink_is_real) {
-               if (has_focus && tblinker && (wanttext[idx] & ATTR_BLINK)) {
-                   wanttext[idx] &= ATTR_MASK;
-                   wanttext[idx] += ' ';
+           tchar = (*d & (CHAR_MASK | CSET_MASK));
+           tattr = (*d & (ATTR_MASK ^ CSET_MASK));
+           switch (tchar & CSET_MASK) {
+             case ATTR_ASCII:
+               tchar = unitab_line[tchar & 0xFF];
+               break;
+             case ATTR_LINEDRW:
+               tchar = unitab_xterm[tchar & 0xFF];
+               break;
+           }
+           tattr |= (tchar & CSET_MASK);
+           tchar &= CHAR_MASK;
+
+           /* Video reversing things */
+           tattr = (tattr ^ rv
+                    ^ (posle(selstart, scrpos) &&
+                       poslt(scrpos, selend) ? ATTR_REVERSE : 0));
+
+           /* 'Real' blinking ? */
+           if (blink_is_real && (tattr & ATTR_BLINK)) {
+               if (has_focus && tblinker) {
+                   tchar = ' ';
+                   tattr &= ~CSET_MASK;
+                   tattr |= ATTR_ACP;
                }
-               wanttext[idx] &= ~ATTR_BLINK;
+               tattr &= ~ATTR_BLINK;
            }
-       }
-    }
 
-    /*
-     * We would perform scrolling optimisations in here, if they
-     * didn't have a nasty tendency to cause the whole sodding
-     * program to hang for a second at speed-critical moments.
-     * We'll leave it well alone...
-     */
+           /* Cursor here ? Save the 'background' */
+           if (i == our_curs_y && j == curs.x) {
+               cursor_background = tattr | tchar;
+               dispcurs = disptext + idx;
+           }
 
-    for (i = 0; i < rows; i++) {
-       int idx = i * (cols + 1);
-       int lattr = (wanttext[idx + cols] & LATTR_MODE);
-       start = -1;
-       for (j = 0; j <= cols; j++, idx++) {
-           unsigned long t = wanttext[idx];
-           int needs_update = (j < cols && t != disptext[idx]);
-           int keep_going = (start != -1 && needs_update &&
-                             (t & ATTR_MASK) == attr &&
-                             j - start < sizeof(ch));
-           if (start != -1 && !keep_going) {
-               do_text(ctx, start, i, ch, j - start, attr, lattr);
-               start = -1;
+           if ((disptext[idx] ^ tattr) & ATTR_WIDE)
+               dirty_line = TRUE;
+
+           break_run = (tattr != attr || j - start >= sizeof(ch));
+
+           /* Special hack for VT100 Linedraw glyphs */
+           if ((attr & CSET_MASK) == 0x2300 && tchar >= 0xBA
+               && tchar <= 0xBD) break_run = TRUE;
+
+           if (!dbcs_screenfont && !dirty_line) {
+               if ((tchar | tattr) == disptext[idx])
+                   break_run = TRUE;
+               else if (!dirty_run && ccount == 1)
+                   break_run = TRUE;
            }
-           if (needs_update) {
-               if (start == -1) {
-                   start = j;
-                   attr = t & ATTR_MASK;
+
+           if (break_run) {
+               if ((dirty_run || last_run_dirty) && ccount > 0) {
+                   do_text(ctx, start, i, ch, ccount, attr, lattr);
+                   updated_line = 1;
+               }
+               start = j;
+               ccount = 0;
+               attr = tattr;
+               if (dbcs_screenfont)
+                   last_run_dirty = dirty_run;
+               dirty_run = dirty_line;
+           }
+
+           if ((tchar | tattr) != disptext[idx])
+               dirty_run = TRUE;
+           ch[ccount++] = (char) tchar;
+           disptext[idx] = tchar | tattr;
+
+           /* If it's a wide char step along to the next one. */
+           if (tattr & ATTR_WIDE) {
+               if (++j < cols) {
+                   idx++;
+                   d++;
+                   /* Cursor is here ? Ouch! */
+                   if (i == our_curs_y && j == curs.x) {
+                       cursor_background = *d;
+                       dispcurs = disptext + idx;
+                   }
+                   if (disptext[idx] != *d)
+                       dirty_run = TRUE;
+                   disptext[idx] = *d;
                }
-               ch[j - start] = (char) (t & CHAR_MASK);
            }
-           disptext[idx] = t;
+       }
+       if (dirty_run && ccount > 0) {
+           do_text(ctx, start, i, ch, ccount, attr, lattr);
+           updated_line = 1;
+       }
+
+       /* Cursor on this line ? (and changed) */
+       if (i == our_curs_y && (curstype != cursor || updated_line)) {
+           ch[0] = (char) (cursor_background & CHAR_MASK);
+           attr = (cursor_background & ATTR_MASK) | cursor;
+           do_cursor(ctx, curs.x, i, ch, 1, attr, lattr);
+           curstype = cursor;
        }
     }
 }
@@ -2267,17 +2660,16 @@ void term_scroll(int rel, int where)
     term_update();
 }
 
-static void clipme(pos top, pos bottom, char *workbuf)
+static void clipme(pos top, pos bottom)
 {
-    char *wbptr;                      /* where next char goes within workbuf */
+    wchar_t *workbuf;
+    wchar_t *wbptr;                   /* where next char goes within workbuf */
     int wblen = 0;                    /* workbuf len */
     int buflen;                               /* amount of memory allocated to workbuf */
 
-    if (workbuf != NULL) {            /* user supplied buffer? */
-       buflen = -1;                   /* assume buffer passed in is big enough */
-       wbptr = workbuf;               /* start filling here */
-    } else
-       buflen = 0;                    /* No data is available yet */
+    buflen = 5120;                    /* Default size */
+    workbuf = smalloc(buflen * sizeof(wchar_t));
+    wbptr = workbuf;                  /* start filling here */
 
     while (poslt(top, bottom)) {
        int nl = FALSE;
@@ -2287,49 +2679,94 @@ static void clipme(pos top, pos bottom, char *workbuf)
        nlpos.y = top.y;
        nlpos.x = cols;
 
-       if (!(ldata[cols] & ATTR_WRAPPED)) {
-           while ((ldata[nlpos.x - 1] & CHAR_MASK) == 0x20
-                  && poslt(top, nlpos)) decpos(nlpos);
+       if (!(ldata[cols] & LATTR_WRAPPED)) {
+           while (((ldata[nlpos.x - 1] & 0xFF) == 0x20 ||
+                   (DIRECT_CHAR(ldata[nlpos.x - 1]) &&
+                    (ldata[nlpos.x - 1] & CHAR_MASK) == 0x20))
+                  && poslt(top, nlpos))
+               decpos(nlpos);
            if (poslt(nlpos, bottom))
                nl = TRUE;
        }
        while (poslt(top, bottom) && poslt(top, nlpos)) {
-           int ch = (ldata[top.x] & CHAR_MASK);
-           int set = (ldata[top.x] & CSET_MASK);
-
-           /* VT Specials -> ISO8859-1 for Cut&Paste */
-           static const unsigned char poorman2[] =
-               "* # HTFFCRLF\xB0 \xB1 NLVT+ + + + + - - - - - + + + + | <=>=PI!=\xA3 \xB7 ";
-
-           if (set && !cfg.rawcnp) {
-               if (set == ATTR_LINEDRW && ch >= 0x60 && ch < 0x7F) {
-                   int x;
-                   if ((x = poorman2[2 * (ch - 0x60) + 1]) == ' ')
-                       x = 0;
-                   ch = (x << 8) + poorman2[2 * (ch - 0x60)];
+#if 0
+           char cbuf[16], *p;
+           sprintf(cbuf, "<U+%04x>", (ldata[top.x] & 0xFFFF));
+#else
+           wchar_t cbuf[16], *p;
+           int uc = (ldata[top.x] & 0xFFFF);
+           int set, c;
+
+           if (uc == UCSWIDE) {
+               top.x++;
+               continue;
+           }
+
+           switch (uc & CSET_MASK) {
+             case ATTR_LINEDRW:
+               if (!cfg.rawcnp) {
+                   uc = unitab_xterm[uc & 0xFF];
+                   break;
                }
+             case ATTR_ASCII:
+               uc = unitab_line[uc & 0xFF];
+               break;
            }
+           switch (uc & CSET_MASK) {
+             case ATTR_ACP:
+               uc = unitab_font[uc & 0xFF];
+               break;
+             case ATTR_OEMCP:
+               uc = unitab_oemcp[uc & 0xFF];
+               break;
+           }
+
+           set = (uc & CSET_MASK);
+           c = (uc & CHAR_MASK);
+           cbuf[0] = uc;
+           cbuf[1] = 0;
+
+           if (DIRECT_FONT(uc)) {
+               if (c >= ' ' && c != 0x7F) {
+                   unsigned char buf[4];
+                   WCHAR wbuf[4];
+                   int rv;
+                   if (IsDBCSLeadByteEx(font_codepage, (BYTE) c)) {
+                       buf[0] = c;
+                       buf[1] = (unsigned char) ldata[top.x + 1];
+                       rv = MultiByteToWideChar(font_codepage,
+                                                0, buf, 2, wbuf, 4);
+                       top.x++;
+                   } else {
+                       buf[0] = c;
+                       rv = MultiByteToWideChar(font_codepage,
+                                                0, buf, 1, wbuf, 4);
+                   }
 
-           while (ch != 0) {
-               if (cfg.rawcnp || !!(ch & 0xE0)) {
-                   if (wblen == buflen) {
-                       workbuf = srealloc(workbuf, buflen += 100);
-                       wbptr = workbuf + wblen;
+                   if (rv > 0) {
+                       memcpy(cbuf, wbuf, rv * sizeof(wchar_t));
+                       cbuf[rv] = 0;
                    }
-                   wblen++;
-                   *wbptr++ = (unsigned char) ch;
                }
-               ch >>= 8;
+           }
+#endif
+
+           for (p = cbuf; *p; p++) {
+               /* Enough overhead for trailing NL and nul */
+               if (wblen >= buflen - 16) {
+                   workbuf =
+                       srealloc(workbuf,
+                                sizeof(wchar_t) * (buflen += 100));
+                   wbptr = workbuf + wblen;
+               }
+               wblen++;
+               *wbptr++ = *p;
            }
            top.x++;
        }
        if (nl) {
            int i;
-           for (i = 0; i < sizeof(sel_nl); i++) {
-               if (wblen == buflen) {
-                   workbuf = srealloc(workbuf, buflen += 100);
-                   wbptr = workbuf + wblen;
-               }
+           for (i = 0; i < sel_nl_sz; i++) {
                wblen++;
                *wbptr++ = sel_nl[i];
            }
@@ -2337,17 +2774,122 @@ static void clipme(pos top, pos bottom, char *workbuf)
        top.y++;
        top.x = 0;
     }
+    wblen++;
+    *wbptr++ = 0;
     write_clip(workbuf, wblen, FALSE); /* transfer to clipboard */
     if (buflen > 0)                   /* indicates we allocated this buffer */
        sfree(workbuf);
-
 }
+
 void term_copyall(void)
 {
     pos top;
     top.y = -count234(scrollback);
     top.x = 0;
-    clipme(top, curs, NULL /* dynamic allocation */ );
+    clipme(top, curs);
+}
+
+/*
+ * The wordness array is mainly for deciding the disposition of the US-ASCII 
+ * characters.
+ */
+static int wordtype(int uc)
+{
+    static struct {
+       int start, end, ctype;
+    } *wptr, ucs_words[] = {
+       {
+       128, 160, 0}, {
+       161, 191, 1}, {
+       215, 215, 1}, {
+       247, 247, 1}, {
+       0x037e, 0x037e, 1},            /* Greek question mark */
+       {
+       0x0387, 0x0387, 1},            /* Greek ano teleia */
+       {
+       0x055a, 0x055f, 1},            /* Armenian punctuation */
+       {
+       0x0589, 0x0589, 1},            /* Armenian full stop */
+       {
+       0x0700, 0x070d, 1},            /* Syriac punctuation */
+       {
+       0x104a, 0x104f, 1},            /* Myanmar punctuation */
+       {
+       0x10fb, 0x10fb, 1},            /* Georgian punctuation */
+       {
+       0x1361, 0x1368, 1},            /* Ethiopic punctuation */
+       {
+       0x166d, 0x166e, 1},            /* Canadian Syl. punctuation */
+       {
+       0x17d4, 0x17dc, 1},            /* Khmer punctuation */
+       {
+       0x1800, 0x180a, 1},            /* Mongolian punctuation */
+       {
+       0x2000, 0x200a, 0},            /* Various spaces */
+       {
+       0x2070, 0x207f, 2},            /* superscript */
+       {
+       0x2080, 0x208f, 2},            /* subscript */
+       {
+       0x200b, 0x27ff, 1},            /* punctuation and symbols */
+       {
+       0x3000, 0x3000, 0},            /* ideographic space */
+       {
+       0x3001, 0x3020, 1},            /* ideographic punctuation */
+       {
+       0x303f, 0x309f, 3},            /* Hiragana */
+       {
+       0x30a0, 0x30ff, 3},            /* Katakana */
+       {
+       0x3300, 0x9fff, 3},            /* CJK Ideographs */
+       {
+       0xac00, 0xd7a3, 3},            /* Hangul Syllables */
+       {
+       0xf900, 0xfaff, 3},            /* CJK Ideographs */
+       {
+       0xfe30, 0xfe6b, 1},            /* punctuation forms */
+       {
+       0xff00, 0xff0f, 1},            /* half/fullwidth ASCII */
+       {
+       0xff1a, 0xff20, 1},            /* half/fullwidth ASCII */
+       {
+       0xff3b, 0xff40, 1},            /* half/fullwidth ASCII */
+       {
+       0xff5b, 0xff64, 1},            /* half/fullwidth ASCII */
+       {
+       0xfff0, 0xffff, 0},            /* half/fullwidth ASCII */
+       {
+       0, 0, 0}
+    };
+
+    uc &= (CSET_MASK | CHAR_MASK);
+
+    switch (uc & CSET_MASK) {
+      case ATTR_LINEDRW:
+       uc = unitab_xterm[uc & 0xFF];
+       break;
+      case ATTR_ASCII:
+       uc = unitab_line[uc & 0xFF];
+       break;
+    }
+    switch (uc & CSET_MASK) {
+      case ATTR_ACP:
+       uc = unitab_font[uc & 0xFF];
+       break;
+      case ATTR_OEMCP:
+       uc = unitab_oemcp[uc & 0xFF];
+       break;
+    }
+
+    if (uc < 0x80)
+       return wordness[uc];
+
+    for (wptr = ucs_words; wptr->start; wptr++) {
+       if (uc >= wptr->start && uc <= wptr->end)
+           return wptr->ctype;
+    }
+
+    return 2;
 }
 
 /*
@@ -2366,7 +2908,7 @@ static pos sel_spread_half(pos p, int dir)
         * In this mode, every character is a separate unit, except
         * for runs of spaces at the end of a non-wrapping line.
         */
-       if (!(ldata[cols] & ATTR_WRAPPED)) {
+       if (!(ldata[cols] & LATTR_WRAPPED)) {
            unsigned long *q = ldata + cols;
            while (q > ldata && (q[-1] & CHAR_MASK) == 0x20)
                q--;
@@ -2381,15 +2923,13 @@ static pos sel_spread_half(pos p, int dir)
         * In this mode, the units are maximal runs of characters
         * whose `wordness' has the same value.
         */
-       wvalue = wordness[ldata[p.x] & CHAR_MASK];
+       wvalue = wordtype(ldata[p.x]);
        if (dir == +1) {
-           while (p.x < cols
-                  && wordness[ldata[p.x + 1] & CHAR_MASK] ==
-                  wvalue) p.x++;
+           while (p.x < cols && wordtype(ldata[p.x + 1]) == wvalue)
+               p.x++;
        } else {
-           while (p.x > 0
-                  && wordness[ldata[p.x - 1] & CHAR_MASK] ==
-                  wvalue) p.x--;
+           while (p.x > 0 && wordtype(ldata[p.x - 1]) == wvalue)
+               p.x--;
        }
        break;
       case SM_LINE:
@@ -2534,51 +3074,49 @@ void term_mouse(Mouse_Button b, Mouse_Action a, int x, int y,
             * We've completed a selection. We now transfer the
             * data to the clipboard.
             */
-           clipme(selstart, selend, selspace);
+           clipme(selstart, selend);
            selstate = SELECTED;
        } else
            selstate = NO_SELECTION;
     } else if (b == MBT_PASTE
               && (a == MA_CLICK || a == MA_2CLK || a == MA_3CLK)) {
-       char *data;
+       wchar_t *data;
        int len;
 
-       get_clip((void **) &data, &len);
+       get_clip(&data, &len);
        if (data) {
-           char *p, *q;
+           wchar_t *p, *q;
 
            if (paste_buffer)
                sfree(paste_buffer);
            paste_pos = paste_hold = paste_len = 0;
-           paste_buffer = smalloc(len);
+           paste_buffer = smalloc(len * sizeof(wchar_t));
 
            p = q = data;
            while (p < data + len) {
                while (p < data + len &&
-                      !(p <= data + len - sizeof(sel_nl) &&
+                      !(p <= data + len - sel_nl_sz &&
                         !memcmp(p, sel_nl, sizeof(sel_nl))))
                    p++;
 
                {
                    int i;
-                   unsigned char c;
                    for (i = 0; i < p - q; i++) {
-                       c = xlat_kbd2tty(q[i]);
-                       paste_buffer[paste_len++] = c;
+                       paste_buffer[paste_len++] = q[i];
                    }
                }
 
-               if (p <= data + len - sizeof(sel_nl) &&
+               if (p <= data + len - sel_nl_sz &&
                    !memcmp(p, sel_nl, sizeof(sel_nl))) {
                    paste_buffer[paste_len++] = '\r';
-                   p += sizeof(sel_nl);
+                   p += sel_nl_sz;
                }
                q = p;
            }
 
            /* Assume a small paste will be OK in one go. */
            if (paste_len < 256) {
-               ldisc_send(paste_buffer, paste_len);
+               luni_send(paste_buffer, paste_len);
                if (paste_buffer)
                    sfree(paste_buffer);
                paste_buffer = 0;
@@ -2623,7 +3161,7 @@ void term_paste()
            if (paste_buffer[paste_pos + n++] == '\r')
                break;
        }
-       ldisc_send(paste_buffer + paste_pos, n);
+       luni_send(paste_buffer + paste_pos, n);
        paste_pos += n;
 
        if (paste_pos < paste_len) {
diff --git a/unicode.c b/unicode.c
new file mode 100644 (file)
index 0000000..0684045
--- /dev/null
+++ b/unicode.c
@@ -0,0 +1,982 @@
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <time.h>
+#include "putty.h"
+
+void init_ucs_tables(void);
+void lpage_send(int codepage, char *buf, int len);
+void luni_send(wchar_t * widebuf, int len);
+
+static void get_unitab(int codepage, wchar_t * unitab, int ftype);
+
+/* Character conversion arrays; they are usually taken from windows,
+ * the xterm one has the four scanlines that have no unicode 2.0
+ * equlivents mapped into the private area.
+ */
+static char **uni_tbl;
+
+static WCHAR unitab_xterm_std[32] = {
+    0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
+    0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
+    0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
+    0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020
+};
+
+extern struct cp_list_item {
+    char *name;
+    int codepage;
+    int cp_size;
+    wchar_t *cp_table;
+} cp_list[];
+
+static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr);
+
+void init_ucs_tables(void)
+{
+    int i, j;
+    int used_dtf = 0;
+    char tbuf[256];
+    for (i = 0; i < 256; i++)
+       tbuf[i] = i;
+
+    /* Decide on the Line and Font codepages */
+    line_codepage = decode_codepage(cfg.line_codepage);
+
+    if (cfg.vtmode == VT_OEMONLY) {
+       font_codepage = 437;
+       dbcs_screenfont = 0;
+       if (line_codepage <= 0)
+           line_codepage = GetACP();
+    } else if (line_codepage <= 0)
+       line_codepage = font_codepage;
+    utf = (line_codepage == CP_UTF8);
+
+    /* Collect screen font ucs table */
+    if (dbcs_screenfont) {
+       get_unitab(font_codepage, unitab_font, 2);
+       for (i = 128; i < 256; i++)
+           unitab_font[i] = (WCHAR) (ATTR_ACP + i);
+    } else {
+       get_unitab(font_codepage, unitab_font, 1);
+
+       /* CP437 fonts are often broken ... */
+       if (font_codepage == 437)
+           unitab_font[0] = unitab_font[255] = 0xFFFF;
+    }
+    if (cfg.vtmode == VT_XWINDOWS)
+       memcpy(unitab_font + 1, unitab_xterm_std,
+              sizeof(unitab_xterm_std));
+
+    /* Collect OEMCP ucs table */
+    get_unitab(CP_OEMCP, unitab_oemcp, 1);
+
+    /* Collect line set ucs table */
+    if (line_codepage == font_codepage &&
+       (dbcs_screenfont || cfg.vtmode == VT_POORMAN)) {
+
+       /* For DBCS and POOR fonts force direct to font */
+       used_dtf = 1;
+       for (i = 0; i < 32; i++)
+           unitab_line[i] = (WCHAR) i;
+       for (i = 32; i < 256; i++)
+           unitab_line[i] = (WCHAR) (ATTR_ACP + i);
+       unitab_line[127] = (WCHAR) 127;
+    } else {
+       get_unitab(line_codepage, unitab_line, 0);
+    }
+
+#if 0
+    debug(
+         ("Line cp%d, Font cp%d%s\n", line_codepage, font_codepage,
+          dbcs_screenfont ? " DBCS" : ""));
+
+    for (i = 0; i < 256; i += 16) {
+       for (j = 0; j < 16; j++) {
+           debug(("%04x%s", unitab_line[i + j], j == 15 ? "" : ","));
+       }
+       debug(("\n"));
+    }
+#endif
+
+    /* VT100 graphics - NB: Broken for non-ascii CP's */
+    memcpy(unitab_xterm, unitab_line, sizeof(unitab_xterm));
+    memcpy(unitab_xterm + '`', unitab_xterm_std, sizeof(unitab_xterm_std));
+    unitab_xterm['_'] = ' ';
+
+    /* Generate UCS ->line page table. */
+    if (uni_tbl) {
+       for (i = 0; i < 256; i++)
+           if (uni_tbl[i])
+               sfree(uni_tbl[i]);
+       sfree(uni_tbl);
+       uni_tbl = 0;
+    }
+    if (!used_dtf) {
+       for (i = 0; i < 256; i++) {
+           if (DIRECT_CHAR(unitab_line[i]))
+               continue;
+           if (DIRECT_FONT(unitab_line[i]))
+               continue;
+           if (!uni_tbl) {
+               uni_tbl = smalloc(256 * sizeof(char *));
+               memset(uni_tbl, 0, 256 * sizeof(char *));
+           }
+           j = ((unitab_line[i] >> 8) & 0xFF);
+           if (!uni_tbl[j]) {
+               uni_tbl[j] = smalloc(256 * sizeof(char));
+               memset(uni_tbl[j], 0, 256 * sizeof(char));
+           }
+           uni_tbl[j][unitab_line[i] & 0xFF] = i;
+       }
+    }
+
+    /* Find the line control characters. */
+    for (i = 0; i < 256; i++)
+       if (unitab_line[i] < ' '
+           || (unitab_line[i] >= 0x7F && unitab_line[i] < 0xA0))
+           unitab_ctrl[i] = i;
+       else
+           unitab_ctrl[i] = 0xFF;
+
+    /* Generate line->screen direct conversion links. */
+    link_font(unitab_line, unitab_font, ATTR_ACP);
+    link_font(unitab_xterm, unitab_font, ATTR_ACP);
+
+    if (cfg.vtmode == VT_OEMANSI || cfg.vtmode == VT_XWINDOWS) {
+       link_font(unitab_line, unitab_oemcp, ATTR_OEMCP);
+       link_font(unitab_xterm, unitab_oemcp, ATTR_OEMCP);
+    }
+
+    /* Last chance, if !unicode then try poorman links. */
+    if (cfg.vtmode != VT_UNICODE) {
+       static char poorman_latin1[] =
+           " !cL.Y|S\"Ca<--R~o+23'u|.,1o>///?AAAAAAACEEEEIIIIDNOOOOOxOUUUUYPBaaaaaaaceeeeiiiionooooo/ouuuuypy";
+       static char poorman_vt100[] = "*#****o~**+++++-----++++|****L.";
+
+       for (i = 160; i < 256; i++)
+           if (!DIRECT_FONT(unitab_line[i]) &&
+               unitab_line[i] >= 160 && unitab_line[i] < 256)
+               unitab_line[i] = (WCHAR) (ATTR_ACP
+                                         + poorman_latin1[unitab_line[i] -
+                                                          160]);
+       for (i = 96; i < 127; i++)
+           if (!DIRECT_FONT(unitab_xterm[i]))
+               unitab_xterm[i] =
+                   (WCHAR) (ATTR_ACP + poorman_vt100[i - 96]);
+    }
+}
+
+static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr)
+{
+    int i, j, k;
+    for (k = 0; k < 256; k++) {
+       i = ((k + 32) & 0xFF);
+       if (DIRECT_FONT(line_tbl[i]))
+           continue;
+       for (j = 0; j < 256; j++) {
+           if (line_tbl[i] == font_tbl[j]) {
+               line_tbl[i] = (WCHAR) (attr + j);
+               break;
+           }
+       }
+    }
+}
+
+void lpage_send(int codepage, char *buf, int len)
+{
+    static wchar_t *widebuffer = 0;
+    static int widesize = 0;
+    int wclen;
+
+    if (codepage < 0) {
+       ldisc_send(buf, len);
+       return;
+    }
+
+    if (len > widesize) {
+       sfree(widebuffer);
+       widebuffer = smalloc(len * 2 * sizeof(wchar_t));
+       widesize = len * 2;
+    }
+
+    wclen =
+       MultiByteToWideChar(codepage, 0, buf, len, widebuffer, widesize);
+    luni_send(widebuffer, wclen);
+}
+
+void luni_send(wchar_t * widebuf, int len)
+{
+    static char *linebuffer = 0;
+    static int linesize = 0;
+    int ratio = (utf) ? 3 : 1;
+    int i;
+    char *p;
+
+    if (len * ratio > linesize) {
+       sfree(linebuffer);
+       linebuffer = smalloc(len * ratio * 2 * sizeof(wchar_t));
+       linesize = len * ratio * 2;
+    }
+
+    if (utf) {
+       /* UTF is a simple algorithm */
+       for (p = linebuffer, i = 0; i < len; i++) {
+           wchar_t ch = widebuf[i];
+           if (ch < 0x80) {
+               *p++ = (char) (ch);
+           } else if (ch < 0x800) {
+               *p++ = (0xC0 | (ch >> 6));
+               *p++ = (0x80 | (ch & 0x3F));
+           } else {
+               *p++ = (0xE0 | (ch >> 12));
+               *p++ = (0x80 | ((ch >> 6) & 0x3F));
+               *p++ = (0x80 | (ch & 0x3F));
+           }
+       }
+    } else if (!uni_tbl) {
+       int rv;
+       rv = WideCharToMultiByte(line_codepage, 0, widebuf, len,
+                                linebuffer, linesize, NULL, NULL);
+       if (rv >= 0)
+           p = linebuffer + rv;
+       else
+           p = linebuffer;
+    } else {
+       /* Others are a lookup in an array */
+       for (p = linebuffer, i = 0; i < len; i++) {
+           wchar_t ch = widebuf[i];
+           int by;
+           char *p1;
+           if (uni_tbl && (p1 = uni_tbl[(ch >> 8) & 0xFF])
+               && (by = p1[ch & 0xFF]))
+               *p++ = by;
+           else if (ch < 0x80)
+               *p++ = (char) ch;
+#if 1
+           else
+               *p++ = '.';
+#endif
+       }
+    }
+    if (p > linebuffer)
+       ldisc_send(linebuffer, p - linebuffer);
+}
+
+int check_compose(int first, int second)
+{
+
+    static struct {
+       char first, second;
+       wchar_t composed;
+    } composetbl[] = {
+       {
+       0x2b, 0x2b, 0x0023}, {
+       0x41, 0x41, 0x0040}, {
+       0x28, 0x28, 0x005b}, {
+       0x2f, 0x2f, 0x005c}, {
+       0x29, 0x29, 0x005d}, {
+       0x28, 0x2d, 0x007b}, {
+       0x2d, 0x29, 0x007d}, {
+       0x2f, 0x5e, 0x007c}, {
+       0x21, 0x21, 0x00a1}, {
+       0x43, 0x2f, 0x00a2}, {
+       0x43, 0x7c, 0x00a2}, {
+       0x4c, 0x2d, 0x00a3}, {
+       0x4c, 0x3d, 0x00a3}, {
+       0x58, 0x4f, 0x00a4}, {
+       0x58, 0x30, 0x00a4}, {
+       0x59, 0x2d, 0x00a5}, {
+       0x59, 0x3d, 0x00a5}, {
+       0x7c, 0x7c, 0x00a6}, {
+       0x53, 0x4f, 0x00a7}, {
+       0x53, 0x21, 0x00a7}, {
+       0x53, 0x30, 0x00a7}, {
+       0x22, 0x22, 0x00a8}, {
+       0x43, 0x4f, 0x00a9}, {
+       0x43, 0x30, 0x00a9}, {
+       0x41, 0x5f, 0x00aa}, {
+       0x3c, 0x3c, 0x00ab}, {
+       0x2c, 0x2d, 0x00ac}, {
+       0x2d, 0x2d, 0x00ad}, {
+       0x52, 0x4f, 0x00ae}, {
+       0x2d, 0x5e, 0x00af}, {
+       0x30, 0x5e, 0x00b0}, {
+       0x2b, 0x2d, 0x00b1}, {
+       0x32, 0x5e, 0x00b2}, {
+       0x33, 0x5e, 0x00b3}, {
+       0x27, 0x27, 0x00b4}, {
+       0x2f, 0x55, 0x00b5}, {
+       0x50, 0x21, 0x00b6}, {
+       0x2e, 0x5e, 0x00b7}, {
+       0x2c, 0x2c, 0x00b8}, {
+       0x31, 0x5e, 0x00b9}, {
+       0x4f, 0x5f, 0x00ba}, {
+       0x3e, 0x3e, 0x00bb}, {
+       0x31, 0x34, 0x00bc}, {
+       0x31, 0x32, 0x00bd}, {
+       0x33, 0x34, 0x00be}, {
+       0x3f, 0x3f, 0x00bf}, {
+       0x60, 0x41, 0x00c0}, {
+       0x27, 0x41, 0x00c1}, {
+       0x5e, 0x41, 0x00c2}, {
+       0x7e, 0x41, 0x00c3}, {
+       0x22, 0x41, 0x00c4}, {
+       0x2a, 0x41, 0x00c5}, {
+       0x41, 0x45, 0x00c6}, {
+       0x2c, 0x43, 0x00c7}, {
+       0x60, 0x45, 0x00c8}, {
+       0x27, 0x45, 0x00c9}, {
+       0x5e, 0x45, 0x00ca}, {
+       0x22, 0x45, 0x00cb}, {
+       0x60, 0x49, 0x00cc}, {
+       0x27, 0x49, 0x00cd}, {
+       0x5e, 0x49, 0x00ce}, {
+       0x22, 0x49, 0x00cf}, {
+       0x2d, 0x44, 0x00d0}, {
+       0x7e, 0x4e, 0x00d1}, {
+       0x60, 0x4f, 0x00d2}, {
+       0x27, 0x4f, 0x00d3}, {
+       0x5e, 0x4f, 0x00d4}, {
+       0x7e, 0x4f, 0x00d5}, {
+       0x22, 0x4f, 0x00d6}, {
+       0x58, 0x58, 0x00d7}, {
+       0x2f, 0x4f, 0x00d8}, {
+       0x60, 0x55, 0x00d9}, {
+       0x27, 0x55, 0x00da}, {
+       0x5e, 0x55, 0x00db}, {
+       0x22, 0x55, 0x00dc}, {
+       0x27, 0x59, 0x00dd}, {
+       0x48, 0x54, 0x00de}, {
+       0x73, 0x73, 0x00df}, {
+       0x60, 0x61, 0x00e0}, {
+       0x27, 0x61, 0x00e1}, {
+       0x5e, 0x61, 0x00e2}, {
+       0x7e, 0x61, 0x00e3}, {
+       0x22, 0x61, 0x00e4}, {
+       0x2a, 0x61, 0x00e5}, {
+       0x61, 0x65, 0x00e6}, {
+       0x2c, 0x63, 0x00e7}, {
+       0x60, 0x65, 0x00e8}, {
+       0x27, 0x65, 0x00e9}, {
+       0x5e, 0x65, 0x00ea}, {
+       0x22, 0x65, 0x00eb}, {
+       0x60, 0x69, 0x00ec}, {
+       0x27, 0x69, 0x00ed}, {
+       0x5e, 0x69, 0x00ee}, {
+       0x22, 0x69, 0x00ef}, {
+       0x2d, 0x64, 0x00f0}, {
+       0x7e, 0x6e, 0x00f1}, {
+       0x60, 0x6f, 0x00f2}, {
+       0x27, 0x6f, 0x00f3}, {
+       0x5e, 0x6f, 0x00f4}, {
+       0x7e, 0x6f, 0x00f5}, {
+       0x22, 0x6f, 0x00f6}, {
+       0x3a, 0x2d, 0x00f7}, {
+       0x6f, 0x2f, 0x00f8}, {
+       0x60, 0x75, 0x00f9}, {
+       0x27, 0x75, 0x00fa}, {
+       0x5e, 0x75, 0x00fb}, {
+       0x22, 0x75, 0x00fc}, {
+       0x27, 0x79, 0x00fd}, {
+       0x68, 0x74, 0x00fe}, {
+       0x22, 0x79, 0x00ff},
+           /* Unicode extras. */
+       {
+       0x6f, 0x65, 0x0153}, {
+       0x4f, 0x45, 0x0152},
+           /* Compose pairs from UCS */
+       {
+       0x41, 0x2D, 0x0100}, {
+       0x61, 0x2D, 0x0101}, {
+       0x43, 0x27, 0x0106}, {
+       0x63, 0x27, 0x0107}, {
+       0x43, 0x5E, 0x0108}, {
+       0x63, 0x5E, 0x0109}, {
+       0x45, 0x2D, 0x0112}, {
+       0x65, 0x2D, 0x0113}, {
+       0x47, 0x5E, 0x011C}, {
+       0x67, 0x5E, 0x011D}, {
+       0x47, 0x2C, 0x0122}, {
+       0x67, 0x2C, 0x0123}, {
+       0x48, 0x5E, 0x0124}, {
+       0x68, 0x5E, 0x0125}, {
+       0x49, 0x7E, 0x0128}, {
+       0x69, 0x7E, 0x0129}, {
+       0x49, 0x2D, 0x012A}, {
+       0x69, 0x2D, 0x012B}, {
+       0x4A, 0x5E, 0x0134}, {
+       0x6A, 0x5E, 0x0135}, {
+       0x4B, 0x2C, 0x0136}, {
+       0x6B, 0x2C, 0x0137}, {
+       0x4C, 0x27, 0x0139}, {
+       0x6C, 0x27, 0x013A}, {
+       0x4C, 0x2C, 0x013B}, {
+       0x6C, 0x2C, 0x013C}, {
+       0x4E, 0x27, 0x0143}, {
+       0x6E, 0x27, 0x0144}, {
+       0x4E, 0x2C, 0x0145}, {
+       0x6E, 0x2C, 0x0146}, {
+       0x4F, 0x2D, 0x014C}, {
+       0x6F, 0x2D, 0x014D}, {
+       0x52, 0x27, 0x0154}, {
+       0x72, 0x27, 0x0155}, {
+       0x52, 0x2C, 0x0156}, {
+       0x72, 0x2C, 0x0157}, {
+       0x53, 0x27, 0x015A}, {
+       0x73, 0x27, 0x015B}, {
+       0x53, 0x5E, 0x015C}, {
+       0x73, 0x5E, 0x015D}, {
+       0x53, 0x2C, 0x015E}, {
+       0x73, 0x2C, 0x015F}, {
+       0x54, 0x2C, 0x0162}, {
+       0x74, 0x2C, 0x0163}, {
+       0x55, 0x7E, 0x0168}, {
+       0x75, 0x7E, 0x0169}, {
+       0x55, 0x2D, 0x016A}, {
+       0x75, 0x2D, 0x016B}, {
+       0x55, 0x2A, 0x016E}, {
+       0x75, 0x2A, 0x016F}, {
+       0x57, 0x5E, 0x0174}, {
+       0x77, 0x5E, 0x0175}, {
+       0x59, 0x5E, 0x0176}, {
+       0x79, 0x5E, 0x0177}, {
+       0x59, 0x22, 0x0178}, {
+       0x5A, 0x27, 0x0179}, {
+       0x7A, 0x27, 0x017A}, {
+       0x47, 0x27, 0x01F4}, {
+       0x67, 0x27, 0x01F5}, {
+       0x4E, 0x60, 0x01F8}, {
+       0x6E, 0x60, 0x01F9}, {
+       0x45, 0x2C, 0x0228}, {
+       0x65, 0x2C, 0x0229}, {
+       0x59, 0x2D, 0x0232}, {
+       0x79, 0x2D, 0x0233}, {
+       0x44, 0x2C, 0x1E10}, {
+       0x64, 0x2C, 0x1E11}, {
+       0x47, 0x2D, 0x1E20}, {
+       0x67, 0x2D, 0x1E21}, {
+       0x48, 0x22, 0x1E26}, {
+       0x68, 0x22, 0x1E27}, {
+       0x48, 0x2C, 0x1E28}, {
+       0x68, 0x2C, 0x1E29}, {
+       0x4B, 0x27, 0x1E30}, {
+       0x6B, 0x27, 0x1E31}, {
+       0x4D, 0x27, 0x1E3E}, {
+       0x6D, 0x27, 0x1E3F}, {
+       0x50, 0x27, 0x1E54}, {
+       0x70, 0x27, 0x1E55}, {
+       0x56, 0x7E, 0x1E7C}, {
+       0x76, 0x7E, 0x1E7D}, {
+       0x57, 0x60, 0x1E80}, {
+       0x77, 0x60, 0x1E81}, {
+       0x57, 0x27, 0x1E82}, {
+       0x77, 0x27, 0x1E83}, {
+       0x57, 0x22, 0x1E84}, {
+       0x77, 0x22, 0x1E85}, {
+       0x58, 0x22, 0x1E8C}, {
+       0x78, 0x22, 0x1E8D}, {
+       0x5A, 0x5E, 0x1E90}, {
+       0x7A, 0x5E, 0x1E91}, {
+       0x74, 0x22, 0x1E97}, {
+       0x77, 0x2A, 0x1E98}, {
+       0x79, 0x2A, 0x1E99}, {
+       0x45, 0x7E, 0x1EBC}, {
+       0x65, 0x7E, 0x1EBD}, {
+       0x59, 0x60, 0x1EF2}, {
+       0x79, 0x60, 0x1EF3}, {
+       0x59, 0x7E, 0x1EF8}, {
+       0x79, 0x7E, 0x1EF9},
+           /* Compatible/possibles from UCS */
+       {
+       0x49, 0x4A, 0x0132}, {
+       0x69, 0x6A, 0x0133}, {
+       0x4C, 0x4A, 0x01C7}, {
+       0x4C, 0x6A, 0x01C8}, {
+       0x6C, 0x6A, 0x01C9}, {
+       0x4E, 0x4A, 0x01CA}, {
+       0x4E, 0x6A, 0x01CB}, {
+       0x6E, 0x6A, 0x01CC}, {
+       0x44, 0x5A, 0x01F1}, {
+       0x44, 0x7A, 0x01F2}, {
+       0x64, 0x7A, 0x01F3}, {
+       0x2E, 0x2E, 0x2025}, {
+       0x21, 0x21, 0x203C}, {
+       0x3F, 0x21, 0x2048}, {
+       0x21, 0x3F, 0x2049}, {
+       0x52, 0x73, 0x20A8}, {
+       0x4E, 0x6F, 0x2116}, {
+       0x53, 0x4D, 0x2120}, {
+       0x54, 0x4D, 0x2122}, {
+       0x49, 0x49, 0x2161}, {
+       0x49, 0x56, 0x2163}, {
+       0x56, 0x49, 0x2165}, {
+       0x49, 0x58, 0x2168}, {
+       0x58, 0x49, 0x216A}, {
+       0x69, 0x69, 0x2171}, {
+       0x69, 0x76, 0x2173}, {
+       0x76, 0x69, 0x2175}, {
+       0x69, 0x78, 0x2178}, {
+       0x78, 0x69, 0x217A}, {
+       0x31, 0x30, 0x2469}, {
+       0x31, 0x31, 0x246A}, {
+       0x31, 0x32, 0x246B}, {
+       0x31, 0x33, 0x246C}, {
+       0x31, 0x34, 0x246D}, {
+       0x31, 0x35, 0x246E}, {
+       0x31, 0x36, 0x246F}, {
+       0x31, 0x37, 0x2470}, {
+       0x31, 0x38, 0x2471}, {
+       0x31, 0x39, 0x2472}, {
+       0x32, 0x30, 0x2473}, {
+       0x31, 0x2E, 0x2488}, {
+       0x32, 0x2E, 0x2489}, {
+       0x33, 0x2E, 0x248A}, {
+       0x34, 0x2E, 0x248B}, {
+       0x35, 0x2E, 0x248C}, {
+       0x36, 0x2E, 0x248D}, {
+       0x37, 0x2E, 0x248E}, {
+       0x38, 0x2E, 0x248F}, {
+       0x39, 0x2E, 0x2490}, {
+       0x64, 0x61, 0x3372}, {
+       0x41, 0x55, 0x3373}, {
+       0x6F, 0x56, 0x3375}, {
+       0x70, 0x63, 0x3376}, {
+       0x70, 0x41, 0x3380}, {
+       0x6E, 0x41, 0x3381}, {
+       0x6D, 0x41, 0x3383}, {
+       0x6B, 0x41, 0x3384}, {
+       0x4B, 0x42, 0x3385}, {
+       0x4D, 0x42, 0x3386}, {
+       0x47, 0x42, 0x3387}, {
+       0x70, 0x46, 0x338A}, {
+       0x6E, 0x46, 0x338B}, {
+       0x6D, 0x67, 0x338E}, {
+       0x6B, 0x67, 0x338F}, {
+       0x48, 0x7A, 0x3390}, {
+       0x66, 0x6D, 0x3399}, {
+       0x6E, 0x6D, 0x339A}, {
+       0x6D, 0x6D, 0x339C}, {
+       0x63, 0x6D, 0x339D}, {
+       0x6B, 0x6D, 0x339E}, {
+       0x50, 0x61, 0x33A9}, {
+       0x70, 0x73, 0x33B0}, {
+       0x6E, 0x73, 0x33B1}, {
+       0x6D, 0x73, 0x33B3}, {
+       0x70, 0x56, 0x33B4}, {
+       0x6E, 0x56, 0x33B5}, {
+       0x6D, 0x56, 0x33B7}, {
+       0x6B, 0x56, 0x33B8}, {
+       0x4D, 0x56, 0x33B9}, {
+       0x70, 0x57, 0x33BA}, {
+       0x6E, 0x57, 0x33BB}, {
+       0x6D, 0x57, 0x33BD}, {
+       0x6B, 0x57, 0x33BE}, {
+       0x4D, 0x57, 0x33BF}, {
+       0x42, 0x71, 0x33C3}, {
+       0x63, 0x63, 0x33C4}, {
+       0x63, 0x64, 0x33C5}, {
+       0x64, 0x42, 0x33C8}, {
+       0x47, 0x79, 0x33C9}, {
+       0x68, 0x61, 0x33CA}, {
+       0x48, 0x50, 0x33CB}, {
+       0x69, 0x6E, 0x33CC}, {
+       0x4B, 0x4B, 0x33CD}, {
+       0x4B, 0x4D, 0x33CE}, {
+       0x6B, 0x74, 0x33CF}, {
+       0x6C, 0x6D, 0x33D0}, {
+       0x6C, 0x6E, 0x33D1}, {
+       0x6C, 0x78, 0x33D3}, {
+       0x6D, 0x62, 0x33D4}, {
+       0x50, 0x48, 0x33D7}, {
+       0x50, 0x52, 0x33DA}, {
+       0x73, 0x72, 0x33DB}, {
+       0x53, 0x76, 0x33DC}, {
+       0x57, 0x62, 0x33DD}, {
+       0x66, 0x66, 0xFB00}, {
+       0x66, 0x69, 0xFB01}, {
+       0x66, 0x6C, 0xFB02}, {
+       0x73, 0x74, 0xFB06}, {
+       0, 0, 0}
+    }, *c;
+
+    static int recurse = 0;
+    int nc = -1;
+
+    for (c = composetbl; c->first; c++) {
+       if (c->first == first && c->second == second)
+           return c->composed;
+    }
+
+    if (recurse == 0) {
+       recurse = 1;
+       nc = check_compose(second, first);
+       if (nc == -1)
+           nc = check_compose(toupper(first), toupper(second));
+       if (nc == -1)
+           nc = check_compose(toupper(second), toupper(first));
+       recurse = 0;
+    }
+    return nc;
+}
+
+int decode_codepage(char *cp_name)
+{
+    char *s, *d;
+    struct cp_list_item *cpi;
+    int codepage = -1;
+    char ch;
+    CPINFO cpinfo;
+
+    if (cp_name && *cp_name)
+       for (cpi = cp_list; cpi->name; cpi++) {
+           s = cp_name;
+           d = cpi->name;
+           for (;;) {
+               while (*s && !isalnum(*s) && *s != ':')
+                   s++;
+               while (*d && !isalnum(*d) && *d != ':')
+                   d++;
+               if (*s == 0) {
+                   codepage = cpi->codepage;
+                   if (codepage == CP_UTF8)
+                       goto break_break;
+                   if (codepage == 0) {
+                       codepage = 65536 + (cpi - cp_list);
+                       goto break_break;
+                   }
+
+                   if (GetCPInfo(codepage, &cpinfo) != 0)
+                       goto break_break;
+               }
+               if (islower(*s))
+                   ch = toupper(*s++);
+               else
+                   ch = *s++;
+               if (ch != *d++)
+                   break;
+           }
+       }
+
+    if (cp_name && *cp_name) {
+       d = cp_name;
+       if (strnicmp(d, "cp", 2) == 0)
+           d += 2;
+       if (strnicmp(d, "ibm", 3) == 0)
+           d += 3;
+       for (s = d; *s >= '0' && *s <= '9'; s++);
+       if (*s == 0 && s != d)
+           codepage = atoi(d);        /* CP999 or IBM999 */
+
+       if (codepage == CP_ACP)
+           codepage = GetACP();
+       if (codepage == CP_OEMCP)
+           codepage = GetOEMCP();
+       if (codepage > 65535)
+           codepage = -2;
+    }
+
+  break_break:;
+    if (codepage != -1) {
+       if (codepage != CP_UTF8 && codepage < 65536) {
+           if (GetCPInfo(codepage, &cpinfo) == 0) {
+               codepage = -2;
+           } else if (cpinfo.MaxCharSize > 1)
+               codepage = -3;
+       }
+    }
+    if (codepage == -1 && *cp_name)
+       codepage = -2;
+    return codepage;
+}
+
+char *cp_name(int codepage)
+{
+    struct cp_list_item *cpi, *cpno;
+    static char buf[32];
+    if (codepage > 0 && codepage < 65536)
+       sprintf(buf, "CP%03d", codepage);
+    else
+       *buf = 0;
+
+    if (codepage >= 65536) {
+       cpno = 0;
+       for (cpi = cp_list; cpi->name; cpi++)
+           if (cpi == cp_list + (codepage - 65536)) {
+               cpno = cpi;
+               break;
+           }
+       if (cpno)
+           for (cpi = cp_list; cpi->name; cpi++) {
+               if (cpno->cp_table == cpi->cp_table)
+                   return cpi->name;
+           }
+    } else {
+       for (cpi = cp_list; cpi->name; cpi++) {
+           if (codepage == cpi->codepage)
+               return cpi->name;
+       }
+    }
+    return buf;
+}
+
+static void get_unitab(int codepage, wchar_t * unitab, int ftype)
+{
+    char tbuf[4];
+    int i, max = 256, flg = MB_ERR_INVALID_CHARS;
+
+    if (ftype)
+       flg |= MB_USEGLYPHCHARS;
+    if (ftype == 2)
+       max = 128;
+
+    if (codepage == CP_UTF8)
+       codepage = 28591;
+    else if (codepage == CP_ACP)
+       codepage = GetACP();
+    else if (codepage == CP_OEMCP)
+       codepage = GetOEMCP();
+
+    if (codepage > 0 && codepage < 65536) {
+       for (i = 0; i < max; i++) {
+           tbuf[0] = i;
+
+           if (MultiByteToWideChar(codepage, flg, tbuf, 1, unitab + i, 1)
+               != 1)
+               unitab[i] = 0xFFFD;
+       }
+    } else {
+       int j = 256 - cp_list[codepage & 0xFFFF].cp_size;
+       for (i = 0; i < max; i++)
+           unitab[i] = i;
+       for (i = j; i < max; i++)
+           unitab[i] = cp_list[codepage & 0xFFFF].cp_table[i - j];
+    }
+}
+
+/*
+ * From here down is the codepage data, if you want to add your own line
+ * codepage do it here.
+ *
+ * If the codepage is non-zero it's a window codepage, zero means use a
+ * local codepage. The name is always converted to the first of any
+ * duplicate definitions.
+ */
+extern wchar_t iso_8859_10[];
+extern wchar_t iso_8859_11[];
+extern wchar_t iso_8859_12[];
+extern wchar_t iso_8859_14[];
+extern wchar_t iso_8859_15[];
+extern wchar_t roman8[];
+extern wchar_t koi8_u[];
+extern wchar_t vscii[];
+extern wchar_t dec_mcs[];
+
+static struct cp_list_item cp_list[] = {
+    {"UTF-8", CP_UTF8},
+
+    {"ISO-8859-1:1987", 28591},
+    {"ISO-8859-2:1987", 28592},
+    {"ISO-8859-3:1988", 28593},
+    {"ISO-8859-4:1988", 28594},
+    {"ISO-8859-5:1988", 28595},
+    {"ISO-8859-6:1987", 28596},
+    {"ISO-8859-7:1987", 28597},
+    {"ISO-8859-8:1988", 28598},
+    {"ISO-8859-9:1989", 28599},
+    {"ISO-8859-10:1993", 0, 96, iso_8859_10},
+    {"ISO-8859-11", 0, 96, iso_8859_11},
+    {"ISO-8859-12", 0, 96, iso_8859_12},
+    {"ISO-8859-14", 0, 96, iso_8859_14},
+    {"ISO-8859-15:1998", 0, 96, iso_8859_15},
+
+    {"KOI8-U", 0, 128, koi8_u},
+    {"KOI8-R", 20866},
+    {"HP-ROMAN8", 0, 96, roman8},
+    {"VSCII", 0, 256, vscii},
+    {"DEC-MCS", 0, 96, dec_mcs},
+
+/* All below here are aliases */
+    {"ROMAN8", 0, 96, roman8},
+    {"R8", 0, 96, roman8},
+
+    /* Note this is Latin ->> */
+    {"LATIN0", 0, 96, iso_8859_15},
+    {"L0", 0, 96, iso_8859_15},
+
+    {"CP819", 28591},
+    {"CP878", 20866},
+    {"L1", 28591},
+    {"L2", 28592},
+    {"L3", 28593},
+    {"L4", 28594},
+    {"L5", 28599},
+    {"LATIN1", 28591},
+    {"LATIN2", 28592},
+    {"LATIN3", 28593},
+    {"LATIN4", 28594},
+    {"LATIN5", 28599},
+    {0, 0}
+};
+
+static wchar_t iso_8859_10[] = {
+    0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7,
+    0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A,
+    0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7,
+    0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2014, 0x016B, 0x014B,
+    0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E,
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF,
+    0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168,
+    0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+    0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F,
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF,
+    0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169,
+    0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138
+};
+
+static wchar_t iso_8859_11[] = {
+    0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
+    0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
+    0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
+    0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
+    0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
+    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
+    0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
+    0x0E38, 0x0E39, 0x0E3A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0E3F,
+    0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
+    0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
+    0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
+    0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD
+};
+
+static wchar_t iso_8859_12[] = {
+    0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7,
+    0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7,
+    0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+    0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+    0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
+    0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+    0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
+    0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+    0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
+    0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019
+};
+
+static wchar_t iso_8859_14[] = {
+    0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7,
+    0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178,
+    0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56,
+    0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF
+};
+
+static wchar_t iso_8859_15[] = {
+    0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7,
+    0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+    0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7,
+    0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf,
+    0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+    0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+    0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+    0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+    0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+    0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+    0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+    0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+};
+
+static wchar_t roman8[] = {
+    0x00A0, 0x00C0, 0x00C2, 0x00C8, 0x00CA, 0x00CB, 0x00CE, 0x00CF,
+    0x00B4, 0x02CB, 0x02C6, 0x00A8, 0x02DC, 0x00D9, 0x00DB, 0x20A4,
+    0x00AF, 0x00DD, 0x00FD, 0x00B0, 0x00C7, 0x00E7, 0x00D1, 0x00F1,
+    0x00A1, 0x00BF, 0x00A4, 0x00A3, 0x00A5, 0x00A7, 0x0192, 0x00A2,
+    0x00E2, 0x00EA, 0x00F4, 0x00FB, 0x00E1, 0x00E9, 0x00F3, 0x00FA,
+    0x00E0, 0x00E8, 0x00F2, 0x00F9, 0x00E4, 0x00EB, 0x00F6, 0x00FC,
+    0x00C5, 0x00EE, 0x00D8, 0x00C6, 0x00E5, 0x00ED, 0x00F8, 0x00E6,
+    0x00C4, 0x00EC, 0x00D6, 0x00DC, 0x00C9, 0x00EF, 0x00DF, 0x00D4,
+    0x00C1, 0x00C3, 0x00E3, 0x00D0, 0x00F0, 0x00CD, 0x00CC, 0x00D3,
+    0x00D2, 0x00D5, 0x00F5, 0x0160, 0x0161, 0x00DA, 0x0178, 0x00FF,
+    0x00DE, 0x00FE, 0x00B7, 0x00B5, 0x00B6, 0x00BE, 0x2014, 0x00BC,
+    0x00BD, 0x00AA, 0x00BA, 0x00AB, 0x25A0, 0x00BB, 0x00B1, 0xFFFD
+};
+
+static wchar_t koi8_u[] = {
+    0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524,
+    0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590,
+    0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2022, 0x221A, 0x2248,
+    0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7,
+    0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457,
+    0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E,
+    0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407,
+    0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9,
+    0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,
+    0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E,
+    0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,
+    0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A,
+    0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,
+    0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
+    0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,
+    0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A
+};
+
+static wchar_t vscii[] = {
+    0x0000, 0x0001, 0x1EB2, 0x0003, 0x0004, 0x1EB4, 0x1EAA, 0x0007,
+    0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+    0x0010, 0x0011, 0x0012, 0x0013, 0x1EF6, 0x0015, 0x0016, 0x0017,
+    0x0018, 0x1EF8, 0x001a, 0x001b, 0x001c, 0x001d, 0x1EF4, 0x001f,
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+    0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+    0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
+    0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+    0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+    0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+    0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+    0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+    0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007f,
+    0x1EA0, 0x1EAE, 0x1EB0, 0x1EB6, 0x1EA4, 0x1EA6, 0x1EA8, 0x1EAC,
+    0x1EBC, 0x1EB8, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1ED0,
+    0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EE2, 0x1EDA, 0x1EDC, 0x1EDE,
+    0x1ECA, 0x1ECE, 0x1ECC, 0x1EC8, 0x1EE6, 0x0168, 0x1EE4, 0x1EF2,
+    0x00D5, 0x1EAF, 0x1EB1, 0x1EB7, 0x1EA5, 0x1EA7, 0x1EA8, 0x1EAD,
+    0x1EBD, 0x1EB9, 0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1ED1,
+    0x1ED3, 0x1ED5, 0x1ED7, 0x1EE0, 0x01A0, 0x1ED9, 0x1EDD, 0x1EDF,
+    0x1ECB, 0x1EF0, 0x1EE8, 0x1EEA, 0x1EEC, 0x01A1, 0x1EDB, 0x01AF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x1EA2, 0x0102, 0x1EB3, 0x1EB5,
+    0x00C8, 0x00C9, 0x00CA, 0x1EBA, 0x00CC, 0x00CD, 0x0128, 0x1EF3,
+    0x0110, 0x1EE9, 0x00D2, 0x00D3, 0x00D4, 0x1EA1, 0x1EF7, 0x1EEB,
+    0x1EED, 0x00D9, 0x00DA, 0x1EF9, 0x1EF5, 0x00DD, 0x1EE1, 0x01B0,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x1EA3, 0x0103, 0x1EEF, 0x1EAB,
+    0x00E8, 0x00E9, 0x00EA, 0x1EBB, 0x00EC, 0x00ED, 0x0129, 0x1EC9,
+    0x0111, 0x1EF1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x1ECF, 0x1ECD,
+    0x1EE5, 0x00F9, 0x00FA, 0x0169, 0x1EE7, 0x00FD, 0x1EE3, 0x1EEE
+};
+
+static wchar_t dec_mcs[] = {
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0xFFFD, 0x00A5, 0xFFFD, 0x00A7,
+    0x00A4, 0x00A9, 0x00AA, 0x00AB, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0xFFFD, 0x00B5, 0x00B6, 0x00B7,
+    0xFFFD, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0xFFFD, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0152,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0178, 0xFFFD, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0153,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FF, 0xFFFD, 0xFFFD
+};
diff --git a/wcwidth.c b/wcwidth.c
new file mode 100644 (file)
index 0000000..8fa3ee2
--- /dev/null
+++ b/wcwidth.c
@@ -0,0 +1,190 @@
+/*
+ * This is an implementation of wcwidth() and wcswidth() as defined in
+ * "The Single UNIX Specification, Version 2, The Open Group, 1997"
+ * <http://www.UNIX-systems.org/online.html>
+ *
+ * Markus Kuhn -- 2000-02-08 -- public domain
+ */
+
+#include <wchar.h>
+
+/* These functions define the column width of an ISO 10646 character
+ * as follows:
+ *
+ *    - The null character (U+0000) has a column width of 0.
+ *
+ *    - Other C0/C1 control characters and DEL will lead to a return
+ *      value of -1.
+ *
+ *    - Non-spacing and enclosing combining characters (general
+ *      category code Mn or Me in the Unicode database) have a
+ *      column width of 0.
+ *
+ *    - Spacing characters in the East Asian Wide (W) or East Asian
+ *      FullWidth (F) category as defined in Unicode Technical
+ *      Report #11 have a column width of 2.
+ *
+ *    - All remaining characters (including all printable
+ *      ISO 8859-1 and WGL4 characters, Unicode control characters,
+ *      etc.) have a column width of 1.
+ *
+ * This implementation assumes that wchar_t characters are encoded
+ * in ISO 10646.
+ */
+
+int wcwidth(wchar_t ucs)
+{
+    /* sorted list of non-overlapping intervals of non-spacing characters */
+    static const struct interval {
+       unsigned short first;
+       unsigned short last;
+    } combining[] = {
+       {
+       0x0300, 0x034E}, {
+       0x0360, 0x0362}, {
+       0x0483, 0x0486}, {
+       0x0488, 0x0489}, {
+       0x0591, 0x05A1}, {
+       0x05A3, 0x05B9}, {
+       0x05BB, 0x05BD}, {
+       0x05BF, 0x05BF}, {
+       0x05C1, 0x05C2}, {
+       0x05C4, 0x05C4}, {
+       0x064B, 0x0655}, {
+       0x0670, 0x0670}, {
+       0x06D6, 0x06E4}, {
+       0x06E7, 0x06E8}, {
+       0x06EA, 0x06ED}, {
+       0x0711, 0x0711}, {
+       0x0730, 0x074A}, {
+       0x07A6, 0x07B0}, {
+       0x0901, 0x0902}, {
+       0x093C, 0x093C}, {
+       0x0941, 0x0948}, {
+       0x094D, 0x094D}, {
+       0x0951, 0x0954}, {
+       0x0962, 0x0963}, {
+       0x0981, 0x0981}, {
+       0x09BC, 0x09BC}, {
+       0x09C1, 0x09C4}, {
+       0x09CD, 0x09CD}, {
+       0x09E2, 0x09E3}, {
+       0x0A02, 0x0A02}, {
+       0x0A3C, 0x0A3C}, {
+       0x0A41, 0x0A42}, {
+       0x0A47, 0x0A48}, {
+       0x0A4B, 0x0A4D}, {
+       0x0A70, 0x0A71}, {
+       0x0A81, 0x0A82}, {
+       0x0ABC, 0x0ABC}, {
+       0x0AC1, 0x0AC5}, {
+       0x0AC7, 0x0AC8}, {
+       0x0ACD, 0x0ACD}, {
+       0x0B01, 0x0B01}, {
+       0x0B3C, 0x0B3C}, {
+       0x0B3F, 0x0B3F}, {
+       0x0B41, 0x0B43}, {
+       0x0B4D, 0x0B4D}, {
+       0x0B56, 0x0B56}, {
+       0x0B82, 0x0B82}, {
+       0x0BC0, 0x0BC0}, {
+       0x0BCD, 0x0BCD}, {
+       0x0C3E, 0x0C40}, {
+       0x0C46, 0x0C48}, {
+       0x0C4A, 0x0C4D}, {
+       0x0C55, 0x0C56}, {
+       0x0CBF, 0x0CBF}, {
+       0x0CC6, 0x0CC6}, {
+       0x0CCC, 0x0CCD}, {
+       0x0D41, 0x0D43}, {
+       0x0D4D, 0x0D4D}, {
+       0x0DCA, 0x0DCA}, {
+       0x0DD2, 0x0DD4}, {
+       0x0DD6, 0x0DD6}, {
+       0x0E31, 0x0E31}, {
+       0x0E34, 0x0E3A}, {
+       0x0E47, 0x0E4E}, {
+       0x0EB1, 0x0EB1}, {
+       0x0EB4, 0x0EB9}, {
+       0x0EBB, 0x0EBC}, {
+       0x0EC8, 0x0ECD}, {
+       0x0F18, 0x0F19}, {
+       0x0F35, 0x0F35}, {
+       0x0F37, 0x0F37}, {
+       0x0F39, 0x0F39}, {
+       0x0F71, 0x0F7E}, {
+       0x0F80, 0x0F84}, {
+       0x0F86, 0x0F87}, {
+       0x0F90, 0x0F97}, {
+       0x0F99, 0x0FBC}, {
+       0x0FC6, 0x0FC6}, {
+       0x102D, 0x1030}, {
+       0x1032, 0x1032}, {
+       0x1036, 0x1037}, {
+       0x1039, 0x1039}, {
+       0x1058, 0x1059}, {
+       0x17B7, 0x17BD}, {
+       0x17C6, 0x17C6}, {
+       0x17C9, 0x17D3}, {
+       0x18A9, 0x18A9}, {
+       0x20D0, 0x20E3}, {
+       0x302A, 0x302F}, {
+       0x3099, 0x309A}, {
+       0xFB1E, 0xFB1E}, {
+       0xFE20, 0xFE23}
+    };
+    int min = 0;
+    int max = sizeof(combining) / sizeof(struct interval) - 1;
+    int mid;
+
+    /* test for 8-bit control characters */
+    if (ucs == 0)
+       return 0;
+    if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
+       return -1;
+
+    /* first quick check for Latin-1 etc. characters */
+    if (ucs < combining[0].first)
+       return 1;
+
+    /* binary search in table of non-spacing characters */
+    while (max >= min) {
+       mid = (min + max) / 2;
+       if (combining[mid].last < ucs)
+           min = mid + 1;
+       else if (combining[mid].first > ucs)
+           max = mid - 1;
+       else if (combining[mid].first <= ucs && combining[mid].last >= ucs)
+           return 0;
+    }
+
+    /* if we arrive here, ucs is not a combining or C0/C1 control character */
+
+    /* fast test for majority of non-wide scripts */
+    if (ucs < 0x1100)
+       return 1;
+
+    return 1 + ((ucs >= 0x1100 && ucs <= 0x115f) ||    /* Hangul Jamo */
+               
+               (ucs >= 0x2e80 && ucs <= 0xa4cf
+                && (ucs & ~0x0011) != 0x300a && ucs != 0x303f) ||      /* CJK ... Yi */
+               (ucs >= 0xac00 && ucs <= 0xd7a3) ||     /* Hangul Syllables */
+               (ucs >= 0xf900 && ucs <= 0xfaff) ||     /* CJK Compatibility Ideographs */
+               (ucs >= 0xfe30 && ucs <= 0xfe6f) ||     /* CJK Compatibility Forms */
+               (ucs >= 0xff00 && ucs <= 0xff5f) ||     /* Fullwidth Forms */
+               (ucs >= 0xffe0 && ucs <= 0xffe6));
+}
+
+
+int wcswidth(const wchar_t * pwcs, size_t n)
+{
+    int w, width = 0;
+
+    for (; *pwcs && n-- > 0; pwcs++)
+       if ((w = wcwidth(*pwcs)) < 0)
+           return -1;
+       else
+           width += w;
+
+    return width;
+}
index 2f9ca56..99cd9c3 100644 (file)
--- a/windlg.c
+++ b/windlg.c
@@ -128,7 +128,7 @@ static int CALLBACK LogProc(HWND hwnd, UINT msg,
                            memcpy(p, sel_nl, sizeof(sel_nl));
                            p += sizeof(sel_nl);
                        }
-                       write_clip(clipdata, size, TRUE);
+                       write_aclip(clipdata, size, TRUE);
                        sfree(clipdata);
                    }
                    sfree(selitems);
@@ -486,18 +486,14 @@ enum { IDCX_ABOUT =
     IDC_TITLE_TRANSLATION,
     IDC_BOX_TRANSLATION1,
     IDC_BOX_TRANSLATION2,
-    IDC_BOX_TRANSLATION3,
-    IDC_XLATSTATIC,
-    IDC_NOXLAT,
-    IDC_KOI8WIN1251,
-    IDC_88592WIN1250,
-    IDC_88592CP852,
-    IDC_CAPSLOCKCYR,
+    IDC_CODEPAGESTATIC,
+    IDC_CODEPAGE,
     IDC_VTSTATIC,
     IDC_VTXWINDOWS,
     IDC_VTOEMANSI,
     IDC_VTOEMONLY,
     IDC_VTPOORMAN,
+    IDC_VTUNICODE,
     translationpanelend,
 
     tunnelspanelstart,
@@ -691,7 +687,7 @@ static void init_dlg_ctrls(HWND hwnd)
        SendDlgItemMessage(hwnd, IDC_CCLIST, LB_SETTABSTOPS, 4,
                           (LPARAM) tabs);
     }
-    for (i = 0; i < 256; i++) {
+    for (i = 0; i < 128; i++) {
        char str[100];
        sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
                (i >= 0x21 && i != 0x7F) ? i : ' ', cfg.wordness[i]);
@@ -717,15 +713,12 @@ static void init_dlg_ctrls(HWND hwnd)
     SetDlgItemInt(hwnd, IDC_GVALUE, cfg.colours[0][1], FALSE);
     SetDlgItemInt(hwnd, IDC_BVALUE, cfg.colours[0][2], FALSE);
 
-    CheckRadioButton(hwnd, IDC_NOXLAT, IDC_88592CP852,
-                    cfg.xlat_88592w1250 ? IDC_88592WIN1250 :
-                    cfg.xlat_88592cp852 ? IDC_88592CP852 :
-                    cfg.xlat_enablekoiwin ? IDC_KOI8WIN1251 : IDC_NOXLAT);
-    CheckDlgButton(hwnd, IDC_CAPSLOCKCYR, cfg.xlat_capslockcyr);
-    CheckRadioButton(hwnd, IDC_VTXWINDOWS, IDC_VTPOORMAN,
+    SetDlgItemText(hwnd, IDC_CODEPAGE, cfg.line_codepage);
+    CheckRadioButton(hwnd, IDC_VTXWINDOWS, IDC_VTUNICODE,
                     cfg.vtmode == VT_XWINDOWS ? IDC_VTXWINDOWS :
                     cfg.vtmode == VT_OEMANSI ? IDC_VTOEMANSI :
                     cfg.vtmode == VT_OEMONLY ? IDC_VTOEMONLY :
+                    cfg.vtmode == VT_UNICODE ? IDC_VTUNICODE :
                     IDC_VTPOORMAN);
 
     CheckDlgButton(hwnd, IDC_X11_FORWARD, cfg.x11_forward);
@@ -781,7 +774,7 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
                      "&Port", IDC_PORTSTATIC, IDC_PORT, 25, NULL);
            if (backends[3].backend == NULL) {
                /* this is PuTTYtel, so only three protocols available */
-               radioline(&cp, "Protocol:", IDC_PROTSTATIC, 4,
+               radioline(&cp, "Protocol:", IDC_PROTSTATIC, 3,
                          "&Raw", IDC_PROTRAW,
                          "&Telnet", IDC_PROTTELNET,
                          "Rlog&in", IDC_PROTRLOGIN, NULL);
@@ -1018,21 +1011,12 @@ static void create_controls(HWND hwnd, int dlgtype, int panel)
                 "Use font in &both ANSI and OEM modes", IDC_VTOEMANSI,
                 "Use font in O&EM mode only", IDC_VTOEMONLY,
                 "&Poor man's line drawing (" "+" ", " "-" " and " "|" ")",
-                IDC_VTPOORMAN, NULL);
+                IDC_VTPOORMAN, "&Unicode mode", IDC_VTUNICODE, NULL);
        endbox(&cp);
        beginbox(&cp, "Enable character set translation on received data",
                 IDC_BOX_TRANSLATION2);
-       radiobig(&cp,
-                "Character set &translation:", IDC_XLATSTATIC,
-                "None", IDC_NOXLAT,
-                "KOI8 / Win-1251", IDC_KOI8WIN1251,
-                "ISO-8859-2 / Win-1250", IDC_88592WIN1250,
-                "ISO-8859-2 / CP852", IDC_88592CP852, NULL);
-       endbox(&cp);
-       beginbox(&cp, "Enable character set translation on input data",
-                IDC_BOX_TRANSLATION3);
-       checkbox(&cp, "CAP&S LOCK acts as cyrillic switch",
-                IDC_CAPSLOCKCYR);
+       multiedit(&cp, "Line codepage:", IDC_CODEPAGESTATIC,
+                 IDC_CODEPAGE, 100, NULL);
        endbox(&cp);
     }
 
@@ -2194,7 +2178,7 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
                    if (!ok)
                        MessageBeep(0);
                    else {
-                       for (i = 0; i < 256; i++)
+                       for (i = 0; i < 128; i++)
                            if (SendDlgItemMessage
                                (hwnd, IDC_CCLIST, LB_GETSEL, i, 0)) {
                                char str[100];
@@ -2288,34 +2272,44 @@ static int GenericMainDlgProc(HWND hwnd, UINT msg,
                    }
                }
                break;
-             case IDC_NOXLAT:
-             case IDC_KOI8WIN1251:
-             case IDC_88592WIN1250:
-             case IDC_88592CP852:
-               cfg.xlat_enablekoiwin =
-                   IsDlgButtonChecked(hwnd, IDC_KOI8WIN1251);
-               cfg.xlat_88592w1250 =
-                   IsDlgButtonChecked(hwnd, IDC_88592WIN1250);
-               cfg.xlat_88592cp852 =
-                   IsDlgButtonChecked(hwnd, IDC_88592CP852);
-               break;
-             case IDC_CAPSLOCKCYR:
-               if (HIWORD(wParam) == BN_CLICKED ||
-                   HIWORD(wParam) == BN_DOUBLECLICKED) {
-                   cfg.xlat_capslockcyr =
-                       IsDlgButtonChecked(hwnd, IDC_CAPSLOCKCYR);
+             case IDC_CODEPAGE:
+               if (HIWORD(wParam) == EN_CHANGE)
+                   GetDlgItemText(hwnd, IDC_CODEPAGE, cfg.line_codepage,
+                                  sizeof(cfg.line_codepage) - 1);
+               if (HIWORD(wParam) == EN_KILLFOCUS) {
+                   int cp = decode_codepage(cfg.line_codepage);
+                   char buf[256];
+                   if (cp < -1) {
+                       if (cp == -2)
+                           sprintf(buf,
+                                   "Unable to identify character set '%s', "
+                                   "translation disabled.",
+                                   cfg.line_codepage);
+                       if (cp == -3)
+                           sprintf(buf,
+                                   "Character set '%s' is a DBCS, "
+                                   "translation is not available.",
+                                   cfg.line_codepage);
+                       MessageBox(hwnd, buf, "PuTTY Error",
+                                  MB_ICONERROR | MB_OK);
+                   }
+                   strcpy(cfg.line_codepage, cp_name(cp));
+                   SetDlgItemText(hwnd, IDC_CODEPAGE, cfg.line_codepage);
                }
                break;
              case IDC_VTXWINDOWS:
              case IDC_VTOEMANSI:
              case IDC_VTOEMONLY:
              case IDC_VTPOORMAN:
+             case IDC_VTUNICODE:
                cfg.vtmode =
                    (IsDlgButtonChecked(hwnd, IDC_VTXWINDOWS) ? VT_XWINDOWS
                     : IsDlgButtonChecked(hwnd,
                                          IDC_VTOEMANSI) ? VT_OEMANSI :
                     IsDlgButtonChecked(hwnd,
                                        IDC_VTOEMONLY) ? VT_OEMONLY :
+                    IsDlgButtonChecked(hwnd,
+                                       IDC_VTUNICODE) ? VT_UNICODE :
                     VT_POORMAN);
                break;
              case IDC_X11_FORWARD:
index 48ba35b..37b3a00 100644 (file)
--- a/window.c
+++ b/window.c
@@ -63,6 +63,8 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
 static void cfgtopalette(void);
 static void init_palette(void);
 static void init_fonts(int);
+static void another_font(int);
+static void deinit_fonts(void);
 
 static int extra_width, extra_height;
 
@@ -77,12 +79,20 @@ static time_t last_movement = 0;
 #define FONT_BOLD 1
 #define FONT_UNDERLINE 2
 #define FONT_BOLDUND 3
-#define FONT_OEM 4
-#define FONT_OEMBOLD 5
-#define FONT_OEMBOLDUND 6
-#define FONT_OEMUND 7
-static HFONT fonts[8];
-static int font_needs_hand_underlining;
+#define FONT_WIDE      0x04
+#define FONT_HIGH      0x08
+#define FONT_NARROW    0x10
+#define FONT_OEM       0x20
+#define FONT_OEMBOLD   0x21
+#define FONT_OEMUND    0x22
+#define FONT_OEMBOLDUND 0x23
+#define FONT_MSGOTHIC  0x40
+#define FONT_MINGLIU   0x60
+#define FONT_GULIMCHE  0x80
+#define FONT_MAXNO     0x8F
+#define FONT_SHIFT     5
+static HFONT fonts[FONT_MAXNO];
+static int fontflag[FONT_MAXNO];
 static enum {
     BOLD_COLOURS, BOLD_SHADOW, BOLD_FONT
 } bold_mode;
@@ -642,12 +652,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
     /*
      * Clean up.
      */
-    {
-       int i;
-       for (i = 0; i < 8; i++)
-           if (fonts[i])
-               DeleteObject(fonts[i]);
-    }
+    deinit_fonts();
     sfree(logpal);
     if (pal)
        DeleteObject(pal);
@@ -811,8 +816,11 @@ static void init_palette(void)
 }
 
 /*
- * Initialise all the fonts we will need. There may be as many as
- * eight or as few as one. We also:
+ * Initialise all the fonts we will need initially. There may be as many as
+ * three or as few as one.  The other (poentially) twentyone fonts are done
+ * if/when they are needed.
+ *
+ * We also:
  *
  * - check the font width and height, correcting our guesses if
  *   necessary.
@@ -827,16 +835,13 @@ static void init_palette(void)
 static void init_fonts(int pick_width)
 {
     TEXTMETRIC tm;
+    CPINFO cpinfo;
+    int fontsize[3];
     int i;
-    int fsize[8];
     HDC hdc;
     int fw_dontcare, fw_bold;
-    int firstchar = ' ';
 
-#ifdef CHECKOEMFONT
-  font_messup:
-#endif
-    for (i = 0; i < 8; i++)
+    for (i = 0; i < FONT_MAXNO; i++)
        fonts[i] = NULL;
 
     if (cfg.fontisbold) {
@@ -862,152 +867,177 @@ static void init_fonts(int pick_width)
                           CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
                           FIXED_PITCH | FF_DONTCARE, cfg.font)
 
-    if (cfg.vtmode != VT_OEMONLY) {
-       f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
+    f(FONT_NORMAL, cfg.fontcharset, fw_dontcare, FALSE);
 
-       SelectObject(hdc, fonts[FONT_NORMAL]);
-       GetTextMetrics(hdc, &tm);
-       font_height = tm.tmHeight;
-       font_width = tm.tmAveCharWidth;
+    SelectObject(hdc, fonts[FONT_NORMAL]);
+    GetTextMetrics(hdc, &tm);
+    font_height = tm.tmHeight;
+    font_width = tm.tmAveCharWidth;
 
-       f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
+    {
+       CHARSETINFO info;
+       DWORD cset = tm.tmCharSet;
+       memset(&info, 0xFF, sizeof(info));
 
-       /*
-        * Some fonts, e.g. 9-pt Courier, draw their underlines
-        * outside their character cell. We successfully prevent
-        * screen corruption by clipping the text output, but then
-        * we lose the underline completely. Here we try to work
-        * out whether this is such a font, and if it is, we set a
-        * flag that causes underlines to be drawn by hand.
-        *
-        * Having tried other more sophisticated approaches (such
-        * as examining the TEXTMETRIC structure or requesting the
-        * height of a string), I think we'll do this the brute
-        * force way: we create a small bitmap, draw an underlined
-        * space on it, and test to see whether any pixels are
-        * foreground-coloured. (Since we expect the underline to
-        * go all the way across the character cell, we only search
-        * down a single column of the bitmap, half way across.)
-        */
-       {
-           HDC und_dc;
-           HBITMAP und_bm, und_oldbm;
-           int i, gotit;
-           COLORREF c;
-
-           und_dc = CreateCompatibleDC(hdc);
-           und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
-           und_oldbm = SelectObject(und_dc, und_bm);
-           SelectObject(und_dc, fonts[FONT_UNDERLINE]);
-           SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
-           SetTextColor(und_dc, RGB(255, 255, 255));
-           SetBkColor(und_dc, RGB(0, 0, 0));
-           SetBkMode(und_dc, OPAQUE);
-           ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
-           gotit = FALSE;
-           for (i = 0; i < font_height; i++) {
-               c = GetPixel(und_dc, font_width / 2, i);
-               if (c != RGB(0, 0, 0))
-                   gotit = TRUE;
-           }
-           SelectObject(und_dc, und_oldbm);
-           DeleteObject(und_bm);
-           DeleteDC(und_dc);
-           font_needs_hand_underlining = !gotit;
-       }
+       /* !!! Yes the next line is right */
+       if (cset == OEM_CHARSET)
+           font_codepage = GetOEMCP();
+       else
+           if (TranslateCharsetInfo
+               ((DWORD *) cset, &info, TCI_SRCCHARSET)) font_codepage =
+               info.ciACP;
+       else
+           font_codepage = -1;
 
-       if (bold_mode == BOLD_FONT) {
-           f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
-           f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
-       }
+       GetCPInfo(font_codepage, &cpinfo);
+       dbcs_screenfont = (cpinfo.MaxCharSize > 1);
+    }
 
-       if (cfg.vtmode == VT_OEMANSI) {
-           f(FONT_OEM, OEM_CHARSET, fw_dontcare, FALSE);
-           f(FONT_OEMUND, OEM_CHARSET, fw_dontcare, TRUE);
+    f(FONT_UNDERLINE, cfg.fontcharset, fw_dontcare, TRUE);
 
-           if (bold_mode == BOLD_FONT) {
-               f(FONT_OEMBOLD, OEM_CHARSET, fw_bold, FALSE);
-               f(FONT_OEMBOLDUND, OEM_CHARSET, fw_bold, TRUE);
-           }
+    /*
+     * Some fonts, e.g. 9-pt Courier, draw their underlines
+     * outside their character cell. We successfully prevent
+     * screen corruption by clipping the text output, but then
+     * we lose the underline completely. Here we try to work
+     * out whether this is such a font, and if it is, we set a
+     * flag that causes underlines to be drawn by hand.
+     *
+     * Having tried other more sophisticated approaches (such
+     * as examining the TEXTMETRIC structure or requesting the
+     * height of a string), I think we'll do this the brute
+     * force way: we create a small bitmap, draw an underlined
+     * space on it, and test to see whether any pixels are
+     * foreground-coloured. (Since we expect the underline to
+     * go all the way across the character cell, we only search
+     * down a single column of the bitmap, half way across.)
+     */
+    {
+       HDC und_dc;
+       HBITMAP und_bm, und_oldbm;
+       int i, gotit;
+       COLORREF c;
+
+       und_dc = CreateCompatibleDC(hdc);
+       und_bm = CreateCompatibleBitmap(hdc, font_width, font_height);
+       und_oldbm = SelectObject(und_dc, und_bm);
+       SelectObject(und_dc, fonts[FONT_UNDERLINE]);
+       SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
+       SetTextColor(und_dc, RGB(255, 255, 255));
+       SetBkColor(und_dc, RGB(0, 0, 0));
+       SetBkMode(und_dc, OPAQUE);
+       ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL);
+       gotit = FALSE;
+       for (i = 0; i < font_height; i++) {
+           c = GetPixel(und_dc, font_width / 2, i);
+           if (c != RGB(0, 0, 0))
+               gotit = TRUE;
+       }
+       SelectObject(und_dc, und_oldbm);
+       DeleteObject(und_bm);
+       DeleteDC(und_dc);
+       if (!gotit) {
+           und_mode = UND_LINE;
+           DeleteObject(fonts[FONT_UNDERLINE]);
+           fonts[FONT_UNDERLINE] = 0;
        }
-    } else {
-       f(FONT_OEM, cfg.fontcharset, fw_dontcare, FALSE);
-
-       SelectObject(hdc, fonts[FONT_OEM]);
-       GetTextMetrics(hdc, &tm);
-       font_height = tm.tmHeight;
-       font_width = tm.tmAveCharWidth;
-
-       f(FONT_OEMUND, cfg.fontcharset, fw_dontcare, TRUE);
+    }
 
-       if (bold_mode == BOLD_FONT) {
-           f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
-           f(FONT_BOLDUND, cfg.fontcharset, fw_bold, TRUE);
-       }
+    if (bold_mode == BOLD_FONT) {
+       f(FONT_BOLD, cfg.fontcharset, fw_bold, FALSE);
     }
 #undef f
 
     descent = tm.tmAscent + 1;
     if (descent >= font_height)
        descent = font_height - 1;
-    firstchar = tm.tmFirstChar;
 
-    for (i = 0; i < 8; i++) {
+    for (i = 0; i < 3; i++) {
        if (fonts[i]) {
            if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm))
-               fsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
+               fontsize[i] = tm.tmAveCharWidth + 256 * tm.tmHeight;
            else
-               fsize[i] = -i;
+               fontsize[i] = -i;
        } else
-           fsize[i] = -i;
+           fontsize[i] = -i;
     }
 
     ReleaseDC(hwnd, hdc);
 
-    /* ... This is wrong in OEM only mode */
-    if (fsize[FONT_UNDERLINE] != fsize[FONT_NORMAL] ||
-       (bold_mode == BOLD_FONT &&
-        fsize[FONT_BOLDUND] != fsize[FONT_BOLD])) {
+    if (fontsize[FONT_UNDERLINE] != fontsize[FONT_NORMAL]) {
        und_mode = UND_LINE;
        DeleteObject(fonts[FONT_UNDERLINE]);
-       if (bold_mode == BOLD_FONT)
-           DeleteObject(fonts[FONT_BOLDUND]);
+       fonts[FONT_UNDERLINE] = 0;
     }
 
-    if (bold_mode == BOLD_FONT && fsize[FONT_BOLD] != fsize[FONT_NORMAL]) {
+    if (bold_mode == BOLD_FONT &&
+       fontsize[FONT_BOLD] != fontsize[FONT_NORMAL]) {
        bold_mode = BOLD_SHADOW;
        DeleteObject(fonts[FONT_BOLD]);
-       if (und_mode == UND_FONT)
-           DeleteObject(fonts[FONT_BOLDUND]);
+       fonts[FONT_BOLD] = 0;
     }
-#ifdef CHECKOEMFONT
-    /* With the fascist font painting it doesn't matter if the linedraw font
-     * isn't exactly the right size anymore so we don't have to check this.
-     */
-    if (cfg.vtmode == VT_OEMANSI && fsize[FONT_OEM] != fsize[FONT_NORMAL]) {
-       if (cfg.fontcharset == OEM_CHARSET) {
-           MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
-                      "different sizes. Using OEM-only mode instead",
-                      "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
-           cfg.vtmode = VT_OEMONLY;
-       } else if (firstchar < ' ') {
-           MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
-                      "different sizes. Using XTerm mode instead",
-                      "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
-           cfg.vtmode = VT_XWINDOWS;
-       } else {
-           MessageBox(NULL, "The OEM and ANSI versions of this font are\n"
-                      "different sizes. Using ISO8859-1 mode instead",
-                      "Font Size Mismatch", MB_ICONINFORMATION | MB_OK);
-           cfg.vtmode = VT_POORMAN;
-       }
+    fontflag[0] = fontflag[1] = fontflag[2] = 1;
+
+    init_ucs_tables();
+}
+
+static void another_font(int fontno)
+{
+    int basefont;
+    int fw_dontcare, fw_bold;
+    int c, u, w, x;
+    char *s;
+
+    if (fontno < 0 || fontno >= FONT_MAXNO || fontflag[fontno])
+       return;
+
+    basefont = (fontno & ~(FONT_BOLDUND));
+    if (basefont != fontno && !fontflag[basefont])
+       another_font(basefont);
+
+    if (cfg.fontisbold) {
+       fw_dontcare = FW_BOLD;
+       fw_bold = FW_HEAVY;
+    } else {
+       fw_dontcare = FW_DONTCARE;
+       fw_bold = FW_BOLD;
+    }
+
+    c = cfg.fontcharset;
+    w = fw_dontcare;
+    u = FALSE;
+    s = cfg.font;
+    x = font_width;
+
+    if (fontno & FONT_WIDE)
+       x *= 2;
+    if (fontno & FONT_NARROW)
+       x /= 2;
+    if (fontno & FONT_OEM)
+       c = OEM_CHARSET;
+    if (fontno & FONT_BOLD)
+       w = fw_bold;
+    if (fontno & FONT_UNDERLINE)
+       u = TRUE;
+
+    fonts[fontno] =
+       CreateFont(font_height * (1 + !!(fontno & FONT_HIGH)), x, 0, 0, w,
+                  FALSE, u, FALSE, c, OUT_DEFAULT_PRECIS,
+                  CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+                  FIXED_PITCH | FF_DONTCARE, s);
+
+    fontflag[fontno] = 1;
+}
 
-       for (i = 0; i < 8; i++)
-           if (fonts[i])
-               DeleteObject(fonts[i]);
-       goto font_messup;
+static void deinit_fonts(void)
+{
+    int i;
+    for (i = 0; i < FONT_MAXNO; i++) {
+       if (fonts[i])
+           DeleteObject(fonts[i]);
+       fonts[i] = 0;
+       fontflag[i] = 0;
     }
-#endif
 }
 
 void request_resize(int w, int h, int refont)
@@ -1018,25 +1048,13 @@ void request_resize(int w, int h, int refont)
     if (IsZoomed(hwnd))
        return;
 
-#ifdef CHECKOEMFONT
-    /* Don't do this in OEMANSI, you may get disable messages */
-    if (refont && w != cols && (cols == 80 || cols == 132)
-       && cfg.vtmode != VT_OEMANSI)
-#else
-    if (refont && w != cols && (cols == 80 || cols == 132))
-#endif
-    {
+    if (refont && w != cols && (cols == 80 || cols == 132)) {
        /* If font width too big for screen should we shrink the font more ? */
        if (w == 132)
            font_width = ((font_width * cols + w / 2) / w);
        else
            font_width = 0;
-       {
-           int i;
-           for (i = 0; i < 8; i++)
-               if (fonts[i])
-                   DeleteObject(fonts[i]);
-       }
+       deinit_fonts();
        bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
        und_mode = UND_FONT;
        init_fonts(font_width);
@@ -1267,12 +1285,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
                }
 
                just_reconfigged = TRUE;
-               {
-                   int i;
-                   for (i = 0; i < 8; i++)
-                       if (fonts[i])
-                           DeleteObject(fonts[i]);
-               }
+               deinit_fonts();
                bold_mode = cfg.bold_colour ? BOLD_COLOURS : BOLD_FONT;
                und_mode = UND_FONT;
                init_fonts(0);
@@ -1823,14 +1836,33 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            }
        }
        return 0;
-      case WM_IME_CHAR:
+      case WM_INPUTLANGCHANGE:
        {
+           /* wParam == Font number */
+           /* lParam == Locale */
+           char lbuf[20];
+           HKL NewInputLocale = (HKL) lParam;
+
+           // lParam == GetKeyboardLayout(0);
+
+           GetLocaleInfo(LOWORD(NewInputLocale),
+                         LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf));
+
+           kbd_codepage = atoi(lbuf);
+       }
+       break;
+      case WM_IME_CHAR:
+       if (wParam & 0xFF00) {
            unsigned char buf[2];
 
            buf[1] = wParam;
            buf[0] = wParam >> 8;
-           ldisc_send(buf, 2);
+           lpage_send(kbd_codepage, buf, 2);
+       } else {
+           char c = (unsigned char) wParam;
+           lpage_send(kbd_codepage, &c, 1);
        }
+       return (0);
       case WM_CHAR:
       case WM_SYSCHAR:
        /*
@@ -1840,8 +1872,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
         * we're ready to cope.
         */
        {
-           char c = xlat_kbd2tty((unsigned char) wParam);
-           ldisc_send(&c, 1);
+           char c = (unsigned char)wParam;
+           lpage_send(CP_ACP, &c, 1);
        }
        return 0;
       case WM_SETCURSOR:
@@ -1881,9 +1913,14 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     RECT line_box;
     int force_manual_underline = 0;
     int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
-    static int *IpDx = 0, IpDxLEN = 0;;
+    int char_width = fnt_width;
+    int text_adjust = 0;
+    static int *IpDx = 0, IpDxLEN = 0;
+
+    if (attr & ATTR_WIDE)
+       char_width *= 2;
 
-    if (len > IpDxLEN || IpDx[0] != fnt_width) {
+    if (len > IpDxLEN || IpDx[0] != char_width) {
        int i;
        if (len > IpDxLEN) {
            sfree(IpDx);
@@ -1891,123 +1928,81 @@ void do_text(Context ctx, int x, int y, char *text, int len,
            IpDxLEN = (len + 16);
        }
        for (i = 0; i < IpDxLEN; i++)
-           IpDx[i] = fnt_width;
+           IpDx[i] = char_width;
     }
 
     x *= fnt_width;
     y *= font_height;
 
-    if ((attr & ATTR_ACTCURS) && cfg.cursor_type == 0) {
-       attr &= (bold_mode == BOLD_COLOURS ? 0x300200 : 0x300300);
+    if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
+       attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
        attr ^= ATTR_CUR_XOR;
     }
 
     nfont = 0;
-    if (cfg.vtmode == VT_OEMONLY)
-       nfont |= FONT_OEM;
-
-    /*
-     * Map high-half characters in order to approximate ISO using
-     * OEM character set. No characters are missing if the OEM codepage
-     * is CP850.
-     */
-    if (nfont & FONT_OEM) {
-       int i;
-       for (i = 0; i < len; i++)
-           if (text[i] >= '\xA0' && text[i] <= '\xFF') {
-#if 0
-               /* This is CP850 ... perfect translation */
-               static const char oemhighhalf[] = "\x20\xAD\xBD\x9C\xCF\xBE\xDD\xF5"    /* A0-A7 */
-                   "\xF9\xB8\xA6\xAE\xAA\xF0\xA9\xEE"  /* A8-AF */
-                   "\xF8\xF1\xFD\xFC\xEF\xE6\xF4\xFA"  /* B0-B7 */
-                   "\xF7\xFB\xA7\xAF\xAC\xAB\xF3\xA8"  /* B8-BF */
-                   "\xB7\xB5\xB6\xC7\x8E\x8F\x92\x80"  /* C0-C7 */
-                   "\xD4\x90\xD2\xD3\xDE\xD6\xD7\xD8"  /* C8-CF */
-                   "\xD1\xA5\xE3\xE0\xE2\xE5\x99\x9E"  /* D0-D7 */
-                   "\x9D\xEB\xE9\xEA\x9A\xED\xE8\xE1"  /* D8-DF */
-                   "\x85\xA0\x83\xC6\x84\x86\x91\x87"  /* E0-E7 */
-                   "\x8A\x82\x88\x89\x8D\xA1\x8C\x8B"  /* E8-EF */
-                   "\xD0\xA4\x95\xA2\x93\xE4\x94\xF6"  /* F0-F7 */
-                   "\x9B\x97\xA3\x96\x81\xEC\xE7\x98"  /* F8-FF */
-               ;
-#endif
-               /* This is CP437 ... junk translation */
-               static const unsigned char oemhighhalf[] = {
-                   0x20, 0xad, 0x9b, 0x9c, 0x6f, 0x9d, 0x7c, 0x15,
-                   0x22, 0x43, 0xa6, 0xae, 0xaa, 0x2d, 0x52, 0xc4,
-                   0xf8, 0xf1, 0xfd, 0x33, 0x27, 0xe6, 0x14, 0xfa,
-                   0x2c, 0x31, 0xa7, 0xaf, 0xac, 0xab, 0x2f, 0xa8,
-                   0x41, 0x41, 0x41, 0x41, 0x8e, 0x8f, 0x92, 0x80,
-                   0x45, 0x90, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49,
-                   0x44, 0xa5, 0x4f, 0x4f, 0x4f, 0x4f, 0x99, 0x78,
-                   0xed, 0x55, 0x55, 0x55, 0x9a, 0x59, 0x50, 0xe1,
-                   0x85, 0xa0, 0x83, 0x61, 0x84, 0x86, 0x91, 0x87,
-                   0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
-                   0x0b, 0xa4, 0x95, 0xa2, 0x93, 0x6f, 0x94, 0xf6,
-                   0xed, 0x97, 0xa3, 0x96, 0x81, 0x79, 0x70, 0x98
-               };
-
-               text[i] = oemhighhalf[(unsigned char) text[i] - 0xA0];
-           }
-    }
-
-    if (attr & ATTR_LINEDRW) {
-       int i;
-       /* ISO 8859-1 */
-       static const char poorman[] =
-           "*#****\xB0\xB1**+++++-----++++|****\xA3\xB7";
-
-       /* CP437 */
-       static const char oemmap_437[] =
-           "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
-           "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3\xF3\xF2\xE3*\x9C\xFA";
-
-       /* CP850 */
-       static const char oemmap_850[] =
-           "\x04\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
-           "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
-
-       /* Poor windows font ... eg: windows courier */
-       static const char oemmap[] =
-           "*\xB1****\xF8\xF1**\xD9\xBF\xDA\xC0\xC5"
-           "\xC4\xC4\xC4\xC4\xC4\xC3\xB4\xC1\xC2\xB3****\x9C\xFA";
-
-       /*
-        * Line drawing mapping: map ` thru ~ (0x60 thru 0x7E) to
-        * VT100 line drawing chars; everything else stays normal.
-        *
-        * Actually '_' maps to space too, but that's done before.
-        */
-       switch (cfg.vtmode) {
-         case VT_XWINDOWS:
-           for (i = 0; i < len; i++)
-               if (text[i] >= '\x60' && text[i] <= '\x7E')
-                   text[i] += '\x01' - '\x60';
+    if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
+       /* Assume a poorman font is borken in other ways too. */
+       lattr = LATTR_WIDE;
+    } else
+       switch (lattr) {
+         case LATTR_NORM:
+           break;
+         case LATTR_WIDE:
+           nfont |= FONT_WIDE;
+           break;
+         default:
+           nfont |= FONT_WIDE + FONT_HIGH;
            break;
-         case VT_OEMANSI:
-           /* Make sure we actually have an OEM font */
-           if (fonts[nfont | FONT_OEM]) {
-         case VT_OEMONLY:
-               nfont |= FONT_OEM;
-               for (i = 0; i < len; i++)
-                   if (text[i] >= '\x60' && text[i] <= '\x7E')
-                       text[i] = oemmap[(unsigned char) text[i] - 0x60];
+       }
+
+    /* Special hack for the VT100 linedraw glyphs. */
+    if ((attr & CSET_MASK) == 0x2300) {
+       if (!dbcs_screenfont &&
+           text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
+           switch ((unsigned char) (text[0])) {
+             case 0xBA:
+               text_adjust = -2 * font_height / 5;
+               break;
+             case 0xBB:
+               text_adjust = -1 * font_height / 5;
+               break;
+             case 0xBC:
+               text_adjust = font_height / 5;
+               break;
+             case 0xBD:
+               text_adjust = 2 * font_height / 5;
                break;
            }
-         case VT_POORMAN:
-           for (i = 0; i < len; i++)
-               if (text[i] >= '\x60' && text[i] <= '\x7E')
-                   text[i] = poorman[(unsigned char) text[i] - 0x60];
-           break;
+           if (lattr == LATTR_TOP || lattr == LATTR_BOT)
+               text_adjust *= 2;
+           attr &= ~CSET_MASK;
+           text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
+           attr |= (unitab_xterm['q'] & CSET_MASK);
+           if (attr & ATTR_UNDER) {
+               attr &= ~ATTR_UNDER;
+               force_manual_underline = 1;
+           }
        }
     }
 
+    /* Anything left as an original character set is unprintable. */
+    if (DIRECT_CHAR(attr)) {
+       attr &= ~CSET_MASK;
+       attr |= 0xFF00;
+       memset(text, 0xFF, len);
+    }
+
+    /* OEM CP */
+    if ((attr & CSET_MASK) == ATTR_OEMCP)
+       nfont |= FONT_OEM;
+
     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
     if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
        nfont |= FONT_BOLD;
     if (und_mode == UND_FONT && (attr & ATTR_UNDER))
        nfont |= FONT_UNDERLINE;
+    another_font(nfont);
     if (!fonts[nfont]) {
        if (nfont & FONT_UNDERLINE)
            force_manual_underline = 1;
@@ -2015,8 +2010,9 @@ void do_text(Context ctx, int x, int y, char *text, int len,
 
        nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
     }
-    if (font_needs_hand_underlining && (attr & ATTR_UNDER))
-       force_manual_underline = 1;
+    another_font(nfont);
+    if (!fonts[nfont])
+       nfont = FONT_NORMAL;
     if (attr & ATTR_REVERSE) {
        t = nfg;
        nfg = nbg;
@@ -2034,62 +2030,151 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     SetBkMode(hdc, OPAQUE);
     line_box.left = x;
     line_box.top = y;
-    line_box.right = x + fnt_width * len;
+    line_box.right = x + char_width * len;
     line_box.bottom = y + font_height;
-    ExtTextOut(hdc, x, y, ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len,
-              IpDx);
-    if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
-       SetBkMode(hdc, TRANSPARENT);
-
-       /* GRR: This draws the character outside it's box and can leave
-        * 'droppings' even with the clip box! I suppose I could loop it
-        * one character at a time ... yuk. 
-        * 
-        * Or ... I could do a test print with "W", and use +1 or -1 for this
-        * shift depending on if the leftmost column is blank...
-        */
-       ExtTextOut(hdc, x - 1, y, ETO_CLIPPED, &line_box, text, len, IpDx);
+
+    /* We're using a private area for direct to font. (512 chars.) */
+    if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
+       /* Ho Hum, dbcs fonts are a PITA! */
+       /* To display on W9x I have to convert to UCS */
+       static wchar_t *uni_buf = 0;
+       static int uni_len = 0;
+       int nlen;
+       if (len > uni_len) {
+           sfree(uni_buf);
+           uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
+       }
+       nlen = MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
+                                  text, len, uni_buf, uni_len);
+
+       if (nlen <= 0)
+           return;                    /* Eeek! */
+
+       ExtTextOutW(hdc, x,
+                   y - font_height * (lattr == LATTR_BOT) + text_adjust,
+                   ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, 0);
+       if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+           SetBkMode(hdc, TRANSPARENT);
+           ExtTextOutW(hdc, x - 1,
+                       y - font_height * (lattr ==
+                                          LATTR_BOT) + text_adjust,
+                       ETO_CLIPPED, &line_box, uni_buf, nlen, 0);
+       }
+    } else if (DIRECT_FONT(attr)) {
+       ExtTextOut(hdc, x,
+                  y - font_height * (lattr == LATTR_BOT) + text_adjust,
+                  ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
+       if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+           SetBkMode(hdc, TRANSPARENT);
+
+           /* GRR: This draws the character outside it's box and can leave
+            * 'droppings' even with the clip box! I suppose I could loop it
+            * one character at a time ... yuk. 
+            * 
+            * Or ... I could do a test print with "W", and use +1 or -1 for this
+            * shift depending on if the leftmost column is blank...
+            */
+           ExtTextOut(hdc, x - 1,
+                      y - font_height * (lattr ==
+                                         LATTR_BOT) + text_adjust,
+                      ETO_CLIPPED, &line_box, text, len, IpDx);
+       }
+    } else {
+       /* And 'normal' unicode characters */
+       static WCHAR *wbuf = NULL;
+       static int wlen = 0;
+       int i;
+       if (wlen < len) {
+           sfree(wbuf);
+           wlen = len;
+           wbuf = smalloc(wlen * sizeof(WCHAR));
+       }
+       for (i = 0; i < len; i++)
+           wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
+
+       ExtTextOutW(hdc, x,
+                   y - font_height * (lattr == LATTR_BOT) + text_adjust,
+                   ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
+
+       /* And the shadow bold hack. */
+       if (bold_mode == BOLD_SHADOW) {
+           SetBkMode(hdc, TRANSPARENT);
+           ExtTextOutW(hdc, x - 1,
+                       y - font_height * (lattr ==
+                                          LATTR_BOT) + text_adjust,
+                       ETO_CLIPPED, &line_box, wbuf, len, IpDx);
+       }
     }
-    if (force_manual_underline ||
-       (und_mode == UND_LINE && (attr & ATTR_UNDER))) {
+    if (lattr != LATTR_TOP && (force_manual_underline ||
+                              (und_mode == UND_LINE
+                               && (attr & ATTR_UNDER)))) {
        HPEN oldpen;
+       int dec = descent;
+       if (lattr == LATTR_BOT)
+           dec = dec * 2 - font_height;
+
        oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
-       MoveToEx(hdc, x, y + descent, NULL);
-       LineTo(hdc, x + len * fnt_width, y + descent);
+       MoveToEx(hdc, x, y + dec, NULL);
+       LineTo(hdc, x + len * char_width, y + dec);
        oldpen = SelectObject(hdc, oldpen);
        DeleteObject(oldpen);
     }
-    if ((attr & ATTR_PASCURS) && cfg.cursor_type == 0) {
+}
+
+void do_cursor(Context ctx, int x, int y, char *text, int len,
+              unsigned long attr, int lattr)
+{
+
+    int fnt_width;
+    int char_width;
+    HDC hdc = ctx;
+    int ctype = cfg.cursor_type;
+
+    if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
+       if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
+           do_text(ctx, x, y, text, len, attr, lattr);
+           return;
+       }
+       ctype = 2;
+       attr |= TATTR_RIGHTCURS;
+    }
+
+    fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
+    if (attr & ATTR_WIDE)
+       char_width *= 2;
+    x *= fnt_width;
+    y *= font_height;
+
+    if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
        POINT pts[5];
        HPEN oldpen;
        pts[0].x = pts[1].x = pts[4].x = x;
-       pts[2].x = pts[3].x = x + fnt_width - 1;
+       pts[2].x = pts[3].x = x + char_width - 1;
        pts[0].y = pts[3].y = pts[4].y = y;
        pts[1].y = pts[2].y = y + font_height - 1;
        oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
        Polyline(hdc, pts, 5);
        oldpen = SelectObject(hdc, oldpen);
        DeleteObject(oldpen);
-    }
-    if ((attr & (ATTR_ACTCURS | ATTR_PASCURS)) && cfg.cursor_type != 0) {
+    } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
        int startx, starty, dx, dy, length, i;
-       if (cfg.cursor_type == 1) {
+       if (ctype == 1) {
            startx = x;
            starty = y + descent;
            dx = 1;
            dy = 0;
-           length = fnt_width;
+           length = char_width;
        } else {
            int xadjust = 0;
-           if (attr & ATTR_RIGHTCURS)
-               xadjust = fnt_width - 1;
+           if (attr & TATTR_RIGHTCURS)
+               xadjust = char_width - 1;
            startx = x + xadjust;
            starty = y;
            dx = 0;
            dy = 1;
            length = font_height;
        }
-       if (attr & ATTR_ACTCURS) {
+       if (attr & TATTR_ACTCURS) {
            HPEN oldpen;
            oldpen =
                SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
@@ -2109,55 +2194,6 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     }
 }
 
-static int check_compose(int first, int second)
-{
-
-    static char *composetbl[] = {
-       "++#", "AA@", "(([", "//\\", "))]", "(-{", "-)}", "/^|", "!!¡",
-       "C/¢",
-       "C|¢", "L-£", "L=£", "XO¤", "X0¤", "Y-¥", "Y=¥", "||¦", "SO§",
-       "S!§",
-       "S0§", "\"\"¨", "CO©", "C0©", "A_ª", "<<«", ",-¬", "--­", "RO®",
-       "-^¯", "0^°", "+-±", "2^²", "3^³", "''´", "/Uµ", "P!¶", ".^·",
-       ",,¸",
-       "1^¹", "O_º", ">>»", "14¼", "12½", "34¾", "??¿", "`AÀ", "'AÁ",
-       "^AÂ",
-       "~AÃ", "\"AÄ", "*AÅ", "AEÆ", ",CÇ", "`EÈ", "'EÉ", "^EÊ", "\"EË",
-       "`IÌ", "'IÍ", "^IÎ", "\"IÏ", "-DÐ", "~NÑ", "`OÒ", "'OÓ", "^OÔ",
-       "~OÕ", "\"OÖ", "XX×", "/OØ", "`UÙ", "'UÚ", "^UÛ", "\"UÜ", "'YÝ",
-       "HTÞ", "ssß", "`aà", "'aá", "^aâ", "~aã", "\"aä", "*aå", "aeæ",
-       ",cç",
-       "`eè", "'eé", "^eê", "\"eë", "`iì", "'ií", "^iî", "\"iï", "-dð",
-       "~nñ",
-       "`oò", "'oó", "^oô", "~oõ", "\"oö", ":-÷", "o/ø", "`uù", "'uú",
-       "^uû",
-       "\"uü", "'yý", "htþ", "\"yÿ",
-       0
-    };
-
-    char **c;
-    static int recurse = 0;
-    int nc = -1;
-
-    for (c = composetbl; *c; c++) {
-       if ((*c)[0] == first && (*c)[1] == second) {
-           return (*c)[2] & 0xFF;
-       }
-    }
-
-    if (recurse == 0) {
-       recurse = 1;
-       nc = check_compose(second, first);
-       if (nc == -1)
-           nc = check_compose(toupper(first), toupper(second));
-       if (nc == -1)
-           nc = check_compose(toupper(second), toupper(first));
-       recurse = 0;
-    }
-    return nc;
-}
-
-
 /*
  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
  * codes. Returns number of bytes used or zero to drop the message
@@ -2171,6 +2207,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
     int r, i, code;
     unsigned char *p = output;
     static int alt_state = 0;
+    static int alt_sum = 0;
 
     HKL kbd_layout = GetKeyboardLayout(0);
 
@@ -2183,6 +2220,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        memset(keystate, 0, sizeof(keystate));
     else {
 #if 0
+#define SHOW_TOASCII_RESULT
        {                              /* Tell us all about key events */
            static BYTE oldstate[256];
            static int first = 1;
@@ -2791,19 +2829,38 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
            *p++ = 0;
            return -2;
        }
+
+       if (left_alt && wParam >= VK_NUMPAD0 && wParam <= VK_NUMPAD9)
+           alt_sum = alt_sum * 10 + wParam - VK_NUMPAD0;
+       else
+           alt_sum = 0;
     }
 
     /* Okay we've done everything interesting; let windows deal with 
      * the boring stuff */
     {
-       BOOL capsOn = keystate[VK_CAPITAL] != 0;
-
-       /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */
-       if (cfg.xlat_capslockcyr)
-           keystate[VK_CAPITAL] = 0;
-
        r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+#ifdef SHOW_TOASCII_RESULT
+       if (r == 1 && !key_down) {
+           if (alt_sum) {
+               if (utf || dbcs_screenfont)
+                   debug((", (U+%04x)", alt_sum));
+               else
+                   debug((", LCH(%d)", alt_sum));
+           } else {
+               debug((", ACH(%d)", keys[0]));
+           }
+       } else if (r > 0) {
+           int r1;
+           debug((", ASC("));
+           for (r1 = 0; r1 < r; r1++) {
+               debug(("%s%d", r1 ? "," : "", keys[r1]));
+           }
+           debug((")"));
+       }
+#endif
        if (r > 0) {
+           WCHAR keybuf;
            p = output;
            for (i = 0; i < r; i++) {
                unsigned char ch = (unsigned char) keys[i];
@@ -2821,20 +2878,30 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
                        MessageBeep(MB_ICONHAND);
                        return 0;
                    }
-                   *p++ = xlat_kbd2tty((unsigned char) nc);
-                   return p - output;
+                   keybuf = nc;
+                   luni_send(&keybuf, 1);
+                   continue;
                }
 
                compose_state = 0;
 
-               if (left_alt && key_down)
-                   *p++ = '\033';
-               if (!key_down)
-                   *p++ = ch;
-               else {
-                   if (capsOn)
-                       ch = xlat_latkbd2win(ch);
-                   *p++ = xlat_kbd2tty(ch);
+               if (!key_down) {
+                   if (alt_sum) {
+                       if (utf || dbcs_screenfont) {
+                           keybuf = alt_sum;
+                           luni_send(&keybuf, 1);
+                       } else {
+                           ch = (char) alt_sum;
+                           ldisc_send(&ch, 1);
+                       }
+                       alt_sum = 0;
+                   } else
+                       lpage_send(kbd_codepage, &ch, 1);
+               } else {
+                   static char cbuf[] = "\033 ";
+                   cbuf[1] = ch;
+                   lpage_send(kbd_codepage, cbuf + !left_alt,
+                              1 + !!left_alt);
                }
            }
 
@@ -2846,6 +2913,9 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
        /* If we're definitly not building up an ALT-54321 then clear it */
        if (!left_alt)
            keys[0] = 0;
+       /* If we will be using alt_sum fix the 256s */
+       else if (keys[0] && (utf || dbcs_screenfont))
+           keys[0] = 10;
     }
 
     /* ALT alone may or may not want to bring up the System menu */
@@ -2976,7 +3046,7 @@ void palette_reset(void)
     }
 }
 
-void write_clip(void *data, int len, int must_deselect)
+void write_aclip(char *data, int len, int must_deselect)
 {
     HGLOBAL clipdata;
     void *lock;
@@ -3005,33 +3075,100 @@ void write_clip(void *data, int len, int must_deselect)
        SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
 }
 
-void get_clip(void **p, int *len)
+/*
+ * Note: unlike write_aclip() this will not append a nul.
+ */
+void write_clip(wchar_t * data, int len, int must_deselect)
+{
+    HGLOBAL clipdata;
+    HGLOBAL clipdata2;
+    int len2;
+    void *lock, *lock2;
+
+    len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL);
+
+    clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,
+                          len * sizeof(wchar_t));
+    clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2);
+
+    if (!clipdata || !clipdata2) {
+       if (clipdata)
+           GlobalFree(clipdata);
+       if (clipdata2)
+           GlobalFree(clipdata2);
+       return;
+    }
+    if (!(lock = GlobalLock(clipdata)))
+       return;
+    if (!(lock2 = GlobalLock(clipdata2)))
+       return;
+
+    memcpy(lock, data, len * sizeof(wchar_t));
+    WideCharToMultiByte(CP_ACP, 0, data, len, lock2, len2, NULL, NULL);
+
+    GlobalUnlock(clipdata);
+    GlobalUnlock(clipdata2);
+
+    if (!must_deselect)
+       SendMessage(hwnd, WM_IGNORE_CLIP, TRUE, 0);
+
+    if (OpenClipboard(hwnd)) {
+       EmptyClipboard();
+       SetClipboardData(CF_UNICODETEXT, clipdata);
+       SetClipboardData(CF_TEXT, clipdata2);
+       CloseClipboard();
+    } else {
+       GlobalFree(clipdata);
+       GlobalFree(clipdata2);
+    }
+
+    if (!must_deselect)
+       SendMessage(hwnd, WM_IGNORE_CLIP, FALSE, 0);
+}
+
+void get_clip(wchar_t ** p, int *len)
 {
     static HGLOBAL clipdata = NULL;
+    static wchar_t *converted = 0;
+    wchar_t *p2;
 
+    if (converted) {
+       sfree(converted);
+       converted = 0;
+    }
     if (!p) {
        if (clipdata)
            GlobalUnlock(clipdata);
        clipdata = NULL;
        return;
-    } else {
-       if (OpenClipboard(NULL)) {
-           clipdata = GetClipboardData(CF_TEXT);
+    } else if (OpenClipboard(NULL)) {
+       if (clipdata = GetClipboardData(CF_UNICODETEXT)) {
            CloseClipboard();
-           if (clipdata) {
-               *p = GlobalLock(clipdata);
-               if (*p) {
-                   *len = strlen(*p);
-                   return;
-               }
+           *p = GlobalLock(clipdata);
+           if (*p) {
+               for (p2 = *p; *p2; p2++);
+               *len = p2 - *p;
+               return;
            }
-       }
+       } else if (clipdata = GetClipboardData(CF_TEXT)) {
+           char *s;
+           int i;
+           CloseClipboard();
+           s = GlobalLock(clipdata);
+           i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0);
+           *p = converted = smalloc(i * sizeof(wchar_t));
+           MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, converted, i);
+           *len = i - 1;
+           return;
+       } else
+           CloseClipboard();
     }
 
     *p = NULL;
     *len = 0;
 }
 
+#if 0
 /*
  * Move `lines' lines from position `from' to position `to' in the
  * window.
@@ -3050,6 +3187,7 @@ void optimised_move(int to, int from, int lines)
     r.bottom = (max + lines) * font_height;
     ScrollWindow(hwnd, 0, (to - from) * font_height, &r, &r);
 }
+#endif
 
 /*
  * Print a message box and perform a fatal exit.
diff --git a/xlat.c b/xlat.c
deleted file mode 100644 (file)
index c1d9efc..0000000
--- a/xlat.c
+++ /dev/null
@@ -1,233 +0,0 @@
-#include <windows.h>
-#include <stdio.h>
-#include "putty.h"
-
-static unsigned char win2koi[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
-    111,
-    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
-    126, 127,
-    128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
-    142, 143,
-    144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
-    158, 159,
-    160, 161, 162, 163, 164, 189, 166, 167, 179, 169, 180, 171, 172, 173,
-    174, 183,
-    176, 177, 182, 166, 173, 181, 182, 183, 163, 185, 164, 187, 188, 189,
-    190, 167,
-    225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238,
-    239, 240,
-    242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252,
-    224, 241,
-    193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206,
-    207, 208,
-    210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220,
-    192, 209
-};
-
-static unsigned char koi2win[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
-    111,
-    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
-    126, 127,
-    128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
-    142, 143,
-    144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
-    158, 159,
-    160, 161, 162, 184, 186, 165, 179, 191, 168, 169, 170, 171, 172, 180,
-    174, 175,
-    176, 177, 178, 168, 170, 181, 178, 175, 184, 185, 186, 187, 188, 165,
-    190, 191,
-    254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236,
-    237, 238,
-    239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249,
-    247, 250,
-    222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204,
-    205, 206,
-    207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217,
-    215, 218
-};
-
-static unsigned char xlatWIN1250toISO88592[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
-    111,
-    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
-    126, 127,
-    128, 129, 39, 131, 34, 46, 124, 124, 136, 47, 169, 60, 166, 171, 174,
-    172,
-    144, 96, 39, 34, 34, 42, 45, 45, 152, 84, 185, 62, 182, 187, 190, 188,
-    160, 183, 162, 163, 164, 161, 124, 167, 168, 99, 170, 34, 39, 173, 82,
-    175,
-    176, 63, 178, 179, 180, 117, 182, 255, 184, 177, 186, 34, 165, 189,
-    181, 191,
-    192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
-    206, 207,
-    208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
-    222, 223,
-    224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
-    238, 239,
-    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253,
-    254, 255
-};
-
-static unsigned char xlatISO88592toWIN1250[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
-    111,
-    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
-    126, 127,
-    128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
-    142, 143,
-    144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
-    158, 159,
-    160, 165, 162, 163, 164, 188, 140, 167, 168, 138, 170, 141, 143, 173,
-    142, 175,
-    176, 185, 178, 179, 180, 190, 156, 161, 184, 154, 186, 157, 159, 189,
-    158, 191,
-    192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
-    206, 207,
-    208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
-    222, 223,
-    224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
-    238, 239,
-    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253,
-    254, 255
-};
-
-static unsigned char xlatISO88592toCP852[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
-    111,
-    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
-    126, 127,
-    242, 32, 34, 32, 34, 46, 43, 35, 32, 47, 138, 60, 151, 141, 166, 141,
-    032, 34, 34, 34, 34, 254, 45, 45, 32, 126, 154, 62, 152, 157, 167, 171,
-    255, 164, 244, 157, 207, 149, 151, 245, 249, 230, 184, 155, 141, 240,
-    166, 189,
-    248, 165, 247, 136, 239, 150, 152, 243, 242, 231, 173, 156, 171, 241,
-    167, 190,
-    232, 181, 182, 198, 142, 145, 143, 128, 172, 144, 168, 211, 183, 214,
-    215, 210,
-    209, 227, 213, 224, 226, 138, 153, 158, 252, 222, 233, 235, 154, 237,
-    221, 225,
-    234, 160, 131, 199, 132, 146, 134, 135, 159, 130, 169, 137, 216, 161,
-    140, 212,
-    208, 228, 229, 162, 147, 139, 148, 246, 253, 133, 163, 251, 129, 236,
-    238, 250,
-};
-
-static unsigned char xlatCP852toISO88592[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
-    64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
-    80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
-    96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110,
-    111,
-    112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
-    126, 127,
-    199, 252, 233, 226, 228, 249, 230, 231, 179, 235, 138, 245, 238, 141,
-    196, 198,
-    201, 197, 229, 244, 246, 165, 181, 140, 156, 214, 154, 171, 187, 157,
-    215, 232,
-    225, 237, 243, 250, 161, 177, 142, 158, 202, 234, 170, 159, 200, 186,
-    174, 175,
-    176, 177, 178, 179, 180, 193, 194, 204, 170, 185, 186, 187, 188, 175,
-    191, 191,
-    192, 193, 194, 195, 196, 197, 195, 227, 200, 201, 202, 203, 204, 205,
-    206, 164,
-    240, 208, 207, 203, 239, 210, 205, 206, 236, 217, 218, 219, 220, 222,
-    217, 223,
-    211, 223, 212, 209, 241, 242, 169, 185, 192, 218, 224, 219, 253, 221,
-    254, 180,
-    173, 189, 128, 183, 162, 167, 247, 178, 176, 168, 255, 251, 216, 248,
-    149, 160,
-};
-
-unsigned char xlat_kbd2tty(unsigned char c)
-{
-    if (cfg.xlat_enablekoiwin)
-       return win2koi[c];
-    else if (cfg.xlat_88592w1250 || cfg.xlat_88592cp852)
-       return xlatWIN1250toISO88592[c];
-    return c;
-}
-
-unsigned char xlat_tty2scr(unsigned char c)
-{
-    if (cfg.xlat_enablekoiwin)
-       return koi2win[c];
-    else if (cfg.xlat_88592w1250)
-       return xlatISO88592toWIN1250[c];
-    else if (cfg.xlat_88592cp852)
-       return xlatISO88592toCP852[c];
-    return c;
-}
-
-
-static unsigned char latkbd2_win[] = {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-    32, 33, 221, 35, 36, 37, 38, 253, 40, 41, 42, 178, 225, 186, 254, 46,
-    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 198, 230, 193, 179, 222, 44,
-    64, 212, 200, 209, 194, 211, 192, 207, 208, 216, 206, 203, 196, 220,
-    210, 217,
-    199, 201, 202, 219, 197, 195, 204, 214, 215, 205, 223, 245, 191, 250,
-    94, 170,
-    96, 244, 232, 241, 226, 243, 224, 239, 240, 248, 238, 235, 228, 252,
-    242, 249,
-    231, 233, 234, 251, 229, 227, 236, 246, 247, 237, 255, 213, 175, 218,
-    126, 127,
-    128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
-    142, 143,
-    144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157,
-    158, 159,
-    160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
-    174, 175,
-    176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
-    190, 191,
-    192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
-    206, 207,
-    208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
-    222, 223,
-    224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
-    238, 239,
-    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253,
-    254, 255
-};
-
-unsigned char xlat_latkbd2win(unsigned char c)
-{
-    if (cfg.xlat_capslockcyr)
-       return latkbd2_win[c];
-    return c;
-}