Logo Search packages:      
Sourcecode: ibm-3270 version File versions  Download package

ansi.c

/*
 * Copyright (c) 1993-2009, Paul Mattes.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the names of Paul Mattes nor the names of his contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 *    ansi.c
 *          ANSI terminal emulation.
 */

#include "globals.h"

#if defined(X3270_ANSI) /*[*/

#if defined(X3270_DISPLAY) /*[*/
#include <X11/Shell.h>
#endif /*]*/

#include "appres.h"
#include "ctlr.h"
#include "3270ds.h"

#include "ansic.h"
#include "charsetc.h"
#include "ctlrc.h"
#include "hostc.h"
#include "screenc.h"
#include "scrollc.h"
#include "tablesc.h"
#include "telnetc.h"
#include "trace_dsc.h"
#include "unicodec.h"
#include "utf8c.h"

#define MB_MAX    16

#define PE_MAX    1024

#define     SC    1     /* save cursor position */
#define RC  2     /* restore cursor position */
#define NL  3     /* new line */
#define UP  4     /* cursor up */
#define     E2    5     /* second level of ESC processing */
#define rS  6     /* reset */
#define IC  7     /* insert chars */
#define DN  8     /* cursor down */
#define RT  9     /* cursor right */
#define LT  10    /* cursor left */
#define CM  11    /* cursor motion */
#define ED  12    /* erase in display */
#define EL  13    /* erase in line */
#define IL  14    /* insert lines */
#define DL  15    /* delete lines */
#define DC  16    /* delete characters */
#define     SG    17    /* set graphic rendition */
#define BL  18    /* ring bell */
#define NP  19    /* new page */
#define BS  20    /* backspace */
#define CR  21    /* carriage return */
#define LF  22    /* line feed */
#define HT  23    /* horizontal tab */
#define E1  24    /* first level of ESC processing */
#define Xx  25    /* undefined control character (nop) */
#define Pc  26    /* printing character */
#define Sc  27    /* semicolon (after ESC [) */
#define Dg  28    /* digit (after ESC [ or ESC [ ?) */
#define RI  29    /* reverse index */
#define DA  30    /* send device attributes */
#define SM  31    /* set mode */
#define RM  32    /* reset mode */
#define DO  33    /* return terminal ID (obsolete) */
#define SR  34    /* device status report */
#define CS  35    /* character set designate */
#define E3  36    /* third level of ESC processing */
#define DS  37    /* DEC private set */
#define DR  38    /* DEC private reset */
#define DV  39    /* DEC private save */
#define DT  40    /* DEC private restore */
#define SS  41    /* set scrolling region */
#define TM  42    /* text mode (ESC ]) */
#define T2  43    /* semicolon (after ESC ]) */
#define TX  44    /* text parameter (after ESC ] n ;) */
#define TB  45    /* text parameter done (ESC ] n ; xxx BEL) */
#define TS  46    /* tab set */
#define TC  47    /* tab clear */
#define C2  48    /* character set designate (finish) */
#define G0  49    /* select G0 character set */
#define G1  50    /* select G1 character set */
#define G2  51    /* select G2 character set */
#define G3  52    /* select G3 character set */
#define S2  53    /* select G2 for next character */
#define S3  54    /* select G3 for next character */
#define MB  55    /* process multi-byte character */

static enum state {
    DATA = 0, ESC = 1, CSDES = 2,
    N1 = 3, DECP = 4, TEXT = 5, TEXT2 = 6,
    MBPEND = 7
} state = DATA;

static enum state ansi_data_mode(int, int);
static enum state dec_save_cursor(int, int);
static enum state dec_restore_cursor(int, int);
static enum state ansi_newline(int, int);
static enum state ansi_cursor_up(int, int);
static enum state ansi_esc2(int, int);
static enum state ansi_reset(int, int);
static enum state ansi_insert_chars(int, int);
static enum state ansi_cursor_down(int, int);
static enum state ansi_cursor_right(int, int);
static enum state ansi_cursor_left(int, int);
static enum state ansi_cursor_motion(int, int);
static enum state ansi_erase_in_display(int, int);
static enum state ansi_erase_in_line(int, int);
static enum state ansi_insert_lines(int, int);
static enum state ansi_delete_lines(int, int);
static enum state ansi_delete_chars(int, int);
static enum state ansi_sgr(int, int);
static enum state ansi_bell(int, int);
static enum state ansi_newpage(int, int);
static enum state ansi_backspace(int, int);
static enum state ansi_cr(int, int);
static enum state ansi_lf(int, int);
static enum state ansi_htab(int, int);
static enum state ansi_escape(int, int);
static enum state ansi_nop(int, int);
static enum state ansi_printing(int, int);
static enum state ansi_semicolon(int, int);
static enum state ansi_digit(int, int);
static enum state ansi_reverse_index(int, int);
static enum state ansi_send_attributes(int, int);
static enum state ansi_set_mode(int, int);
static enum state ansi_reset_mode(int, int);
static enum state dec_return_terminal_id(int, int);
static enum state ansi_status_report(int, int);
static enum state ansi_cs_designate(int, int);
static enum state ansi_esc3(int, int);
static enum state dec_set(int, int);
static enum state dec_reset(int, int);
static enum state dec_save(int, int);
static enum state dec_restore(int, int);
static enum state dec_scrolling_region(int, int);
static enum state xterm_text_mode(int, int);
static enum state xterm_text_semicolon(int, int);
static enum state xterm_text(int, int);
static enum state xterm_text_do(int, int);
static enum state ansi_htab_set(int, int);
static enum state ansi_htab_clear(int, int);
static enum state ansi_cs_designate2(int, int);
static enum state ansi_select_g0(int, int);
static enum state ansi_select_g1(int, int);
static enum state ansi_select_g2(int, int);
static enum state ansi_select_g3(int, int);
static enum state ansi_one_g2(int, int);
static enum state ansi_one_g3(int, int);
static enum state ansi_multibyte(int, int);

typedef enum state (*afn_t)(int, int);
static afn_t ansi_fn[] = {
/* 0 */           &ansi_data_mode,
/* 1 */           &dec_save_cursor,
/* 2 */           &dec_restore_cursor,
/* 3 */           &ansi_newline,
/* 4 */           &ansi_cursor_up,
/* 5 */           &ansi_esc2,
/* 6 */           &ansi_reset,
/* 7 */           &ansi_insert_chars,
/* 8 */           &ansi_cursor_down,
/* 9 */           &ansi_cursor_right,
/* 10 */    &ansi_cursor_left,
/* 11 */    &ansi_cursor_motion,
/* 12 */    &ansi_erase_in_display,
/* 13 */    &ansi_erase_in_line,
/* 14 */    &ansi_insert_lines,
/* 15 */    &ansi_delete_lines,
/* 16 */    &ansi_delete_chars,
/* 17 */    &ansi_sgr,
/* 18 */    &ansi_bell,
/* 19 */    &ansi_newpage,
/* 20 */    &ansi_backspace,
/* 21 */    &ansi_cr,
/* 22 */    &ansi_lf,
/* 23 */    &ansi_htab,
/* 24 */    &ansi_escape,
/* 25 */    &ansi_nop,
/* 26 */    &ansi_printing,
/* 27 */    &ansi_semicolon,
/* 28 */    &ansi_digit,
/* 29 */    &ansi_reverse_index,
/* 30 */    &ansi_send_attributes,
/* 31 */    &ansi_set_mode,
/* 32 */    &ansi_reset_mode,
/* 33 */    &dec_return_terminal_id,
/* 34 */    &ansi_status_report,
/* 35 */    &ansi_cs_designate,
/* 36 */    &ansi_esc3,
/* 37 */    &dec_set,
/* 38 */    &dec_reset,
/* 39 */    &dec_save,
/* 40 */    &dec_restore,
/* 41 */    &dec_scrolling_region,
/* 42 */    &xterm_text_mode,
/* 43 */    &xterm_text_semicolon,
/* 44 */    &xterm_text,
/* 45 */    &xterm_text_do,
/* 46 */    &ansi_htab_set,
/* 47 */    &ansi_htab_clear,
/* 48 */    &ansi_cs_designate2,
/* 49 */    &ansi_select_g0,
/* 50 */    &ansi_select_g1,
/* 51 */    &ansi_select_g2,
/* 52 */    &ansi_select_g3,
/* 53 */    &ansi_one_g2,
/* 54 */    &ansi_one_g3,
/* 55 */    &ansi_multibyte,
};

static unsigned char st[8][256] = {
/*
 * State table for base processing (state == DATA)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */       Xx,Xx,Xx,Xx,Xx,Xx,Xx,BL,BS,HT,LF,LF,NP,CR,G1,G0,
/* 10 */       Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,E1,Xx,Xx,Xx,Xx,
/* 20 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* 30 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* 40 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* 50 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* 60 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* 70 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Xx,
/* 80 */       Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,
/* 90 */       Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,Xx,
/* a0 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* b0 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* c0 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* d0 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* e0 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,
/* f0 */       Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc,Pc
},

/*
 * State table for ESC processing (state == ESC)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 20 */    0, 0, 0, 0, 0, 0, 0, 0,CS,CS,CS,CS, 0, 0, 0, 0,
/* 30 */    0, 0, 0, 0, 0, 0, 0,SC,RC, 0, 0, 0, 0, 0, 0, 0,
/* 40 */    0, 0, 0, 0, 0,NL, 0, 0,TS, 0, 0, 0, 0,RI,S2,S3,
/* 50 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,E2, 0,TM, 0, 0,
/* 60 */    0, 0, 0,rS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,G2,G3,
/* 70 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 80 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 90 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* b0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* c0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* d0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* e0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* f0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},

/*
 * State table for ESC ()*+ C processing (state == CSDES)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 20 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 30 */       C2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 40 */    0,C2,C2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 50 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 60 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 70 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 80 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 90 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* b0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* c0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* d0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* e0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* f0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},

/*
 * State table for ESC [ processing (state == N1)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 20 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 30 */       Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg, 0,Sc, 0, 0, 0,E3,
/* 40 */       IC,UP,DN,RT,LT, 0, 0, 0,CM, 0,ED,EL,IL,DL, 0, 0,
/* 50 */       DC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 60 */    0, 0, 0,DA, 0, 0,CM,TC,SM, 0, 0, 0,RM,SG,SR, 0,
/* 70 */    0, 0,SS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 80 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 90 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* b0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* c0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* d0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* e0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* f0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},

/*
 * State table for ESC [ ? processing (state == DECP)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 20 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 30 */       Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg, 0, 0, 0, 0, 0, 0,
/* 40 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 50 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 60 */    0, 0, 0, 0, 0, 0, 0, 0,DS, 0, 0, 0,DR, 0, 0, 0,
/* 70 */    0, 0,DT,DV, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 80 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 90 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* b0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* c0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* d0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* e0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* f0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},

/*
 * State table for ESC ] processing (state == TEXT)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 20 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 30 */       Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg,Dg, 0,T2, 0, 0, 0, 0,
/* 40 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 50 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 60 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 70 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 80 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 90 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* a0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* b0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* c0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* d0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* e0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* f0 */    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
},

/*
 * State table for ESC ] n ; processing (state == TEXT2)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */        0, 0, 0, 0, 0, 0, 0,TB, 0, 0, 0, 0, 0, 0, 0, 0,
/* 10 */        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 20 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* 30 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* 40 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* 50 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* 60 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* 70 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,Xx,
/* 80 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* 90 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* a0 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* b0 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* c0 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* d0 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* e0 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,
/* f0 */       TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX,TX
},
/*
 * State table for multi-byte characters (state == MBPEND)
 */
{
           /* 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  */
/* 00 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 10 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 20 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 30 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 40 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 50 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 60 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 70 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 80 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* 90 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* a0 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* b0 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* c0 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* d0 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* e0 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,
/* f0 */       MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB,MB
},
};

/* Character sets. */
#define CS_G0           0
#define CS_G1           1
#define CS_G2           2
#define CS_G3           3

/* Character set designations. */
#define CSD_LD          0
#define CSD_UK          1
#define CSD_US          2

static int      saved_cursor = 0;
#define NN  20
static int      n[NN], nx = 0;
#define NT  256
static char     text[NT + 1];
static int      tx = 0;
static int      ansi_ch;
static unsigned char gr = 0;
static unsigned char saved_gr = 0;
static unsigned char fg = 0;
static unsigned char saved_fg = 0;
static unsigned char bg = 0;
static unsigned char saved_bg = 0;
static int  cset = CS_G0;
static int  saved_cset = CS_G0;
static int  csd[4] = { CSD_US, CSD_US, CSD_US, CSD_US };
static int  saved_csd[4] = { CSD_US, CSD_US, CSD_US, CSD_US };
static int  once_cset = -1;
static int      insert_mode = 0;
static int      auto_newline_mode = 0;
static int      appl_cursor = 0;
static int      saved_appl_cursor = 0;
static int      wraparound_mode = 1;
static int      saved_wraparound_mode = 1;
static int      rev_wraparound_mode = 0;
static int      saved_rev_wraparound_mode = 0;
static int  allow_wide_mode = 0;
static int  saved_allow_wide_mode = 0;
static int  wide_mode = 0;
static int  saved_wide_mode = 0;
static Boolean  saved_altbuffer = False;
static int      scroll_top = -1;
static int      scroll_bottom = -1;
static unsigned char *tabs = (unsigned char *) NULL;
static char gnnames[] = "()*+";
static char csnames[] = "0AB";
static int  cs_to_change;
static int  pmi = 0;
static char pending_mbs[MB_MAX];
static int  pe = 0;
static unsigned char ped[PE_MAX];

static Boolean  held_wrap = False;

static void ansi_scroll(void);

static enum state
ansi_data_mode(int ig1 _is_unused, int ig2 _is_unused)
{
      return DATA;
}

static enum state
dec_save_cursor(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      saved_cursor = cursor_addr;
      saved_cset = cset;
      for (i = 0; i < 4; i++)
            saved_csd[i] = csd[i];
      saved_fg = fg;
      saved_bg = bg;
      saved_gr = gr;
      return DATA;
}

static enum state
dec_restore_cursor(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      cset = saved_cset;
      for (i = 0; i < 4; i++)
            csd[i] = saved_csd[i];
      fg = saved_fg;
      bg = saved_bg;
      gr = saved_gr;
      cursor_move(saved_cursor);
      held_wrap = False;
      return DATA;
}

static enum state
ansi_newline(int ig1 _is_unused, int ig2 _is_unused)
{
      int nc;

      cursor_move(cursor_addr - (cursor_addr % COLS));
      nc = cursor_addr + COLS;
      if (nc < scroll_bottom * COLS)
            cursor_move(nc);
      else
            ansi_scroll();
      held_wrap = False;
      return DATA;
}

static enum state
ansi_cursor_up(int nn, int ig2 _is_unused)
{
      int rr;

      if (nn < 1)
            nn = 1;
      rr = cursor_addr / COLS;
      if (rr - nn < 0)
            cursor_move(cursor_addr % COLS);
      else
            cursor_move(cursor_addr - (nn * COLS));
      held_wrap = False;
      return DATA;
}

static enum state
ansi_esc2(int ig1 _is_unused, int ig2 _is_unused)
{
      register int      i;

      for (i = 0; i < NN; i++)
            n[i] = 0;
      nx = 0;
      return N1;
}

static enum state
ansi_reset(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;
      static Boolean first = True;

      gr = 0;
      saved_gr = 0;
      fg = 0;
      saved_fg = 0;
      bg = 0;
      saved_bg = 0;
      cset = CS_G0;
      saved_cset = CS_G0;
      csd[0] = csd[1] = csd[2] = csd[3] = CSD_US;
      saved_csd[0] = saved_csd[1] = saved_csd[2] = saved_csd[3] = CSD_US;
      once_cset = -1;
      saved_cursor = 0;
      insert_mode = 0;
      auto_newline_mode = 0;
      appl_cursor = 0;
      saved_appl_cursor = 0;
      wraparound_mode = 1;
      saved_wraparound_mode = 1;
      rev_wraparound_mode = 0;
      saved_rev_wraparound_mode = 0;
      allow_wide_mode = 0;
      saved_allow_wide_mode = 0;
      wide_mode = 0;
      allow_wide_mode = 0;
      saved_altbuffer = False;
      scroll_top = 1;
      scroll_bottom = ROWS;
      Replace(tabs, (unsigned char *)Malloc((COLS+7)/8));
      for (i = 0; i < (COLS+7)/8; i++)
            tabs[i] = 0x01;
      held_wrap = False;
      if (!first) {
            ctlr_altbuffer(True);
            ctlr_aclear(0, ROWS * COLS, 1);
            ctlr_altbuffer(False);
            ctlr_clear(False);
            screen_80();
      }
      first = False;
      pmi = 0;
      return DATA;
}

static enum state
ansi_insert_chars(int nn, int ig2 _is_unused)
{
      int cc = cursor_addr % COLS;  /* current col */
      int mc = COLS - cc;           /* max chars that can be inserted */
      int ns;                       /* chars that are shifting */

      if (nn < 1)
            nn = 1;
      if (nn > mc)
            nn = mc;

      /* Move the surviving chars right */
      ns = mc - nn;
      if (ns)
            ctlr_bcopy(cursor_addr, cursor_addr + nn, ns, 1);

      /* Clear the middle of the line */
      ctlr_aclear(cursor_addr, nn, 1);
      return DATA;
}

static enum state
ansi_cursor_down(int nn, int ig2 _is_unused)
{
      int rr;

      if (nn < 1)
            nn = 1;
      rr = cursor_addr / COLS;
      if (rr + nn >= ROWS)
            cursor_move((ROWS-1)*COLS + (cursor_addr%COLS));
      else
            cursor_move(cursor_addr + (nn * COLS));
      held_wrap = False;
      return DATA;
}

static enum state
ansi_cursor_right(int nn, int ig2 _is_unused)
{
      int cc;

      if (nn < 1)
            nn = 1;
      cc = cursor_addr % COLS;
      if (cc == COLS-1)
            return DATA;
      if (cc + nn >= COLS)
            nn = COLS - 1 - cc;
      cursor_move(cursor_addr + nn);
      held_wrap = False;
      return DATA;
}

static enum state
ansi_cursor_left(int nn, int ig2 _is_unused)
{
      int cc;

      if (held_wrap) {
            held_wrap = False;
            return DATA;
      }
      if (nn < 1)
            nn = 1;
      cc = cursor_addr % COLS;
      if (!cc)
            return DATA;
      if (nn > cc)
            nn = cc;
      cursor_move(cursor_addr - nn);
      return DATA;
}

static enum state
ansi_cursor_motion(int n1, int n2)
{
      if (n1 < 1) n1 = 1;
      if (n1 > ROWS) n1 = ROWS;
      if (n2 < 1) n2 = 1;
      if (n2 > COLS) n2 = COLS;
      cursor_move((n1 - 1) * COLS + (n2 - 1));
      held_wrap = False;
      return DATA;
}

static enum state
ansi_erase_in_display(int nn, int ig2 _is_unused)
{
      switch (nn) {
          case 0: /* below */
            ctlr_aclear(cursor_addr, (ROWS * COLS) - cursor_addr, 1);
            break;
          case 1: /* above */
            ctlr_aclear(0, cursor_addr + 1, 1);
            break;
          case 2: /* all (without moving cursor) */
            if (cursor_addr == 0 && !is_altbuffer)
                  scroll_save(ROWS, True);
            ctlr_aclear(0, ROWS * COLS, 1);
            break;
      }
      return DATA;
}

static enum state
ansi_erase_in_line(int nn, int ig2 _is_unused)
{
      int nc = cursor_addr % COLS;

      switch (nn) {
          case 0: /* to right */
            ctlr_aclear(cursor_addr, COLS - nc, 1);
            break;
          case 1: /* to left */
            ctlr_aclear(cursor_addr - nc, nc+1, 1);
            break;
          case 2: /* all */
            ctlr_aclear(cursor_addr - nc, COLS, 1);
            break;
      }
      return DATA;
}

static enum state
ansi_insert_lines(int nn, int ig2 _is_unused)
{
      int rr = cursor_addr / COLS;  /* current row */
      int mr = scroll_bottom - rr;  /* rows left at and below this one */
      int ns;                       /* rows that are shifting */

      /* If outside of the scrolling region, do nothing */
      if (rr < scroll_top - 1 || rr >= scroll_bottom)
            return DATA;

      if (nn < 1)
            nn = 1;
      if (nn > mr)
            nn = mr;
      
      /* Move the victims down */
      ns = mr - nn;
      if (ns)
            ctlr_bcopy(rr * COLS, (rr + nn) * COLS, ns * COLS, 1);

      /* Clear the middle of the screen */
      ctlr_aclear(rr * COLS, nn * COLS, 1);
      return DATA;
}

static enum state
ansi_delete_lines(int nn, int ig2 _is_unused)
{
      int rr = cursor_addr / COLS;  /* current row */
      int mr = scroll_bottom - rr;  /* max rows that can be deleted */
      int ns;                       /* rows that are shifting */

      /* If outside of the scrolling region, do nothing */
      if (rr < scroll_top - 1 || rr >= scroll_bottom)
            return DATA;

      if (nn < 1)
            nn = 1;
      if (nn > mr)
            nn = mr;

      /* Move the surviving rows up */
      ns = mr - nn;
      if (ns)
            ctlr_bcopy((rr + nn) * COLS, rr * COLS, ns * COLS, 1);

      /* Clear the rest of the screen */
      ctlr_aclear((rr + ns) * COLS, nn * COLS, 1);
      return DATA;
}

static enum state
ansi_delete_chars(int nn, int ig2 _is_unused)
{
      int cc = cursor_addr % COLS;  /* current col */
      int mc = COLS - cc;           /* max chars that can be deleted */
      int ns;                       /* chars that are shifting */

      if (nn < 1)
            nn = 1;
      if (nn > mc)
            nn = mc;

      /* Move the surviving chars left */
      ns = mc - nn;
      if (ns)
            ctlr_bcopy(cursor_addr + nn, cursor_addr, ns, 1);

      /* Clear the end of the line */
      ctlr_aclear(cursor_addr + ns, nn, 1);
      return DATA;
}

static enum state
ansi_sgr(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      for (i = 0; i <= nx && i < NN; i++)
          switch (n[i]) {
            case 0:
                gr = 0;
                fg = 0;
                bg = 0;
                break;
            case 1:
                gr |= GR_INTENSIFY;
                break;
            case 4:
                gr |= GR_UNDERLINE;
                break;
            case 5:
                gr |= GR_BLINK;
                break;
            case 7:
                gr |= GR_REVERSE;
                break;
            case 30:
                fg = 0xf0;    /* black */
                break;
            case 31:
                fg = 0xf2;    /* red */
                break;
            case 32:
                fg = 0xf4;    /* green */
                break;
            case 33:
                fg = 0xf6;    /* yellow */
                break;
            case 34:
                fg = 0xf1;    /* blue */
                break;
            case 35:
                fg = 0xf3;    /* magenta */
                break;
            case 36:
#if defined(WC3270) /*[*/
                fg = 0xf6;    /* turquoise */
#else /*][*/
                fg = 0xfd;    /* cyan */
#endif /*]*/
                break;
            case 37:
#if defined(WC3270) /*[*/
                fg = 0xf7;    /* white */
#else /*][*/
                fg = 0xff;    /* white */
#endif /*]*/
                break;
            case 39:
                fg = 0; /* default */
                break;
            case 40:
                bg = 0xf0;    /* black */
                break;
            case 41:
                bg = 0xf2;    /* red */
                break;
            case 42:
                bg = 0xf4;    /* green */
                break;
            case 43:
                bg = 0xf6;    /* yellow */
                break;
            case 44:
                bg = 0xf1;    /* blue */
                break;
            case 45:
                bg = 0xf3;    /* magenta */
                break;
            case 46:
#if defined(WC3270) /*[*/
                bg = 0xf6;    /* turquoise */
#else /*][*/
                bg = 0xfd;    /* cyan */
#endif /*]*/
                break;
            case 47:
#if defined(WC3270) /*[*/
                bg = 0xf7;    /* white */
#else /*][*/
                bg = 0xff;    /* white */
#endif /*]*/
                break;
            case 49:
                bg = 0; /* default */
                break;
          }

      return DATA;
}

static enum state
ansi_bell(int ig1 _is_unused, int ig2 _is_unused)
{
      ring_bell();
      return DATA;
}

static enum state
ansi_newpage(int ig1 _is_unused, int ig2 _is_unused)
{
      ctlr_clear(False);
      return DATA;
}

static enum state
ansi_backspace(int ig1 _is_unused, int ig2 _is_unused)
{
      if (held_wrap) {
            held_wrap = False;
            return DATA;
      }
      if (rev_wraparound_mode) {
            if (cursor_addr > (scroll_top - 1) * COLS)
                  cursor_move(cursor_addr - 1);
      } else {
            if (cursor_addr % COLS)
                  cursor_move(cursor_addr - 1);
      }
      return DATA;
}

static enum state
ansi_cr(int ig1 _is_unused, int ig2 _is_unused)
{
      if (cursor_addr % COLS)
            cursor_move(cursor_addr - (cursor_addr % COLS));
      if (auto_newline_mode)
            (void) ansi_lf(0, 0);
      held_wrap = False;
      return DATA;
}

static enum state
ansi_lf(int ig1 _is_unused, int ig2 _is_unused)
{
      int nc = cursor_addr + COLS;

      held_wrap = False;

      /* If we're below the scrolling region, don't scroll. */
      if ((cursor_addr / COLS) >= scroll_bottom) {
            if (nc < ROWS * COLS)
                  cursor_move(nc);
            return DATA;
      }

      if (nc < scroll_bottom * COLS)
            cursor_move(nc);
      else
            ansi_scroll();
      return DATA;
}

static enum state
ansi_htab(int ig1 _is_unused, int ig2 _is_unused)
{
      int col = cursor_addr % COLS;
      int i;

      held_wrap = False;
      if (col == COLS-1)
            return DATA;
      for (i = col+1; i < COLS-1; i++)
            if (tabs[i/8] & 1<<(i%8))
                  break;
      cursor_move(cursor_addr - col + i);
      return DATA;
}

static enum state
ansi_escape(int ig1 _is_unused, int ig2 _is_unused)
{
      return ESC;
}

static enum state
ansi_nop(int ig1 _is_unused, int ig2 _is_unused)
{
      return DATA;
}

#define PWRAP { \
    nc = cursor_addr + 1; \
    if (nc < scroll_bottom * COLS) \
          cursor_move(nc); \
    else { \
          if (cursor_addr / COLS >= scroll_bottom) \
                cursor_move(cursor_addr / COLS * COLS); \
          else { \
                ansi_scroll(); \
                cursor_move(nc - COLS); \
          } \
    } \
}

static enum state
ansi_printing(int ig1 _is_unused, int ig2 _is_unused)
{
      int nc;
      unsigned short ebc_ch;
#if defined(X3270_DBCS) /*[*/
      enum dbcs_state d;
#endif /*]*/

      if ((pmi == 0) && (ansi_ch & 0x80)) {
            char mbs[2];
            int consumed;
            enum me_fail fail;
            unsigned long ucs4;

            mbs[0] = (char)ansi_ch;
            mbs[1] = '\0';
            ucs4 = multibyte_to_unicode(mbs, 1, &consumed, &fail);
            if (ucs4 == 0) {
                  switch (fail) {
                  case ME_SHORT:
                        /* Start munching multi-byte. */
                        pmi = 0;
                        pending_mbs[pmi++] = (char)ansi_ch;
                        return MBPEND;
                  case ME_INVALID:
                  default:
                        /* Invalid multi-byte -> '?' */
                        ansi_ch = '?';
                        break;
                  }
            } else {
                  ansi_ch = ucs4;
            }
      }
      pmi = 0;

      /* Translate to EBCDIC to see if it's DBCS. */
      ebc_ch = unicode_to_ebcdic(ansi_ch);
      if (ebc_ch & ~0xff) {
#if defined(X3270_DBCS) /*[*/
            if (!dbcs)
#endif
            {
                  ansi_ch = '?';
                  ebc_ch = asc2ebc0['?'];
            }
      }

      if (held_wrap) {
            PWRAP;
            held_wrap = False;
      }

      if (insert_mode)
            (void) ansi_insert_chars(1, 0);
#if defined(X3270_DBCS) /*[*/
      d = ctlr_dbcs_state(cursor_addr);
#endif /*]*/
      switch (csd[(once_cset != -1) ? once_cset : cset]) {
          case CSD_LD:  /* line drawing "0" */
            if (ansi_ch >= 0x5f && ansi_ch <= 0x7e)
                  ctlr_add(cursor_addr, (unsigned char)(ansi_ch - 0x5f),
                      CS_LINEDRAW);
            else if (ebc_ch & ~0xff)
                  ctlr_add(cursor_addr, unicode_to_ebcdic('?'), CS_BASE);
            else
                  ctlr_add(cursor_addr, ebc_ch, CS_BASE);
            break;
          case CSD_UK:  /* UK "A" */
            if (ansi_ch == '#')
                  ctlr_add(cursor_addr, 0x1e, CS_LINEDRAW);
            else if (ebc_ch & ~0xff)
                  ctlr_add(cursor_addr, unicode_to_ebcdic('?'), CS_BASE);
            else
                  ctlr_add(cursor_addr, ebc_ch, CS_BASE);
            break;
          case CSD_US:  /* US "B" */
#if !defined(X3270_DBCS) /*[*/
            if (ebc_ch & ~0xff)
                  ctlr_add(cursor_addr, unicode_to_ebcdic('?'), CS_BASE);
            else
                  ctlr_add(cursor_addr, ebc_ch, CS_BASE);
#else /*][*/
            if (ebc_ch & ~0xff) {

                  /* Add a DBCS character to the buffer. */
                  if (!dbcs) {
                        /* Not currently using a DBCS character set. */
                        ctlr_add(cursor_addr, unicode_to_ebcdic('?'),
                              CS_BASE);
                        break;
                  }

                  /* Get past the last column. */
                  if ((cursor_addr % COLS) == (COLS-1)) {
                        if (!wraparound_mode)
                              return DATA;
                        ctlr_add(cursor_addr, EBC_space, CS_BASE);
                        ctlr_add_gr(cursor_addr, gr);
                        ctlr_add_fg(cursor_addr, fg);
                        ctlr_add_bg(cursor_addr, bg);
                        cursor_addr = cursor_addr + 1;
                        d = ctlr_dbcs_state(cursor_addr);
                  }

                  /* Add the left half. */
                  ctlr_add(cursor_addr, (ebc_ch >> 8) & 0xff, CS_DBCS);
                  ctlr_add_gr(cursor_addr, gr);
                  ctlr_add_fg(cursor_addr, fg);
                  ctlr_add_bg(cursor_addr, bg);

                  /* Handle unaligned DBCS overwrite. */
                  if (d == DBCS_RIGHT || d == DBCS_RIGHT_WRAP) {
                        int xaddr;

                        xaddr = cursor_addr;
                        DEC_BA(xaddr);
                        ctlr_add(xaddr, EBC_space, CS_BASE);
                        ea_buf[xaddr].db = DBCS_NONE;
                  }

                  /* Add the right half. */
                  INC_BA(cursor_addr);
                  ctlr_add(cursor_addr, ebc_ch & 0xff, CS_DBCS);
                  ctlr_add_gr(cursor_addr, gr);
                  ctlr_add_fg(cursor_addr, fg);
                  ctlr_add_bg(cursor_addr, bg);

                  /* Handle cursor wrap. */
                  if (wraparound_mode) {
                        if (!((cursor_addr + 1) % COLS)) {
                              held_wrap = True;
                        } else {
                              PWRAP;
                        }
                  } else {
                        if ((cursor_addr % COLS) != (COLS - 1))
                              cursor_move(cursor_addr + 1);
                  }
                  (void) ctlr_dbcs_postprocess();
                  return DATA;
            }

            /* Add an SBCS character to the buffer. */
            ctlr_add(cursor_addr, ebc_ch, CS_BASE);
#endif /*]*/
            break;
      }

#if defined(X3270_DBCS) /*[*/
      /* Handle conflicts with existing DBCS characters. */
      if (d == DBCS_RIGHT || d == DBCS_RIGHT_WRAP) {
            int xaddr;

            xaddr = cursor_addr;
            DEC_BA(xaddr);
            ctlr_add(xaddr, EBC_space, CS_BASE);
            ea_buf[xaddr].db = DBCS_NONE;
            ea_buf[cursor_addr].db = DBCS_NONE;
            (void) ctlr_dbcs_postprocess();
      }
      if (d == DBCS_LEFT || d == DBCS_LEFT_WRAP) {
            int xaddr;

            xaddr = cursor_addr;
            INC_BA(xaddr);
            ctlr_add(xaddr, EBC_space, CS_BASE);
            ea_buf[xaddr].db = DBCS_NONE;
            ea_buf[cursor_addr].db = DBCS_NONE;
            (void) ctlr_dbcs_postprocess();
      }
#endif /*]*/

      once_cset = -1;
      ctlr_add_gr(cursor_addr, gr);
      ctlr_add_fg(cursor_addr, fg);
      ctlr_add_bg(cursor_addr, bg);
      if (wraparound_mode) {
            /*
             * There is a fascinating behavior of xterm which we will
             * attempt to emulate here.  When a character is printed in the
             * last column, the cursor sticks there, rather than wrapping
             * to the next line.  Another printing character will put the
             * cursor in column 2 of the next line.  One cursor-left
             * sequence won't budge it; two will.  Saving and restoring
             * the cursor won't move the cursor, but will cancel all of
             * the above behaviors...
             *
             * In my opinion, very strange, but among other things, 'vi'
             * depends on it!
             */
            if (!((cursor_addr + 1) % COLS)) {
                  held_wrap = True;
            } else {
                  PWRAP;
            }
      } else {
            if ((cursor_addr % COLS) != (COLS - 1))
                  cursor_move(cursor_addr + 1);
      }
      return DATA;
}

static enum state
ansi_multibyte(int ig1, int ig2)
{
      unsigned long ucs4;
      int consumed;
      enum me_fail fail;
      afn_t fn;

      if (pmi >= MB_MAX - 2) {
            /* String too long. */
            pmi = 0;
            ansi_ch = '?';
            return ansi_printing(ig1, ig2);
      }

      pending_mbs[pmi++] = (char)ansi_ch;
      pending_mbs[pmi] = '\0';
      ucs4 = multibyte_to_unicode(pending_mbs, pmi, &consumed, &fail);
      if (ucs4 != 0) {
            /* Success! */
            ansi_ch = ucs4;
            return ansi_printing(ig1, ig2);
      }
      if (fail == ME_SHORT) {
            /* Go get more. */
            return MBPEND;
      }

      /* Failure. */

      /* Replace the sequence with '?'. */
      ucs4 = ansi_ch; /* save for later */
      pmi = 0;
      ansi_ch = '?';
      (void) ansi_printing(ig1, ig2);

      /*
       * Reprocess whatever we choked on (especially if it's a control
       * character).
       */
      ansi_ch = ucs4;
      state = DATA;
      fn = ansi_fn[st[(int)DATA][ansi_ch]];
      return (*fn)(n[0], n[1]);
}

static enum state
ansi_semicolon(int ig1 _is_unused, int ig2 _is_unused)
{
      if (nx >= NN)
            return DATA;
      nx++;
      return state;
}

static enum state
ansi_digit(int ig1 _is_unused, int ig2 _is_unused)
{
      n[nx] = (n[nx] * 10) + (ansi_ch - '0');
      return state;
}

static enum state
ansi_reverse_index(int ig1 _is_unused, int ig2 _is_unused)
{
      int rr = cursor_addr / COLS;  /* current row */
      int np = (scroll_top - 1) - rr;     /* number of rows in the scrolling
                                 region, above this line */
      int ns;                       /* number of rows to scroll */
      int nn = 1;             /* number of rows to index */

      held_wrap = False;

      /* If the cursor is above the scrolling region, do a simple margined
         cursor up.  */
      if (np < 0) {
            (void) ansi_cursor_up(nn, 0);
            return DATA;
      }

      /* Split the number of lines to scroll into ns */
      if (nn > np) {
            ns = nn - np;
            nn = np;
      } else
            ns = 0;

      /* Move the cursor up without scrolling */
      if (nn)
            (void) ansi_cursor_up(nn, 0);

      /* Insert lines at the top for backward scroll */
      if (ns)
            (void) ansi_insert_lines(ns, 0);

      return DATA;
}

static enum state
ansi_send_attributes(int nn, int ig2 _is_unused)
{
      if (!nn)
            net_sends("\033[?1;2c");
      return DATA;
}

static enum state
dec_return_terminal_id(int ig1 _is_unused, int ig2 _is_unused)
{
      return ansi_send_attributes(0, 0);
}

static enum state
ansi_set_mode(int nn, int ig2 _is_unused)
{
      switch (nn) {
          case 4:
            insert_mode = 1;
            break;
          case 20:
            auto_newline_mode = 1;
            break;
      }
      return DATA;
}

static enum state
ansi_reset_mode(int nn, int ig2 _is_unused)
{
      switch (nn) {
          case 4:
            insert_mode = 0;
            break;
          case 20:
            auto_newline_mode = 0;
            break;
      }
      return DATA;
}

static enum state
ansi_status_report(int nn, int ig2 _is_unused)
{
      static char cpr[11];

      switch (nn) {
          case 5:
            net_sends("\033[0n");
            break;
          case 6:
            (void) sprintf(cpr, "\033[%d;%dR",
                (cursor_addr/COLS) + 1, (cursor_addr%COLS) + 1);
            net_sends(cpr);
            break;
      }
      return DATA;
}

static enum state
ansi_cs_designate(int ig1 _is_unused, int ig2 _is_unused)
{
      cs_to_change = strchr(gnnames, ansi_ch) - gnnames;
      return CSDES;
}

static enum state
ansi_cs_designate2(int ig1 _is_unused, int ig2 _is_unused)
{
      csd[cs_to_change] = strchr(csnames, ansi_ch) - csnames;
      return DATA;
}

static enum state
ansi_select_g0(int ig1 _is_unused, int ig2 _is_unused)
{
      cset = CS_G0;
      return DATA;
}

static enum state
ansi_select_g1(int ig1 _is_unused, int ig2 _is_unused)
{
      cset = CS_G1;
      return DATA;
}

static enum state
ansi_select_g2(int ig1 _is_unused, int ig2 _is_unused)
{
      cset = CS_G2;
      return DATA;
}

static enum state
ansi_select_g3(int ig1 _is_unused, int ig2 _is_unused)
{
      cset = CS_G3;
      return DATA;
}

static enum state
ansi_one_g2(int ig1 _is_unused, int ig2 _is_unused)
{
      once_cset = CS_G2;
      return DATA;
}

static enum state
ansi_one_g3(int ig1 _is_unused, int ig2 _is_unused)
{
      once_cset = CS_G3;
      return DATA;
}

static enum state
ansi_esc3(int ig1 _is_unused, int ig2 _is_unused)
{
      return DECP;
}

static enum state
dec_set(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      for (i = 0; i <= nx && i < NN; i++)
            switch (n[i]) {
                case 1: /* application cursor keys */
                  appl_cursor = 1;
                  break;
                case 2: /* set G0-G3 */
                  csd[0] = csd[1] = csd[2] = csd[3] = CSD_US;
                  break;
                case 3: /* 132-column mode */
                  if (allow_wide_mode) {
                        wide_mode = 1;
                        screen_132();
                  }
                  break;
                case 7: /* wraparound mode */
                  wraparound_mode = 1;
                  break;
                case 40:      /* allow 80/132 switching */
                  allow_wide_mode = 1;
                  break;
                case 45:      /* reverse-wraparound mode */
                  rev_wraparound_mode = 1;
                  break;
                case 47:      /* alt buffer */
                case 1049:
                  ctlr_altbuffer(True);
                  break;
            }
      return DATA;
}

static enum state
dec_reset(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      for (i = 0; i <= nx && i < NN; i++)
            switch (n[i]) {
                case 1: /* normal cursor keys */
                  appl_cursor = 0;
                  break;
                case 3: /* 132-column mode */
                  if (allow_wide_mode) {
                        wide_mode = 0;
                        screen_80();
                  }
                  break;
                case 7: /* no wraparound mode */
                  wraparound_mode = 0;
                  break;
                case 40:      /* allow 80/132 switching */
                  allow_wide_mode = 0;
                  break;
                case 45:      /* no reverse-wraparound mode */
                  rev_wraparound_mode = 0;
                  break;
                case 47:      /* alt buffer */
                case 1049:
                  ctlr_altbuffer(False);
                  break;
            }
      return DATA;
}

static enum state
dec_save(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      for (i = 0; i <= nx && i < NN; i++)
            switch (n[i]) {
                case 1: /* application cursor keys */
                  saved_appl_cursor = appl_cursor;
                  break;
                case 3: /* 132-column mode */
                  saved_wide_mode = wide_mode;
                  break;
                case 7: /* wraparound mode */
                  saved_wraparound_mode = wraparound_mode;
                  break;
                case 40:      /* allow 80/132 switching */
                  saved_allow_wide_mode = allow_wide_mode;
                  break;
                case 45:      /* reverse-wraparound mode */
                  saved_rev_wraparound_mode = rev_wraparound_mode;
                  break;
                case 47:      /* alt buffer */
                case 1049:
                  saved_altbuffer = is_altbuffer;
                  break;
            }
      return DATA;
}

static enum state
dec_restore(int ig1 _is_unused, int ig2 _is_unused)
{
      int i;

      for (i = 0; i <= nx && i < NN; i++)
            switch (n[i]) {
                case 1: /* application cursor keys */
                  appl_cursor = saved_appl_cursor;
                  break;
                case 3: /* 132-column mode */
                  if (allow_wide_mode) {
                        wide_mode = saved_wide_mode;
                        if (wide_mode)
                              screen_132();
                        else
                              screen_80();
                  }
                  break;
                case 7: /* wraparound mode */
                  wraparound_mode = saved_wraparound_mode;
                  break;
                case 40:      /* allow 80/132 switching */
                  allow_wide_mode = saved_allow_wide_mode;
                  break;
                case 45:      /* reverse-wraparound mode */
                  rev_wraparound_mode = saved_rev_wraparound_mode;
                  break;
                case 47:      /* alt buffer */
                case 1049:    /* alt buffer */
                  ctlr_altbuffer(saved_altbuffer);
                  break;
            }
      return DATA;
}

static enum state
dec_scrolling_region(int top, int bottom)
{
      if (top < 1)
            top = 1;
      if (bottom > ROWS)
            bottom = ROWS;
      if (top <= bottom && (top > 1 || bottom < ROWS)) {
            scroll_top = top;
            scroll_bottom = bottom;
            cursor_move(0);
      } else {
            scroll_top = 1;
            scroll_bottom = ROWS;
      }
      return DATA;
}

static enum state
xterm_text_mode(int ig1 _is_unused, int ig2 _is_unused)
{
      nx = 0;
      n[0] = 0;
      return TEXT;
}

static enum state
xterm_text_semicolon(int ig1 _is_unused, int ig2 _is_unused)
{
      tx = 0;
      return TEXT2;
}

static enum state
xterm_text(int ig1 _is_unused, int ig2 _is_unused)
{
      if (tx < NT)
            text[tx++] = ansi_ch;
      return state;
}

static enum state
xterm_text_do(int ig1 _is_unused, int ig2 _is_unused)
{
#if defined(X3270_DISPLAY) || defined(WC3270) /*[*/
      text[tx] = '\0';
#endif /*]*/

#if defined(X3270_DISPLAY) /*[*/
      switch (n[0]) {
          case 0: /* icon name and window title */
            XtVaSetValues(toplevel, XtNiconName, text, NULL);
            XtVaSetValues(toplevel, XtNtitle, text, NULL);
            break;
          case 1: /* icon name */
            XtVaSetValues(toplevel, XtNiconName, text, NULL);
            break;
          case 2: /* window_title */
            XtVaSetValues(toplevel, XtNtitle, text, NULL);
            break;
          case 50:      /* font */
            screen_newfont(text, False, False);
            break;
          default:
            break;
      }
#endif /*]*/

#if defined(WC3270) /*[*/
      switch (n[0]) {
          case 0: /* icon name and window title */
          case 2: /* window_title */
            screen_title(text);
            break;
          default:
            break;
      }
#endif /*]*/

      return DATA;
}

static enum state
ansi_htab_set(int ig1 _is_unused, int ig2 _is_unused)
{
      register int col = cursor_addr % COLS;

      tabs[col/8] |= 1<<(col%8);
      return DATA;
}

static enum state
ansi_htab_clear(int nn, int ig2 _is_unused)
{
      register int col, i;

      switch (nn) {
          case 0:
            col = cursor_addr % COLS;
            tabs[col/8] &= ~(1<<(col%8));
            break;
          case 3:
            for (i = 0; i < (COLS+7)/8; i++)
                  tabs[i] = 0;
            break;
      }
      return DATA;
}

/*
 * Scroll the screen or the scrolling region.
 */
static void
ansi_scroll(void)
{
      held_wrap = False;

      /* Save the top line */
      if (scroll_top == 1 && scroll_bottom == ROWS) {
            if (!is_altbuffer)
                  scroll_save(1, False);
            ctlr_scroll();
            return;
      }

      /* Scroll all but the last line up */
      if (scroll_bottom > scroll_top)
            ctlr_bcopy(scroll_top * COLS,
                (scroll_top - 1) * COLS,
                (scroll_bottom - scroll_top) * COLS,
                1);

      /* Clear the last line */
      ctlr_aclear((scroll_bottom - 1) * COLS, COLS, 1);
}

/* Callback for when we enter ANSI mode. */
static void
ansi_in3270(Boolean in3270)
{
      if (!in3270)
            (void) ansi_reset(0, 0);
}


/*
 * External entry points
 */

void
ansi_init(void)
{
      register_schange(ST_3270_MODE, ansi_in3270);
}

void
ansi_process(unsigned int c)
{
      afn_t fn;

      c &= 0xff;
      ansi_ch = c;

      scroll_to_bottom();

#if defined(X3270_TRACE) /*[*/
      if (toggled(SCREEN_TRACE))
            trace_char((char)c);
#endif /*]*/

      fn = ansi_fn[st[(int)state][c]];
      state = (*fn)(n[0], n[1]);

      /* Saving pending escape data. */
      if (state == DATA)
            pe = 0;
      else if (pe < PE_MAX)
            ped[pe++] = c;
}

void
ansi_send_up(void)
{
      if (appl_cursor)
            net_sends("\033OA");
      else
            net_sends("\033[A");
}

void
ansi_send_down(void)
{
      if (appl_cursor)
            net_sends("\033OB");
      else
            net_sends("\033[B");
}

void
ansi_send_right(void)
{
      if (appl_cursor)
            net_sends("\033OC");
      else
            net_sends("\033[C");
}

void
ansi_send_left(void)
{
      if (appl_cursor)
            net_sends("\033OD");
      else
            net_sends("\033[D");
}

void
ansi_send_home(void)
{
      net_sends("\033[H");
}

void
ansi_send_clear(void)
{
      net_sends("\033[2K");
}

void
ansi_send_pf(int nn)
{
      static char fn_buf[6];
      static int code[] = {
            /*
             * F1 through F12 are VT220 codes. (Note the discontinuity --
             * \E[16~ is missing)
             */
            11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 23, 24,
            /*
             * F13 through F20 are defined for xterm.
             */
            25, 26, 28, 29, 31, 32, 33, 34,
            /*
             * F21 through F24 are x3270 extensions.
             */
            35, 36, 37, 38
      };

      if (nn < 1 || (unsigned)nn > sizeof(code)/sizeof(code[0]))
            return;
      (void) sprintf(fn_buf, "\033[%d~", code[nn-1]);
      net_sends(fn_buf);
}

void
ansi_send_pa(int nn)
{
      static char fn_buf[4];
      static char code[4] = { 'P', 'Q', 'R', 'S' };

      if (nn < 1 || nn > 4)
            return;
      (void) sprintf(fn_buf, "\033O%c", code[nn-1]);
      net_sends(fn_buf);
}

void
toggle_lineWrap(struct toggle *t _is_unused, enum toggle_type type _is_unused)
{
      if (toggled(LINE_WRAP))
            wraparound_mode = 1;
      else
            wraparound_mode = 0;
}

#if defined(X3270_TRACE) /*[*/

/* Emit an SGR command. */
static void
emit_sgr(int mode)
{
      space3270out((mode < 10)? 4: 5);
      *obptr++ = 0x1b;
      *obptr++ = '[';
      if (mode > 9)
            *obptr++ = '0' + (mode / 10);
      *obptr++ = '0' + (mode % 10);
      *obptr++ = 'm';
}

/* Emit a DEC Private Mode command. */
static void
emit_decpriv(int mode, char op)
{
      space3270out((mode < 10)? 5: 6);
      *obptr++ = 0x1b;
      *obptr++ = '[';
      *obptr++ = '?';
      if (mode > 9)
            *obptr++ = '0' + (mode / 10);
      *obptr++ = '0' + (mode % 10);
      *obptr++ = op;
}

/* Emit a CUP (cursor position) command. */
static void
emit_cup(int baddr)
{
      if (baddr) {
            char cup_buf[11];
            int sl;

            sl = sprintf(cup_buf, "\033[%d;%dH",
                  (baddr / COLS) + 1,
                  (baddr % COLS) + 1);
            space3270out(sl);
            strcpy((char *)obptr, cup_buf);
            obptr += sl;
      } else {
            space3270out(3);
            *obptr++ = 0x1b;
            *obptr++ = '[';
            *obptr++ = 'H';
      }
}

/* Emit <n> spaces or a CUP, whichever is shorter. */
static int
ansi_dump_spaces(int spaces, int baddr)
{
      if (spaces) {
            char cup_buf[11];
            int sl;

            /*
             * Move the cursor, if it takes less space than
             * expanding the spaces.
             * It is possible to optimize this further with clever
             * CU[UDFB] sequences, but not (yet) worth the effort.
             */
            sl = sprintf(cup_buf, "\033[%d;%dH",
                  (baddr / COLS) + 1,
                  (baddr % COLS) + 1);
            if (sl < spaces) {
                  space3270out(sl);
                  strcpy((char *)obptr, cup_buf);
                  obptr += sl;
            } else {
                  space3270out(spaces);
                  while (spaces--)
                        *obptr++ = ' ';
            }
      }
      return 0;
}

/*
 * Snap the provided screen buffer (primary or alternate).
 * This is (mostly) optimized to draw the minimum necessary, assuming a
 * blank screen.
 */
static void
ansi_snap_one(struct ea *buf)
{
      int baddr;
      int cur_gr = 0;
      int cur_fg = 0;
      int cur_bg = 0;
      int spaces = 0;
      static int uncolor_table[16] = {
          /* 0xf0 */ 0,
          /* 0xf1 */ 4,
          /* 0xf2 */ 1,
          /* 0xf3 */ 5,
          /* 0xf4 */ 2,
          /* 0xf5 */ 0, /* can't happen */
#if defined(WC3270) /*[*/
          /* 0xf6 */ 6,
#else /*][*/
          /* 0xf6 */ 3,
#endif /*]*/
#if defined(WC3270) /*[*/
          /* 0xf7 */ 7,
#else /*][*/
          /* 0xf7 */ 0, /* can't happen */
#endif /*]*/
          /* 0xf8 */ 0, /* can't happen */
          /* 0xf9 */ 0, /* can't happen */
          /* 0xfa */ 0, /* can't happen */
          /* 0xfb */ 0, /* can't happen */
          /* 0xfc */ 0, /* can't happen */
#if defined(WC3270) /*[*/
          /* 0xfd */ 0, /* can't happen */
#else /*][*/
          /* 0xfd */ 6, /* can't happen */
#endif /*]*/
          /* 0xfe */ 0, /* can't happen */
#if defined(WC3270) /*[*/
          /* 0xff */ 0, /* can't happen */
#else /*][*/
          /* 0xff */ 7, /* can't happen */
#endif /*]*/
      };
      char mb[16];
      int len;
      int xlen;
      int i;
      enum dbcs_state d;
      int c;
      int last_sgr = 0;
#define     EMIT_SGR(n) { emit_sgr(n); last_sgr = (n); }

      /* Draw what's on the screen. */
      baddr = 0;
      do {
            int xgr = buf[baddr].gr;

            /* Set the attributes. */
            if (xgr != cur_gr) {
                  spaces = ansi_dump_spaces(spaces, baddr);
                  if ((xgr ^ cur_gr) & cur_gr) {
                        /*
                         * Something turned off. Turn everything off,
                         * then turn the remaining modes on below.
                         */
                        EMIT_SGR(0);
                        xgr = 0;
                  } else {
                        /*
                         * Clear the bits in xgr that are already set
                         * in cur_gr.  Turn on the new modes.
                         */
                        xgr &= ~cur_gr;
                  }
                  /* Turn on the attributes remaining in xgr. */
                  if (xgr & GR_INTENSIFY)
                        EMIT_SGR(1);
                  if (xgr & GR_UNDERLINE)
                        EMIT_SGR(4);
                  if (xgr & GR_BLINK)
                        EMIT_SGR(5);
                  if (xgr & GR_REVERSE)
                        EMIT_SGR(7);
                  cur_gr = buf[baddr].gr;
            }

            /* Set the colors. */
            if (buf[baddr].fg != cur_fg) {
                  spaces = ansi_dump_spaces(spaces, baddr);
                  if (buf[baddr].fg)
                        c = uncolor_table[buf[baddr].fg & 0x0f];
                  else
                        c = 9;
                  EMIT_SGR(30 + c);
                  cur_fg = buf[baddr].fg;
            }
            if (buf[baddr].bg != cur_bg) {
                  spaces = ansi_dump_spaces(spaces, baddr);
                  if (buf[baddr].bg)
                        c = uncolor_table[buf[baddr].bg & 0x0f];
                  else
                        c = 9;
                  EMIT_SGR(40 + c);
                  cur_bg = buf[baddr].bg;
            }

            /* Expand the current character to multibyte. */
            d = ctlr_dbcs_state(baddr);
            if (IS_LEFT(d)) {
                  int xaddr = baddr;
                  INC_BA(xaddr);
                  len = ebcdic_to_multibyte(buf[baddr].cc << 8 |
                                  buf[xaddr].cc,
                                  mb, sizeof(mb));
            } else if (IS_RIGHT(d)) {
                  len = 0;
            } else {
                  len = ebcdic_to_multibyte(buf[baddr].cc,
                                  mb, sizeof(mb));
            }
            if (len > 0)
                  len--; /* terminating NUL */
            xlen = 0;
            for (i = 0; i < len; i++) {
                  if ((mb[i] & 0xff) == 0xff)
                        xlen++;
            }

            /* Optimize for white space. */
            if (!cur_fg &&
                !cur_bg &&
                !cur_gr &&
                ((len + xlen) == 1) &&
                (mb[0] == ' ')) {
                  spaces++;
            } else {
                  if (spaces)
                        spaces = ansi_dump_spaces(spaces, baddr);

                  /* Emit the current character. */
                  space3270out(len + xlen);
                  for (i = 0; i < len; i++) {
                        if ((mb[i] & 0xff) == 0xff)
                              *obptr++ = 0xff;
                        *obptr++ = mb[i];
                  }
            }

            INC_BA(baddr);
      } while (baddr != 0);

      /* Remove any attributes we set above. */
      if (last_sgr != 0)
            emit_sgr(0);
}

/* Snap the contents of the screen buffers in NVT mode. */
void
ansi_snap(void)
{
      /*
       * Note that ea_buf is the live buffer, and aea_buf is the other
       * buffer.  So the task here is to draw the other buffer first,
       * then switch modes and draw the live one.
       */
      if (is_altbuffer) {
            /* Draw the primary screen first. */
            ansi_snap_one(aea_buf);
            emit_cup(0);

            /* Switch to the alternate. */
            emit_decpriv(47, 'h');

            /* Draw the secondary, and stay in alternate mode. */
            ansi_snap_one(ea_buf);
      } else {
            int i;
            int any = 0;
            static struct ea zea = { 0, 0, 0, 0, 0, 0, 0, 0 };

            /* See if aea_buf has anything in it. */
            for (i = 0; i < ROWS * COLS; i++) {
                  if (memcmp(&aea_buf[i], &zea, sizeof(struct ea))) {
                        any = 1;
                        break;
                  }
            }

            if (any) {
                  /* Switch to the alternate. */
                  emit_decpriv(47, 'h');

                  /* Draw the alternate screen. */
                  ansi_snap_one(aea_buf);
                  emit_cup(0);

                  /* Switch to the primary. */
                  emit_decpriv(47, 'l');
            }

            /* Draw the primary, and stay in primary mode. */
            ansi_snap_one(ea_buf);
      }
}

/*
 * Snap the non-default terminal modes.
 * This is a subtle piece of logic, and may harbor a few bugs yet.
 */
void
ansi_snap_modes(void)
{
      int i;
      static char csdsel[4] = "()*+";

      /* Set up the saved cursor (cursor, fg, bg, gr, cset, csd). */
      if (saved_cursor != 0 ||
          saved_fg != 0 ||
          saved_bg != 0 ||
          saved_gr != 0 ||
          saved_cset != CS_G0 ||
          saved_csd[0] != CSD_US ||
          saved_csd[1] != CSD_US ||
          saved_csd[2] != CSD_US ||
          saved_csd[3] != CSD_US) {

            if (saved_cursor != 0)
                  emit_cup(saved_cursor);
            if (saved_fg != 0)
                  emit_sgr(30 + saved_fg);
            if (saved_bg != 0)
                  emit_sgr(40 + saved_bg);
            if (saved_gr != 0) {
                  if (saved_gr & GR_INTENSIFY)
                        emit_sgr(1);
                  if (saved_gr & GR_UNDERLINE)
                        emit_sgr(4);
                  if (saved_gr & GR_BLINK)
                        emit_sgr(5);
                  if (saved_gr & GR_REVERSE)
                        emit_sgr(7);
            }
            if (saved_cset != CS_G0) {
                  switch (saved_cset) {
                  case CS_G1:
                        space3270out(1);
                        *obptr++ = 0x0e;
                        break;
                  case CS_G2:
                        space3270out(2);
                        *obptr++ = 0x1b;
                        *obptr++ = 'N';
                        break;
                  case CS_G3:
                        space3270out(2);
                        *obptr++ = 0x1b;
                        *obptr++ = 'O';
                        break;
                  default:
                        break;
                  }
            }
            for (i = 0; i < 4; i++) {
                  if (saved_csd[i] != CSD_US) {
                        space3270out(3);
                        *obptr++ = 0x1b;
                        *obptr++ = csdsel[i];
                        *obptr++ = gnnames[saved_csd[i]];
                  }
            }

            /* Emit a SAVE CURSOR to stash these away. */
            space3270out(2);
            *obptr++ = 0x1b;
            *obptr++ = '7';
      }

      /* Now set the above to their current values, except for the cursor. */
      if (fg != saved_fg)
            emit_sgr(30 + fg);
      if (bg != saved_bg)
            emit_sgr(40 + bg);
      if (gr != saved_gr) {
            emit_sgr(0);
            if (gr & GR_INTENSIFY)
                  emit_sgr(1);
            if (gr & GR_UNDERLINE)
                  emit_sgr(4);
            if (gr & GR_BLINK)
                  emit_sgr(5);
            if (gr & GR_REVERSE)
                  emit_sgr(7);
      }
      if (cset != saved_cset) {
            switch (cset) {
            case CS_G0:
                  space3270out(1);
                  *obptr++ = 0x0f;
                  break;
            case CS_G1:
                  space3270out(1);
                  *obptr++ = 0x0e;
                  break;
            case CS_G2:
                  space3270out(2);
                  *obptr++ = 0x1b;
                  *obptr++ = 'n';
                  break;
            case CS_G3:
                  space3270out(2);
                  *obptr++ = 0x1b;
                  *obptr++ = 'o';
                  break;
            default:
                  break;
            }
      }
      for (i = 0; i < 4; i++) {
            if (csd[i] != saved_csd[i]) {
                  space3270out(3);
                  *obptr++ = 0x1b;
                  *obptr++ = csdsel[i];
                  *obptr++ = gnnames[csd[i]];
            }
      }

      /*
       * Handle appl_cursor, wrapaparound_mode, rev_wraparound_mode,
       * allow_wide_mode, wide_mode and altbuffer, both the saved values and
       * the current ones.
       */
      if (saved_appl_cursor) {
            emit_decpriv(1, 'h');         /* set */
            emit_decpriv(1, 's');         /* save */
            if (!appl_cursor)
                  emit_decpriv(1, 'l');   /* reset */
      } else if (appl_cursor) {
            emit_decpriv(1, 'h');         /* set */
      }
      if (saved_wide_mode) {
            emit_decpriv(3, 'h');         /* set */
            emit_decpriv(3, 's');         /* save */
            if (!wide_mode)
                  emit_decpriv(3, 'l');   /* reset */
      } else if (wide_mode) {
            emit_decpriv(3, 'h');         /* set */
      }
      if (saved_wraparound_mode == 0) {
            emit_decpriv(7, 'h');         /* set (no-wraparound mode) */
            emit_decpriv(7, 's');         /* save */
            if (wraparound_mode)
                  emit_decpriv(7, 'l');   /* reset */
      } else if (!wraparound_mode) {
            emit_decpriv(7, 'h');         /* set (no-wraparound mode) */
      }
      if (saved_allow_wide_mode) {
            emit_decpriv(40, 'h');        /* set */
            emit_decpriv(40, 's');        /* save */
            if (!allow_wide_mode)
                  emit_decpriv(40, 'l');  /* reset */
      } else if (allow_wide_mode) {
            emit_decpriv(40, 'h');        /* set */
      }
      if (saved_rev_wraparound_mode) {
            emit_decpriv(45, 'h');        /* set (rev--wraparound mode) */
            emit_decpriv(45, 's');        /* save */
            if (!rev_wraparound_mode)
                  emit_decpriv(45, 'l');  /* reset */
      } else if (rev_wraparound_mode) {
            emit_decpriv(45, 'h');        /* set (rev-wraparound mode) */
      }
      if (saved_altbuffer) {
            emit_decpriv(47, 'h');        /* set */
            emit_decpriv(47, 's');        /* save */
            if (!is_altbuffer)
                  emit_decpriv(47, 'l');  /* reset */
      } /* else not necessary to set it now -- it was already set when the
           screen was drawn */

      /*
       * Now take care of auto_newline, insert mode, the scroll region
       * and tabs.
       */
      if (auto_newline_mode) {
            space3270out(4);
            *obptr++ = 0x1b;
            *obptr++ = '[';
            *obptr++ = '4';
            *obptr++ = 'h';
      }
      if (insert_mode) {
            space3270out(5);
            *obptr++ = 0x1b;
            *obptr++ = '[';
            *obptr++ = '2';
            *obptr++ = '0';
            *obptr++ = 'h';
      }
      if (scroll_top != 1 || scroll_bottom != ROWS) {
            space3270out(10);
            obptr += sprintf((char *)obptr, "\033[%d;%dr",
                  scroll_top, scroll_bottom);
      }
      if (tabs) {
            unsigned char *deftabs;

            deftabs = (unsigned char *)Malloc((COLS+7)/8);
            for (i = 0; i < (COLS+7)/8; i++)
                  deftabs[i] = 0x01;
            for (i = 0; i < COLS; i++) {
                  if (tabs[i/8] & 1<<(i%8)) {
                        if (!(deftabs[i/8] & 1<<(i%8))) {
                              /* Tab was cleared. */
                              space3270out(15);
                              obptr += sprintf((char *)obptr,
                                    "\033[%d;%dH",
                                    (cursor_addr / COLS) + 1,
                                    ((cursor_addr + i) % COLS) + 1);
                              *obptr++ = 0x1b;
                              *obptr++ = '[';
                              *obptr++ = '0';
                              *obptr++ = 'g';
                        }
                  } else {
                        if (deftabs[i/8] & 1<<(i%8)) {
                              /* Tab was set. */
                              space3270out(13);
                              obptr += sprintf((char *)obptr,
                                    "\033[%d;%dH",
                                    (cursor_addr / COLS) + 1,
                                    ((cursor_addr + i) % COLS) + 1);
                              *obptr++ = 0x1b;
                              *obptr++ = 'H';
                        }
                  }
            }
      }

      /*
       * We're done moving the cursor for other purposes (saving it,
       * messing with tabs).  Put it where it should be now.
       */
      emit_cup(cursor_addr);

      /* Now add any pending single-character CS change. */
      switch (once_cset) {
      case CS_G2:
            space3270out(2);
            *obptr++ = 0x1b;
            *obptr++ = 'N';
            break;
      case CS_G3:
            space3270out(2);
            *obptr++ = 0x1b;
            *obptr++ = 'O';
            break;
      default:
            break;
      }

      /* Now add any incomplete escape sequence. */
      if (pe) {
            int xlen = 0;

            for (i = 0; i < pe; i++)
                  if (ped[i] == 0xff)
                        xlen++;
            space3270out(pe + xlen);
            for (i = 0; i < pe; i++) {
                  if (ped[i] == 0xff)
                        *obptr++ = 0xff;
                  *obptr++ = ped[i];
            }
      }

      /* Last, emit any incomplete multi-byte data. */
      if (pmi) {
            space3270out(pmi);
            for (i = 0; i < pmi; i++) {
                  *obptr++ = pending_mbs[i];
            }
      }
}

#endif /*]*/

#endif /*]*/

Generated by  Doxygen 1.6.0   Back to index