fis-gtm/sr_unix/dm_read.c

940 lines
32 KiB
C

/****************************************************************
* *
* Copyright 2001, 2011 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. *
* *
****************************************************************/
/* WARNING: this module contains a mixture of ASCII and EBCDIC on S390*/
#include "mdef.h"
#include "gtm_string.h"
#include <errno.h>
#include <wctype.h>
#include <wchar.h>
#include <signal.h>
#include "gtm_unistd.h"
#include "gtm_stdlib.h"
#include "iotcp_select.h"
#include "io.h"
#include "trmdef.h"
#include "iottdef.h"
#include "iottdefsp.h"
#include "iott_edit.h"
#include "stringpool.h"
#include "comline.h"
#include "gtmio.h"
#include "eintr_wrappers.h"
#include "cli.h"
#include "outofband.h"
#include "dm_read.h"
#include "gtm_tputs.h"
#include "op.h"
#ifdef UNICODE_SUPPORTED
#include "gtm_icu_api.h"
#include "gtm_utf8.h"
#endif
GBLREF bool prin_in_dev_failure;
GBLREF boolean_t dollar_zininterrupt, gtm_utf8_mode;
GBLREF char *CLR_EOL, *CURSOR_DOWN, *CURSOR_LEFT, *CURSOR_RIGHT, *CURSOR_UP;
GBLREF char *KEY_BACKSPACE, *KEY_DC, *KEY_DOWN, *KEY_INSERT, *KEY_LEFT, *KEY_RIGHT, *KEY_UP;
GBLREF char *KEYPAD_LOCAL, *KEYPAD_XMIT;
GBLREF int AUTO_RIGHT_MARGIN, EAT_NEWLINE_GLITCH;
GBLDEF int comline_index, recall_num;
GBLREF io_pair io_curr_device, io_std_device;
GBLREF io_desc *active_device;
GBLREF mstr *comline_base;
GBLREF mv_stent *mv_chain;
GBLREF stack_frame *frame_pointer;
GBLREF spdesc stringpool;
GBLREF unsigned char *msp, *stackbase, *stacktop, *stackwarn;
GBLREF volatile int4 outofband;
LITREF unsigned char lower_to_upper_table[];
#ifdef UNICODE_SUPPORTED
LITREF UChar32 u32_line_term[];
#endif
#define MAX_ERR_MSG_LEN 40
#define REC "rec"
#define RECALL "recall"
enum RECALL_ERR_CODE
{
NO_ERROR,
ERR_OUT_OF_BOUNDS,
ERR_NO_MATCH_STR
};
static unsigned char cr = '\r';
static unsigned char recall_error_msg[][MAX_ERR_MSG_LEN] =
{
"",
"Recall Error : Number exceeds limit",
"Recall Error : No matching string"
};
error_def(ERR_IOEOF);
error_def(ERR_STACKCRIT);
error_def(ERR_STACKOFLOW);
error_def(ERR_ZINTDIRECT);
error_def(ERR_ZINTRECURSEIO);
#define WRITE_GTM_PROMPT \
if (0 < (TREF(gtmprompt)).len) \
{ \
write_str((TREF(gtmprompt)).addr, (TREF(gtmprompt)).len, 0, TRUE, TRUE); \
if (utf8_active) \
{ /* cannot use "gtm_wcswidth" function as we also want to compute "dx_start" in the loop */ \
dx_start = 0; \
for (ptr = (unsigned char *)(TREF(gtmprompt)).addr, ptrtop = ptr + (TREF(gtmprompt)).len; \
ptr < ptrtop; ptr = ptrnext) \
{ \
ptrnext = UTF8_MBTOWC(ptr, ptrtop, codepoint); \
if (WEOF == codepoint) \
UTF8_BADCHAR(0, ptr, ptrtop, 0, NULL); \
GTM_IO_WCWIDTH(codepoint, inchar_width); \
assert(0 <= inchar_width); \
assert(inchar_width <= ioptr_width);/* width cannot be less than a wide character's display width */ \
if (((dx_start % ioptr_width) + inchar_width) > ioptr_width) \
dx_start = ROUND_UP(dx_start, ioptr_width);/* add $X padding for wide char in last column */ \
dx_start += inchar_width; \
} \
assert(0 <= dx_start); \
} else \
dx_start = (TREF(gtmprompt)).len; \
dx = dx_start % ioptr_width; \
} else \
dx = dx_start = 0;
#define MOVE_CURSOR_LEFT_ONE_CHAR(dx, instr, dx_instr, dx_start, ioptr_width) \
{ \
dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start); \
delchar_width = dx_instr - dx_prev; \
move_cursor_left(dx, delchar_width); \
dx = (dx - delchar_width + ioptr_width) % ioptr_width; \
instr--; \
dx_instr -= delchar_width; \
}
#define DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width) \
{ \
assert(instr <= outlen); \
if (instr != outlen) \
{ /* First write spaces on all the display columns that the current string occupied. \
* Then overwrite that with the new string. This way we are guaranteed all \
* display columns are clean. \
*/ \
STORE_OFF(' ', outlen); \
outlen--; \
IOTT_COMBINED_CHAR_CHECK; \
MOVE_BUFF(instr, BUFF_ADDR(instr + 1), outlen - instr); \
write_str_spaces(dx_outlen - dx_instr, dx, FALSE); \
write_str(BUFF_ADDR(instr), outlen - instr, dx, FALSE, FALSE); \
dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start); \
} \
}
void dm_read (mval *v)
{
boolean_t buffer_moved, insert_mode, terminator_seen, utf8_active, zint_restart;
# ifdef UNICODE_SUPPORTED
boolean_t matched;
char *recptr = RECALL;
# endif
char *argv[3];
char temp_str[MAX_RECALL_NUMBER_LENGTH + 1];
const char delimiter_string[] = " \t";
d_tt_struct *tt_ptr;
enum RECALL_ERR_CODE err_recall = NO_ERROR;
fd_set input_fd;
int backspace, cl, cur_cl, cur_value, delete, down, hist, histidx, index, insert_key, keypad_len, left;
int delchar_width; /* display width of deleted char */
int delta_width; /* display width change for replaced char */
int dx, dx_start; /* local dollar X, starting value */
int dx_instr, dx_outlen; /* wcwidth of string to insert point, whole string */
int dx_prev, dx_cur, dx_next;/* wcwidth of string to char BEFORE, AT and AFTER the insert point */
int inchar_width; /* display width of inchar */
int instr; /* insert point in input string */
int ioptr_width; /* display width of the IO device */
int outlen; /* total characters in line so far */
int match_length, msk_in, msk_num, num_chars_left, num_lines_above, right, selstat, status, up, utf8_more;
io_desc *io_ptr;
io_termmask mask_term;
mv_stent *mvc, *mv_zintdev;
struct timeval input_timeval;
tt_interrupt *tt_state;
uint4 mask;
unsigned int exp_length, len, length;
unsigned char *buffer_start; /* beginning of non UTF8 buffer */
unsigned char *current_ptr; /* insert next character into buffer here */
unsigned char escape_sequence[ESC_LEN];
unsigned char inbyte, *outptr, *outtop, *ptr, *ptrnext, *ptrtop;
unsigned char more_buf[GTM_MB_LEN_MAX + 1], *more_ptr; /* to build up multi byte for character */
unsigned short escape_length = 0;
wint_t *buffer_32_start, codepoint, *current_32_ptr, inchar, *ptr32;
# ifdef __MVS__
wint_t asc_inchar;
# endif
DCL_THREADGBL_ACCESS;
SETUP_THREADGBL_ACCESS;
assert(stringpool.free >= stringpool.base);
assert(stringpool.free <= stringpool.top);
if ( NULL == comline_base)
{
comline_base = (mstr *)malloc(MAX_RECALL * SIZEOF(mstr));
memset(comline_base, 0, (MAX_RECALL * SIZEOF(mstr)));
}
active_device = io_curr_device.in;
io_ptr = io_curr_device.in;
tt_ptr = (d_tt_struct *)(io_ptr->dev_sp);
assert (io_ptr->state == dev_open);
if (tt == io_curr_device.out->type)
iott_flush(io_curr_device.out);
insert_mode = !(TT_NOINSERT & tt_ptr->ext_cap);
utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE;
length = tt_ptr->in_buf_sz + ESC_LEN; /* for possible escape sequence */
exp_length = utf8_active ? (uint4)((SIZEOF(wint_t) * length) + (GTM_MB_LEN_MAX * length) + SIZEOF(gtm_int64_t)) : length;
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_ZINTDIRECT);
}
assert(length == tt_state->length);
if (dmread != tt_state->who_saved)
GTMASSERT; /* ZINTRECURSEIO should have caught */
mv_zintdev = io_find_mvstent(io_ptr, FALSE);
if (mv_zintdev && mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid)
{
buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr;
current_ptr = buffer_start;
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 */
buffer_moved = (buffer_start != tt_state->buffer_start);
if (utf8_active)
{ /* need to properly align U32 buffer */
assert(exp_length == tt_state->exp_length);
buffer_32_start = (wint_t *)ROUND_UP2((INTPTR_T)(buffer_start + (GTM_MB_LEN_MAX * length)),
SIZEOF(gtm_int64_t));
if (buffer_moved && (((unsigned char *)buffer_32_start - buffer_start)
!= ((unsigned char *)tt_state->buffer_32_start - tt_state->buffer_start)))
memmove(buffer_32_start, buffer_start + ((unsigned char *)tt_state->buffer_32_start
- tt_state->buffer_start), (SIZEOF(wint_t) * length));
current_32_ptr = buffer_32_start;
utf8_more = tt_state->utf8_more;
more_ptr = tt_state->more_ptr;
memcpy(more_buf, tt_state->more_buf, SIZEOF(more_buf));
}
instr = tt_state->instr;
outlen = tt_state->outlen;
dx = tt_state->dx;
dx_start = tt_state->dx_start;
dx_instr = tt_state->dx_instr;
dx_outlen = tt_state->dx_outlen;
index = tt_state->index;
insert_mode = tt_state->insert_mode;
cl = tt_state->cl;
escape_length = tt_state->escape_length;
memcpy(escape_sequence, tt_state->escape_sequence, ESC_LEN);
tt_state->who_saved = ttwhichinvalid;
tt_ptr->mupintr = FALSE;
zint_restart = TRUE;
}
}
if (!zint_restart)
{
ENSURE_STP_FREE_SPACE(exp_length);
buffer_start = current_ptr = stringpool.free;
if (utf8_active)
{
buffer_32_start = (wint_t *)ROUND_UP2((INTPTR_T)(stringpool.free + (GTM_MB_LEN_MAX * length)),
SIZEOF(gtm_int64_t));
current_32_ptr = buffer_32_start;
}
instr = outlen = 0;
dx_instr = dx_outlen = 0;
utf8_more = 0;
io_ptr->esc_state = START;
io_ptr->dollar.za = 0;
io_ptr->dollar.zeof = FALSE;
dx_start = (int)io_ptr->dollar.x;
index = 0;
cl = clmod(comline_index - index);
}
mask = tt_ptr->term_ctrl;
mask_term = tt_ptr->mask_term;
mask_term.mask[ESC / NUM_BITS_IN_INT4] &= ~(1 << ESC);
ioptr_width = io_ptr->width;
if (!zint_restart)
{
DOWRITE_A(tt_ptr->fildes, &cr, 1);
WRITE_GTM_PROMPT;
}
/* to turn keypad on if possible */
# ifndef __MVS__
if (!zint_restart && (NULL != KEYPAD_XMIT) && (keypad_len = STRLEN(KEYPAD_XMIT))) /* NOTE assignment */
DOWRITE(tt_ptr->fildes, KEYPAD_XMIT, keypad_len);
# endif
while (outlen < length)
{
if (outofband)
{
if (jobinterrupt == outofband)
{ /* save state if jobinterrupt */
tt_state = &tt_ptr->tt_state_save;
tt_state->who_saved = dmread;
tt_state->length = length;
tt_state->buffer_start = buffer_start;
PUSH_MV_STENT(MVST_ZINTDEV);
mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)buffer_start;
mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = exp_length;
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
if (utf8_active)
{
tt_state->exp_length = exp_length;
tt_state->buffer_32_start = buffer_32_start;
tt_state->utf8_more = utf8_more;
tt_state->more_ptr = more_ptr;
memcpy(tt_state->more_buf, more_buf, SIZEOF(more_buf));
}
if (buffer_start == stringpool.free) /* BYPASSOK */
stringpool.free += exp_length; /* reserve space */
tt_state->instr = instr;
tt_state->outlen = outlen;
tt_state->dx = dx;
tt_state->dx_start = dx_start;
tt_state->dx_instr = dx_instr;
tt_state->dx_outlen = dx_outlen;
tt_state->index = index;
tt_state->insert_mode = insert_mode;
tt_state->cl = cl;
tt_state->escape_length = escape_length;
memcpy(tt_state->escape_sequence, escape_sequence, ESC_LEN);
tt_ptr->mupintr = TRUE;
} else
instr = 0;
outofband_action(FALSE);
break;
}
FD_ZERO(&input_fd);
FD_SET(tt_ptr->fildes, &input_fd);
assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd));
/* Arbitrarily-chosen timeout value to prevent consumption of resources in tight loop when no input is available. */
input_timeval.tv_sec = 100;
input_timeval.tv_usec = 0;
/* N.B. On some Unix systems, the documentation for select() is ambiguous with respect to the first argument.
* It specifies the number of contiguously-numbered file descriptors to be tested, starting with descriptor zero.
* Thus, it should be equal to the highest-numbered file descriptor to test plus one.
* (See _UNIX_Network_Programming_ by W. Richard Stevens, Section 6.13, pp. 328-331)
*/
selstat = select(tt_ptr->fildes + 1, (void *)&input_fd, (void *)NULL, (void *)NULL, &input_timeval);
if (0 > selstat)
if (EINTR != errno)
rts_error(VARLSTCNT(1) errno);
else
continue;
else if (0 == selstat)
continue; /* timeout but still not ready for reading, try again */
/* selstat > 0; try reading something */
else if (0 > (status = (int)read(tt_ptr->fildes, &inbyte, 1)))
{ /* Error return from read(). */
if (EINTR != errno)
{ /* If error was EINTR, go to the top of the loop to check for outofband. */
tt_ptr->discard_lf = FALSE;
io_ptr->dollar.za = 9;
rts_error(VARLSTCNT(1) errno);
} else
continue;
} else if (0 == status)
{ /* 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
exit(errno);
}
tt_ptr->discard_lf = FALSE;
rts_error(VARLSTCNT(1) ERR_IOEOF);
}
else if (0 < status)
{ /* select() says it's ready to read and read() found some data */
assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd));
/* set prin_in_dev_failure to FALSE in case it was set to TRUE in the previous read which may have failed */
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;
utf8_badchar((int)(more_ptr - more_buf), more_buf, more_ptr, 0, NULL);
break;
}
} else if (0 < (utf8_more = UTF8_MBFOLLOW(&inbyte))) /* NOTE assignment */
{
if (0 > utf8_more)
{ /* invalid character */
io_ptr->dollar.za = 9;
more_buf[0] = inbyte;
utf8_badchar(1, more_buf, &more_buf[1], 0, NULL); /* ERR_BADCHAR */
break;
} else if (GTM_MB_LEN_MAX < utf8_more)
{ /* too big to be valid */
io_ptr->dollar.za = 9;
more_buf[0] = inbyte;
utf8_badchar(1, more_buf, &more_buf[1], 0, NULL); /* ERR_BADCHAR */
break;
} else
{
more_ptr = more_buf;
*more_ptr++ = inbyte;
continue; /* get next byte */
}
} else
{ /* single byte */
more_buf[0] = inbyte;
more_buf[1] = 0;
UTF8_MBTOWC(more_buf, &more_buf[1], inchar);
if (WEOF == inchar)
{ /* invalid char */
io_ptr->dollar.za = 9;
utf8_badchar(1, more_buf, &more_buf[1], 0, NULL); /* ERR_BADCHAR */
break;
}
}
if (!tt_ptr->done_1st_read)
{
tt_ptr->done_1st_read = TRUE;
if (BOM_CODEPOINT == inchar)
continue;
}
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 ((dx >= ioptr_width) && io_ptr->wrap && !(mask & TRM_NOECHO))
{
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
dx = 0;
}
terminator_seen = FALSE;
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));
if (msk_in & mask_term.mask[msk_num])
terminator_seen = TRUE;
} else if (utf8_active && tt_ptr->default_mask_term && ((INPUT_CHAR == u32_line_term[U32_LT_NL])
|| (INPUT_CHAR == u32_line_term[U32_LT_LS]) || (INPUT_CHAR == u32_line_term[U32_LT_PS])))
{ /* UTF and default terminators and Unicode terminators above ASCII_MAX */
terminator_seen = TRUE;
}
if (terminator_seen)
{ /* carriage return or other line terminator has been typed */
STORE_OFF(0, outlen);
if (utf8_active && (ASCII_CR == INPUT_CHAR))
tt_ptr->discard_lf = TRUE;
/* exceeding the maximum length exits the while loop, so it must fit here . */
# ifdef UNICODE_SUPPORTED
if (utf8_active)
{ /* recall buffer kept as UTF-8 */
matched = TRUE;
for (match_length = 0; (SIZEOF(RECALL) - 1) > match_length && outlen > match_length;
match_length++)
{
if (ASCII_MAX < GET_OFF(match_length)
|| recptr[match_length] != (char)GET_OFF(match_length))
{
matched = FALSE;
break;
}
}
if (!matched && (outlen > match_length)
&& (((SIZEOF(REC) - 1) == match_length) || (SIZEOF(RECALL) == match_length))
&& ((' ' == GET_OFF(match_length)) || ('\t' == GET_OFF(match_length))))
matched = TRUE; /* REC or RECALL then space or tab */
else if (matched)
if (((SIZEOF(RECALL) - 1) != match_length) && ((SIZEOF(REC) - 1) != match_length))
matched = FALSE; /* wrong size */
else if ((outlen > match_length)
&& (' ' != GET_OFF(match_length) && ('\t' != GET_OFF(match_length))))
matched = FALSE; /* or RECALL then not eol, space, or tab */
if (!matched)
break; /* not RECALL so end of line */
match_length++; /* get past space or tab */
if (outlen <= match_length)
argv[1] = NULL; /* nothing after RECALL */
else
argv[1] = (char *)buffer_start;
for (outptr = buffer_start ; outlen > match_length; match_length++)
{
inchar = GET_OFF(match_length);
outptr = UTF8_WCTOMB(inchar, outptr);
if ((ASCII_MAX > GET_OFF(match_length))
&& ((' ' == GET_OFF(match_length)) || ('\t' == GET_OFF(match_length))))
break;
}
*outptr = '\0';
} else
{
# endif
match_length = (uint4)strcspn((const char *)buffer_start, delimiter_string);
/* only "rec" and "recall" should be accepted */
if (((strlen(REC) == match_length) || (strlen(RECALL) == match_length))
&& (0 == strncmp((const char *)buffer_start, RECALL, match_length)))
{
strtok((char *)buffer_start, delimiter_string);
argv[1] = strtok(NULL, "");
} else
break; /* not RECALL so end of line */
# ifdef UNICODE_SUPPORTED
}
# endif
index = 0;
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
if (argv[1] == NULL)
{ /* print out all the history strings */
for (hist = recall_num; hist > 0; hist--)
{
cur_value = recall_num + 1 - hist;
temp_str[MAX_RECALL_NUMBER_LENGTH] = ' ';
for (histidx = MAX_RECALL_NUMBER_LENGTH - 1; histidx >= 0; histidx--)
{
temp_str[histidx] = '0' + cur_value % 10;
cur_value = cur_value / 10;
if (('0' == temp_str[histidx]) && (0 == cur_value))
temp_str[histidx] = ' ';
}
cur_cl = clmod(comline_index - hist);
DOWRITE_A(tt_ptr->fildes, temp_str, SIZEOF(temp_str));
write_str((unsigned char *)comline_base[cur_cl].addr,
comline_base[cur_cl].len, SIZEOF(temp_str), TRUE, TRUE);
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
}
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
WRITE_GTM_PROMPT;
instr = dx_instr = outlen = dx_outlen = 0;
} else
{
histidx = -1;
if (!cli_is_dcm(argv[1]))
{ /* Not a positive decimal number */
len = STRLEN(argv[1]);
for (hist = 1; hist <= recall_num; hist++)
{
if (0 == strncmp(comline_base[clmod(comline_index - hist)].addr, argv[1]
, len))
{
histidx = clmod(comline_index - hist);
break;
}
}
if (-1 == histidx)
err_recall = ERR_NO_MATCH_STR; /* no matching string found */
} else
{
if (ATOI(argv[1]) > recall_num)
err_recall = ERR_OUT_OF_BOUNDS; /* out of bounds error */
else
histidx = clmod(comline_index + ATOI(argv[1]) - recall_num - 1);
}
if (-1 != histidx)
{
WRITE_GTM_PROMPT;
write_str((unsigned char *)comline_base[histidx].addr,
comline_base[histidx].len, dx_start, TRUE, TRUE);
# ifdef UNICODE_SUPPORTED
if (utf8_active)
{ /* note out* variables are used for input here */
len = comline_base[histidx].len;
instr = dx_instr = 0;
outtop = (unsigned char *)comline_base[histidx].addr + len;
for (outptr = (unsigned char *)comline_base[histidx].addr;
outptr < outtop; )
{
outptr = UTF8_MBTOWC(outptr, outtop, inchar);
if (WEOF != inchar)
{
STORE_OFF(inchar, instr);
instr++;
GTM_IO_WCWIDTH(inchar, inchar_width);
dx_instr += inchar_width;
}
}
outlen = instr;
dx_outlen = dx_instr;
} else
{ /* using memcpy since areas definitely don't overlap. */
# endif
memcpy(buffer_start, comline_base[histidx].addr,
comline_base[histidx].len);
instr = outlen = comline_base[histidx].len;
dx_instr = dx_outlen = instr;
# ifdef UNICODE_SUPPORTED
}
# endif
dx = (dx_start + dx_outlen) % ioptr_width;
}
}
if (NO_ERROR != err_recall)
{
DOWRITE_A(tt_ptr->fildes, recall_error_msg[err_recall],
strlen((char *)recall_error_msg[err_recall]));
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
WRITE_GTM_PROMPT;
instr = dx_instr = outlen = dx_outlen = 0;
}
continue; /* to allow more input */
}
if ((((int)inchar == tt_ptr->ttio_struct->c_cc[VERASE])
|| ((('\0' == KEY_BACKSPACE[1]) && (inchar == KEY_BACKSPACE[0])))) && !(mask & TRM_PASTHRU))
{
if (0 < instr)
{
MOVE_CURSOR_LEFT_ONE_CHAR(dx, instr, dx_instr, dx_start, ioptr_width);
DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width);
}
} else if (NATIVE_ESC == inchar)
{
escape_sequence[escape_length++] = (unsigned char)inchar;
io_ptr->esc_state = START;
iott_escape(&escape_sequence[escape_length - 1], &escape_sequence[escape_length], io_ptr);
} else if (0 != escape_length)
{
if (utf8_active && (ASCII_MAX < inchar))
continue; /* skip invalid char in escape sequence */
escape_sequence[escape_length++] = (unsigned char)inchar;
iott_escape(&escape_sequence[escape_length - 1], &escape_sequence[escape_length], io_ptr);
} else
{
switch (inchar)
{
case EDIT_SOL:
{ /* ctrl A - start of line */
num_lines_above = (dx_instr + dx_start) / ioptr_width;
num_chars_left = dx - dx_start;
move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left);
instr = dx_instr = 0;
dx = dx_start;
break;
}
case EDIT_EOL:
{ /* ctrl E- end of line */
num_lines_above = (dx_instr + dx_start) / ioptr_width -
(dx_outlen + dx_start) / ioptr_width;
/* For some reason, a CURSOR_DOWN ("\n") seems to reposition the cursor
* at the beginning of the next line rather than maintain the vertical
* position. Therefore if we are moving down, we need to calculate
* the num_chars_left differently to accommodate this.
*/
if (0 <= num_lines_above)
num_chars_left = dx - (dx_outlen + dx_start) % ioptr_width;
else
num_chars_left = - ((dx_outlen + dx_start) % ioptr_width);
move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left);
instr = outlen;
dx_instr = dx_outlen;
dx = (dx_outlen + dx_start) % ioptr_width;
break;
}
case EDIT_LEFT:
{ /* ctrl B - left one */
if (0 != instr)
{
dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start);
inchar_width = dx_instr - dx_prev;
move_cursor_left(dx, inchar_width);
instr--;
dx = (dx - inchar_width + ioptr_width) % ioptr_width;
dx_instr -= inchar_width;
}
break;
}
case EDIT_RIGHT:
{ /* ctrl F - right one */
if (instr != outlen)
{
dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start);
inchar_width = dx_next - dx_instr;
move_cursor_right(dx, inchar_width);
instr++;
dx = (dx + inchar_width) % ioptr_width;
dx_instr += inchar_width;
}
break;
}
case EDIT_DEOL:
{ /* ctrl K - delete to end of line */
write_str_spaces(dx_outlen - dx_instr, dx, FALSE);
SET_BUFF(instr, ' ', outlen - instr);
outlen = instr;
dx_outlen = dx_instr;
break;
}
case EDIT_ERASE:
{ /* ctrl U - delete whole line */
num_lines_above = (dx_instr + dx_start) / ioptr_width;
num_chars_left = dx - dx_start;
move_cursor(tt_ptr->fildes, num_lines_above, num_chars_left);
SET_BUFF(0, ' ', outlen);
write_str_spaces(dx_outlen, dx_start, FALSE);
instr = 0;
outlen = 0;
dx = dx_start;
dx_instr = dx_outlen = 0;
break;
}
case EDIT_DELETE:
{ /* ctrl D - delete char */
if (0 == outlen)
{ /* line is empty new line and exit - Thanks to Sam Habiel */
op_wteol(1);
op_halt();
}
DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width);
break;
}
default:
{
if (instr == outlen)
{ /* at end of input */
STORE_OFF(inchar, instr);
write_str(BUFF_ADDR(instr), 1, dx, FALSE, FALSE);
} else
{
if (insert_mode)
MOVE_BUFF(instr + 1, BUFF_ADDR(instr), outlen - instr)
else
{
GTM_IO_WCWIDTH(GET_OFF(instr), delchar_width);
delta_width = inchar_width - delchar_width;
}
STORE_OFF(inchar, instr);
/* First write spaces on all the display columns that the current string
* occupied. Then overwrite that with the new string. This way we are
* guaranteed all display columns are clean. Note that this space
* overwrite is needed even in case of insert mode because due to
* differing wide-character alignments before and after the insert, it
* is possible that a column might be left empty in the post insert
* write of the new string even though it had something displayed before.
*/
write_str_spaces(dx_outlen - dx_instr, dx, FALSE);
write_str(BUFF_ADDR(instr), outlen - instr + (insert_mode ? 1 : 0),
dx, FALSE, FALSE);
}
if (insert_mode || (instr == outlen))
outlen++;
instr++;
/* Compute value of dollarx at the new cursor position */
dx_cur = compute_dx(BUFF_ADDR(0), instr, ioptr_width, dx_start);
inchar_width = dx_cur - dx_instr;
move_cursor_right(dx, inchar_width);
dx = (dx + inchar_width) % ioptr_width;
dx_instr = dx_cur;
dx_outlen = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start);
break;
}
}
/* Ensure that the actual display position of the current character matches the computed value */
assert(dx_instr == compute_dx(BUFF_ADDR(0), instr, ioptr_width, dx_start));
assert(dx_outlen == compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start));
}
}
if ((0 != escape_length) && (FINI <= io_ptr->esc_state))
{
down = strncmp((const char *)escape_sequence, KEY_DOWN, escape_length);
up = strncmp((const char *)escape_sequence, KEY_UP, escape_length);
right = strncmp((const char *)escape_sequence, KEY_RIGHT, escape_length);
left = strncmp((const char *)escape_sequence, KEY_LEFT, escape_length);
backspace = delete = insert_key = -1;
if (NULL != KEY_BACKSPACE)
backspace = strncmp((const char *)escape_sequence, KEY_BACKSPACE, escape_length);
if (NULL != KEY_DC)
delete = strncmp((const char *)escape_sequence, KEY_DC, escape_length);
if ((NULL != KEY_INSERT) && ('\0' != KEY_INSERT[0]))
insert_key = strncmp((const char *)escape_sequence, KEY_INSERT, escape_length);
memset(escape_sequence, '\0', escape_length);
escape_length = 0;
if (BADESC == io_ptr->esc_state)
{
io_ptr->esc_state = START;
break;
}
if ((0 == backspace) || (0 == delete))
{
if (0 < instr)
{
MOVE_CURSOR_LEFT_ONE_CHAR(dx, instr, dx_instr, dx_start, ioptr_width);
DEL_ONE_CHAR_AT_CURSOR(outlen, dx_outlen, dx, dx_instr, dx_start, ioptr_width);
}
}
if (0 == insert_key)
insert_mode = !insert_mode; /* toggle */
else if ((0 == up) || (0 == down))
{
DOWRITE_A(tt_ptr->fildes, &cr, 1);
WRITE_GTM_PROMPT;
gtm_tputs(CLR_EOL, 1, outc);
instr = dx_instr = outlen = dx_outlen = 0;
if (0 == up)
{
if (((MAX_RECALL + 1 != index) && (0 != (*(comline_base + cl)).len) || (0 == index)))
index++;
} else
{
assert (0 == down);
if (0 != index)
--index;
}
if ((0 < index) && (MAX_RECALL >= index))
{
cl = clmod (comline_index - index);
instr = outlen = comline_base[cl].len;
# ifdef UNICODE_SUPPORTED
if (utf8_active)
{
len = comline_base[cl].len;
instr = dx_instr = 0;
outtop = (unsigned char *)comline_base[cl].addr + len;
for (outptr = (unsigned char *)comline_base[cl].addr; outptr < outtop; )
{
outptr = UTF8_MBTOWC(outptr, outtop, inchar);
if (WEOF != inchar)
{
STORE_OFF(inchar, instr);
instr++;
}
}
outlen = instr;
dx_instr = compute_dx(BUFF_ADDR(0), outlen, ioptr_width, dx_start);
dx_outlen = dx_instr;
} else
{
# endif
if (0 != instr)
memcpy(buffer_start, comline_base[cl].addr, outlen);
dx_instr = dx_outlen = comline_base[cl].len;
# ifdef UNICODE_SUPPORTED
}
# endif
dx = (unsigned)(dx_instr + dx_start) % ioptr_width;
if (0 != instr)
write_str(BUFF_ADDR(0), instr, dx_start, TRUE, FALSE);
}
} else if (!(mask & TRM_NOECHO) && !((0 == right) && (instr == outlen)) && !((0 == left) && (0 == instr)))
{
if (0 == right)
{
dx_next = compute_dx(BUFF_ADDR(0), instr + 1, ioptr_width, dx_start);
inchar_width = dx_next - dx_instr;
move_cursor_right(dx, inchar_width);
instr++;
dx = (dx + inchar_width) % ioptr_width;
dx_instr += inchar_width;
}
if (0 == left)
{
dx_prev = compute_dx(BUFF_ADDR(0), instr - 1, ioptr_width, dx_start);
inchar_width = dx_instr - dx_prev;
move_cursor_left(dx, inchar_width);
instr--;
dx = (dx - inchar_width + ioptr_width) % ioptr_width;
dx_instr -= inchar_width;
}
}
io_ptr->esc_state = START;
}
}
/* turn keypad off */
# ifndef __MVS__
if ((NULL != KEYPAD_LOCAL) && (keypad_len = STRLEN(KEYPAD_LOCAL))) /* NOTE assignment */
DOWRITE(tt_ptr->fildes, KEYPAD_LOCAL, keypad_len);
# endif
if (outlen == length)
outlen = length - 1;
io_ptr->dollar.za = 0;
v->mvtype = MV_STR;
# ifdef UNICODE_SUPPORTED
if (utf8_active)
{
int i;
outptr = buffer_start;
outtop = ((unsigned char *)buffer_32_start);
current_32_ptr = buffer_32_start;
for (i = 0; i < outlen && outptr < outtop; i++, current_32_ptr++)
outptr = UTF8_WCTOMB(*current_32_ptr, outptr);
v->str.len = INTCAST(outptr - buffer_start);
} else
# endif
v->str.len = outlen;
v->str.addr = (char *)buffer_start;
if (0 != v->str.len)
{
cl = clmod (comline_index - 1);
if ((v->str.len != comline_base[cl].len) || (memcmp(comline_base[cl].addr, buffer_start, v->str.len)))
{
comline_base[comline_index] = v->str;
comline_index = clmod (comline_index + 1);
if (MAX_RECALL != recall_num)
recall_num ++;
}
if (buffer_start == stringpool.free) /* BYPASSOK */
stringpool.free += v->str.len; /* otherwise using space from before interrupt */
}
if (!(mask & TRM_NOECHO))
{
if ((io_ptr->dollar.x += dx_outlen) >= ioptr_width && io_ptr->wrap)
{
io_ptr->dollar.y += (io_ptr->dollar.x / ioptr_width);
if (0 != io_ptr->length)
io_ptr->dollar.y %= io_ptr->length;
io_ptr->dollar.x %= ioptr_width;
if (0 == io_ptr->dollar.x)
DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); /* BYPASSOK */
}
}
active_device = 0;
return;
}