/**************************************************************** * * * Copyright 2001, 2009 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 #include #include #include #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[]; 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; error_def(ERR_CTRAP); error_def(ERR_IOEOF); error_def(ERR_NOPRINCIO); error_def(ERR_ZINTRECURSEIO); error_def(ERR_STACKOFLOW); error_def(ERR_STACKCRIT); 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(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(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(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(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(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(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(VARLSTCNT(1) ERR_NOPRINCIO); stop_image_no_core(); } } if (io_ptr->dollar.zeof) { io_ptr->dollar.za = 9; rts_error(VARLSTCNT(1) ERR_IOEOF); } else { io_ptr->dollar.zeof = TRUE; io_ptr->dollar.za = 0; if (io_ptr->error_handler.len > 0) rts_error(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(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)); } } } return ret; }