557 lines
15 KiB
C
557 lines
15 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2013 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#include <errno.h>
|
|
#include <wctype.h>
|
|
#include <wchar.h>
|
|
#include <signal.h>
|
|
#include "gtm_string.h"
|
|
|
|
#include "iotcp_select.h"
|
|
|
|
#include "io.h"
|
|
#include "iottdef.h"
|
|
#include "iott_edit.h"
|
|
#include "trmdef.h"
|
|
#include "iotimer.h"
|
|
#include "gt_timer.h"
|
|
#include "gtmio.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "send_msg.h"
|
|
#include "outofband.h"
|
|
#include "error.h"
|
|
#include "std_dev_outbndset.h"
|
|
#include "wake_alarm.h"
|
|
#ifdef UNICODE_SUPPORTED
|
|
#include "gtm_icu_api.h"
|
|
#include "gtm_utf8.h"
|
|
#endif
|
|
|
|
GBLREF volatile int4 outofband;
|
|
GBLREF boolean_t dollar_zininterrupt;
|
|
GBLREF mv_stent *mv_chain;
|
|
GBLREF stack_frame *frame_pointer;
|
|
GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn;
|
|
GBLREF io_pair io_curr_device;
|
|
GBLREF io_pair io_std_device;
|
|
GBLREF bool prin_in_dev_failure;
|
|
GBLREF int4 ctrap_action_is;
|
|
GBLREF bool out_of_time;
|
|
GBLREF boolean_t gtm_utf8_mode;
|
|
|
|
LITREF unsigned char lower_to_upper_table[];
|
|
|
|
error_def(ERR_CTRAP);
|
|
error_def(ERR_IOEOF);
|
|
error_def(ERR_NOPRINCIO);
|
|
error_def(ERR_ZINTRECURSEIO);
|
|
error_def(ERR_STACKOFLOW);
|
|
error_def(ERR_STACKCRIT);
|
|
|
|
int iott_rdone (mint *v, int4 timeout) /* timeout in seconds */
|
|
{
|
|
boolean_t ret = FALSE, timed, utf8_active, zint_restart, first_time;
|
|
unsigned char inbyte;
|
|
wint_t inchar;
|
|
#ifdef __MVS__
|
|
wint_t asc_inchar;
|
|
#endif
|
|
char dc1, dc3;
|
|
short int i;
|
|
io_desc *io_ptr;
|
|
d_tt_struct *tt_ptr;
|
|
tt_interrupt *tt_state;
|
|
TID timer_id;
|
|
int rdlen, selstat, status, utf8_more, inchar_width;
|
|
int4 msec_timeout; /* timeout in milliseconds */
|
|
uint4 mask;
|
|
int msk_in, msk_num;
|
|
unsigned char *zb_ptr, *zb_top;
|
|
unsigned char more_buf[GTM_MB_LEN_MAX + 1], *more_ptr; /* to build up multi byte for character */
|
|
fd_set input_fd;
|
|
struct timeval input_timeval;
|
|
ABS_TIME cur_time, end_time;
|
|
mv_stent *mv_zintdev;
|
|
|
|
io_ptr = io_curr_device.in;
|
|
assert (io_ptr->state == dev_open);
|
|
iott_flush(io_curr_device.out);
|
|
tt_ptr = (d_tt_struct*) io_ptr->dev_sp;
|
|
timer_id = (TID) iott_rdone;
|
|
*v = -1;
|
|
dc1 = (char) 17;
|
|
dc3 = (char) 19;
|
|
mask = tt_ptr->term_ctrl;
|
|
utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE;
|
|
zint_restart = FALSE;
|
|
if (tt_ptr->mupintr)
|
|
{ /* restore state to before job interrupt */
|
|
tt_state = &tt_ptr->tt_state_save;
|
|
if (ttwhichinvalid == tt_state->who_saved)
|
|
GTMASSERT;
|
|
if (dollar_zininterrupt)
|
|
{
|
|
tt_ptr->mupintr = FALSE;
|
|
tt_state->who_saved = ttwhichinvalid;
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO);
|
|
}
|
|
if (ttrdone != tt_state->who_saved)
|
|
GTMASSERT; /* ZINTRECURSEIO should have caught */
|
|
mv_zintdev = io_find_mvstent(io_ptr, FALSE);
|
|
if (NULL != mv_zintdev)
|
|
{
|
|
mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE;
|
|
mv_zintdev->mv_st_cont.mvs_zintdev.io_ptr = NULL;
|
|
if (mv_chain == mv_zintdev)
|
|
POP_MV_STENT(); /* pop if top of stack */
|
|
end_time = tt_state->end_time;
|
|
if (utf8_active)
|
|
{
|
|
utf8_more = tt_state->utf8_more;
|
|
more_ptr = tt_state->more_ptr;
|
|
memcpy(more_buf, tt_state->more_buf, SIZEOF(more_buf));
|
|
}
|
|
zb_ptr = tt_state->zb_ptr;
|
|
zb_top = tt_state->zb_top;
|
|
tt_state->who_saved = ttwhichinvalid;
|
|
tt_ptr->mupintr = FALSE;
|
|
zint_restart = TRUE;
|
|
}
|
|
}
|
|
if (!zint_restart)
|
|
{
|
|
utf8_more = 0;
|
|
/* ---------------------------------------------------------
|
|
* zb_ptr is used to fill-in the value of $zb as we go
|
|
* If we drop-out with error or otherwise permaturely,
|
|
* consider $zb to be null.
|
|
* ---------------------------------------------------------
|
|
*/
|
|
zb_ptr = io_ptr->dollar.zb;
|
|
zb_top = zb_ptr + SIZEOF(io_ptr->dollar.zb) - 1;
|
|
*zb_ptr = 0;
|
|
io_ptr->esc_state = START;
|
|
io_ptr->dollar.za = 0;
|
|
io_ptr->dollar.zeof = FALSE;
|
|
if (mask & TRM_NOTYPEAHD)
|
|
TCFLUSH(tt_ptr->fildes, TCIFLUSH, status);
|
|
if (mask & TRM_READSYNC)
|
|
{
|
|
DOWRITERC(tt_ptr->fildes, &dc1, 1, status);
|
|
if (0 != status)
|
|
{
|
|
io_ptr->dollar.za = 9;
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status);
|
|
}
|
|
}
|
|
}
|
|
out_of_time = FALSE;
|
|
if (timeout == NO_M_TIMEOUT)
|
|
{
|
|
timed = FALSE;
|
|
input_timeval.tv_sec = 100;
|
|
msec_timeout = NO_M_TIMEOUT;
|
|
} else
|
|
{
|
|
timed = TRUE;
|
|
input_timeval.tv_sec = timeout;
|
|
msec_timeout = timeout2msec(timeout);
|
|
if (0 == msec_timeout)
|
|
{
|
|
if (!zint_restart)
|
|
iott_mterm(io_ptr);
|
|
} else
|
|
{
|
|
sys_get_curr_time(&cur_time);
|
|
if (!zint_restart)
|
|
add_int_to_abs_time(&cur_time, msec_timeout, &end_time);
|
|
}
|
|
}
|
|
input_timeval.tv_usec = 0;
|
|
first_time = TRUE;
|
|
do
|
|
{
|
|
if (outofband)
|
|
{
|
|
if (jobinterrupt == outofband)
|
|
{ /* save state if jobinterrupt */
|
|
tt_state = &tt_ptr->tt_state_save;
|
|
tt_state->exp_length = 0;
|
|
tt_state->who_saved = ttrdone;
|
|
tt_state->end_time = end_time;
|
|
if (utf8_active)
|
|
{
|
|
tt_state->utf8_more = utf8_more;
|
|
tt_state->more_ptr = more_ptr;
|
|
memcpy(tt_state->more_buf, more_buf, SIZEOF(more_buf));
|
|
}
|
|
tt_state->zb_ptr = zb_ptr;
|
|
tt_state->zb_top = zb_top;
|
|
PUSH_MV_STENT(MVST_ZINTDEV); /* used as a flag only */
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = FALSE;
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
|
|
tt_ptr->mupintr = TRUE;
|
|
} else
|
|
{
|
|
if (timed && (0 == msec_timeout))
|
|
iott_rterm(io_ptr);
|
|
}
|
|
outofband_action(FALSE);
|
|
break;
|
|
}
|
|
if (!first_time)
|
|
{
|
|
if (timed)
|
|
{
|
|
if (0 != msec_timeout)
|
|
{
|
|
sys_get_curr_time(&cur_time);
|
|
cur_time = sub_abs_time(&end_time, &cur_time);
|
|
if (0 > cur_time.at_sec)
|
|
{
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
input_timeval.tv_sec = cur_time.at_sec;
|
|
input_timeval.tv_usec = (gtm_tv_usec_t)cur_time.at_usec;
|
|
}
|
|
} else
|
|
{ /* This is an untimed read. We had set the select timeout to be 100 seconds by default. But since
|
|
* "select" changes the timeout (in Linux) to account for the elapsed wait time, we need to reset
|
|
* the timeout to be 100 seconds (else multiple iterations of this for loop could cause it to be
|
|
* reset to 0 causing a CPU spin loop).
|
|
*/
|
|
input_timeval.tv_sec = 100;
|
|
}
|
|
} else
|
|
first_time = FALSE;
|
|
FD_ZERO(&input_fd);
|
|
FD_SET(tt_ptr->fildes, &input_fd);
|
|
assert(FD_ISSET(tt_ptr->fildes, &input_fd) != 0);
|
|
/* the checks for EINTR below are valid and should not be converted to EINTR
|
|
* wrapper macros, since the select/read is not retried on EINTR.
|
|
*/
|
|
selstat = select(tt_ptr->fildes + 1, (void *)&input_fd, (void *)NULL, (void *)NULL, &input_timeval);
|
|
if (0 > selstat)
|
|
{
|
|
if (EINTR != errno)
|
|
{
|
|
io_ptr->dollar.za = 9;
|
|
if (timed && (0 == msec_timeout))
|
|
iott_rterm(io_ptr);
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno);
|
|
break;
|
|
}
|
|
} else if (0 == selstat)
|
|
{
|
|
if (timed)
|
|
{
|
|
wake_alarm(); /* sets out_of_time to be true for zero as well as non-zero timeouts */
|
|
break;
|
|
}
|
|
continue; /* select() timeout; try again */
|
|
} else if ((rdlen = (int)(read(tt_ptr->fildes, &inbyte, 1))) == 1) /* This read is protected */
|
|
{
|
|
assert(FD_ISSET(tt_ptr->fildes, &input_fd) != 0);
|
|
|
|
/* --------------------------------------------------
|
|
* set prin_in_dev_failure to FALSE to indicate that
|
|
* input device is working now.
|
|
* --------------------------------------------------
|
|
*/
|
|
prin_in_dev_failure = FALSE;
|
|
|
|
#ifdef UNICODE_SUPPORTED
|
|
if (utf8_active)
|
|
{
|
|
if (tt_ptr->discard_lf)
|
|
{ /* saw CR last time so ignore following LF */
|
|
tt_ptr->discard_lf = FALSE;
|
|
if (NATIVE_LF == inbyte)
|
|
continue;
|
|
}
|
|
if (utf8_more)
|
|
{ /* needed extra bytes */
|
|
*more_ptr++ = inbyte;
|
|
if (--utf8_more)
|
|
continue; /* get next byte */
|
|
UTF8_MBTOWC(more_buf, more_ptr, inchar);
|
|
if (WEOF == inchar)
|
|
{ /* invalid char */
|
|
io_ptr->dollar.za = 9;
|
|
/* No data to return */
|
|
iott_readfl_badchar(NULL, NULL, 0,
|
|
(int)((more_ptr - more_buf)), more_buf, more_ptr, NULL);
|
|
utf8_badchar((int)(more_ptr - more_buf), more_buf, more_ptr, 0, NULL); /* BADCHAR */
|
|
break;
|
|
}
|
|
} else
|
|
{
|
|
more_ptr = more_buf;
|
|
if (0 < (utf8_more = UTF8_MBFOLLOW(&inbyte))) /* assignment */
|
|
{
|
|
if ((0 > utf8_more) || (GTM_MB_LEN_MAX < utf8_more))
|
|
{ /* invalid character */
|
|
io_ptr->dollar.za = 9;
|
|
*more_ptr++ = inbyte;
|
|
/* No data to return */
|
|
iott_readfl_badchar(NULL, NULL, 0, 1, more_buf, more_ptr, NULL);
|
|
utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */
|
|
break;
|
|
} else
|
|
{
|
|
*more_ptr++ = inbyte;
|
|
continue; /* get next byte */
|
|
}
|
|
} else
|
|
{ /* single byte */
|
|
*more_ptr++ = inbyte;
|
|
UTF8_MBTOWC(more_buf, more_ptr, inchar);
|
|
if (WEOF == inchar)
|
|
{ /* invalid char */
|
|
io_ptr->dollar.za = 9;
|
|
/* No data to return */
|
|
iott_readfl_badchar(NULL, NULL, 0, 1, more_buf, more_ptr, NULL);
|
|
utf8_badchar(1, more_buf, more_ptr, 0, NULL); /* ERR_BADCHAR */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (mask & TRM_CONVERT)
|
|
inchar = u_toupper(inchar);
|
|
GTM_IO_WCWIDTH(inchar, inchar_width);
|
|
} else
|
|
{
|
|
#endif
|
|
if (mask & TRM_CONVERT)
|
|
NATIVE_CVT2UPPER(inbyte, inbyte);
|
|
inchar = inbyte;
|
|
inchar_width = 1;
|
|
#ifdef UNICODE_SUPPORTED
|
|
}
|
|
#endif
|
|
|
|
GETASCII(asc_inchar, inchar);
|
|
if (INPUT_CHAR < ' ' && ((1 << INPUT_CHAR) & tt_ptr->enbld_outofbands.mask))
|
|
{
|
|
std_dev_outbndset(INPUT_CHAR);
|
|
if (timed)
|
|
{
|
|
if (0 == msec_timeout)
|
|
iott_rterm(io_ptr);
|
|
}
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_CTRAP, 1, ctrap_action_is);
|
|
ret = FALSE;
|
|
break;
|
|
}
|
|
else if ( ((mask & TRM_ESCAPE) != 0)
|
|
&& (inchar == NATIVE_ESC || io_ptr->esc_state != START))
|
|
{
|
|
*v = INPUT_CHAR;
|
|
ret = FALSE;
|
|
do
|
|
{
|
|
if (zb_ptr >= zb_top UNICODE_ONLY(|| (utf8_active && ASCII_MAX < inchar)))
|
|
{
|
|
/* -------------
|
|
* $zb overflow
|
|
* -------------
|
|
*/
|
|
|
|
io_ptr->dollar.za = 2;
|
|
break;
|
|
}
|
|
*zb_ptr++ = (unsigned char)inchar;
|
|
iott_escape(zb_ptr - 1, zb_ptr, io_ptr);
|
|
/* RESTORE_DOLLARZB(asc_inchar); */
|
|
*(zb_ptr - 1) = (unsigned char)INPUT_CHAR; /* need to store ASCII value */
|
|
if (io_ptr->esc_state == FINI)
|
|
{
|
|
ret = TRUE;
|
|
break;
|
|
}
|
|
if (io_ptr->esc_state == BADESC)
|
|
{
|
|
/* ------------------------------
|
|
* Escape sequence failed parse.
|
|
* ------------------------------
|
|
*/
|
|
|
|
io_ptr->dollar.za = 2;
|
|
break;
|
|
}
|
|
DOREADRL(tt_ptr->fildes, &inbyte, 1, rdlen);
|
|
inchar = inbyte;
|
|
GETASCII(asc_inchar, inchar);
|
|
} while (1 == rdlen);
|
|
|
|
*zb_ptr++ = 0;
|
|
|
|
if (rdlen != 1 && io_ptr->dollar.za == 0)
|
|
io_ptr->dollar.za = 9;
|
|
|
|
/* -------------------------------------------------
|
|
* End of escape sequence...do not process further.
|
|
* -------------------------------------------------
|
|
*/
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* may need to deal with terminators > ASCII_MAX and/or LS and PS if default_mask_term */
|
|
ret = TRUE;
|
|
if (!utf8_active || ASCII_MAX >= INPUT_CHAR)
|
|
{
|
|
msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4;
|
|
msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4));
|
|
} else
|
|
msk_num = msk_in = 0; /* force to not match terminator */
|
|
if ((!(msk_in & tt_ptr->mask_term.mask[msk_num])) && (!(mask & TRM_NOECHO)))
|
|
{
|
|
status = iott_write_raw(tt_ptr->fildes,
|
|
utf8_active ? (void *)&inchar : (void *)&inbyte, 1);
|
|
if (0 >= status)
|
|
{
|
|
status = errno;
|
|
io_ptr->dollar.za = 9;
|
|
if (timed)
|
|
{
|
|
if (0 == msec_timeout)
|
|
iott_rterm(io_ptr);
|
|
}
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} else if (rdlen < 0)
|
|
{
|
|
if (errno != EINTR)
|
|
{
|
|
io_ptr->dollar.za = 9;
|
|
if (timed && (0 == msec_timeout))
|
|
iott_rterm(io_ptr);
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errno);
|
|
break;
|
|
}
|
|
} else
|
|
/* ------------
|
|
* rdlen == 0
|
|
* ------------
|
|
*/
|
|
{ /* ---------------------------------------------------------
|
|
* select() says there's something to read, but
|
|
* read() found zero characters; assume connection dropped.
|
|
* ---------------------------------------------------------
|
|
*/
|
|
if (io_curr_device.in == io_std_device.in)
|
|
{
|
|
if (!prin_in_dev_failure)
|
|
prin_in_dev_failure = TRUE;
|
|
else
|
|
{
|
|
send_msg_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_NOPRINCIO);
|
|
stop_image_no_core();
|
|
}
|
|
}
|
|
if (io_ptr->dollar.zeof)
|
|
{
|
|
io_ptr->dollar.za = 9;
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_IOEOF);
|
|
} else
|
|
{
|
|
io_ptr->dollar.zeof = TRUE;
|
|
io_ptr->dollar.za = 0;
|
|
if (io_ptr->error_handler.len > 0)
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_IOEOF);
|
|
}
|
|
break;
|
|
}
|
|
} while (!out_of_time);
|
|
|
|
if (timed)
|
|
{
|
|
if (0 == msec_timeout)
|
|
iott_rterm(io_ptr);
|
|
}
|
|
|
|
if (mask & TRM_READSYNC)
|
|
{
|
|
DOWRITERC(tt_ptr->fildes, &dc3, 1, status);
|
|
if (0 != status)
|
|
{
|
|
io_ptr->dollar.za = 9;
|
|
rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status);
|
|
}
|
|
}
|
|
|
|
if (outofband && jobinterrupt != outofband)
|
|
{
|
|
io_ptr->dollar.za = 9;
|
|
return FALSE;
|
|
}
|
|
io_ptr->dollar.za = 0;
|
|
|
|
if (ret && io_ptr->esc_state != FINI)
|
|
{
|
|
*v = INPUT_CHAR;
|
|
if ((TT_EDITING & tt_ptr->ext_cap) && !((TRM_PASTHRU|TRM_NOECHO) & mask))
|
|
{ /* keep above test in sync with iott_readfl */
|
|
assert(tt_ptr->recall_buff.addr);
|
|
if (!utf8_active)
|
|
{
|
|
tt_ptr->recall_buff.addr[0] = INPUT_CHAR;
|
|
tt_ptr->recall_buff.len = 1;
|
|
}
|
|
#ifdef UNICODE_SUPPORTED
|
|
else
|
|
{
|
|
memcpy(tt_ptr->recall_buff.addr, &INPUT_CHAR, SIZEOF(INPUT_CHAR));
|
|
tt_ptr->recall_buff.len = SIZEOF(INPUT_CHAR);
|
|
}
|
|
#endif
|
|
tt_ptr->recall_width = inchar_width;
|
|
}
|
|
/* SIMPLIFY THIS! */
|
|
if (!utf8_active || ASCII_MAX >= INPUT_CHAR)
|
|
{ /* may need changes to allow terminator > MAX_ASCII and/or LS and PS if default_mask_term */
|
|
msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4;
|
|
msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4));
|
|
} else
|
|
msk_num = msk_in = 0; /* force no match to terminator */
|
|
if (msk_in & tt_ptr->mask_term.mask[msk_num])
|
|
{
|
|
*zb_ptr++ = INPUT_CHAR;
|
|
*zb_ptr++ = 0;
|
|
}
|
|
else
|
|
io_ptr->dollar.zb[0] = '\0';
|
|
if ((!(msk_in & tt_ptr->mask_term.mask[msk_num])) && (!(mask & TRM_NOECHO)))
|
|
{
|
|
if ((io_ptr->dollar.x += inchar_width) >= io_ptr->width && io_ptr->wrap)
|
|
{
|
|
++(io_ptr->dollar.y);
|
|
if (io_ptr->length)
|
|
io_ptr->dollar.y %= io_ptr->length;
|
|
io_ptr->dollar.x %= io_ptr->width;
|
|
if (io_ptr->dollar.x == 0)
|
|
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, STRLEN(NATIVE_TTEOL));
|
|
}
|
|
}
|
|
}
|
|
memcpy(io_ptr->dollar.key, io_ptr->dollar.zb, (zb_ptr - io_ptr->dollar.zb));
|
|
|
|
return ret;
|
|
}
|