fis-gtm/sr_port/iosocket_readfl.c

877 lines
34 KiB
C

/****************************************************************
* *
* Copyright 2001, 2010 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. *
* *
****************************************************************/
/* iosocket_readfl.c */
#include "mdef.h"
#include <errno.h>
#include "gtm_stdio.h"
#include "gtm_time.h"
#ifdef __MVS__
#include <sys/time.h>
#endif
#include "gtm_socket.h"
#include "gtm_inet.h"
#include "gtm_string.h"
#ifdef UNIX
#include "gtm_fcntl.h"
#include "eintr_wrappers.h"
#endif
#include "gt_timer.h"
#include "io.h"
#include "iotimer.h"
#include "iotcpdef.h"
#include "iotcproutine.h"
#include "stringpool.h"
#include "iosocketdef.h"
#include "min_max.h"
#include "outofband.h"
#include "wake_alarm.h"
#include "gtm_conv.h"
#include "gtm_utf8.h"
#include "rtnhdr.h"
#include "stack_frame.h"
#include "mv_stent.h"
#include "send_msg.h"
#include "error.h"
GBLREF stack_frame *frame_pointer;
GBLREF unsigned char *stackbase, *stacktop, *msp, *stackwarn;
GBLREF mv_stent *mv_chain;
GBLREF io_pair io_curr_device;
#ifdef UNIX
GBLREF io_pair io_std_device;
GBLREF bool prin_in_dev_failure;
#endif
GBLREF bool out_of_time;
GBLREF spdesc stringpool;
GBLREF volatile int4 outofband;
GBLREF mstr chset_names[];
GBLREF UConverter *chset_desc[];
GBLREF boolean_t dollar_zininterrupt;
GBLREF int socketus_interruptus;
GBLREF boolean_t gtm_utf8_mode;
#ifdef UNICODE_SUPPORTED
/* Maintenance of $KEY, $DEVICE and $ZB on a badchar error */
void iosocket_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr,
unsigned char *strend)
{
int tmplen, len;
unsigned char *delimend;
io_desc *iod;
d_socket_struct *dsocketptr;
iod = io_curr_device.in;
dsocketptr = (d_socket_struct *)(iod->dev_sp);
vmvalptr->str.len = datalen;
vmvalptr->str.addr = (char *)stringpool.free;
if (0 < datalen)
{ /* Return how much input we got */
if (CHSET_M != iod->ichset && CHSET_UTF8 != iod->ichset)
{
SOCKET_DEBUG(PRINTF("socrflbc: Converting UTF16xx data back to UTF8 for internal use\n"); DEBUGSOCKFLUSH);
vmvalptr->str.len = gtm_conv(chset_desc[iod->ichset], chset_desc[CHSET_UTF8], &vmvalptr->str, NULL, NULL);
vmvalptr->str.addr = (char *)stringpool.free;
}
stringpool.free += vmvalptr->str.len;
}
if (NULL != strend && NULL != delimptr)
{ /* First find the end of the delimiter (max of 4 bytes) */
if (0 == delimlen)
{
for (delimend = delimptr; GTM_MB_LEN_MAX >= delimlen && delimend < strend; ++delimend, ++delimlen)
{
if (UTF8_VALID(delimend, strend, tmplen))
break;
}
}
if (0 < delimlen)
{ /* Set $KEY and $ZB with the failing badchar */
memcpy(iod->dollar.zb, delimptr, MIN(delimlen, ESC_LEN - 1));
iod->dollar.zb[MIN(delimlen, ESC_LEN - 1)] = '\0';
memcpy(dsocketptr->dollar_key, delimptr, MIN(delimlen, DD_BUFLEN - 1));
dsocketptr->dollar_key[MIN(delimlen, DD_BUFLEN - 1)] = '\0';
}
}
len = SIZEOF(ONE_COMMA) - 1;
memcpy(dsocketptr->dollar_device, ONE_COMMA, len);
memcpy(&dsocketptr->dollar_device[len], BADCHAR_DEVICE_MSG, SIZEOF(BADCHAR_DEVICE_MSG));
}
#endif
/* VMS uses the UCX interface; should support others that emulate it */
int iosocket_readfl(mval *v, int4 width, int4 timeout)
/* width == 0 is a flag for non-fixed length read */
/* timeout in seconds */
{
int ret, byteperchar;
boolean_t timed, vari, more_data, terminator, has_delimiter, requeue_done;
boolean_t zint_restart, outofband_terminate, one_read_done, utf8_active;
int flags, len, real_errno, save_errno, fcntl_res, errlen, charlen, stp_need;
int bytes_read, orig_bytes_read, ii, max_bufflen, bufflen, chars_read, mb_len, match_delim;
io_desc *iod;
d_socket_struct *dsocketptr;
socket_struct *socketptr;
int4 msec_timeout; /* timeout in milliseconds */
TID timer_id;
ABS_TIME cur_time, end_time, time_for_read, zero;
char *errptr;
unsigned char *buffptr, *c_ptr, *c_top, *inv_beg, *buffer_start;
ssize_t status;
gtm_chset_t ichset;
mv_stent *mv_zintdev;
socket_interrupt *sockintr;
error_def(ERR_IOEOF);
error_def(ERR_TEXT);
error_def(ERR_CURRSOCKOFR);
error_def(ERR_NOSOCKETINDEV);
error_def(ERR_GETSOCKOPTERR);
error_def(ERR_SETSOCKOPTERR);
error_def(ERR_MAXSTRLEN);
error_def(ERR_BOMMISMATCH);
error_def(ERR_ZINTRECURSEIO);
error_def(ERR_STACKCRIT);
error_def(ERR_STACKOFLOW);
UNIX_ONLY(error_def(ERR_NOPRINCIO);)
assert(stringpool.free >= stringpool.base);
assert(stringpool.free <= stringpool.top);
iod = io_curr_device.in;
ichset = iod->ichset;
assert(dev_open == iod->state);
assert(gtmsocket == iod->type);
dsocketptr = (d_socket_struct *)(iod->dev_sp);
if (0 >= dsocketptr->n_socket)
{
iod->dollar.za = 9;
rts_error(VARLSTCNT(1) ERR_NOSOCKETINDEV);
return 0;
}
if (dsocketptr->n_socket <= dsocketptr->current_socket)
{
iod->dollar.za = 9;
rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket);
return 0;
}
utf8_active = NON_UNICODE_ONLY(FALSE) UNICODE_ONLY(gtm_utf8_mode ? IS_UTF_CHSET(ichset) : FALSE);
byteperchar = UNICODE_ONLY(utf8_active ? GTM_MB_LEN_MAX :) 1;
if (0 == width)
{ /* op_readfl won't do this; must be a call from iosocket_read */
vari = TRUE;
width = MAX_STRLEN;
} else
{
vari = FALSE;
width = (width < MAX_STRLEN) ? width : MAX_STRLEN;
}
/* if width is set to MAX_STRLEN, we might be overly generous (assuming every char is just one byte) we must check if byte
* length crosses the MAX_STRLEN boundary */
socketptr = dsocketptr->socket[dsocketptr->current_socket];
assert(socketptr);
sockintr = &dsocketptr->sock_save_state;
assert(sockintr);
outofband_terminate = FALSE;
one_read_done = FALSE;
zint_restart = FALSE;
/* Check if new or resumed read */
if (dsocketptr->mupintr)
{ /* We have a pending read restart of some sort */
if (sockwhich_invalid == sockintr->who_saved)
GTMASSERT; /* Interrupt should never have an invalid save state */
/* check we aren't recursing on this device */
if (dollar_zininterrupt)
rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO);
if (sockwhich_readfl != sockintr->who_saved)
GTMASSERT; /* ZINTRECURSEIO should have caught */
SOCKET_DEBUG(PRINTF("socrfl: *#*#*#*#*#*#*# Restarted interrupted read\n"); DEBUGSOCKFLUSH);
dsocketptr->mupintr = FALSE;
sockintr->who_saved = sockwhich_invalid;
mv_zintdev = io_find_mvstent(iod, FALSE);
assert(mv_zintdev);
if (mv_zintdev)
{
if (mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid)
{
bytes_read = mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len;
assert(bytes_read == sockintr->bytes_read);
if (bytes_read)
{
buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr;
zint_restart = TRUE;
}
} else
{
assert(FALSE);
SOCKET_DEBUG(PRINTF("Evidence of an interrupt, but it was invalid\n"); DEBUGSOCKFLUSH);
}
/* Done with this mv_stent. Pop it off if we can, else mark it inactive. */
if (mv_chain == mv_zintdev)
POP_MV_STENT(); /* pop if top of stack */
else
{ /* else mark it unused */
mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid = FALSE;
mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = 0;
mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = NULL;
}
} else
{
sockintr->end_time_valid = FALSE;
SOCKET_DEBUG(PRINTF("Evidence of an interrupt, but no MV_STENT\n"); DEBUGSOCKFLUSH);
}
} else
sockintr->end_time_valid = FALSE;
if (!zint_restart)
{ /* Simple path (not restart or nothing read,) no worries*/
/* compute the worst case byte requirement knowing that width is in chars */
max_bufflen = (vari) ? MAX_STRBUFF_INIT
: ((MAX_STRLEN > (width * byteperchar)) ? (width * byteperchar) : MAX_STRLEN);
ENSURE_STP_FREE_SPACE(max_bufflen);
bytes_read = 0;
chars_read = 0;
} else
{
max_bufflen = sockintr->max_bufflen;
chars_read = sockintr->chars_read;
assert(chars_read <= bytes_read);
SOCKET_DEBUG2(PRINTF("socrfl: .. mv_stent found - bytes_read: %d chars_read: %d max_bufflen: %d"
" interrupts: %d\n", bytes_read, chars_read, max_bufflen, socketus_interruptus);
DEBUGSOCKFLUSH);
SOCKET_DEBUG(PRINTF("socrfl: .. timeout: %d\n", timeout); DEBUGSOCKFLUSH);
SOCKET_DEBUG(if (sockintr->end_time_valid) PRINTF("socrfl: .. endtime: %d/%d\n", end_time.at_sec,
end_time.at_usec); DEBUGSOCKFLUSH);
SOCKET_DEBUG2(PRINTF("socrfl: .. buffer address: 0x%08lx stringpool: 0x%08lx\n",
buffer_start, stringpool.free); DEBUGSOCKFLUSH);
if (stringpool.free != (buffer_start + bytes_read)) /* BYPASSOK */
{ /* Not @ stringpool.free - must move what we have, so we need room for the whole anticipated message */
SOCKET_DEBUG2(PRINTF("socrfl: .. Stuff put on string pool after our buffer\n"); DEBUGSOCKFLUSH);
stp_need = max_bufflen;
} else
{ /* Previously read buffer piece is still last thing in stringpool, so we need room for the rest */
SOCKET_DEBUG2(PRINTF("socrfl: .. Our buffer did not move in the stringpool\n"); DEBUGSOCKFLUSH);
stp_need = max_bufflen - bytes_read;
assert(stp_need < max_bufflen);
}
if (!IS_STP_SPACE_AVAILABLE(stp_need))
{ /* need more room */
SOCKET_DEBUG2(PRINTF("socrfl: .. garbage collection done in starting after interrupt\n"); DEBUGSOCKFLUSH);
v->str.addr = (char *)buffer_start; /* Protect buffer from reclaim */
v->str.len = bytes_read;
INVOKE_STP_GCOL(max_bufflen);
buffer_start = (unsigned char *)v->str.addr;
}
if ((buffer_start + bytes_read) < stringpool.free) /* BYPASSOK */
{ /* now need to move it to the top */
assert(stp_need == max_bufflen);
memcpy(stringpool.free, buffer_start, bytes_read);
} else
{ /* it should still be just under the used space */
assert((buffer_start + bytes_read) == stringpool.free); /* BYPASSOK */
stringpool.free = buffer_start; /* backup the free pointer */
}
v->str.len = 0; /* Clear incase interrupt or error -- don't want to hold this buffer */
/* At this point, our previous buffer is sitting at stringpool.free and can be
expanded if necessary. This is what the rest of the code is expecting.
*/
}
if (iod->dollar.x && (TCP_WRITE == socketptr->lastop))
{ /* switching from write to read */
assert(!zint_restart);
if (!iod->dollar.za)
iosocket_flush(iod);
iod->dollar.x = 0;
}
socketptr->lastop = TCP_READ;
ret = TRUE;
has_delimiter = (0 < socketptr->n_delimiter);
time_for_read.at_sec = 0;
if (0 == timeout)
time_for_read.at_usec = 0;
else if (socketptr->def_moreread_timeout)
time_for_read.at_usec = socketptr->moreread_timeout * 1000;
else
time_for_read.at_usec = INITIAL_MOREREAD_TIMEOUT * 1000;
SOCKET_DEBUG(PRINTF("socrfl: moreread_timeout = %d def_moreread_timeout= %d time = %d \n",
socketptr->moreread_timeout,socketptr->def_moreread_timeout,time_for_read.at_usec); DEBUGSOCKFLUSH);
timer_id = (TID)iosocket_readfl;
out_of_time = FALSE;
if (NO_M_TIMEOUT == timeout)
{
timed = FALSE;
msec_timeout = NO_M_TIMEOUT;
assert(!sockintr->end_time_valid);
} else
{
timed = TRUE;
msec_timeout = timeout2msec(timeout);
if (msec_timeout > 0)
{ /* there is time to wait */
#ifdef UNIX
/* set blocking I/O */
FCNTL2(socketptr->sd, F_GETFL, flags);
if (flags < 0)
{
iod->dollar.za = 9;
save_errno = errno;
errptr = (char *)STRERROR(errno);
rts_error(VARLSTCNT(7) ERR_GETSOCKOPTERR, 5, LEN_AND_LIT("F_GETFL FOR NON BLOCKING I/O"),
save_errno, LEN_AND_STR(errptr));
}
FCNTL3(socketptr->sd, F_SETFL, flags & (~(O_NDELAY | O_NONBLOCK)), fcntl_res);
if (fcntl_res < 0)
{
iod->dollar.za = 9;
save_errno = errno;
errptr = (char *)STRERROR(errno);
rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR NON BLOCKING I/O"),
save_errno, LEN_AND_STR(errptr));
}
#endif
sys_get_curr_time(&cur_time);
if (!sockintr->end_time_valid)
add_int_to_abs_time(&cur_time, msec_timeout, &end_time);
else
{ /* end_time taken from restart data. Compute what msec_timeout should be so timeout timer
gets set correctly below.
*/
end_time = sockintr->end_time; /* Restore end_time for timeout */
cur_time = sub_abs_time(&end_time, &cur_time);
if (0 > cur_time.at_sec)
{
msec_timeout = -1;
out_of_time = TRUE;
} else
msec_timeout = (int4)(cur_time.at_sec * 1000 + cur_time.at_usec / 1000);
SOCKET_DEBUG(PRINTF("socrfl: Taking timeout end time from read restart data - "
"computed msec_timeout: %d\n", msec_timeout); DEBUGSOCKFLUSH);
}
if (0 < msec_timeout)
start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL);
} else
{
out_of_time = TRUE;
SOCKET_DEBUG(PRINTF("socrfl: No timeout specified, assuming out_of_time\n"));
}
}
sockintr->end_time_valid = FALSE;
dsocketptr->dollar_key[0] = '\0';
iod->dollar.zb[0] = '\0';
more_data = TRUE;
real_errno = 0;
requeue_done = FALSE;
SOCKET_DEBUG(PRINTF("socrfl: ##################### Entering read loop ######################\n");
DEBUGSOCKFLUSH);
for (status = 0; status >= 0;)
{
SOCKET_DEBUG2(PRINTF("socrfl: ********* Top of read loop - bytes_read: %d chars_read: %d "
"buffered_length: %d\n", bytes_read, chars_read, socketptr->buffered_length); DEBUGSOCKFLUSH);
SOCKET_DEBUG2(PRINTF("socrfl: ********* read-width: %d vari: %d status: %d\n", width, vari, status);
DEBUGSOCKFLUSH);
if (bytes_read >= max_bufflen)
{ /* more buffer needed. Extend the stringpool buffer by doubling the size as much as we
extended previously */
SOCKET_DEBUG2(PRINTF("socrfl: Buffer expand1 bytes_read(%d) max_bufflen(%d)\n",
bytes_read, max_bufflen); DEBUGSOCKFLUSH);
assert(vari);
max_bufflen += max_bufflen;
if (MAX_STRLEN < max_bufflen)
max_bufflen = MAX_STRLEN;
if (!IS_STP_SPACE_AVAILABLE(bytes_read + max_bufflen))
{
v->str.len = bytes_read; /* to keep the data read so far from being garbage collected */
v->str.addr = (char *)stringpool.free;
stringpool.free += v->str.len;
assert(stringpool.free <= stringpool.top);
INVOKE_STP_GCOL(max_bufflen);
memcpy(stringpool.free, v->str.addr, v->str.len);
v->str.len = 0; /* If interrupted, don't hold onto old space */
}
}
if (has_delimiter || requeue_done || (socketptr->first_read && CHSET_M != ichset))
{ /* Delimiter scanning needs one char at a time. Question is how big is a char?
For the UTF character sets, we have a similar issue (with the same solution) in that
we need to make sure the entire BOM we may have is in the buffer. If the last read
caused chars to be requeued, we have to make sure that we read in one full character
(buffer likely contains only a partial char) in order to make forward progess. If we
didn't do this, we would just pull the partial char out of the buffer, notice its incompleteness
and requeue it again and again. Forcing a full char read lets us continue past this point.
*/
requeue_done = FALSE; /* housekeeping -- We don't want to come back here for this reason
until it happens again */
SOCKET_DEBUG2(if (socketptr->first_read && CHSET_M != ichset)
PRINTF("socrfl: Prebuffering because ichset = UTF16\n");
else PRINTF("socrfl: Prebuffering because we have delimiter or requeue\n"); DEBUGSOCKFLUSH);
if (CHSET_M == iod->ichset)
bufflen = 1; /* M mode, 1 char == 1 byte */
else
{ /* We need to make sure the read buffer contains a complete UTF character and to make sure
we know how long that character is so we can read it completely. The issue is that if we
read a partial utf character, the utf checking code below will notice this and rebuffer it
so that it gets re-read on the next iteration. However, this will then re-read the same
partial character and we have a loop. We make a call here which will take a peek at the
first byte of the next character (and read it in if necessary), determine how long the
character is and verify that many characters are available in the buffer and return the
character length to us to use for bufflen.
*/
charlen = (int)iosocket_snr_utf_prebuffer(iod, socketptr, 0, &time_for_read,
(!vari || has_delimiter || 0 == chars_read));
SOCKET_DEBUG2(PRINTF("socrfl: charlen from iosocket_snr_utf_prebuffer = %d\n", charlen);
DEBUGSOCKFLUSH);
if (0 < charlen)
{ /* We know how long the next char is. If it will fit in our buffer, then it is
the correct bufflen. If it won't, our buffer is full and we need to expand.
*/
if ((max_bufflen - bytes_read) < charlen)
{
SOCKET_DEBUG2(PRINTF("socrfl: Buffer expand2 - max_bufflen(%d) "
"bytes_read(%d)\n",
max_bufflen, bytes_read); DEBUGSOCKFLUSH);
assert(vari);
max_bufflen += max_bufflen;
if (MAX_STRLEN < max_bufflen)
max_bufflen = MAX_STRLEN;
if (!IS_STP_SPACE_AVAILABLE(bytes_read + max_bufflen))
{
v->str.len = bytes_read; /* to keep the data read so far from
being garbage collected */
v->str.addr = (char *)stringpool.free;
stringpool.free += bytes_read;
assert(stringpool.free <= stringpool.top);
INVOKE_STP_GCOL(max_bufflen);
memcpy(stringpool.free, v->str.addr, v->str.len);
v->str.len = 0; /* If interrupted, don't hold onto old space */
}
}
bufflen = charlen;
} else if (0 == charlen)
{ /* No data was available or there was a timeout. We set status to -3 here
so that we end up bypassing the call to iosocket_snr below which was to
do the actual IO. No need to repeat our lack of IO issue.
*/
SOCKET_DEBUG(PRINTF("socrfl: Timeout or end of data stream\n"); DEBUGSOCKFLUSH);
status = -3; /* To differentiate from status=0 if prebuffer bypassed */
DEBUG_ONLY(bufflen = 0); /* Triggers assert in iosocket_snr if we end up there anyway */
} else
{ /* Something bad happened. Feed the error to the lower levels for proper handling */
SOCKET_DEBUG2(PRINTF("socrfl: Read error: status: %d errno: %d\n", charlen, errno);
DEBUGSOCKFLUSH);
status = charlen;
DEBUG_ONLY(bufflen = 0); /* Triggers assert in iosocket_snr if we end up there anyway */
}
}
} else
{
bufflen = max_bufflen - bytes_read;
SOCKET_DEBUG2(PRINTF("socrfl: Regular read .. bufflen = %d\n", bufflen); DEBUGSOCKFLUSH);
status = 0;
}
buffptr = (stringpool.free + bytes_read);
terminator = FALSE;
if (0 <= status)
/* An IO above in prebuffering may have failed in which case we do not want to reset status */
status = iosocket_snr(socketptr, buffptr, bufflen, 0, &time_for_read);
else
{
SOCKET_DEBUG2(PRINTF("socrfl: Data read bypassed - status: %d\n", status); DEBUGSOCKFLUSH);
}
if (0 == status || -3 == status) /* -3 status can happen on EOB from prebuffering */
{
SOCKET_DEBUG2(PRINTF("socrfl: No more data available\n"); DEBUGSOCKFLUSH);
more_data = FALSE;
status = 0; /* Consistent treatment of no more data */
} else if (0 < status)
{
if (timeout && !socketptr->def_moreread_timeout && !one_read_done)
{
one_read_done = TRUE;
SOCKET_DEBUG(PRINTF("socrfl: before moreread_timeout = %d timeout = %d \n",
socketptr->moreread_timeout,time_for_read.at_usec); DEBUGSOCKFLUSH);
time_for_read.at_usec = DEFAULT_MOREREAD_TIMEOUT * 1000;
SOCKET_DEBUG(PRINTF("socrfl: after timeout = %d \n",time_for_read.at_usec); DEBUGSOCKFLUSH);
}
SOCKET_DEBUG2(PRINTF("socrfl: Bytes read: %d\n", status); DEBUGSOCKFLUSH);
bytes_read += (int)status;
UNIX_ONLY(if (iod == io_std_device.out)
prin_in_dev_failure = FALSE;)
if (socketptr->first_read && CHSET_M != ichset) /* May have a BOM to defuse */
{
if (CHSET_UTF8 != ichset)
{ /* When the type is UTF16xx, we need to check for a BOM at the beginning of the file. If
found it will tell us which of UTF-16BE (default if no BOM) or UTF-16LE mode the data
is being written with. The call to iosocket_snr_utf_prebuffer() above should have made
sure that there were at least two chars available in the buffer if the char is a BOM.
*/
if (UTF16BE_BOM_LEN <= bytes_read) /* All UTF16xx BOM lengths are the same */
{
if (0 == memcmp(buffptr, UTF16BE_BOM, UTF16BE_BOM_LEN))
{
if (CHSET_UTF16LE == ichset)
{
iod->dollar.za = 9;
rts_error(VARLSTCNT(6) ERR_BOMMISMATCH, 4,
chset_names[CHSET_UTF16BE].len,
chset_names[CHSET_UTF16BE].addr,
chset_names[CHSET_UTF16LE].len,
chset_names[CHSET_UTF16LE].addr);
} else
{
iod->ichset = ichset = CHSET_UTF16BE;
bytes_read -= UTF16BE_BOM_LEN; /* Throw way BOM */
SOCKET_DEBUG2(PRINTF("socrfl: UTF16BE BOM detected\n");
DEBUGSOCKFLUSH);
}
} else if (0 == memcmp(buffptr, UTF16LE_BOM, UTF16LE_BOM_LEN))
{
if (CHSET_UTF16BE == ichset)
{
iod->dollar.za = 9;
rts_error(VARLSTCNT(6) ERR_BOMMISMATCH, 4,
chset_names[CHSET_UTF16LE].len,
chset_names[CHSET_UTF16LE].addr,
chset_names[CHSET_UTF16BE].len,
chset_names[CHSET_UTF16BE].addr);
} else
{
iod->ichset = ichset = CHSET_UTF16LE;
bytes_read -= UTF16BE_BOM_LEN; /* Throw away BOM */
SOCKET_DEBUG2(PRINTF("socrfl: UTF16LE BOM detected\n");
DEBUGSOCKFLUSH);
}
} else
{ /* No BOM specified. If UTF16, default BOM to UTF16BE per Unicode
standard
*/
if (CHSET_UTF16 == ichset)
{
SOCKET_DEBUG2(PRINTF("socrfl: UTF16BE BOM assumed\n");
DEBUGSOCKFLUSH);
iod->ichset = ichset = CHSET_UTF16BE;
}
}
} else
{ /* Insufficient characters to form a BOM so no BOM present. Like above, if in
UTF16 mode, default to UTF16BE per the Unicode standard.
*/
if (CHSET_UTF16 == ichset)
{
SOCKET_DEBUG2(PRINTF("socrfl: UTF16BE BOM assumed\n");
DEBUGSOCKFLUSH);
iod->ichset = ichset = CHSET_UTF16BE;
}
}
} else
{ /* Check for UTF8 BOM. If found, just eliminate it. */
if (UTF8_BOM_LEN <= bytes_read && (0 == memcmp(buffptr, UTF8_BOM, UTF8_BOM_LEN)))
{
bytes_read -= UTF8_BOM_LEN; /* Throw way BOM */
SOCKET_DEBUG2(PRINTF("socrfl: UTF8 BOM detected/ignored\n");
DEBUGSOCKFLUSH);
}
}
}
if (socketptr->first_read)
{
if (CHSET_UTF16BE == ichset || CHSET_UTF16LE == ichset)
{
get_chset_desc(&chset_names[ichset]);
if (has_delimiter)
iosocket_delim_conv(socketptr, ichset);
}
socketptr->first_read = FALSE;
}
if (bytes_read && has_delimiter)
{ /* ------- check to see if it is a delimiter -------- */
SOCKET_DEBUG2(PRINTF("socrfl: Searching for delimiter\n"); DEBUGSOCKFLUSH);
for (ii = 0; ii < socketptr->n_delimiter; ii++)
{
if (bytes_read < socketptr->idelimiter[ii].len)
continue;
if (0 == memcmp(socketptr->idelimiter[ii].addr,
stringpool.free + bytes_read - socketptr->idelimiter[ii].len,
socketptr->idelimiter[ii].len))
{
terminator = TRUE;
match_delim = ii;
memcpy(iod->dollar.zb, socketptr->idelimiter[ii].addr,
MIN(socketptr->idelimiter[ii].len, ESC_LEN - 1));
iod->dollar.zb[MIN(socketptr->idelimiter[ii].len, ESC_LEN - 1)] = '\0';
memcpy(dsocketptr->dollar_key, socketptr->idelimiter[ii].addr,
MIN(socketptr->idelimiter[ii].len, DD_BUFLEN - 1));
dsocketptr->dollar_key[MIN(socketptr->idelimiter[ii].len, DD_BUFLEN - 1)] = '\0';
break;
}
}
SOCKET_DEBUG2(
if (terminator)
PRINTF("socrfl: Delimiter found - match_delim: %d\n", match_delim);
else
PRINTF("socrfl: Delimiter not found\n");
DEBUGSOCKFLUSH;
);
}
if (!terminator)
more_data = TRUE;
} else if (EINTR == errno && !out_of_time) /* unrelated timer popped */
{
status = 0;
continue;
} else
{
real_errno = errno;
break;
}
if (bytes_read > MAX_STRLEN)
{
iod->dollar.za = 9;
rts_error(VARLSTCNT(1) ERR_MAXSTRLEN);
}
orig_bytes_read = bytes_read;
if (0 != bytes_read)
{ /* find n chars read from [buffptr, buffptr + bytes_read) */
SOCKET_DEBUG2(PRINTF("socrfl: Start char scan - c_ptr: 0x%08lx c_top: 0x%08lx\n",
buffptr, (buffptr + status)); DEBUGSOCKFLUSH);
for (c_ptr = buffptr, c_top = buffptr + status;
c_ptr < c_top && chars_read < width;
c_ptr += mb_len, chars_read++)
{
mb_len = 1; /* In case of CHSET_M */
if (!((CHSET_M == ichset) ? 1 :
(CHSET_UTF8 == ichset) ? UTF8_VALID(c_ptr, c_top, mb_len) :
(CHSET_UTF16BE == ichset) ? UTF16BE_VALID(c_ptr, c_top, mb_len) :
UTF16LE_VALID(c_ptr, c_top, mb_len)))
{ /* This char is not valid unicode but this is only an error if entire char is
in the buffer. Else we will ignore it and it will be rebuffered further down.
First, we need to find its (real) length as xx_VALID set it to one when it
was determined to be invalid.
*/
mb_len = (CHSET_M == ichset) ? 0 :
(CHSET_UTF8 == ichset) ? UTF8_MBFOLLOW(c_ptr) :
(CHSET_UTF16BE == ichset) ? UTF16BE_MBFOLLOW(c_ptr, c_top) :
UTF16LE_MBFOLLOW(c_ptr, c_top);
mb_len++; /* Account for first byte of char */
if (0 == mb_len || c_ptr + mb_len <= c_top)
{ /* The entire char is in the buffer.. badchar */
#ifdef UNICODE_SUPPORTED
if (CHSET_UTF8 == ichset)
{
iosocket_readfl_badchar(v, (int)((unsigned char *)c_ptr - stringpool.free),
0, c_ptr, c_top);
UTF8_BADCHAR(0, c_ptr, c_top, 0, NULL);
} else /* UTF16LE or UTF16BE */
{
inv_beg = c_ptr;
if ((c_ptr += 2) >= c_top)
c_ptr = c_top;
iosocket_readfl_badchar(v,
(int)((unsigned char *)inv_beg - stringpool.free),
(int)(c_ptr - inv_beg), inv_beg, c_top);
UTF8_BADCHAR((int)(c_ptr - inv_beg), inv_beg, c_top,
chset_names[ichset].len, chset_names[ichset].addr);
}
#endif
}
}
if (c_ptr + mb_len > c_top) /* Verify entire char is in buffer */
break;
}
SOCKET_DEBUG2(PRINTF("socrfl: End char scan - c_ptr: 0x%08lx c_top: 0x%08lx\n",
c_ptr, c_top); DEBUGSOCKFLUSH);
if (c_ptr < c_top) /* width size READ completed OR partial last char, push back bytes into input buffer */
{
iosocket_unsnr(socketptr, c_ptr, c_top - c_ptr);
bytes_read -= (int)(c_top - c_ptr); /* We will be re-reading these bytes */
requeue_done = TRUE; /* Force single (full) char read next time through */
SOCKET_DEBUG2(PRINTF("socrfl: Requeue of %d bytes done - adjusted bytes_read: %d\n",
(c_top - c_ptr), bytes_read); DEBUGSOCKFLUSH);
}
}
if (terminator)
{
assert(0 != bytes_read);
bytes_read -= socketptr->idelimiter[match_delim].len;
c_ptr -= socketptr->idelimiter[match_delim].len;
UNICODE_ONLY(chars_read -= socketptr->idelimiter[match_delim].char_len);
NON_UNICODE_ONLY(chars_read = bytes_read);
SOCKET_DEBUG2(PRINTF("socrfl: Terminator found - bytes_read reduced by %d bytes to %d\n",
socketptr->idelimiter[match_delim].len, bytes_read); DEBUGSOCKFLUSH);
SOCKET_DEBUG2(PRINTF("socrfl: .. c_ptr also reduced to 0x%08lx\n", c_ptr); DEBUGSOCKFLUSH);
}
/* If we read as much as we needed or if the buffer was totally full (last char or 3 might be part of an
incomplete character than can never be completed in this buffer) or if variable length, no delim with
chars available and no more data or outofband or have data including a terminator, we are then done. Note
that we are explicitly not handling jobinterrupt outofband here because it is handled above where it needs
to be done to be able to cleanly requeue any input (before delimiter procesing).
*/
if ((chars_read >= width) ||
(MAX_STRLEN <= orig_bytes_read) ||
(vari && !has_delimiter && 0 != chars_read && !more_data) ||
(status > 0 && terminator))
break;
if (0 != outofband)
{
outofband_terminate = TRUE;
break;
}
if (timed)
{
if (msec_timeout > 0)
{
sys_get_curr_time(&cur_time);
cur_time = sub_abs_time(&end_time, &cur_time);
if (0 > cur_time.at_sec)
{
out_of_time = TRUE;
cancel_timer(timer_id);
SOCKET_DEBUG(PRINTF("socrfl: Out of time detected and set\n"));
break;
}
} else if (!more_data)
break;
}
}
if (EINTR == real_errno)
status = 0; /* don't treat a <CTRL-C> or timeout as an error */
if (timed)
{
if (0 < msec_timeout)
{
#ifdef UNIX
FCNTL3(socketptr->sd, F_SETFL, flags, fcntl_res);
if (fcntl_res < 0)
{
iod->dollar.za = 9;
save_errno = errno;
errptr = (char *)STRERROR(errno);
rts_error(VARLSTCNT(7) ERR_SETSOCKOPTERR, 5, LEN_AND_LIT("F_SETFL FOR RESTORING SOCKET OPTIONS"),
save_errno, LEN_AND_STR(errptr));
}
#endif
if (out_of_time)
{
ret = FALSE;
SOCKET_DEBUG(PRINTF("socrfl: Out of time to be returned (1)\n"));
} else
cancel_timer(timer_id);
} else if ((chars_read < width) && !(has_delimiter && terminator))
{
ret = FALSE;
SOCKET_DEBUG(PRINTF("socrfl: Out of time to be returned (2)\n"));
}
}
/* If we terminated due to outofband, set up restart info. We may or may not restart (any outofband is capable of
restart) but set it up for at least the more common reasons (^C and job interrupt).
Some restart info is kept in our iodesc block, but the buffer address information is kept in an mv_stent so if
the stack is garbage collected during the interrupt we don't lose track of where our stuff is saved away.
*/
if (outofband_terminate)
{
SOCKET_DEBUG(PRINTF("socrfl: outofband interrupt received (%d) -- queueing mv_stent for read intr\n", outofband);
DEBUGSOCKFLUSH);
PUSH_MV_STENT(MVST_ZINTDEV);
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = iod;
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr = (char *)stringpool.free;
mv_chain->mv_st_cont.mvs_zintdev.curr_sp_buffer.len = bytes_read;
sockintr->who_saved = sockwhich_readfl;
if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout)
{
sockintr->end_time = end_time;
sockintr->end_time_valid = TRUE;
cancel_timer(timer_id); /* Worry about timer if/when we come back */
}
sockintr->max_bufflen = max_bufflen;
sockintr->bytes_read = bytes_read;
sockintr->chars_read = chars_read;
dsocketptr->mupintr = TRUE;
stringpool.free += bytes_read; /* Don't step on our parade in the interrupt */
socketus_interruptus++;
SOCKET_DEBUG2(PRINTF("socrfl: .. mv_stent: bytes_read: %d chars_read: %d max_bufflen: %d "
"interrupts: %d buffer_start: 0x%08lx\n",
bytes_read, chars_read, max_bufflen, socketus_interruptus, stringpool.free); DEBUGSOCKFLUSH);
SOCKET_DEBUG(if (sockintr->end_time_valid) PRINTF("socrfl: .. endtime: %d/%d timeout: %d msec_timeout: %d\n",
end_time.at_sec, end_time.at_usec, timeout, msec_timeout);
DEBUGSOCKFLUSH);
outofband_action(FALSE);
GTMASSERT; /* Should *never* return from outofband_action */
return FALSE; /* For the compiler.. */
}
if (chars_read > 0)
{ /* there's something to return */
v->str.len = INTCAST(c_ptr - stringpool.free);
v->str.addr = (char *)stringpool.free;
UNICODE_ONLY(v->str.char_len = chars_read);
assert(v->str.len == bytes_read);
SOCKET_DEBUG(PRINTF("socrfl: String to return bytelen: %d charlen: %d iod-width: %d wrap: %d\n",
v->str.len, chars_read, iod->width, iod->wrap); DEBUGSOCKFLUSH);
SOCKET_DEBUG2(PRINTF("socrfl: x: %d y: %d\n", iod->dollar.x, iod->dollar.y); DEBUGSOCKFLUSH);
if (((iod->dollar.x += chars_read) >= iod->width) && iod->wrap)
{
iod->dollar.y += (iod->dollar.x / iod->width);
if (0 != iod->length)
iod->dollar.y %= iod->length;
iod->dollar.x %= iod->width;
}
if (CHSET_M != ichset && CHSET_UTF8 != ichset)
{
SOCKET_DEBUG2(PRINTF("socrfl: Converting UTF16xx data back to UTF8 for internal use\n"); DEBUGSOCKFLUSH);
v->str.len = gtm_conv(chset_desc[ichset], chset_desc[CHSET_UTF8], &v->str, NULL, NULL);
v->str.addr = (char *)stringpool.free;
stringpool.free += v->str.len;
}
} else
{
v->str.len = 0;
v->str.addr = dsocketptr->dollar_key;
}
if (status >= 0)
{ /* no real problems */
iod->dollar.zeof = FALSE;
iod->dollar.za = 0;
memcpy(dsocketptr->dollar_device, "0", SIZEOF("0"));
} else
{ /* there's a significant problem */
SOCKET_DEBUG(PRINTF("socrfl: Error handling triggered - status: %d\n", status); DEBUGSOCKFLUSH);
if (0 == chars_read)
iod->dollar.x = 0;
iod->dollar.za = 9;
len = SIZEOF(ONE_COMMA) - 1;
memcpy(dsocketptr->dollar_device, ONE_COMMA, len);
errptr = (char *)STRERROR(real_errno);
errlen = STRLEN(errptr);
memcpy(&dsocketptr->dollar_device[len], errptr, errlen + 1); /* + 1 for null */
#ifdef UNIX
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();
}
}
#endif
if (iod->dollar.zeof || -1 == status || 0 < iod->error_handler.len)
{
iod->dollar.zeof = TRUE;
if (socketptr->ioerror)
rts_error(VARLSTCNT(6) ERR_IOEOF, 0, ERR_TEXT, 2, errlen, errptr);
} else
iod->dollar.zeof = TRUE;
}
SOCKET_DEBUG(if (!ret && out_of_time) PRINTF("socrfl: Returning from read due to timeout\n");
else PRINTF("socrfl: Returning from read with success indicator set to %d\n", ret));
SOCKET_DEBUG(fflush(stdout));
return (ret);
}