/*
* Modifications Copyright 1993, 1994, 1995, 1996, 1999,
* 2000, 2001, 2002, 2004 by Paul Mattes.
* Original X11 Port Copyright 1990 by Jeff Sparkes.
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation.
*
* Copyright 1989 by Georgia Tech Research Corporation, Atlanta, GA 30332.
* All Rights Reserved. GTRC hereby grants public use of this software.
* Derivative works based on this software must incorporate this copyright
* notice.
*
* x3270, c3270, s3270 and tcl3270 are distributed in the hope that they will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file LICENSE
* for more details.
*/
/*
* ctlr.c
* This module handles interpretation of the 3270 data stream and
* maintenance of the 3270 device state. It was split out from
* screen.c, which handles X operations.
*
*/
#include "globals.h"
#include <errno.h>
#include "3270ds.h"
#include "appres.h"
#include "ctlr.h"
#include "screen.h"
#include "resources.h"
#include "ctlrc.h"
#include "ftc.h"
#include "ft_cutc.h"
#include "ft_dftc.h"
#include "hostc.h"
#include "kybdc.h"
#include "macrosc.h"
#include "popupsc.h"
#include "screenc.h"
#include "scrollc.h"
#include "seec.h"
#include "selectc.h"
#include "sfc.h"
#include "statusc.h"
#include "tablesc.h"
#include "telnetc.h"
#include "trace_dsc.h"
#include "utilc.h"
#include "widec.h"
/* Externals: kybd.c */
extern unsigned char aid;
/* Globals */
int ROWS, COLS;
int maxROWS, maxCOLS;
int ov_rows, ov_cols;
int model_num;
int cursor_addr, buffer_addr;
Boolean screen_alt = False; /* alternate screen? */
Boolean is_altbuffer = False;
struct ea *ea_buf; /* 3270 device buffer */
/* ea_buf[-1] is the dummy default field
attribute */
Boolean formatted = False; /* set in screen_disp */
Boolean screen_changed = False;
int first_changed = -1;
int last_changed = -1;
unsigned char reply_mode = SF_SRM_FIELD;
int crm_nattr = 0;
unsigned char crm_attr[16];
Boolean dbcs = False;
/* Statics */
static struct ea *aea_buf; /* alternate 3270 extended attribute buffer */
static unsigned char *zero_buf; /* empty buffer, for area clears */
static void set_formatted(void);
static void ctlr_blanks(void);
static Boolean trace_primed = False;
static unsigned char default_fg;
static unsigned char default_gr;
static unsigned char default_cs;
static unsigned char default_ic;
static void ctlr_half_connect(Boolean ignored);
static void ctlr_connect(Boolean ignored);
static int sscp_start;
static void ticking_stop(void);
static void ctlr_add_ic(int baddr, unsigned char ic);
/*
* code_table is used to translate buffer addresses and attributes to the 3270
* datastream representation
*/
static unsigned char code_table[64] = {
0x40, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
0xD8, 0xD9, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
};
#define IsBlank(c) ((c == EBC_null) || (c == EBC_space))
#define ALL_CHANGED { \
screen_changed = True; \
if (IN_ANSI) { first_changed = 0; last_changed = ROWS*COLS; } }
#define REGION_CHANGED(f, l) { \
screen_changed = True; \
if (IN_ANSI) { \
if (first_changed == -1 || f < first_changed) first_changed = f; \
if (last_changed == -1 || l > last_changed) last_changed = l; } }
#define ONE_CHANGED(n) REGION_CHANGED(n, n+1)
#define DECODE_BADDR(c1, c2) \
((((c1) & 0xC0) == 0x00) ? \
(((c1) & 0x3F) << 8) | (c2) : \
(((c1) & 0x3F) << 6) | ((c2) & 0x3F))
#define ENCODE_BADDR(ptr, addr) { \
if ((addr) > 0xfff) { \
*(ptr)++ = ((addr) >> 8) & 0x3F; \
*(ptr)++ = (addr) & 0xFF; \
} else { \
*(ptr)++ = code_table[((addr) >> 6) & 0x3F]; \
*(ptr)++ = code_table[(addr) & 0x3F]; \
} \
}
/*
* Initialize the emulated 3270 hardware.
*/
void
ctlr_init(unsigned cmask unused)
{
/* Register callback routines. */
register_schange(ST_HALF_CONNECT, ctlr_half_connect);
register_schange(ST_CONNECT, ctlr_connect);
register_schange(ST_3270_MODE, ctlr_connect);
}
/*
* Reinitialize the emulated 3270 hardware.
*/
void
ctlr_reinit(unsigned cmask)
{
if (cmask & MODEL_CHANGE) {
/* Allocate buffers */
if (ea_buf)
Free((char *)(ea_buf - 1));
ea_buf = (struct ea *)Calloc(sizeof(struct ea),
(maxROWS * maxCOLS) + 1);
ea_buf++;
if (aea_buf)
Free((char *)(aea_buf - 1));
aea_buf = (struct ea *)Calloc(sizeof(struct ea),
(maxROWS * maxCOLS) + 1);
aea_buf++;
Replace(zero_buf, (unsigned char *)Calloc(sizeof(struct ea),
maxROWS * maxCOLS));
cursor_addr = 0;
buffer_addr = 0;
}
}
/*
* Deal with the relationships between model numbers and rows/cols.
*/
void
set_rows_cols(int mn, int ovc, int ovr)
{
int defmod;
switch (mn) {
case 2:
maxCOLS = 80;
maxROWS = 24;
model_num = 2;
break;
case 3:
maxCOLS = 80;
maxROWS = 32;
model_num = 3;
break;
case 4:
#if defined(RESTRICT_3279) /*[*/
if (appres.m3279) {
popup_an_error("No 3279 Model 4\nDefaulting to model 3");
set_rows_cols("3", ovc, ovr);
return;
}
#endif /*]*/
maxCOLS = 80;
maxROWS = 43;
model_num = 4;
break;
case 5:
#if defined(RESTRICT_3279) /*[*/
if (appres.m3279) {
popup_an_error("No 3279 Model 5\nDefaulting to model 3");
set_rows_cols(3, ovc, ovr);
return;
}
#endif /*]*/
maxCOLS = 132;
maxROWS = 27;
model_num = 5;
break;
default:
#if defined(RESTRICT_3279) /*[*/
defmod = appres.m3279 ? 3 : 4;
#else /*][*/
defmod = 4;
#endif
popup_an_error("Unknown model: %d\nDefaulting to %d", mn,
defmod);
set_rows_cols(defmod, ovc, ovr);
return;
}
/* Apply oversize. */
ov_cols = 0;
ov_rows = 0;
if (ovc != 0 || ovr != 0) {
if (ovc <= 0 || ovr <= 0)
popup_an_error("Invalid %s %dx%d:\nNegative or zero",
ResOversize, ovc, ovr);
else if (ovc * ovr >= 0x4000)
popup_an_error("Invalid %s %dx%d:\nToo big",
ResOversize, ovc, ovr);
else if (ovc > 0 && ovc < maxCOLS)
popup_an_error("Invalid %s cols (%d):\nLess than model %d cols (%d)",
ResOversize, ovc, model_num, maxCOLS);
else if (ovr > 0 && ovr < maxROWS)
popup_an_error("Invalid %s rows (%d):\nLess than model %d rows (%d)",
ResOversize, ovr, model_num, maxROWS);
else {
ov_cols = maxCOLS = ovc;
ov_rows = maxROWS = ovr;
}
}
/* Update the model name. */
(void) sprintf(model_name, "327%c-%d%s",
appres.m3279 ? '9' : '8',
model_num,
appres.extended ? "-E" : "");
/* Make sure that the current rows/cols are still 24x80. */
COLS = 80;
ROWS = 24;
screen_alt = False;
}
/*
* Set the formatted screen flag. A formatted screen is a screen that
* has at least one field somewhere on it.
*/
static void
set_formatted(void)
{
register int baddr;
formatted = False;
baddr = 0;
do {
if (ea_buf[baddr].fa) {
formatted = True;
break;
}
INC_BA(baddr);
} while (baddr != 0);
}
/*
* Called when a host is half connected.
*/
static void
ctlr_half_connect(Boolean ignored unused)
{
ticking_start(True);
}
/*
* Called when a host connects, disconnects, or changes ANSI/3270 modes.
*/
static void
ctlr_connect(Boolean ignored unused)
{
ticking_stop();
status_untiming();
if (ever_3270)
ea_buf[-1].fa = FA_PRINTABLE | FA_MODIFY;
else
ea_buf[-1].fa = FA_PRINTABLE | FA_PROTECT;
if (!IN_3270 || (IN_SSCP && (kybdlock & KL_OIA_TWAIT))) {
kybdlock_clr(KL_OIA_TWAIT, "ctlr_connect");
status_reset();
}
default_fg = 0x00;
default_gr = 0x00;
default_cs = 0x00;
default_ic = 0x00;
reply_mode = SF_SRM_FIELD;
crm_nattr = 0;
}
/*
* Find the buffer address of the field attribute for a given buffer address.
* Returns -1 if the screen isn't formatted.
*/
int
find_field_attribute(int baddr)
{
int sbaddr;
if (!formatted)
return -1;
sbaddr = baddr;
do {
if (ea_buf[baddr].fa)
return baddr;
DEC_BA(baddr);
} while (baddr != sbaddr);
return -1;
}
/*
* Find the field attribute for the given buffer address. Return its address
* rather than its value.
*/
unsigned char
get_field_attribute(register int baddr)
{
return ea_buf[find_field_attribute(baddr)].fa;
}
/*
* Find the field attribute for the given buffer address, bounded by another
* buffer address. Return the attribute in a parameter.
*
* Returns True if an attribute is found, False if boundary hit.
*/
Boolean
get_bounded_field_attribute(register int baddr, register int bound,
unsigned char *fa_out)
{
int sbaddr;
if (!formatted) {
*fa_out = ea_buf[-1].fa;
return True;
}
sbaddr = baddr;
do {
if (ea_buf[baddr].fa) {
*fa_out = ea_buf[baddr].fa;
return True;
}
DEC_BA(baddr);
} while (baddr != sbaddr && baddr != bound);
/* Screen is unformatted (and 'formatted' is inaccurate). */
if (baddr == sbaddr) {
*fa_out = ea_buf[-1].fa;
return True;
}
/* Wrapped to boundary. */
return False;
}
/*
* Given the address of a field attribute, return the address of the
* extended attribute structure.
*/
struct ea *
fa2ea(int baddr)
{
return &ea_buf[baddr];
}
/*
* Find the next unprotected field. Returns the address following the
* unprotected attribute byte, or 0 if no nonzero-width unprotected field
* can be found.
*/
int
next_unprotected(int baddr0)
{
register int baddr, nbaddr;
nbaddr = baddr0;
do {
baddr = nbaddr;
INC_BA(nbaddr);
if (ea_buf[baddr].fa &&
!FA_IS_PROTECTED(ea_buf[baddr].fa) &&
!ea_buf[nbaddr].fa)
return nbaddr;
} while (nbaddr != baddr0);
return 0;
}
/*
* Perform an erase command, which may include changing the (virtual) screen
* size.
*/
void
ctlr_erase(Boolean alt)
{
kybd_inhibit(False);
ctlr_clear(True);
/* Let a script go. */
sms_host_output();
if (alt == screen_alt)
return;
screen_disp(True);
if (alt) {
/* Going from 24x80 to maximum. */
screen_disp(False);
ROWS = maxROWS;
COLS = maxCOLS;
} else {
/* Going from maximum to 24x80. */
if (maxROWS > 24 || maxCOLS > 80) {
if (visible_control) {
ctlr_blanks();
screen_disp(False);
}
ROWS = 24;
COLS = 80;
}
}
screen_alt = alt;
}
/*
* Interpret an incoming 3270 command.
*/
enum pds
process_ds(unsigned char *buf, int buflen)
{
enum pds rv;
if (!buflen)
return PDS_OKAY_NO_OUTPUT;
scroll_to_bottom();
trace_ds("< ");
switch (buf[0]) { /* 3270 command */
case CMD_EAU: /* erase all unprotected */
case SNA_CMD_EAU:
trace_ds("EraseAllUnprotected\n");
ctlr_erase_all_unprotected();
return PDS_OKAY_NO_OUTPUT;
break;
case CMD_EWA: /* erase/write alternate */
case SNA_CMD_EWA:
trace_ds("EraseWriteAlternate");
ctlr_erase(True);
if ((rv = ctlr_write(buf, buflen, True)) < 0)
return rv;
return PDS_OKAY_NO_OUTPUT;
break;
case CMD_EW: /* erase/write */
case SNA_CMD_EW:
trace_ds("EraseWrite");
ctlr_erase(False);
if ((rv = ctlr_write(buf, buflen, True)) < 0)
return rv;
return PDS_OKAY_NO_OUTPUT;
break;
case CMD_W: /* write */
case SNA_CMD_W:
trace_ds("Write");
if ((rv = ctlr_write(buf, buflen, False)) < 0)
return rv;
return PDS_OKAY_NO_OUTPUT;
break;
case CMD_RB: /* read buffer */
case SNA_CMD_RB:
trace_ds("ReadBuffer\n");
ctlr_read_buffer(aid);
return PDS_OKAY_OUTPUT;
break;
case CMD_RM: /* read modifed */
case SNA_CMD_RM:
trace_ds("ReadModified\n");
ctlr_read_modified(aid, False);
return PDS_OKAY_OUTPUT;
break;
case CMD_RMA: /* read modifed all */
case SNA_CMD_RMA:
trace_ds("ReadModifiedAll\n");
ctlr_read_modified(aid, True);
return PDS_OKAY_OUTPUT;
break;
case CMD_WSF: /* write structured field */
case SNA_CMD_WSF:
trace_ds("WriteStructuredField");
return write_structured_field(buf, buflen);
break;
case CMD_NOP: /* no-op */
trace_ds("NoOp\n");
return PDS_OKAY_NO_OUTPUT;
break;
default:
/* unknown 3270 command */
popup_an_error("Unknown 3270 Data Stream command: 0x%X\n",
buf[0]);
return PDS_BAD_CMD;
}
}
/*
* Functions to insert SA attributes into the inbound data stream.
*/
static void
insert_sa1(unsigned char attr, unsigned char value, unsigned char *currentp, Boolean *anyp)
{
if (value == *currentp)
return;
*currentp = value;
space3270out(3);
*obptr++ = ORDER_SA;
*obptr++ = attr;
*obptr++ = value;
if (*anyp)
trace_ds("'");
trace_ds(" SetAttribute(%s)", see_efa(attr, value));
*anyp = False;
}
/*
* Translate an internal character set number to a 3270DS characte set number.
*/
static unsigned char
host_cs(unsigned char cs)
{
switch (cs & CS_MASK) {
case CS_APL:
case CS_LINEDRAW:
return 0xf0 | (cs & CS_MASK);
case CS_DBCS:
return 0xf8;
default:
return 0;
}
}
static void
insert_sa(int baddr, unsigned char *current_fgp, unsigned char *current_grp, unsigned char *current_csp, Boolean *anyp)
{
if (reply_mode != SF_SRM_CHAR)
return;
if (memchr((char *)crm_attr, XA_FOREGROUND, crm_nattr))
insert_sa1(XA_FOREGROUND, ea_buf[baddr].fg, current_fgp, anyp);
if (memchr((char *)crm_attr, XA_HIGHLIGHTING, crm_nattr)) {
unsigned char gr;
gr = ea_buf[baddr].gr;
if (gr)
gr |= 0xf0;
insert_sa1(XA_HIGHLIGHTING, gr, current_grp, anyp);
}
if (memchr((char *)crm_attr, XA_CHARSET, crm_nattr)) {
insert_sa1(XA_CHARSET, host_cs(ea_buf[baddr].cs), current_csp,
anyp);
}
}
/*
* Process a 3270 Read-Modified command and transmit the data back to the
* host.
*/
void
ctlr_read_modified(unsigned char aid_byte, Boolean all)
{
register int baddr, sbaddr;
Boolean send_data = True;
Boolean short_read = False;
unsigned char current_fg = 0x00;
unsigned char current_gr = 0x00;
unsigned char current_cs = 0x00;
if (IN_SSCP && aid_byte != AID_ENTER)
return;
#if defined(X3270_FT) /*[*/
if (aid_byte == AID_SF) {
dft_read_modified();
return;
}
#endif /*]*/
trace_ds("> ");
obptr = obuf;
switch (aid_byte) {
case AID_SYSREQ: /* test request */
space3270out(4);
*obptr++ = 0x01; /* soh */
*obptr++ = 0x5b; /* % */
*obptr++ = 0x61; /* / */
*obptr++ = 0x02; /* stx */
trace_ds("SYSREQ");
break;
case AID_PA1: /* short-read AIDs */
case AID_PA2:
case AID_PA3:
case AID_CLEAR:
if (!all)
short_read = True;
/* fall through... */
case AID_SELECT: /* No data on READ MODIFIED */
if (!all)
send_data = False;
/* fall through... */
default: /* ordinary AID */
if (!IN_SSCP) {
space3270out(3);
*obptr++ = aid_byte;
trace_ds(see_aid(aid_byte));
if (short_read)
goto rm_done;
ENCODE_BADDR(obptr, cursor_addr);
trace_ds(rcba(cursor_addr));
} else {
space3270out(1); /* just in case */
}
break;
}
baddr = 0;
if (formatted) {
/* find first field attribute */
do {
if (ea_buf[baddr].fa)
break;
INC_BA(baddr);
} while (baddr != 0);
sbaddr = baddr;
do {
if (FA_IS_MODIFIED(ea_buf[baddr].fa)) {
Boolean any = False;
INC_BA(baddr);
space3270out(3);
*obptr++ = ORDER_SBA;
ENCODE_BADDR(obptr, baddr);
trace_ds(" SetBufferAddress%s", rcba(baddr));
while (!ea_buf[baddr].fa) {
if (send_data &&
ea_buf[baddr].cc) {
insert_sa(baddr,
¤t_fg,
¤t_gr,
¤t_cs,
&any);
if (ea_buf[baddr].cs & CS_GE) {
space3270out(1);
*obptr++ = ORDER_GE;
if (any)
trace_ds("'");
trace_ds(" GraphicEscape");
any = False;
}
space3270out(1);
*obptr++ = ea_buf[baddr].cc;
if (!any)
trace_ds(" '");
trace_ds("%s",
see_ebc(ea_buf[baddr].cc));
any = True;
}
INC_BA(baddr);
}
if (any)
trace_ds("'");
}
else { /* not modified - skip */
do {
INC_BA(baddr);
} while (!ea_buf[baddr].fa);
}
} while (baddr != sbaddr);
} else {
Boolean any = False;
int nbytes = 0;
/*
* If we're in SSCP-LU mode, the starting point is where the
* host left the cursor.
*/
if (IN_SSCP)
baddr = sscp_start;
do {
if (ea_buf[baddr].cc) {
insert_sa(baddr,
¤t_fg,
¤t_gr,
¤t_cs,
&any);
if (ea_buf[baddr].cs & CS_GE) {
space3270out(1);
*obptr++ = ORDER_GE;
if (any)
trace_ds("' ");
trace_ds(" GraphicEscape ");
any = False;
}
space3270out(1);
*obptr++ = ea_buf[baddr].cc;
if (!any)
trace_ds("'");
trace_ds(see_ebc(ea_buf[baddr].cc));
any = True;
nbytes++;
}
INC_BA(baddr);
/*
* If we're in SSCP-LU mode, end the return value at
* 255 bytes, or where the screen wraps.
*/
if (IN_SSCP && (nbytes >= 255 || !baddr))
break;
} while (baddr != 0);
if (any)
trace_ds("'");
}
rm_done:
trace_ds("\n");
net_output();
}
/*
* Process a 3270 Read-Buffer command and transmit the data back to the
* host.
*/
void
ctlr_read_buffer(unsigned char aid_byte)
{
register int baddr;
unsigned char fa;
Boolean any = False;
int attr_count = 0;
unsigned char current_fg = 0x00;
unsigned char current_gr = 0x00;
unsigned char current_cs = 0x00;
#if defined(X3270_FT) /*[*/
if (aid_byte == AID_SF) {
dft_read_modified();
return;
}
#endif /*]*/
trace_ds("> ");
obptr = obuf;
space3270out(3);
*obptr++ = aid_byte;
ENCODE_BADDR(obptr, cursor_addr);
trace_ds("%s%s", see_aid(aid_byte), rcba(cursor_addr));
baddr = 0;
do {
if (ea_buf[baddr].fa) {
if (reply_mode == SF_SRM_FIELD) {
space3270out(2);
*obptr++ = ORDER_SF;
} else {
space3270out(4);
*obptr++ = ORDER_SFE;
attr_count = obptr - obuf;
*obptr++ = 1; /* for now */
*obptr++ = XA_3270;
}
fa = ea_buf[baddr].fa & ~FA_PRINTABLE;
*obptr++ = code_table[fa];
if (any)
trace_ds("'");
trace_ds(" StartField%s%s%s",
(reply_mode == SF_SRM_FIELD) ? "" : "Extended",
rcba(baddr), see_attr(fa));
if (reply_mode != SF_SRM_FIELD) {
if (ea_buf[baddr].fg) {
space3270out(2);
*obptr++ = XA_FOREGROUND;
*obptr++ = ea_buf[baddr].fg;
trace_ds("%s", see_efa(XA_FOREGROUND,
ea_buf[baddr].fg));
(*(obuf + attr_count))++;
}
if (ea_buf[baddr].gr) {
space3270out(2);
*obptr++ = XA_HIGHLIGHTING;
*obptr++ = ea_buf[baddr].gr | 0xf0;
trace_ds("%s", see_efa(XA_HIGHLIGHTING,
ea_buf[baddr].gr | 0xf0));
(*(obuf + attr_count))++;
}
if (ea_buf[baddr].cs & CS_MASK) {
space3270out(2);
*obptr++ = XA_CHARSET;
*obptr++ = host_cs(ea_buf[baddr].cs);
trace_ds("%s", see_efa(XA_CHARSET,
host_cs(ea_buf[baddr].cs)));
(*(obuf + attr_count))++;
}
}
any = False;
} else {
insert_sa(baddr,
¤t_fg,
¤t_gr,
¤t_cs,
&any);
if (ea_buf[baddr].cs & CS_GE) {
space3270out(1);
*obptr++ = ORDER_GE;
if (any)
trace_ds("'");
trace_ds(" GraphicEscape");
any = False;
}
space3270out(1);
*obptr++ = ea_buf[baddr].cc;
if (ea_buf[baddr].cc <= 0x3f ||
ea_buf[baddr].cc == 0xff) {
if (any)
trace_ds("'");
trace_ds(" %s", see_ebc(ea_buf[baddr].cc));
any = False;
} else {
if (!any)
trace_ds(" '");
trace_ds("%s", see_ebc(ea_buf[baddr].cc));
any = True;
}
}
INC_BA(baddr);
} while (baddr != 0);
if (any)
trace_ds("'");
trace_ds("\n");
net_output();
}
#if defined(X3270_TRACE) /*[*/
/*
* Construct a 3270 command to reproduce the current state of the display.
*/
void
ctlr_snap_buffer(void)
{
register int baddr = 0;
int attr_count;
unsigned char current_fg = 0x00;
unsigned char current_gr = 0x00;
unsigned char current_cs = 0x00;
unsigned char av;
space3270out(2);
*obptr++ = screen_alt ? CMD_EWA : CMD_EW;
*obptr++ = code_table[0];
do {
if (ea_buf[baddr].fa) {
space3270out(4);
*obptr++ = ORDER_SFE;
attr_count = obptr - obuf;
*obptr++ = 1; /* for now */
*obptr++ = XA_3270;
*obptr++ = code_table[ea_buf[baddr].fa & ~FA_PRINTABLE];
if (ea_buf[baddr].fg) {
space3270out(2);
*obptr++ = XA_FOREGROUND;
*obptr++ = ea_buf[baddr].fg;
(*(obuf + attr_count))++;
}
if (ea_buf[baddr].gr) {
space3270out(2);
*obptr++ = XA_HIGHLIGHTING;
*obptr++ = ea_buf[baddr].gr | 0xf0;
(*(obuf + attr_count))++;
}
if (ea_buf[baddr].cs & CS_MASK) {
space3270out(2);
*obptr++ = XA_CHARSET;
*obptr++ = host_cs(ea_buf[baddr].cs);
(*(obuf + attr_count))++;
}
} else {
av = ea_buf[baddr].fg;
if (current_fg != av) {
current_fg = av;
space3270out(3);
*obptr++ = ORDER_SA;
*obptr++ = XA_FOREGROUND;
*obptr++ = av;
}
av = ea_buf[baddr].gr;
if (av)
av |= 0xf0;
if (current_gr != av) {
current_gr = av;
space3270out(3);
*obptr++ = ORDER_SA;
*obptr++ = XA_HIGHLIGHTING;
*obptr++ = av;
}
av = ea_buf[baddr].cs & CS_MASK;
if (av)
av = host_cs(av);
if (current_cs != av) {
current_cs = av;
space3270out(3);
*obptr++ = ORDER_SA;
*obptr++ = XA_CHARSET;
*obptr++ = av;
}
if (ea_buf[baddr].cs & CS_GE) {
space3270out(1);
*obptr++ = ORDER_GE;
}
space3270out(1);
*obptr++ = ea_buf[baddr].cc;
}
INC_BA(baddr);
} while (baddr != 0);
space3270out(4);
*obptr++ = ORDER_SBA;
ENCODE_BADDR(obptr, cursor_addr);
*obptr++ = ORDER_IC;
}
/*
* Construct a 3270 command to reproduce the reply mode.
* Returns a Boolean indicating if one is necessary.
*/
Boolean
ctlr_snap_modes(void)
{
int i;
if (!IN_3270 || reply_mode == SF_SRM_FIELD)
return False;
space3270out(6 + crm_nattr);
*obptr++ = CMD_WSF;
*obptr++ = 0x00; /* implicit length */
*obptr++ = 0x00;
*obptr++ = SF_SET_REPLY_MODE;
*obptr++ = 0x00; /* partition 0 */
*obptr++ = reply_mode;
if (reply_mode == SF_SRM_CHAR)
for (i = 0; i < crm_nattr; i++)
*obptr++ = crm_attr[i];
return True;
}
#endif /*]*/
/*
* Process a 3270 Erase All Unprotected command.
*/
void
ctlr_erase_all_unprotected(void)
{
register int baddr, sbaddr;
unsigned char fa;
Boolean f;
kybd_inhibit(False);
ALL_CHANGED;
if (formatted) {
/* find first field attribute */
baddr = 0;
do {
if (ea_buf[baddr].fa)
break;
INC_BA(baddr);
} while (baddr != 0);
sbaddr = baddr;
f = False;
do {
fa = ea_buf[baddr].fa;
if (!FA_IS_PROTECTED(fa)) {
mdt_clear(baddr);
do {
INC_BA(baddr);
if (!f) {
cursor_move(baddr);
f = True;
}
if (!ea_buf[baddr].fa) {
ctlr_add(baddr, EBC_null, 0);
}
} while (!ea_buf[baddr].fa);
}
else {
do {
INC_BA(baddr);
} while (!ea_buf[baddr].fa);
}
} while (baddr != sbaddr);
if (!f)
cursor_move(0);
} else {
ctlr_clear(True);
}
aid = AID_NO;
do_reset(False);
}
/*
* Process a 3270 Write command.
*/
enum pds
ctlr_write(unsigned char buf[], int buflen, Boolean erase)
{
register unsigned char *cp;
register int baddr;
unsigned char current_fa;
Boolean last_cmd;
Boolean last_zpt;
Boolean wcc_keyboard_restore, wcc_sound_alarm;
Boolean ra_ge;
int i;
unsigned char na;
int any_fa;
unsigned char efa_fg;
unsigned char efa_gr;
unsigned char efa_cs;
unsigned char efa_ic;
const char *paren = "(";
enum { NONE, ORDER, SBA, TEXT, NULLCH } previous = NONE;
enum pds rv = PDS_OKAY_NO_OUTPUT;
int fa_addr;
Boolean add_dbcs;
unsigned char add_c1, add_c2 = 0;
enum dbcs_state d;
enum dbcs_why why;
Boolean aborted = False;
#if defined(X3270_DBCS) /*[*/
char mb[16];
#endif /*]*/
#define END_TEXT0 { if (previous == TEXT) trace_ds("'"); }
#define END_TEXT(cmd) { END_TEXT0; trace_ds(" %s", cmd); }
/* XXX: Should there be a ctlr_add_cs call here? */
#define START_FIELD(fa) { \
current_fa = fa; \
ctlr_add_fa(buffer_addr, fa, 0); \
ctlr_add_cs(buffer_addr, 0); \
ctlr_add_fg(buffer_addr, 0); \
ctlr_add_gr(buffer_addr, 0); \
ctlr_add_ic(buffer_addr, 0); \
trace_ds(see_attr(fa)); \
formatted = True; \
}
kybd_inhibit(False);
if (buflen < 2)
return PDS_BAD_CMD;
default_fg = 0;
default_gr = 0;
default_cs = 0;
default_ic = 0;
trace_primed = True;
buffer_addr = cursor_addr;
if (WCC_RESET(buf[1])) {
if (erase)
reply_mode = SF_SRM_FIELD;
trace_ds("%sreset", paren);
paren = ",";
}
wcc_sound_alarm = WCC_SOUND_ALARM(buf[1]);
if (wcc_sound_alarm) {
trace_ds("%salarm", paren);
paren = ",";
}
wcc_keyboard_restore = WCC_KEYBOARD_RESTORE(buf[1]);
if (wcc_keyboard_restore)
ticking_stop();
if (wcc_keyboard_restore) {
trace_ds("%srestore", paren);
paren = ",";
}
if (WCC_RESET_MDT(buf[1])) {
trace_ds("%sresetMDT", paren);
paren = ",";
baddr = 0;
if (appres.modified_sel)
ALL_CHANGED;
do {
if (ea_buf[baddr].fa) {
mdt_clear(baddr);
}
INC_BA(baddr);
} while (baddr != 0);
}
if (strcmp(paren, "("))
trace_ds(")");
last_cmd = True;
last_zpt = False;
current_fa = get_field_attribute(buffer_addr);
#define ABORT_WRITEx { \
rv = PDS_BAD_ADDR; \
aborted = True; \
break; \
}
#define ABORT_WRITE(s) { \
trace_ds(" [" s "; write aborted]\n"); \
ABORT_WRITEx; \
} \
for (cp = &buf[2]; !aborted && cp < (buf + buflen); cp++) {
switch (*cp) {
case ORDER_SF: /* start field */
END_TEXT("StartField");
if (previous != SBA)
trace_ds(rcba(buffer_addr));
previous = ORDER;
cp++; /* skip field attribute */
START_FIELD(*cp);
ctlr_add_fg(buffer_addr, 0);
INC_BA(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case ORDER_SBA: /* set buffer address */
cp += 2; /* skip buffer address */
buffer_addr = DECODE_BADDR(*(cp-1), *cp);
END_TEXT("SetBufferAddress");
previous = SBA;
trace_ds(rcba(buffer_addr));
if (buffer_addr >= COLS * ROWS) {
ABORT_WRITE("invalid SBA address");
}
current_fa = get_field_attribute(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case ORDER_IC: /* insert cursor */
END_TEXT("InsertCursor");
if (previous != SBA)
trace_ds(rcba(buffer_addr));
previous = ORDER;
cursor_move(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case ORDER_PT: /* program tab */
END_TEXT("ProgramTab");
previous = ORDER;
/*
* If the buffer address is the field attribute of
* of an unprotected field, simply advance one
* position.
*/
if (ea_buf[buffer_addr].fa &&
!FA_IS_PROTECTED(ea_buf[buffer_addr].fa)) {
INC_BA(buffer_addr);
last_zpt = False;
last_cmd = True;
break;
}
/*
* Otherwise, advance to the first position of the
* next unprotected field.
*/
baddr = next_unprotected(buffer_addr);
if (baddr < buffer_addr)
baddr = 0;
/*
* Null out the remainder of the current field -- even
* if protected -- if the PT doesn't follow a command
* or order, or (honestly) if the last order we saw was
* a null-filling PT that left the buffer address at 0.
* XXX: There's some funky DBCS rule here.
*/
if (!last_cmd || last_zpt) {
trace_ds("(nulling)");
while ((buffer_addr != baddr) &&
(!ea_buf[buffer_addr].fa)) {
ctlr_add(buffer_addr, EBC_null, 0);
ctlr_add_cs(buffer_addr, 0);
ctlr_add_fg(buffer_addr, 0);
ctlr_add_gr(buffer_addr, 0);
ctlr_add_ic(buffer_addr, 0);
INC_BA(buffer_addr);
}
if (baddr == 0)
last_zpt = True;
} else
last_zpt = False;
buffer_addr = baddr;
last_cmd = True;
break;
case ORDER_RA: /* repeat to address */
END_TEXT("RepeatToAddress");
cp += 2; /* skip buffer address */
baddr = DECODE_BADDR(*(cp-1), *cp);
trace_ds(rcba(baddr));
cp++; /* skip char to repeat */
add_dbcs = False;
ra_ge = False;
previous = ORDER;
#if defined(X3270_DBCS) /*[*/
if (dbcs) {
d = ctlr_lookleft_state(buffer_addr, &why);
if (d == DBCS_RIGHT) {
ABORT_WRITE("RA over right half of DBCS character");
}
if (default_cs == CS_DBCS || d == DBCS_LEFT) {
add_dbcs = True;
}
}
if (add_dbcs) {
if ((baddr - buffer_addr) % 2) {
ABORT_WRITE("DBCS RA with odd length");
}
add_c1 = *cp;
cp++;
if (cp >= buf + buflen) {
ABORT_WRITE("missing second half of DBCS character");
}
add_c2 = *cp;
if (add_c1 == EBC_null) {
switch (add_c2) {
case EBC_null:
case EBC_nl:
case EBC_em:
case EBC_ff:
case EBC_cr:
case EBC_dup:
case EBC_fm:
break;
default:
trace_ds(" [invalid DBCS RA control character X'%02x%02x'; write aborted]",
add_c1, add_c2);
ABORT_WRITEx;
}
} else if (add_c1 < 0x40 || add_c1 > 0xfe ||
add_c2 < 0x40 || add_c2 > 0xfe) {
trace_ds(" [invalid DBCS RA character X'%02x%02x'; write aborted]",
add_c1, add_c2);
ABORT_WRITEx;
}
dbcs_to_mb(add_c1, add_c2, mb);
trace_ds_nb("'%s'", mb);
} else
#endif /*]*/
{
if (*cp == ORDER_GE) {
ra_ge = True;
trace_ds("GraphicEscape");
cp++;
}
add_c1 = *cp;
if (add_c1)
trace_ds("'");
trace_ds("%s", see_ebc(add_c1));
if (add_c1)
trace_ds("'");
}
if (baddr >= COLS * ROWS) {
ABORT_WRITE("invalid RA address");
}
do {
if (add_dbcs) {
ctlr_add(buffer_addr, add_c1,
default_cs);
} else {
if (ra_ge)
ctlr_add(buffer_addr, add_c1,
CS_GE);
else if (default_cs)
ctlr_add(buffer_addr, add_c1,
default_cs);
else
ctlr_add(buffer_addr, add_c1,
0);
}
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
if (add_dbcs) {
ctlr_add(buffer_addr, add_c2,
default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
}
} while (buffer_addr != baddr);
current_fa = get_field_attribute(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case ORDER_EUA: /* erase unprotected to address */
cp += 2; /* skip buffer address */
baddr = DECODE_BADDR(*(cp-1), *cp);
END_TEXT("EraseUnprotectedAll");
if (previous != SBA)
trace_ds(rcba(baddr));
previous = ORDER;
if (baddr >= COLS * ROWS) {
ABORT_WRITE("invalid EUA address");
}
d = ctlr_lookleft_state(buffer_addr, &why);
if (d == DBCS_RIGHT) {
ABORT_WRITE("EUA overwriting right half of DBCS character");
}
d = ctlr_lookleft_state(baddr, &why);
if (d == DBCS_LEFT) {
ABORT_WRITE("EUA overwriting left half of DBCS character");
}
do {
if (ea_buf[buffer_addr].fa)
current_fa = ea_buf[buffer_addr].fa;
else if (!FA_IS_PROTECTED(current_fa)) {
ctlr_add(buffer_addr, EBC_null,
CS_BASE);
}
INC_BA(buffer_addr);
} while (buffer_addr != baddr);
current_fa = get_field_attribute(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case ORDER_GE: /* graphic escape */
/* XXX: DBCS? */
END_TEXT("GraphicEscape ");
cp++; /* skip char */
previous = ORDER;
if (*cp)
trace_ds("'");
trace_ds("%s", see_ebc(*cp));
if (*cp)
trace_ds("'");
ctlr_add(buffer_addr, *cp, CS_GE);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
current_fa = get_field_attribute(buffer_addr);
last_cmd = False;
last_zpt = False;
break;
case ORDER_MF: /* modify field */
END_TEXT("ModifyField");
if (previous != SBA)
trace_ds(rcba(buffer_addr));
previous = ORDER;
cp++;
na = *cp;
if (ea_buf[buffer_addr].fa) {
for (i = 0; i < (int)na; i++) {
cp++;
if (*cp == XA_3270) {
trace_ds(" 3270");
cp++;
ctlr_add_fa(buffer_addr, *cp,
ea_buf[buffer_addr].cs);
trace_ds(see_attr(*cp));
} else if (*cp == XA_FOREGROUND) {
trace_ds("%s",
see_efa(*cp,
*(cp + 1)));
cp++;
if (appres.m3279)
ctlr_add_fg(buffer_addr, *cp);
} else if (*cp == XA_HIGHLIGHTING) {
trace_ds("%s",
see_efa(*cp,
*(cp + 1)));
cp++;
ctlr_add_gr(buffer_addr, *cp & 0x07);
} else if (*cp == XA_CHARSET) {
int cs = 0;
trace_ds("%s",
see_efa(*cp,
*(cp + 1)));
cp++;
if (*cp == 0xf1)
cs = CS_APL;
else if (*cp == 0xf8)
cs = CS_DBCS;
ctlr_add_cs(buffer_addr, cs);
} else if (*cp == XA_ALL) {
trace_ds("%s",
see_efa(*cp,
*(cp + 1)));
cp++;
} else if (*cp == XA_INPUT_CONTROL) {
trace_ds("%s",
see_efa(*cp,
*(cp + 1)));
ctlr_add_ic(buffer_addr,
(*(cp + 1) == 1));
cp++;
} else {
trace_ds("%s[unsupported]", see_efa(*cp, *(cp + 1)));
cp++;
}
}
INC_BA(buffer_addr);
} else
cp += na * 2;
last_cmd = True;
last_zpt = False;
break;
case ORDER_SFE: /* start field extended */
END_TEXT("StartFieldExtended");
if (previous != SBA)
trace_ds(rcba(buffer_addr));
previous = ORDER;
cp++; /* skip order */
na = *cp;
any_fa = 0;
efa_fg = 0;
efa_gr = 0;
efa_cs = 0;
efa_ic = 0;
for (i = 0; i < (int)na; i++) {
cp++;
if (*cp == XA_3270) {
trace_ds(" 3270");
cp++;
START_FIELD(*cp);
any_fa++;
} else if (*cp == XA_FOREGROUND) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
cp++;
if (appres.m3279)
efa_fg = *cp;
} else if (*cp == XA_HIGHLIGHTING) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
cp++;
efa_gr = *cp & 0x07;
} else if (*cp == XA_CHARSET) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
cp++;
if (*cp == 0xf1)
efa_cs = CS_APL;
else if (dbcs && (*cp == 0xf8))
efa_cs = CS_DBCS;
else
efa_cs = CS_BASE;
} else if (*cp == XA_ALL) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
cp++;
} else if (*cp == XA_INPUT_CONTROL) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
if (dbcs)
efa_ic = (*(cp + 1) == 1);
cp++;
} else {
trace_ds("%s[unsupported]", see_efa(*cp, *(cp + 1)));
cp++;
}
}
if (!any_fa)
START_FIELD(0);
ctlr_add_cs(buffer_addr, efa_cs);
ctlr_add_fg(buffer_addr, efa_fg);
ctlr_add_gr(buffer_addr, efa_gr);
ctlr_add_ic(buffer_addr, efa_ic);
INC_BA(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case ORDER_SA: /* set attribute */
END_TEXT("SetAttribtue");
previous = ORDER;
cp++;
if (*cp == XA_FOREGROUND) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
if (appres.m3279)
default_fg = *(cp + 1);
} else if (*cp == XA_HIGHLIGHTING) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
default_gr = *(cp + 1) & 0x07;
} else if (*cp == XA_ALL) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
default_fg = 0;
default_gr = 0;
default_cs = 0;
default_ic = 0;
} else if (*cp == XA_CHARSET) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
switch (*(cp + 1)) {
case 0xf1:
default_cs = CS_APL;
break;
case 0xf8:
default_cs = CS_DBCS;
break;
default:
default_cs = CS_BASE;
break;
}
} else if (*cp == XA_INPUT_CONTROL) {
trace_ds("%s", see_efa(*cp, *(cp + 1)));
if (*(cp + 1) == 1)
default_ic = 1;
else
default_ic = 0;
} else
trace_ds("%s[unsupported]",
see_efa(*cp, *(cp + 1)));
cp++;
last_cmd = True;
last_zpt = False;
break;
case FCORDER_SUB: /* format control orders */
case FCORDER_DUP:
case FCORDER_FM:
case FCORDER_FF:
case FCORDER_CR:
case FCORDER_NL:
case FCORDER_EM:
case FCORDER_EO:
END_TEXT(see_ebc(*cp));
previous = ORDER;
d = ctlr_lookleft_state(buffer_addr, &why);
if (default_cs == CS_DBCS || d != DBCS_NONE) {
ABORT_WRITE("invalid format control order in DBCS field");
}
ctlr_add(buffer_addr, *cp, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case FCORDER_SO:
/* Look left for errors. */
END_TEXT(see_ebc(*cp));
d = ctlr_lookleft_state(buffer_addr, &why);
if (d == DBCS_RIGHT) {
ABORT_WRITE("SO overwriting right half of DBCS character");
}
if (d != DBCS_NONE && why == DBCS_FIELD) {
ABORT_WRITE("SO in DBCS field");
}
if (d != DBCS_NONE && why == DBCS_SUBFIELD) {
ABORT_WRITE("double SO");
}
/* All is well. */
previous = ORDER;
ctlr_add(buffer_addr, *cp, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case FCORDER_SI:
/* Look left for errors. */
END_TEXT(see_ebc(*cp));
d = ctlr_lookleft_state(buffer_addr, &why);
if (d == DBCS_RIGHT) {
ABORT_WRITE("SI overwriting right half of DBCS character");
}
if (d != DBCS_NONE && why == DBCS_FIELD) {
ABORT_WRITE("SI in DBCS field");
}
fa_addr = find_field_attribute(buffer_addr);
baddr = buffer_addr;
DEC_BA(baddr);
while (!aborted &&
((fa_addr >= 0 && baddr != fa_addr) ||
(fa_addr < 0 && baddr != ROWS*COLS - 1))) {
if (ea_buf[baddr].cc == FCORDER_SI) {
ABORT_WRITE("double SI");
}
if (ea_buf[baddr].cc == FCORDER_SO)
break;
DEC_BA(baddr);
}
if (aborted)
break;
if (ea_buf[baddr].cc != FCORDER_SO) {
ABORT_WRITE("SI without SO");
}
/* All is well. */
previous = ORDER;
ctlr_add(buffer_addr, *cp, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
last_cmd = True;
last_zpt = False;
break;
case FCORDER_NULL: /* NULL or DBCS control char */
previous = NULLCH;
add_dbcs = False;
d = ctlr_lookleft_state(buffer_addr, &why);
if (d == DBCS_RIGHT) {
ABORT_WRITE("NULL overwriting right half of DBCS character");
}
if (d != DBCS_NONE || default_cs == CS_DBCS) {
add_c1 = EBC_null;
cp++;
if (cp >= buf + buflen) {
ABORT_WRITE("missing second half of DBCS character");
}
add_c2 = *cp;
switch (add_c2) {
case EBC_null:
case EBC_nl:
case EBC_em:
case EBC_ff:
case EBC_cr:
case EBC_dup:
case EBC_fm:
/* DBCS control code */
END_TEXT(see_ebc(add_c2));
add_dbcs = True;
break;
case ORDER_SF:
case ORDER_SFE:
/* Dead position */
END_TEXT("DeadNULL");
cp--;
break;
default:
trace_ds(" [invalid DBCS control character X'%02x%02x'; write aborted]",
add_c1, add_c2);
ABORT_WRITEx;
break;
}
if (aborted)
break;
} else {
END_TEXT("NULL");
add_c1 = *cp;
}
ctlr_add(buffer_addr, add_c1, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_gr(buffer_addr, default_ic);
INC_BA(buffer_addr);
if (add_dbcs) {
ctlr_add(buffer_addr, add_c2, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_gr(buffer_addr, default_ic);
INC_BA(buffer_addr);
}
last_cmd = False;
last_zpt = False;
break;
default: /* enter character */
if (*cp <= 0x3F) {
END_TEXT("UnsupportedOrder");
trace_ds("(%02X)", *cp);
previous = ORDER;
last_cmd = True;
last_zpt = False;
break;
}
if (previous != TEXT)
trace_ds(" '");
previous = TEXT;
#if defined(X3270_DBCS) /*[*/
add_dbcs = False;
d = ctlr_lookleft_state(buffer_addr, &why);
if (d == DBCS_RIGHT) {
ABORT_WRITE("overwriting right half of DBCS character");
}
if (d != DBCS_NONE || default_cs == CS_DBCS) {
add_c1 = *cp;
cp++;
if (cp >= buf + buflen) {
ABORT_WRITE("missing second half of DBCS character");
}
add_c2 = *cp;
if (add_c1 < 0x40 || add_c1 > 0xfe ||
add_c2 < 0x40 || add_c2 > 0xfe) {
trace_ds(" [invalid DBCS character X'%02x%02x'; write aborted]",
add_c1, add_c2);
ABORT_WRITEx;
}
add_dbcs = True;
dbcs_to_mb(add_c1, add_c2, mb);
trace_ds_nb("%s", mb);
} else {
#endif /*]*/
add_c1 = *cp;
trace_ds("%s", see_ebc(*cp));
#if defined(X3270_DBCS) /*[*/
}
#endif /*]*/
ctlr_add(buffer_addr, add_c1, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
#if defined(X3270_DBCS) /*[*/
if (add_dbcs) {
ctlr_add(buffer_addr, add_c2, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_gr(buffer_addr, default_ic);
INC_BA(buffer_addr);
}
#endif /*]*/
last_cmd = False;
last_zpt = False;
break;
}
}
set_formatted();
END_TEXT0;
trace_ds("\n");
if (wcc_keyboard_restore) {
aid = AID_NO;
do_reset(False);
} else if (kybdlock & KL_OIA_TWAIT) {
kybdlock_clr(KL_OIA_TWAIT, "ctlr_write");
status_syswait();
}
if (wcc_sound_alarm)
ring_bell();
/* Set up the DBCS state. */
if (ctlr_dbcs_postprocess() < 0 && rv == PDS_OKAY_NO_OUTPUT)
rv = PDS_BAD_ADDR;
trace_primed = False;
ps_process();
/* Let a script go. */
sms_host_output();
/* Tell 'em what happened. */
return rv;
}
#undef START_FIELDx
#undef START_FIELD0
#undef START_FIELD
#undef END_TEXT0
#undef END_TEXT
#undef ABORT_WRITEx
#undef ABORT_WRITE
/*
* Write SSCP-LU data, which is quite a bit dumber than regular 3270
* output.
*/
void
ctlr_write_sscp_lu(unsigned char buf[], int buflen)
{
int i;
unsigned char *cp = buf;
int s_row;
unsigned char c;
int baddr;
/*
* The 3174 Functionl Description says that anything but NL, NULL, FM,
* or DUP is to be displayed as a graphic. However, to deal with
* badly-behaved hosts, we filter out SF, IC and SBA sequences, and
* we display other control codes as spaces.
*/
trace_ds("SSCP-LU data\n");
for (i = 0; i < buflen; cp++, i++) {
switch (*cp) {
case FCORDER_NL:
/*
* Insert NULLs to the end of the line and advance to
* the beginning of the next line.
*/
s_row = buffer_addr / COLS;
while ((buffer_addr / COLS) == s_row) {
ctlr_add(buffer_addr, EBC_null, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
}
break;
case ORDER_SF:
/* Some hosts forget they're talking SSCP-LU. */
cp++;
i++;
trace_ds(" StartField%s %s [translated to space]\n",
rcba(buffer_addr), see_attr(*cp));
ctlr_add(buffer_addr, EBC_space, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
break;
case ORDER_IC:
trace_ds(" InsertCursor%s [ignored]\n",
rcba(buffer_addr));
break;
case ORDER_SBA:
baddr = DECODE_BADDR(*(cp+1), *(cp+2));
trace_ds(" SetBufferAddress%s [ignored]\n", rcba(baddr));
cp += 2;
i += 2;
break;
case ORDER_GE:
cp++;
if (++i >= buflen)
break;
if (*cp <= 0x40)
c = EBC_space;
else
c = *cp;
ctlr_add(buffer_addr, c, CS_GE);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
break;
default:
ctlr_add(buffer_addr, *cp, default_cs);
ctlr_add_fg(buffer_addr, default_fg);
ctlr_add_gr(buffer_addr, default_gr);
ctlr_add_ic(buffer_addr, default_ic);
INC_BA(buffer_addr);
break;
}
}
cursor_move(buffer_addr);
sscp_start = buffer_addr;
/* Unlock the keyboard. */
aid = AID_NO;
do_reset(False);
/* Let a script go. */
sms_host_output();
}
#if defined(X3270_DBCS) /*[*/
/*
* Determine the DBCS state of a buffer location strictly by looking left.
* Used only to validate write operations.
* Returns only DBCS_LEFT, DBCS_RIGHT or DBCS_NONE.
* Also returns whether the location is part of a DBCS field (SFE with the
* DBCS character set), DBCS subfield (to the right of an SO within a non-DBCS
* field), or DBCS attribute (has the DBCS character set extended attribute
* within a non-DBCS field).
*
* This function should be used only to determine the legality of adding a
* DBCS or SBCS character at baddr.
*/
enum dbcs_state
ctlr_lookleft_state(int baddr, enum dbcs_why *why)
{
int faddr;
int fdist;
int xaddr;
Boolean si = False;
#define AT_END(f, b) \
(((f) < 0 && (b) == ROWS*COLS - 1) || \
((f) >= 0 && (b) == (f)))
/* If we're not in DBCS state, everything is DBCS_NONE. */
if (!dbcs)
return DBCS_NONE;
/* Find the field attribute, if any. */
faddr = find_field_attribute(baddr);
/*
* First in precedence is a DBCS field.
* DBCS SA and SO/SI inside a DBCS field are errors, but are considered
* defective DBCS characters.
*/
if (ea_buf[faddr].cs == CS_DBCS) {
*why = DBCS_FIELD;
fdist = (baddr + ROWS*COLS) - faddr;
return (fdist % 2)? DBCS_LEFT: DBCS_RIGHT;
}
/*
* The DBCS attribute takes precedence next.
* SO and SI can appear within such a region, but they are single-byte
* characters which effectively split it.
*/
if (ea_buf[baddr].cs == CS_DBCS) {
if (ea_buf[baddr].cc == EBC_so || ea_buf[baddr].cc == EBC_si)
return DBCS_NONE;
xaddr = baddr;
while (!AT_END(faddr, xaddr) &&
ea_buf[xaddr].cs == CS_DBCS &&
ea_buf[xaddr].cc != EBC_so &&
ea_buf[xaddr].cc != EBC_si) {
DEC_BA(xaddr);
}
*why = DBCS_ATTRIBUTE;
fdist = (baddr + ROWS*COLS) - xaddr;
return (fdist % 2)? DBCS_LEFT: DBCS_RIGHT;
}
/*
* Finally, look for a SO not followed by an SI.
*/
xaddr = baddr;
DEC_BA(xaddr);
while (!AT_END(faddr, xaddr)) {
if (ea_buf[xaddr].cc == EBC_si)
si = True;
else if (ea_buf[xaddr].cc == EBC_so) {
if (si)
si = False;
else {
*why = DBCS_SUBFIELD;
fdist = (baddr + ROWS*COLS) - xaddr;
return (fdist % 2)? DBCS_LEFT: DBCS_RIGHT;
}
}
DEC_BA(xaddr);
}
/* Nada. */
return DBCS_NONE;
}
static Boolean
valid_dbcs_char(unsigned char c1, unsigned char c2)
{
if (c1 >= 0x40 && c1 < 0xff && c2 >= 0x40 && c2 < 0xff)
return True;
if (c1 != 0x00 || c2 < 0x40 || c2 >= 0xff)
return False;
switch (c2) {
case EBC_null:
case EBC_nl:
case EBC_em:
case EBC_ff:
case EBC_cr:
case EBC_dup:
case EBC_fm:
return True;
default:
return False;
}
}
/*
* Post-process DBCS state in the buffer.
* This has two purposes:
*
* - Required post-processing validation, per the data stream spec, which can
* cause the write operation to be rejected.
* - Setting up the value of the all the db fields in ea_buf.
*
* This function is called at the end of every 3270 write operation, and also
* after each batch of NVT write operations. It could also be called after
* significant keyboard operations, but that might be too expensive.
*
* Returns 0 for success, -1 for failure.
*/
int
ctlr_dbcs_postprocess(void)
{
int baddr; /* current buffer address */
int faddr0; /* address of first field attribute */
int faddr; /* address of current field attribute */
int last_baddr; /* last buffer address to search */
int pbaddr = -1; /* previous buffer address */
int dbaddr = -1; /* first data position of current DBCS (sub-)
field */
Boolean so = False, si = False;
Boolean dbcs_field = False;
int rc = 0;
/* If we're not in DBCS mode, do nothing. */
if (!dbcs)
return 0;
/*
* Find the field attribute for location 0. If unformatted, it's the
* dummy at -1. Also compute the starting and ending points for the
* scan: the first location after that field attribute.
*/
faddr0 = find_field_attribute(0);
baddr = faddr0;
INC_BA(baddr);
if (faddr0 < 0)
last_baddr = 0;
else
last_baddr = faddr0;
faddr = faddr0;
dbcs_field = (ea_buf[faddr].cs & CS_MASK) == CS_DBCS;
do {
if (ea_buf[baddr].fa) {
faddr = baddr;
ea_buf[faddr].db = DBCS_NONE;
dbcs_field = (ea_buf[faddr].cs & CS_MASK) == CS_DBCS;
if (dbcs_field) {
dbaddr = baddr;
INC_BA(dbaddr);
} else {
dbaddr = -1;
}
/*
* An SI followed by a field attribute shouldn't be
* displayed with a wide cursor.
*/
if (pbaddr >= 0 && ea_buf[pbaddr].db == DBCS_SI)
ea_buf[pbaddr].db = DBCS_NONE;
} else {
switch (ea_buf[baddr].cc) {
case EBC_so:
/* Two SO's or SO in DBCS field are invalid. */
if (so || dbcs_field) {
trace_ds("DBCS postprocess: invalid SO "
"found at %s\n", rcba(baddr));
rc = -1;
} else {
dbaddr = baddr;
INC_BA(dbaddr);
}
ea_buf[baddr].db = DBCS_NONE;
so = True;
si = False;
break;
case EBC_si:
/* Two SI's or SI in DBCS field are invalid. */
if (si || dbcs_field) {
trace_ds("Postprocess: Invalid SO found "
"at %s\n", rcba(baddr));
rc = -1;
ea_buf[baddr].db = DBCS_NONE;
} else {
ea_buf[baddr].db = DBCS_SI;
}
dbaddr = -1;
si = True;
so = False;
break;
default:
/* Non-base CS in DBCS subfield is invalid. */
if (so && ea_buf[baddr].cs != CS_BASE) {
trace_ds("DBCS postprocess: invalid "
"character set found at %s\n",
rcba(baddr));
rc = -1;
ea_buf[baddr].cs = CS_BASE;
}
if ((ea_buf[baddr].cs & CS_MASK) == CS_DBCS) {
/*
* Beginning or continuation of an SA DBCS
* subfield.
*/
if (dbaddr < 0) {
dbaddr = baddr;
}
} else if (!so && !dbcs_field) {
/*
* End of SA DBCS subfield.
*/
dbaddr = -1;
}
if (dbaddr >= 0) {
/*
* Turn invalid characters into spaces,
* silently.
*/
if ((baddr + ROWS*COLS - dbaddr) % 2) {
if (!valid_dbcs_char(
ea_buf[pbaddr].cc,
ea_buf[baddr].cc)) {
ea_buf[pbaddr].cc =
EBC_space;
ea_buf[baddr].cc =
EBC_space;
}
MAKE_RIGHT(baddr);
} else {
MAKE_LEFT(baddr);
}
} else
ea_buf[baddr].db = DBCS_NONE;
break;
}
}
/*
* Check for dead positions.
* Turn them into NULLs, silently.
*/
if (pbaddr >= 0 &&
IS_LEFT(ea_buf[pbaddr].db) &&
!IS_RIGHT(ea_buf[baddr].db) &&
ea_buf[pbaddr].db != DBCS_DEAD) {
if (!ea_buf[baddr].fa) {
trace_ds("DBCS postprocess: dead position "
"at %s\n", rcba(pbaddr));
rc = -1;
}
ea_buf[pbaddr].cc = EBC_null;
ea_buf[pbaddr].db = DBCS_DEAD;
}
/* Check for SB's, which follow SIs. */
if (pbaddr >= 0 && ea_buf[pbaddr].db == DBCS_SI)
ea_buf[baddr].db = DBCS_SB;
/* Save this position as the previous and increment. */
pbaddr = baddr;
INC_BA(baddr);
} while (baddr != last_baddr);
return rc;
}
#endif /*]*/
/*
* Process pending input.
*/
void
ps_process(void)
{
while (run_ta())
;
sms_continue();
#if defined(X3270_FT) /*[*/
/* Process file transfers. */
if (ft_state != FT_NONE && /* transfer in progress */
formatted && /* screen is formatted */
!screen_alt && /* 24x80 screen */
!kybdlock && /* keyboard not locked */
/* magic field */
ea_buf[1919].fa && FA_IS_SKIP(ea_buf[1919].fa)) {
ft_cut_data();
}
#endif /*]*/
}
/*
* Tell me if there is any data on the screen.
*/
Boolean
ctlr_any_data(void)
{
register int i;
for (i = 0; i < ROWS*COLS; i++) {
if (!IsBlank(ea_buf[i].cc))
return True;
}
return False;
}
/*
* Clear the text (non-status) portion of the display. Also resets the cursor
* and buffer addresses and extended attributes.
*/
void
ctlr_clear(Boolean can_snap)
{
/* Snap any data that is about to be lost into the trace file. */
if (ctlr_any_data()) {
#if defined(X3270_TRACE) /*[*/
if (can_snap && !trace_skipping && toggled(SCREEN_TRACE))
trace_screen();
#endif /*]*/
scroll_save(maxROWS, ever_3270 ? False : True);
}
#if defined(X3270_TRACE) /*[*/
trace_skipping = False;
#endif /*]*/
/* Clear the screen. */
(void) memset((char *)ea_buf, 0, ROWS*COLS*sizeof(struct ea));
ALL_CHANGED;
cursor_move(0);
buffer_addr = 0;
unselect(0, ROWS*COLS);
formatted = False;
default_fg = 0;
default_gr = 0;
default_ic = 0;
sscp_start = 0;
}
/*
* Fill the screen buffer with blanks.
*/
static void
ctlr_blanks(void)
{
int baddr;
for (baddr = 0; baddr < ROWS*COLS; baddr++) {
if (!ea_buf[baddr].fa)
ea_buf[baddr].cc = EBC_space;
}
ALL_CHANGED;
cursor_move(0);
buffer_addr = 0;
unselect(0, ROWS*COLS);
formatted = False;
}
/*
* Change a character in the 3270 buffer.
* Removes any field attribute defined at that location.
*/
void
ctlr_add(int baddr, unsigned char c, unsigned char cs)
{
unsigned char oc = 0;
if (ea_buf[baddr].fa ||
((oc = ea_buf[baddr].cc) != c || ea_buf[baddr].cs != cs)) {
if (trace_primed && !IsBlank(oc)) {
#if defined(X3270_TRACE) /*[*/
if (toggled(SCREEN_TRACE))
trace_screen();
#endif /*]*/
scroll_save(maxROWS, False);
trace_primed = False;
}
if (SELECTED(baddr))
unselect(baddr, 1);
ONE_CHANGED(baddr);
ea_buf[baddr].cc = c;
ea_buf[baddr].cs = cs;
ea_buf[baddr].fa = 0;
}
}
/*
* Set a field attribute in the 3270 buffer.
*/
void
ctlr_add_fa(int baddr, unsigned char fa, unsigned char cs)
{
/* Put a null in the display buffer. */
ctlr_add(baddr, EBC_null, cs);
/*
* Store the new attribute, setting the 'printable' bits so that the
* value will be non-zero.
*/
ea_buf[baddr].fa = FA_PRINTABLE | (fa & FA_MASK);
}
/*
* Change the character set for a field in the 3270 buffer.
*/
void
ctlr_add_cs(int baddr, unsigned char cs)
{
if (ea_buf[baddr].cs != cs) {
if (SELECTED(baddr))
unselect(baddr, 1);
ONE_CHANGED(baddr);
ea_buf[baddr].cs = cs;
}
}
/*
* Change the graphic rendition of a character in the 3270 buffer.
*/
void
ctlr_add_gr(int baddr, unsigned char gr)
{
if (ea_buf[baddr].gr != gr) {
if (SELECTED(baddr))
unselect(baddr, 1);
ONE_CHANGED(baddr);
ea_buf[baddr].gr = gr;
if (gr & GR_BLINK)
blink_start();
}
}
/*
* Change the foreground color for a character in the 3270 buffer.
*/
void
ctlr_add_fg(int baddr, unsigned char color)
{
if (!appres.m3279)
return;
if ((color & 0xf0) != 0xf0)
color = 0;
if (ea_buf[baddr].fg != color) {
if (SELECTED(baddr))
unselect(baddr, 1);
ONE_CHANGED(baddr);
ea_buf[baddr].fg = color;
}
}
#if defined(X3270_ANSI) /*[*/
/*
* Change the background color for a character in the 3270 buffer.
*/
void
ctlr_add_bg(int baddr, unsigned char color)
{
if (!appres.m3279)
return;
if ((color & 0xf0) != 0xf0)
color = 0;
if (ea_buf[baddr].bg != color) {
if (SELECTED(baddr))
unselect(baddr, 1);
ONE_CHANGED(baddr);
ea_buf[baddr].bg = color;
}
}
#endif /*]*/
/*
* Change the input control bit for a character in the 3270 buffer.
*/
static void
ctlr_add_ic(int baddr, unsigned char ic)
{
ea_buf[baddr].ic = ic;
}
/*
* Wrapping bersion of ctlr_bcopy.
*/
void
ctlr_wrapping_memmove(int baddr_to, int baddr_from, int count)
{
/*
* The 'to' region, the 'from' region, or both can wrap the screen,
* and can overlap each other. memmove() is smart enough to deal with
* overlaps, but not across a screen wrap.
*
* It's faster to figure out if none of this is true, then do a slow
* location-at-a-time version only if it happens.
*/
if (baddr_from + count <= ROWS*COLS &&
baddr_to + count <= ROWS*COLS) {
ctlr_bcopy(baddr_from, baddr_to, count, True);
} else {
int i, from, to;
for (i = 0; i < count; i++) {
if (baddr_to > baddr_from) {
/* Shifting right, move left. */
to = (baddr_to + count - 1 - i) % ROWS*COLS;
from = (baddr_from + count - 1 - i) % ROWS*COLS;
} else {
/* Shifting left, move right. */
to = (baddr_to + i) % ROWS*COLS;
from = (baddr_from + i) % ROWS*COLS;
}
ctlr_bcopy(from, to, 1, True);
}
}
}
/*
* Copy a block of characters in the 3270 buffer, optionally including all of
* the extended attributes. (The character set, which is actually kept in the
* extended attributes, is considered part of the characters here.)
*/
void
ctlr_bcopy(int baddr_from, int baddr_to, int count, int move_ea)
{
/* Move the characters. */
if (memcmp((char *) &ea_buf[baddr_from],
(char *) &ea_buf[baddr_to],
count * sizeof(struct ea))) {
(void) memmove(&ea_buf[baddr_to], &ea_buf[baddr_from],
count * sizeof(struct ea));
REGION_CHANGED(baddr_to, baddr_to + count);
/*
* For the time being, if any selected text shifts around on
* the screen, unhighlight it. Eventually there should be
* logic for preserving the highlight if the *all* of the
* selected text moves.
*/
if (area_is_selected(baddr_to, count))
unselect(baddr_to, count);
}
/* XXX: What about move_ea? */
}
#if defined(X3270_ANSI) /*[*/
/*
* Erase a region of the 3270 buffer, optionally clearing extended attributes
* as well.
*/
void
ctlr_aclear(int baddr, int count, int clear_ea)
{
if (memcmp((char *) &ea_buf[baddr], (char *) zero_buf,
count * sizeof(struct ea))) {
(void) memset((char *) &ea_buf[baddr], 0,
count * sizeof(struct ea));
REGION_CHANGED(baddr, baddr + count);
if (area_is_selected(baddr, count))
unselect(baddr, count);
}
/* XXX: What about clear_ea? */
}
/*
* Scroll the screen 1 row.
*
* This could be accomplished with ctlr_bcopy() and ctlr_aclear(), but this
* operation is common enough to warrant a separate path.
*/
void
ctlr_scroll(void)
{
int qty = (ROWS - 1) * COLS;
Boolean obscured;
/* Make sure nothing is selected. (later this can be fixed) */
unselect(0, ROWS*COLS);
/* Synchronize pending changes prior to this. */
obscured = screen_obscured();
if (!obscured && screen_changed)
screen_disp(False);
/* Move ea_buf. */
(void) memmove(&ea_buf[0], &ea_buf[COLS],
qty * sizeof(struct ea));
/* Clear the last line. */
(void) memset((char *) &ea_buf[qty], 0, COLS * sizeof(struct ea));
/* Update the screen. */
if (obscured) {
ALL_CHANGED;
} else {
screen_scroll();
}
}
#endif /*]*/
/*
* Note that a particular region of the screen has changed.
*/
void
ctlr_changed(int bstart, int bend)
{
REGION_CHANGED(bstart, bend);
}
#if defined(X3270_ANSI) /*[*/
/*
* Swap the regular and alternate screen buffers
*/
void
ctlr_altbuffer(Boolean alt)
{
struct ea *etmp;
if (alt != is_altbuffer) {
etmp = ea_buf;
ea_buf = aea_buf;
aea_buf = etmp;
is_altbuffer = alt;
ALL_CHANGED;
unselect(0, ROWS*COLS);
/*
* There may be blinkers on the alternate screen; schedule one
* iteration just in case.
*/
blink_start();
}
}
#endif /*]*/
/*
* Set or clear the MDT on an attribute
*/
void
mdt_set(int baddr)
{
int faddr;
faddr = find_field_attribute(baddr);
if (faddr >= 0 && !(ea_buf[faddr].fa & FA_MODIFY)) {
ea_buf[faddr].fa |= FA_MODIFY;
if (appres.modified_sel)
ALL_CHANGED;
}
}
void
mdt_clear(int baddr)
{
int faddr;
faddr = find_field_attribute(baddr);
if (faddr >= 0 && (ea_buf[faddr].fa & FA_MODIFY)) {
ea_buf[faddr].fa &= ~FA_MODIFY;
if (appres.modified_sel)
ALL_CHANGED;
}
}
/*
* Support for screen-size swapping for scrolling
*/
void
ctlr_shrink(void)
{
int baddr;
for (baddr = 0; baddr < ROWS*COLS; baddr++) {
if (!ea_buf[baddr].fa)
ea_buf[baddr].cc =
visible_control? EBC_space : EBC_null;
}
ALL_CHANGED;
screen_disp(False);
}
#if defined(X3270_DBCS) /*[*/
/*
* DBCS state query.
* Returns:
* DBCS_NONE: Buffer position is SBCS.
* DBCS_LEFT: Buffer position is left half of a DBCS character.
* DBCS_RIGHT: Buffer position is right half of a DBCS character.
* DBCS_SI: Buffer position is the SI terminating a DBCS subfield (treated
* as DBCS_LEFT for wide cursor tests)
* DBCS_SB: Buffer position is an SBCS character after an SI (treated as
* DBCS_RIGHT for wide cursor tests)
*
* Takes line-wrapping into account, which probably isn't done all that well.
*/
enum dbcs_state
ctlr_dbcs_state(int baddr)
{
return dbcs? ea_buf[baddr].db: DBCS_NONE;
}
#endif /*]*/
/*
* Transaction timing. The time between sending an interrupt (PF, PA, Enter,
* Clear) and the host unlocking the keyboard is indicated on the status line
* to an accuracy of 0.1 seconds. If we don't repaint the screen before we see
* the unlock, the time should be fairly accurate.
*/
static struct timeval t_start;
static Boolean ticking = False;
static Boolean mticking = False;
static unsigned long tick_id;
static struct timeval t_want;
/* Return the difference in milliseconds between two timevals. */
static long
delta_msec(struct timeval *t1, struct timeval *t0)
{
return (t1->tv_sec - t0->tv_sec) * 1000 +
(t1->tv_usec - t0->tv_usec + 500) / 1000;
}
static void
keep_ticking(void)
{
struct timeval t1;
long msec;
do {
(void) gettimeofday(&t1, (struct timezone *) 0);
t_want.tv_sec++;
msec = delta_msec(&t_want, &t1);
} while (msec <= 0);
tick_id = AddTimeOut(msec, keep_ticking);
status_timing(&t_start, &t1);
}
void
ticking_start(Boolean anyway)
{
(void) gettimeofday(&t_start, (struct timezone *) 0);
mticking = True;
if (!toggled(SHOW_TIMING) && !anyway)
return;
status_untiming();
if (ticking)
RemoveTimeOut(tick_id);
ticking = True;
tick_id = AddTimeOut(1000, keep_ticking);
t_want = t_start;
}
static void
ticking_stop(void)
{
struct timeval t1;
(void) gettimeofday(&t1, (struct timezone *) 0);
if (mticking) {
sms_accumulate_time(&t_start, &t1);
mticking = False;
} else {
return;
}
if (!ticking)
return;
RemoveTimeOut(tick_id);
ticking = False;
status_timing(&t_start, &t1);
}
void
toggle_showTiming(struct toggle *t unused, enum toggle_type tt unused)
{
if (!toggled(SHOW_TIMING))
status_untiming();
}
/*
* No-op toggle.
*/
void
toggle_nop(struct toggle *t unused, enum toggle_type tt unused)
{
}
syntax highlighted by Code2HTML, v. 0.9.1