452 lines
13 KiB
C
452 lines
13 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2005, 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. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
/* WARNING: this module contains a mixture of ASCII and EBCDIC on S390*/
|
|
#include "mdef.h"
|
|
|
|
#include "gtm_string.h"
|
|
#include <signal.h>
|
|
#include "gtm_unistd.h"
|
|
#include "gtm_stdlib.h"
|
|
#include <wctype.h>
|
|
|
|
#include "io.h"
|
|
#include "trmdef.h"
|
|
#include "iottdef.h"
|
|
#include "iottdefsp.h"
|
|
#include "iott_edit.h"
|
|
#include "gtmio.h"
|
|
#include "eintr_wrappers.h"
|
|
#ifdef UNICODE_SUPPORTED
|
|
#include "gtm_icu_api.h"
|
|
#include "gtm_utf8.h"
|
|
#endif
|
|
#include "min_max.h"
|
|
|
|
GBLREF io_pair io_curr_device;
|
|
|
|
GBLREF int AUTO_RIGHT_MARGIN, EAT_NEWLINE_GLITCH;
|
|
GBLREF char *CURSOR_UP, *CURSOR_DOWN, *CURSOR_LEFT, *CURSOR_RIGHT;
|
|
|
|
int iott_write_raw(int fildes, void *str832, unsigned int len)
|
|
{
|
|
/* Writes len characters without considering the cursor position
|
|
* The characters are really Unicode codepoints if the current
|
|
* device is in UTF-8 mode
|
|
* Returns -1 if error or number of bytes written
|
|
*/
|
|
unsigned char *str, string[TTDEF_BUF_SZ], *outptr, *outtop;
|
|
wint_t *str32, temp32;
|
|
int written, this_write, outlen;
|
|
io_desc *io_ptr = io_curr_device.in;
|
|
d_tt_struct *tt_ptr;
|
|
boolean_t utf8_active;
|
|
|
|
if (0 == len)
|
|
return 0;
|
|
tt_ptr = (d_tt_struct *)io_ptr->dev_sp;
|
|
utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE;
|
|
if (!utf8_active)
|
|
{
|
|
str = (unsigned char *)str832;
|
|
DOWRITERL_A(fildes, str, len, written);
|
|
if (0 > written)
|
|
return -1;
|
|
#ifdef UNICODE_SUPPORTED
|
|
} else
|
|
{
|
|
str32 = (wint_t *)str832;
|
|
for (written = 0; 0 < len; )
|
|
{
|
|
for (outptr = string, outtop = (unsigned char *)(string + SIZEOF(string)); 0 < len &&
|
|
(outptr + GTM_MB_LEN_MAX) < outtop; str32++, len--)
|
|
{
|
|
temp32 = *str32; /* so argument not modified */
|
|
outptr = UTF8_WCTOMB(temp32, outptr);
|
|
}
|
|
/* either end of input or end of conversion buffer */
|
|
if (0 < (outlen = (int)((outptr - string))))
|
|
{ /* something to write */
|
|
DOWRITERL_A(fildes, string, outlen, this_write);
|
|
if (0 > this_write)
|
|
return -1;
|
|
written += this_write;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return written;
|
|
}
|
|
|
|
int write_str(void *str832, unsigned int len, unsigned int start_x, boolean_t move, boolean_t multibyte)
|
|
{
|
|
/*
|
|
writes a specified string starting from the current cursor position
|
|
and returns the cursor back to the same place.
|
|
|
|
str832 -> the string to write, may be Unicode code points
|
|
len -> the length of the string
|
|
start_x -> is the current cursor's column in the window.
|
|
move -> whether the cursor moves or not.
|
|
multibyte -> str832 is always bytes, if utf8_active then multibyte possible
|
|
|
|
returns -1 if error, 0 if successful
|
|
*/
|
|
|
|
int number_of_lines_up, number_of_chars_left, written, ret, outlen;
|
|
unsigned int cur_x;
|
|
int width = io_curr_device.in->width, cur_width, line_width, this_width;
|
|
int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes;
|
|
unsigned char *str, string[TTDEF_BUF_SZ], *outptr, *outtop, *strstart, *nextptr;
|
|
wint_t *str32, temp32;
|
|
io_desc *io_ptr = io_curr_device.in;
|
|
d_tt_struct *tt_ptr;
|
|
boolean_t utf8_active, writenewline;
|
|
|
|
assert(width);
|
|
tt_ptr = (d_tt_struct *)io_ptr->dev_sp;
|
|
utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE;
|
|
if (utf8_active && !multibyte)
|
|
str32 = (wint_t *)str832;
|
|
else
|
|
str = (unsigned char *)str832;
|
|
if (multibyte && utf8_active)
|
|
{
|
|
outptr = str;
|
|
outtop = str + len;
|
|
}
|
|
cur_x = start_x % width; /* start_x is absolute, get relative value into cur_x */
|
|
number_of_lines_up = 0;
|
|
#ifdef UNICODE_SUPPORTED
|
|
if (utf8_active)
|
|
{
|
|
cur_width = width - cur_x;
|
|
for (line_width = 0; 0 < len; )
|
|
{
|
|
writenewline = FALSE;
|
|
if (multibyte)
|
|
{ /* go through mb string one line at a time */
|
|
for (strstart = outptr ; outptr < outtop; )
|
|
{
|
|
nextptr = UTF8_MBTOWC(outptr, outtop, temp32);
|
|
GTM_IO_WCWIDTH(temp32, this_width);
|
|
if ((line_width + this_width) > cur_width)
|
|
{
|
|
writenewline = TRUE;
|
|
break;
|
|
}
|
|
line_width += this_width;
|
|
outptr = nextptr;
|
|
}
|
|
outlen = (int)(outptr - strstart);
|
|
len -= outlen;
|
|
} else
|
|
{
|
|
for (outptr = string, outtop = string + SIZEOF(string);
|
|
(0 < len) && ((outptr + GTM_MB_LEN_MAX) < outtop);
|
|
str32++, len--)
|
|
{
|
|
GTM_IO_WCWIDTH(*str32, this_width);
|
|
if ((line_width + this_width) > cur_width)
|
|
{
|
|
writenewline = TRUE;
|
|
break;
|
|
}
|
|
line_width += this_width;
|
|
temp32 = *str32; /* preserve argument */
|
|
outptr = UTF8_WCTOMB(temp32, outptr);
|
|
}
|
|
outlen =(int)(outptr - string);
|
|
strstart = string;
|
|
}
|
|
assert(!writenewline || len);
|
|
assert(line_width <= cur_width);
|
|
if (line_width >= cur_width) /* our write has positioned us to end of width. write new line */
|
|
writenewline = TRUE;
|
|
/* either end of input, end of conversion buffer, or full line */
|
|
if (0 < outlen)
|
|
{ /* something to write */
|
|
DOWRITERL_A(fildes, strstart, outlen, written);
|
|
if (0 > written)
|
|
return -1;
|
|
}
|
|
if (!writenewline)
|
|
{
|
|
if (0 < len)
|
|
continue; /* end of buffer in middle of line so no EOL */
|
|
if (0 == len)
|
|
{ /* last line is partial so no new line */
|
|
cur_x = cur_x + line_width;
|
|
break;
|
|
}
|
|
}
|
|
/* -------------------------------------------------------------------------
|
|
* for terminals that have the EAT_NEWLINE_GLITCH auto_margin doesn't work
|
|
* even though AUTO_RIGHT_MARGIN may be 1. So you have to check both
|
|
* before writing the TTEOL
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
if (!AUTO_RIGHT_MARGIN || EAT_NEWLINE_GLITCH)
|
|
{
|
|
DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret);
|
|
if (0 != ret)
|
|
return -1;
|
|
}
|
|
number_of_lines_up++;
|
|
cur_x = line_width = 0;
|
|
cur_width = width;
|
|
}
|
|
} else
|
|
{
|
|
#endif
|
|
for ( ; 0 < len; )
|
|
{
|
|
cur_width = width - cur_x;
|
|
if (cur_width)
|
|
{
|
|
if (len < cur_width)
|
|
cur_width = len;
|
|
DOWRITERL_A(fildes, str, cur_width, written);
|
|
if (0 > written)
|
|
return -1;
|
|
str += written;
|
|
len -= written;
|
|
cur_x += cur_width;
|
|
}
|
|
if ((cur_x < width) && (0 == len))
|
|
break; /* last line is partial so no new line */
|
|
/* -------------------------------------------------------------------------
|
|
* for terminals that have the EAT_NEWLINE_GLITCH auto_margin doesn't work
|
|
* even though AUTO_RIGHT_MARGIN may be 1. So you have to check both
|
|
* before writing the TTEOL
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
if (!AUTO_RIGHT_MARGIN || EAT_NEWLINE_GLITCH)
|
|
{
|
|
DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret);
|
|
if (0 != ret)
|
|
return -1;
|
|
}
|
|
number_of_lines_up++;
|
|
cur_x = 0;
|
|
cur_width = width;
|
|
}
|
|
#ifdef UNICODE_SUPPORTED
|
|
}
|
|
#endif
|
|
number_of_chars_left = cur_x - start_x;
|
|
if (!move)
|
|
{
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_UP, number_of_lines_up);
|
|
if (0 > ret)
|
|
return -1;
|
|
if (number_of_chars_left > 0)
|
|
{
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_LEFT, number_of_chars_left);
|
|
if (0 > ret)
|
|
return -1;
|
|
} else
|
|
{
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, -number_of_chars_left);
|
|
if (0 > ret)
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This function is almost the same as "write_str" except that the string that it writes is spaces. It uses "write_str" in turn */
|
|
int write_str_spaces(unsigned int len, unsigned int start_x, boolean_t move)
|
|
{
|
|
static unsigned char *space_buf = NULL;
|
|
static int buflen = 0;
|
|
int i;
|
|
|
|
if (len > buflen)
|
|
{
|
|
if (NULL != space_buf)
|
|
free(space_buf);
|
|
space_buf = (unsigned char *)malloc(len);
|
|
buflen = len;
|
|
for (i = 0; i < buflen; i++)
|
|
space_buf[i] = ' ';
|
|
}
|
|
/* Currently "write_str" treats any input string as a "wint_t" array if in utf8 mode. To avoid that we take the
|
|
* sleazy route of calling it with "multibyte" option (last parameter) set to TRUE that way it treats this as
|
|
* a byte array rather than a wint_t array.
|
|
*/
|
|
return write_str(&space_buf[0], len, start_x, move, TRUE);
|
|
}
|
|
|
|
int move_cursor_left(int col, int num_cols)
|
|
{
|
|
/* -------------------------------------------------------
|
|
* moves cursor left by num_cols columns. if col is leftmost,
|
|
* then it goes back to the end of the previous line
|
|
* returns 0 if success, != 0 if error
|
|
* -------------------------------------------------------
|
|
*/
|
|
int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes;
|
|
int ret;
|
|
|
|
if (0 == num_cols)
|
|
ret = 0;
|
|
else if (0 > num_cols)
|
|
ret = move_cursor_right(col, -num_cols);
|
|
else if (0 < col)
|
|
{
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_LEFT, MIN(col, num_cols));
|
|
num_cols -= MIN(col, num_cols);
|
|
if (num_cols)
|
|
{
|
|
DOWRITERC(fildes, CURSOR_UP, strlen(CURSOR_UP), ret);
|
|
if (0 > ret)
|
|
return -1;
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, io_curr_device.in->width - num_cols);
|
|
}
|
|
} else
|
|
{
|
|
DOWRITERC(fildes, CURSOR_UP, strlen(CURSOR_UP), ret);
|
|
if (0 > ret)
|
|
return -1;
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, io_curr_device.in->width - num_cols);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int move_cursor_right(int col, int num_cols)
|
|
{
|
|
/* -------------------------------------------------------
|
|
* moves cursor right by num_cols columns, if col is rightmost,
|
|
* then it goes to the start of the next line
|
|
* returns 0 if success, != 0 if error
|
|
* -------------------------------------------------------
|
|
*/
|
|
int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes;
|
|
int ret;
|
|
io_desc *io_ptr = io_curr_device.in;
|
|
|
|
if (0 == num_cols)
|
|
ret = 0;
|
|
else if (0 > num_cols)
|
|
ret = move_cursor_left(col, -num_cols);
|
|
else if ((io_curr_device.in->width - num_cols) > col)
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, num_cols);
|
|
else
|
|
{
|
|
DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret);
|
|
if (0 > ret)
|
|
return -1;
|
|
num_cols -= (io_curr_device.in->width - col);
|
|
if (num_cols)
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, num_cols);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int write_loop(int fildes, unsigned char *str, int num_times)
|
|
{
|
|
int i, size_required, ret;
|
|
unsigned char string[1024];
|
|
unsigned char *temp = NULL;
|
|
|
|
*string = '\0';
|
|
size_required = num_times * STRLEN((char *)str);
|
|
|
|
if (size_required > SIZEOF(string))
|
|
{
|
|
for (i = 0; i < num_times; i++)
|
|
{
|
|
DOWRITERC(fildes, str, strlen((char *)str), ret);
|
|
if (0 > ret)
|
|
return -1;
|
|
}
|
|
} else if (num_times)
|
|
{
|
|
for (i = 0; i < num_times; i++)
|
|
{
|
|
strcat((char *)string, (char *)str);
|
|
}
|
|
DOWRITERC(fildes, string, size_required, ret);
|
|
if (0 > ret)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int move_cursor(int fildes, int num_up, int num_left)
|
|
{
|
|
int ret;
|
|
|
|
if (num_up < 0)
|
|
{
|
|
ret = write_loop (fildes, (unsigned char *)CURSOR_DOWN, -num_up);
|
|
if (0 > ret)
|
|
return -1;
|
|
} else if (num_up > 0)
|
|
{
|
|
ret = write_loop (fildes, (unsigned char *)CURSOR_UP, num_up);
|
|
if (0 > ret)
|
|
return -1;
|
|
}
|
|
|
|
if (num_left < 0)
|
|
{
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, -num_left);
|
|
if (0 > ret)
|
|
return -1;
|
|
} else if (num_left > 0)
|
|
{
|
|
ret = write_loop(fildes, (unsigned char *)CURSOR_LEFT, num_left);
|
|
if (0 > ret)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* This function computes the absolute value of $X (i.e. without doing modulo the terminal device width)
|
|
* for an arbitrary index in an array of characters. This requires the width of the terminal as input as
|
|
* well as the starting display column (dx_start) for the 0th element of the character array. The final
|
|
* value that it returns does not include "dx_start" as this is how "dx_instr" and "dx_outlen" is maintained
|
|
* in the caller functions (dm_read and iott_readfl).
|
|
*
|
|
* str832 -> the array of characters, may be Unicode code points
|
|
* index -> the index of this array upto which we want to cumulative dollarx computed
|
|
* width -> the WIDTH of the terminal device
|
|
* dx_start -> the value of $X after the GT.M prompt has been displayed (in case of dm_read) and 0 (for iott_readfl).
|
|
*
|
|
*/
|
|
int compute_dx(void *str832, unsigned int index, unsigned int width, unsigned int dx_start)
|
|
{
|
|
boolean_t utf8_active;
|
|
io_desc *io_ptr = io_curr_device.in;
|
|
wint_t *str32;
|
|
int dx_ret, this_width, i;
|
|
|
|
utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE;
|
|
str32 = (wint_t *)str832;
|
|
if (utf8_active)
|
|
{
|
|
dx_ret = dx_start;
|
|
for (i = 0; i < index; i++)
|
|
{
|
|
GTM_IO_WCWIDTH(str32[i], this_width);
|
|
assert(this_width <= width); /* width cannot be less than one wide character's display width */
|
|
if (((dx_ret % width) + this_width) > width)
|
|
dx_ret = ROUND_UP(dx_ret, width); /* add $X padding for wide character in last column */
|
|
dx_ret += this_width;
|
|
}
|
|
return dx_ret - dx_start; /* before returning make sure "dx_ret" is of same dimension as "dx_instr"
|
|
* variable in "dm_read" or "iott_readfl" (the callers) */
|
|
} else
|
|
return index; /* every character is 1-display-column wide so no need to look at "str832" */
|
|
}
|