1261 lines
44 KiB
C
1261 lines
44 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2001, 2012 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 "gtm_fcntl.h"
|
|
#include "gtm_string.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_unistd.h"
|
|
|
|
#include "io.h"
|
|
#include "iotimer.h"
|
|
#include "iormdef.h"
|
|
#include "stringpool.h"
|
|
#include "gt_timer.h"
|
|
#include "gtmio.h"
|
|
#include "have_crit.h"
|
|
#include "eintr_wrappers.h"
|
|
#include "wake_alarm.h"
|
|
#include "min_max.h"
|
|
#include "outofband.h"
|
|
#include "mv_stent.h"
|
|
#ifdef UNICODE_SUPPORTED
|
|
#include "gtm_conv.h"
|
|
#include "gtm_utf8.h"
|
|
#endif
|
|
|
|
GBLREF io_pair io_curr_device;
|
|
GBLREF spdesc stringpool;
|
|
GBLREF volatile bool out_of_time;
|
|
GBLREF boolean_t gtm_utf8_mode;
|
|
GBLREF volatile int4 outofband;
|
|
GBLREF mv_stent *mv_chain;
|
|
GBLREF boolean_t dollar_zininterrupt;
|
|
#ifdef UNICODE_SUPPORTED
|
|
LITREF UChar32 u32_line_term[];
|
|
LITREF mstr chset_names[];
|
|
GBLREF UConverter *chset_desc[];
|
|
#endif
|
|
error_def(ERR_IOEOF);
|
|
error_def(ERR_SYSCALL);
|
|
error_def(ERR_ZINTRECURSEIO);
|
|
error_def(ERR_DEVICEWRITEONLY);
|
|
|
|
#define fl_copy(a, b) (a > b ? b : a)
|
|
|
|
#define SETZACANCELTIMER \
|
|
io_ptr->dollar.za = 9; \
|
|
v->str.len = 0; \
|
|
if (timed && !out_of_time) \
|
|
cancel_timer(timer_id);
|
|
|
|
#ifdef UNICODE_SUPPORTED
|
|
/* Maintenance of $ZB on a badchar error and returning partial data (if any) */
|
|
void iorm_readfl_badchar(mval *vmvalptr, int datalen, int delimlen, unsigned char *delimptr, unsigned char *strend)
|
|
{
|
|
int tmplen, len;
|
|
unsigned char *delimend;
|
|
io_desc *iod;
|
|
d_rm_struct *rm_ptr;
|
|
|
|
assert(0 <= datalen);
|
|
iod = io_curr_device.in;
|
|
rm_ptr = (d_rm_struct *)(iod->dev_sp);
|
|
assert(NULL != rm_ptr);
|
|
vmvalptr->str.len = datalen;
|
|
vmvalptr->str.addr = (char *)stringpool.free;
|
|
if (0 < datalen)
|
|
/* Return how much input we got */
|
|
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(rm_ptr->dollar_key, delimptr, MIN(delimlen, RM_BUFLEN - 1));
|
|
rm_ptr->dollar_key[MIN(delimlen, RM_BUFLEN - 1)] = '\0';
|
|
}
|
|
}
|
|
/* set dollar_device in the output device */
|
|
len = SIZEOF(ONE_COMMA) - 1;
|
|
memcpy(rm_ptr->dollar_device, ONE_COMMA, len);
|
|
memcpy(&rm_ptr->dollar_device[len], BADCHAR_DEVICE_MSG, SIZEOF(BADCHAR_DEVICE_MSG));
|
|
}
|
|
#endif
|
|
|
|
int iorm_readfl (mval *v, int4 width, int4 timeout) /* timeout in seconds */
|
|
{
|
|
boolean_t ret, timed, utf_active, line_term_seen = FALSE, rdone = FALSE, zint_restart;
|
|
char inchar, *temp, *temp_start;
|
|
unsigned char *nextmb, *char_ptr, *char_start, *buffer_start;
|
|
int flags = 0;
|
|
int len;
|
|
int errlen, real_errno;
|
|
int fcntl_res, stp_need;
|
|
int4 msec_timeout; /* timeout in milliseconds */
|
|
int4 bytes2read, bytes_read, char_bytes_read, add_bytes, reclen;
|
|
int4 buff_len, mblen, char_count, bytes_count, tot_bytes_read, utf_tot_bytes_read;
|
|
int4 status, max_width, ltind, exp_width, from_bom;
|
|
wint_t utf_code;
|
|
char *errptr;
|
|
io_desc *io_ptr;
|
|
d_rm_struct *rm_ptr;
|
|
gtm_chset_t chset;
|
|
TID timer_id;
|
|
int fildes;
|
|
FILE *filstr;
|
|
boolean_t pipe_zero_timeout = FALSE;
|
|
boolean_t pipe_or_fifo = FALSE;
|
|
int blocked_in = TRUE;
|
|
int do_clearerr = FALSE;
|
|
int saved_lastop;
|
|
int min_bytes_to_copy;
|
|
ABS_TIME cur_time, end_time, time_for_read;
|
|
pipe_interrupt *pipeintr;
|
|
mv_stent *mv_zintdev;
|
|
unsigned int *dollarx_ptr;
|
|
unsigned int *dollary_ptr;
|
|
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
|
|
#ifdef DEBUG_PIPE
|
|
pid=getpid();
|
|
#endif
|
|
assert(stringpool.free >= stringpool.base);
|
|
assert(stringpool.free <= stringpool.top);
|
|
|
|
io_ptr = io_curr_device.in;
|
|
/* don't allow a read from a writeonly fifo */
|
|
if (((d_rm_struct *)io_ptr->dev_sp)->write_only)
|
|
rts_error(VARLSTCNT(1) ERR_DEVICEWRITEONLY);
|
|
#ifdef __MVS__
|
|
/* on zos if it is a fifo device then point to the pair.out for $X and $Y */
|
|
if (((d_rm_struct *)io_ptr->dev_sp)->fifo)
|
|
{
|
|
dollarx_ptr = &(io_ptr->pair.out->dollar.x);
|
|
dollary_ptr = &(io_ptr->pair.out->dollar.y);
|
|
} else
|
|
#endif
|
|
{
|
|
dollarx_ptr = &(io_ptr->dollar.x);
|
|
dollary_ptr = &(io_ptr->dollar.y);
|
|
}
|
|
assert (io_ptr->state == dev_open);
|
|
rm_ptr = (d_rm_struct *)(io_ptr->dev_sp);
|
|
assert(NULL != rm_ptr);
|
|
pipeintr = &rm_ptr->pipe_save_state;
|
|
if (rm_ptr->pipe || rm_ptr->fifo)
|
|
pipe_or_fifo = TRUE;
|
|
|
|
PIPE_DEBUG(PRINTF(" %d enter iorm_readfl\n",pid); DEBUGPIPEFLUSH);
|
|
/* if it is a pipe and it's the stdout returned then we need to get the read file descriptor
|
|
from rm_ptr->read_fildes and the stream pointer from rm_ptr->read_filstr */
|
|
if ((rm_ptr->pipe ZOS_ONLY(|| rm_ptr->fifo)) && (0 < rm_ptr->read_fildes))
|
|
{
|
|
assert(rm_ptr->read_filstr);
|
|
fildes = rm_ptr->read_fildes;
|
|
filstr = rm_ptr->read_filstr;
|
|
} else
|
|
{
|
|
fildes = rm_ptr->fildes;
|
|
filstr = rm_ptr->filstr;
|
|
}
|
|
|
|
utf_active = gtm_utf8_mode ? (IS_UTF_CHSET(io_ptr->ichset)) : FALSE;
|
|
/* If the last operation was a write and $X is non-zero we may have to call iorm_wteol() */
|
|
if (*dollarx_ptr && rm_ptr->lastop == RM_WRITE)
|
|
{
|
|
/* don't need to flush the pipe device for a streaming read */
|
|
/* Fixed mode read may output pad characters in iorm_wteol() for all device types */
|
|
if (!io_ptr->dollar.za && (!rm_ptr->pipe || rm_ptr->fixed))
|
|
iorm_wteol(1, io_ptr);
|
|
*dollarx_ptr = 0;
|
|
}
|
|
|
|
/* if it's a fifo and not system input and the last operation was a write and O_NONBLOCK is set then
|
|
turn if off. A write will turn it on. The default is RM_NOOP. */
|
|
if (rm_ptr->fifo && (0 != rm_ptr->fildes) && (RM_WRITE == rm_ptr->lastop))
|
|
{
|
|
flags = 0;
|
|
FCNTL2(rm_ptr->fildes, F_GETFL, flags);
|
|
if (0 > flags)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
if (flags & O_NONBLOCK)
|
|
{
|
|
FCNTL3(rm_ptr->fildes, F_SETFL, (flags & ~O_NONBLOCK), fcntl_res);
|
|
if (0 > fcntl_res)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
}
|
|
}
|
|
|
|
zint_restart = FALSE;
|
|
/* Check if new or resumed read */
|
|
if (rm_ptr->mupintr)
|
|
{ /* We have a pending read restart of some sort */
|
|
if (pipewhich_invalid == pipeintr->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 (pipewhich_readfl != pipeintr->who_saved)
|
|
GTMASSERT; /* ZINTRECURSEIO should have caught */
|
|
PIPE_DEBUG(PRINTF("piperfl: *#*#*#*#*#*#*# Restarted interrupted read\n"); DEBUGPIPEFLUSH);
|
|
mv_zintdev = io_find_mvstent(io_ptr, FALSE);
|
|
if (mv_zintdev)
|
|
{
|
|
if (mv_zintdev->mv_st_cont.mvs_zintdev.buffer_valid)
|
|
{
|
|
assert(mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.len == pipeintr->bytes_read);
|
|
buffer_start = (unsigned char *)mv_zintdev->mv_st_cont.mvs_zintdev.curr_sp_buffer.addr;
|
|
zint_restart = TRUE;
|
|
} else
|
|
{
|
|
PIPE_DEBUG(PRINTF("Evidence of an interrupt, but it was invalid\n"); DEBUGPIPEFLUSH);
|
|
assert(FALSE);
|
|
}
|
|
/* 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
|
|
{
|
|
pipeintr->end_time_valid = FALSE;
|
|
PIPE_DEBUG(PRINTF("Evidence of an interrupt, but no MV_STENT\n"); DEBUGPIPEFLUSH);
|
|
}
|
|
rm_ptr->mupintr = FALSE;
|
|
pipeintr->who_saved = pipewhich_invalid;
|
|
} else
|
|
pipeintr->end_time_valid = FALSE;
|
|
|
|
/* save the lastop for zeof test later */
|
|
saved_lastop = rm_ptr->lastop;
|
|
rm_ptr->lastop = RM_READ;
|
|
timer_id = (TID)iorm_readfl;
|
|
max_width = io_ptr->width - *dollarx_ptr;
|
|
if (0 == width)
|
|
{
|
|
width = io_ptr->width; /* called from iorm_read */
|
|
if (!utf_active || !rm_ptr->fixed)
|
|
max_width = width; /* preserve prior functionality */
|
|
} else if (-1 == width)
|
|
{
|
|
rdone = TRUE; /* called from iorm_rdone */
|
|
width = 1;
|
|
}
|
|
width = (width < max_width) ? width : max_width;
|
|
tot_bytes_read = char_bytes_read = char_count = bytes_count = 0;
|
|
ret = TRUE;
|
|
if (!zint_restart)
|
|
{ /* Simple path (not restart or nothing read,) no worries*/
|
|
/* compute the worst case byte requirement knowing that width is in chars */
|
|
/* if utf_active, need room for multi byte characters */
|
|
exp_width = utf_active ? (GTM_MB_LEN_MAX * width) : width;
|
|
ENSURE_STP_FREE_SPACE(exp_width);
|
|
temp = (char *)stringpool.free;
|
|
} else
|
|
{
|
|
exp_width = pipeintr->max_bufflen;
|
|
bytes_read = pipeintr->bytes_read;
|
|
/* some locals needed by unicode streaming mode */
|
|
if (utf_active && !rm_ptr->fixed)
|
|
{
|
|
bytes2read = pipeintr->bytes2read;
|
|
char_count = pipeintr->char_count;
|
|
bytes_count = pipeintr->bytes_count;
|
|
add_bytes = pipeintr->add_bytes;
|
|
}
|
|
|
|
PIPE_DEBUG(PRINTF("piperfl: .. mv_stent found - bytes_read: %d max_bufflen: %d"
|
|
" interrupts: %d\n", bytes_read, exp_width, TREF(pipefifo_interrupt)); DEBUGPIPEFLUSH);
|
|
PIPE_DEBUG(PRINTF("piperfl: .. timeout: %d\n", timeout); DEBUGPIPEFLUSH);
|
|
PIPE_DEBUG(if (pipeintr->end_time_valid) PRINTF("piperfl: .. endtime: %d/%d\n", end_time.at_sec,
|
|
end_time.at_usec); DEBUGPIPEFLUSH);
|
|
PIPE_DEBUG(PRINTF("piperfl: .. buffer address: 0x%08lx stringpool: 0x%08lx\n",
|
|
buffer_start, stringpool.free); DEBUGPIPEFLUSH);
|
|
PIPE_DEBUG(PRINTF("buffer_start =%s\n",buffer_start); DEBUGPIPEFLUSH);
|
|
/* If it is fixed and utf mode then we are not doing any mods affecting stringpool during the read and
|
|
don't use temp, so skip the following stringpool checks */
|
|
if (!utf_active || !rm_ptr->fixed)
|
|
{
|
|
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 */
|
|
PIPE_DEBUG(PRINTF("socrfl: .. Stuff put on string pool after our buffer\n"); DEBUGPIPEFLUSH);
|
|
stp_need = exp_width;
|
|
} else
|
|
{ /* Previously read buffer piece is still last thing in stringpool, so we need room for the rest */
|
|
PIPE_DEBUG(PRINTF("piperfl: .. Our buffer did not move in the stringpool\n"); DEBUGPIPEFLUSH);
|
|
stp_need = exp_width - bytes_read;
|
|
assert(stp_need <= exp_width);
|
|
}
|
|
if (!IS_STP_SPACE_AVAILABLE(stp_need))
|
|
{ /* need more room */
|
|
PIPE_DEBUG(PRINTF("piperfl: .. garbage collection done in starting after interrrupt\n");
|
|
DEBUGPIPEFLUSH);
|
|
v->str.addr = (char *)buffer_start; /* Protect buffer from reclaim */
|
|
v->str.len = bytes_read;
|
|
INVOKE_STP_GCOL(exp_width);
|
|
/* if v->str.len is 0 then v->str.add is ignored by garbage collection so reset it to
|
|
stringpool.free */
|
|
if (v->str.len == 0)
|
|
v->str.addr = (char *)stringpool.free;
|
|
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 == exp_width);
|
|
memcpy(stringpool.free, buffer_start, bytes_read);
|
|
} else
|
|
{ /* it should still be just under the used space */
|
|
assert(!bytes_read || ((buffer_start + bytes_read) == stringpool.free)); /* BYPASSOK */
|
|
stringpool.free = buffer_start;
|
|
}
|
|
v->str.len = 0; /* Clear in case interrupt or error -- don't want to hold this buffer */
|
|
|
|
temp = (char *)(stringpool.free + bytes_read);
|
|
tot_bytes_read = bytes_count = bytes_read;
|
|
width -= bytes_read;
|
|
}
|
|
}
|
|
|
|
if (utf_active)
|
|
bytes_read = 0;
|
|
|
|
out_of_time = FALSE;
|
|
if (timeout == NO_M_TIMEOUT)
|
|
{
|
|
timed = FALSE;
|
|
msec_timeout = NO_M_TIMEOUT;
|
|
assert(!pipeintr->end_time_valid);
|
|
} else
|
|
{
|
|
/* For timed input, only one timer will be set in this routine. The first case is for a read x:n
|
|
and the second case is potentially for the pipe device doing a read x:0. If a timer is set, the
|
|
out_of_time variable will start as FALSE. The out_of_time variable will change from FALSE
|
|
to TRUE if the timer exires prior to a read completing. For the read x:0 case for a pipe, an attempt
|
|
is made to read one character in non-blocking mode. If it succeeds then the pipe is set to
|
|
blocking mode and a timer is set. In addition, the blocked_in variable is set to TRUE to prevent
|
|
doing this a second time. If a timer is set, it is checked at the end of this routine
|
|
under the "if (timed)" clause. The timed variable is set to true for both read x:n and x:0, but
|
|
msec_timeout will be 0 for read x:0 case unless it's a pipe and has read one character and started the 1 sec
|
|
timer. */
|
|
timed = TRUE;
|
|
msec_timeout = timeout2msec(timeout);
|
|
if (msec_timeout > 0)
|
|
{
|
|
/* for the read x:n case a timer started here. It is checked in the (timed) clause
|
|
at the end of this routine and canceled if it has not expired. */
|
|
sys_get_curr_time(&cur_time);
|
|
if (!zint_restart || !pipeintr->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 = pipeintr->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);
|
|
PIPE_DEBUG(PRINTF("piperfl: Taking timeout end time from read restart data - "
|
|
"computed msec_timeout: %d\n", msec_timeout); DEBUGPIPEFLUSH);
|
|
}
|
|
PIPE_DEBUG(PRINTF("msec_timeout: %d\n", msec_timeout); DEBUGPIPEFLUSH);
|
|
if (0 < msec_timeout)
|
|
start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL);
|
|
}
|
|
else
|
|
{
|
|
/* out_of_time is set to TRUE because no timer is set for read x:0 for any device type at
|
|
this point. It will be set to FALSE for a pipe device if it has read one character as
|
|
described above and set a timer. */
|
|
out_of_time = TRUE;
|
|
FCNTL2(fildes, F_GETFL, flags);
|
|
if (0 > flags)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
FCNTL3(fildes, F_SETFL, (flags | O_NONBLOCK), fcntl_res);
|
|
if (0 > fcntl_res)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
blocked_in = FALSE;
|
|
if (rm_ptr->pipe)
|
|
pipe_zero_timeout = TRUE;
|
|
}
|
|
}
|
|
pipeintr->end_time_valid = FALSE;
|
|
errno = status = 0;
|
|
chset = io_ptr->ichset;
|
|
if (!utf_active)
|
|
{
|
|
if (rm_ptr->fixed)
|
|
{ /* This is M mode - one character is one byte.
|
|
* Note the check for EINTR below is valid and should not be converted to an EINTR
|
|
* wrapper macro, since action is taken on EINTR, not a retry.
|
|
*/
|
|
/* If it is a pipe and at least one character is read, a timer with timer_id
|
|
will be started. It is canceled later in this routine if not expired
|
|
prior to return */
|
|
DOREADRLTO2(fildes, temp, width, out_of_time, &blocked_in, rm_ptr->pipe, flags, status,
|
|
&tot_bytes_read, timer_id, &msec_timeout, pipe_zero_timeout, FALSE, pipe_or_fifo);
|
|
|
|
PIPE_DEBUG(PRINTF(" %d fixed\n", pid); DEBUGPIPEFLUSH);
|
|
|
|
if (0 > status)
|
|
{
|
|
if (pipe_or_fifo)
|
|
bytes_count = tot_bytes_read;
|
|
else
|
|
bytes_count = 0;
|
|
if (errno == EINTR && out_of_time)
|
|
status = -2;
|
|
} else
|
|
{
|
|
tot_bytes_read = bytes_count = status;
|
|
rm_ptr->file_pos += status;
|
|
}
|
|
|
|
if (zint_restart)
|
|
{
|
|
tot_bytes_read += bytes_read;
|
|
bytes_count = tot_bytes_read;
|
|
PIPE_DEBUG(PRINTF(" %d temp= %s tot_bytes_read = %d\n", pid, temp, tot_bytes_read); DEBUGPIPEFLUSH);
|
|
}
|
|
|
|
if (pipe_or_fifo && outofband)
|
|
{
|
|
PIPE_DEBUG(PRINTF(" %d fixed outofband\n", pid); DEBUGPIPEFLUSH);
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
|
|
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 = tot_bytes_read;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
|
|
pipeintr->who_saved = pipewhich_readfl;
|
|
if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout)
|
|
{
|
|
pipeintr->end_time = end_time;
|
|
pipeintr->end_time_valid = TRUE;
|
|
cancel_timer(timer_id); /* Worry about timer if/when we come back */
|
|
}
|
|
pipeintr->max_bufflen = exp_width;
|
|
pipeintr->bytes_read = tot_bytes_read;
|
|
rm_ptr->mupintr = TRUE;
|
|
stringpool.free += tot_bytes_read; /* Don't step on our parade in the interrupt */
|
|
(TREF(pipefifo_interrupt))++;
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
|
|
} else if (!rm_ptr->pipe && !rm_ptr->fifo)
|
|
{ /* rms-file device */
|
|
do
|
|
{
|
|
if (EOF != (status = getc(filstr)))
|
|
{
|
|
inchar = (unsigned char)status;
|
|
tot_bytes_read++;
|
|
rm_ptr->file_pos++;
|
|
if (inchar == NATIVE_NL)
|
|
{
|
|
line_term_seen = TRUE;
|
|
if (!rdone)
|
|
break;
|
|
}
|
|
*temp++ = inchar;
|
|
bytes_count++;
|
|
} else
|
|
{
|
|
inchar = 0;
|
|
if (feof(filstr))
|
|
{
|
|
status = 0;
|
|
clearerr(filstr);
|
|
}
|
|
else
|
|
do_clearerr = TRUE;
|
|
break;
|
|
}
|
|
} while (bytes_count < width);
|
|
} else
|
|
{ /* fifo or pipe device */
|
|
do
|
|
{
|
|
unsigned char tchar;
|
|
int tfcntl_res;
|
|
if (EOF != (status = getc(filstr)))
|
|
{
|
|
tchar = (unsigned char)status;
|
|
/* force it to process below in case character read is a 0 */
|
|
if (!status)
|
|
status = 1;
|
|
}
|
|
else if (feof(filstr))
|
|
{
|
|
status = 0;
|
|
clearerr(filstr);
|
|
}
|
|
|
|
if (0 > status)
|
|
{
|
|
do_clearerr = TRUE;
|
|
if (!rm_ptr->pipe || !timed || 0 != msec_timeout)
|
|
{
|
|
/* process for a non-pipe or r x or r x:1 or r x:0 after timer started*/
|
|
inchar = 0;
|
|
/* When a sigusr1 is sent it sets errno to EINTR */
|
|
/* if it is outofband don't ignore it - don't take the "continue" */
|
|
/* Don't ignore the out_of_time, however */
|
|
if (EINTR == errno)
|
|
{
|
|
if (out_of_time)
|
|
status = -2;
|
|
else if (0 == outofband || !pipe_or_fifo)
|
|
continue; /* Ignore interrupt if not our wakeup and not outofband */
|
|
}
|
|
}
|
|
/* if it is not an outofband signal for a pipe/fifo then break out of the loop */
|
|
if (0 == outofband || !pipe_or_fifo)
|
|
break;
|
|
} else if (status)
|
|
{
|
|
status = tchar;
|
|
inchar = (unsigned char)status;
|
|
if (pipe_zero_timeout && blocked_in == FALSE)
|
|
{
|
|
FCNTL3(fildes, F_SETFL, flags, tfcntl_res);
|
|
if (0 > tfcntl_res)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5,
|
|
LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
blocked_in = TRUE;
|
|
out_of_time = FALSE;
|
|
/* Set a timer for 1 sec so atomic read x:0 will still work
|
|
on loaded systems but timeout on incomplete reads. Any
|
|
characters read prior a timeout will be returned and
|
|
$device will be set to "0".*/
|
|
msec_timeout = timeout2msec(1);
|
|
start_timer(timer_id, msec_timeout, wake_alarm, 0, NULL);
|
|
}
|
|
tot_bytes_read++;
|
|
if (NATIVE_NL == inchar)
|
|
{
|
|
line_term_seen = TRUE;
|
|
if (!rdone)
|
|
break;
|
|
}
|
|
*temp++ = inchar;
|
|
bytes_count++;
|
|
} else
|
|
break; /* it's an EOF */
|
|
|
|
/* process outofband if set and we didn't see a line terminator or an EOF */
|
|
if (pipe_or_fifo && outofband)
|
|
{
|
|
PIPE_DEBUG(PRINTF(" %d outofband\n", pid); DEBUGPIPEFLUSH);
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
|
|
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 = tot_bytes_read;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
|
|
pipeintr->who_saved = pipewhich_readfl;
|
|
if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout)
|
|
{
|
|
pipeintr->end_time = end_time;
|
|
pipeintr->end_time_valid = TRUE;
|
|
cancel_timer(timer_id); /* Worry about timer if/when we come back */
|
|
}
|
|
pipeintr->max_bufflen = exp_width;
|
|
pipeintr->bytes_read = tot_bytes_read;
|
|
rm_ptr->mupintr = TRUE;
|
|
stringpool.free += tot_bytes_read; /* Don't step on our parade in the interrupt */
|
|
(TREF(pipefifo_interrupt))++;
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
} while (bytes_count < width);
|
|
}
|
|
} else
|
|
{ /* Unicode mode */
|
|
assert(NULL != rm_ptr->inbuf);
|
|
if (rm_ptr->fixed)
|
|
{
|
|
buff_len = (int)(rm_ptr->inbuf_top - rm_ptr->inbuf_off);
|
|
assert(buff_len < rm_ptr->recordsize);
|
|
/* zint_restart can only be set if we haven't finished filling the buffer. Let iorm_get() decide
|
|
what to do. */
|
|
if ((0 == buff_len) || zint_restart)
|
|
{ /* need to refill the buffer */
|
|
buff_len = iorm_get(io_ptr, &blocked_in, rm_ptr->pipe, flags, &tot_bytes_read,
|
|
timer_id, &msec_timeout, pipe_zero_timeout, zint_restart);
|
|
if (0 > buff_len)
|
|
{
|
|
bytes_count = 0;
|
|
if (errno == EINTR && out_of_time)
|
|
buff_len = -2;
|
|
} else if (pipe_or_fifo && outofband && (buff_len < rm_ptr->recordsize))
|
|
{
|
|
PIPE_DEBUG(PRINTF(" %d utf fixed outofband, buff_len: %d done_1st_read: %d\n", pid,
|
|
buff_len, rm_ptr->done_1st_read); DEBUGPIPEFLUSH);
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
|
|
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 = 0;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
|
|
pipeintr->who_saved = pipewhich_readfl;
|
|
if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout)
|
|
{
|
|
pipeintr->end_time = end_time;
|
|
pipeintr->end_time_valid = TRUE;
|
|
cancel_timer(timer_id); /* Worry about timer if/when we come back */
|
|
}
|
|
pipeintr->max_bufflen = exp_width;
|
|
/* since nothing read into stringpool.free set pipeintr->bytes_read to zero
|
|
as no bytes need to be copied in restart. */
|
|
pipeintr->bytes_read = 0;
|
|
rm_ptr->mupintr = TRUE;
|
|
(TREF(pipefifo_interrupt))++;
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
chset = io_ptr->ichset; /* in case UTF-16 was changed */
|
|
}
|
|
status = tot_bytes_read = buff_len; /* for EOF checking at the end */
|
|
char_ptr = rm_ptr->inbuf_off;
|
|
|
|
if (0 < buff_len)
|
|
{
|
|
for (char_count = 0; char_count < width && char_ptr < rm_ptr->inbuf_top; char_count++)
|
|
{ /* count chars and check for validity */
|
|
switch (chset)
|
|
{
|
|
case CHSET_UTF8:
|
|
if (UTF8_VALID(char_ptr, rm_ptr->inbuf_top, mblen))
|
|
{
|
|
bytes_count += mblen;
|
|
char_ptr += mblen;
|
|
} else
|
|
{
|
|
SETZACANCELTIMER;
|
|
/* nothing in temp so set size to 0 */
|
|
iorm_readfl_badchar(v, 0, mblen, char_ptr, rm_ptr->inbuf_top);
|
|
rm_ptr->inbuf_off = char_ptr + mblen; /* mark as read */
|
|
UTF8_BADCHAR(mblen, char_ptr, rm_ptr->inbuf_top,
|
|
chset_names[chset].len, chset_names[chset].addr);
|
|
}
|
|
break;
|
|
case CHSET_UTF16BE:
|
|
if (UTF16BE_VALID(char_ptr, rm_ptr->inbuf_top, mblen))
|
|
{
|
|
bytes_count += mblen;
|
|
char_ptr += mblen;
|
|
} else
|
|
{
|
|
SETZACANCELTIMER;
|
|
/* nothing in temp so set size to 0 */
|
|
iorm_readfl_badchar(v, 0, mblen, char_ptr, rm_ptr->inbuf_top);
|
|
rm_ptr->inbuf_off = char_ptr + mblen; /* mark as read */
|
|
UTF8_BADCHAR(mblen, char_ptr, rm_ptr->inbuf_top,
|
|
chset_names[chset].len, chset_names[chset].addr);
|
|
}
|
|
break;
|
|
case CHSET_UTF16LE:
|
|
if (UTF16LE_VALID(char_ptr, rm_ptr->inbuf_top, mblen))
|
|
{
|
|
bytes_count += mblen;
|
|
char_ptr += mblen;
|
|
} else
|
|
{
|
|
SETZACANCELTIMER;
|
|
/* nothing in temp so set size to 0 */
|
|
iorm_readfl_badchar(v, 0, mblen, char_ptr, rm_ptr->inbuf_top);
|
|
rm_ptr->inbuf_off = char_ptr + mblen; /* mark as read */
|
|
UTF8_BADCHAR(mblen, char_ptr, rm_ptr->inbuf_top,
|
|
chset_names[chset].len, chset_names[chset].addr);
|
|
}
|
|
break;
|
|
default:
|
|
GTMASSERT;
|
|
}
|
|
}
|
|
v->str.len = INTCAST(char_ptr - rm_ptr->inbuf_off);
|
|
UNICODE_ONLY(v->str.char_len = char_count;)
|
|
if (0 < v->str.len)
|
|
{
|
|
if (CHSET_UTF8 == chset)
|
|
{
|
|
if (temp != (char *)stringpool.free) /* BYPASSOK */
|
|
{
|
|
/* make sure enough space to store buffer */
|
|
ENSURE_STP_FREE_SPACE(GTM_MB_LEN_MAX * width);
|
|
}
|
|
memcpy(stringpool.free, rm_ptr->inbuf_off, v->str.len);
|
|
}
|
|
else
|
|
{
|
|
v->str.addr = (char *)rm_ptr->inbuf_off;
|
|
v->str.len = gtm_conv(chset_desc[chset], chset_desc[CHSET_UTF8],
|
|
&v->str, NULL, NULL);
|
|
}
|
|
v->str.addr = (char *)stringpool.free;
|
|
rm_ptr->inbuf_off += char_ptr - rm_ptr->inbuf_off;
|
|
}
|
|
}
|
|
} else
|
|
{ /* VARIABLE or STREAM */
|
|
PIPE_DEBUG(PRINTF("enter utf stream: %d inbuf_pos: %d inbuf_off: %d\n", rm_ptr->done_1st_read,
|
|
rm_ptr->inbuf_pos, rm_ptr->inbuf_off); DEBUGPIPEFLUSH);
|
|
assert(IS_UTF_CHSET(chset));
|
|
if (rm_ptr->inbuf_pos <= rm_ptr->inbuf_off)
|
|
{ /* reset buffer pointers */
|
|
if (!zint_restart)
|
|
{
|
|
rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf;
|
|
bytes2read = (CHSET_UTF8 == chset) ? 1 : 2;
|
|
}
|
|
} else
|
|
{ /* use bytes from buffer for character left over from last read */
|
|
assert(rm_ptr->done_1st_read);
|
|
if (!zint_restart)
|
|
{
|
|
bytes2read = 0; /* use bytes from buffer left over from last read */
|
|
}
|
|
bytes_read = char_bytes_read = (int)(rm_ptr->inbuf_pos - rm_ptr->inbuf_off);
|
|
if (!zint_restart)
|
|
add_bytes = bytes_read - 1; /* to satisfy asserts */
|
|
}
|
|
PIPE_DEBUG(PRINTF("1: status: %d bytes2read: %d rm_ptr->utf_start_pos: %d "
|
|
"rm_ptr->utf_tot_bytes_in_buffer: %d char_bytes_read: %d add_bytes: %d\n",
|
|
status, bytes2read,rm_ptr->utf_start_pos,rm_ptr->utf_tot_bytes_in_buffer,
|
|
char_bytes_read, add_bytes); DEBUGPIPEFLUSH);
|
|
char_start = rm_ptr->inbuf_off;
|
|
do
|
|
{
|
|
if (!rm_ptr->done_1st_read)
|
|
{
|
|
/* need to check BOM */
|
|
status = iorm_get_bom(io_ptr, &blocked_in, rm_ptr->pipe, flags, &tot_bytes_read,
|
|
timer_id, &msec_timeout, pipe_zero_timeout);
|
|
|
|
/* if we got an interrupt then the iorm_get_bom did not complete so not as much state needs
|
|
to be saved*/
|
|
|
|
if (pipe_or_fifo && outofband)
|
|
{
|
|
PIPE_DEBUG(PRINTF(" %d utf1 stream outofband\n", pid); DEBUGPIPEFLUSH);
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
|
|
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 = 0;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
|
|
pipeintr->who_saved = pipewhich_readfl;
|
|
if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout)
|
|
{
|
|
pipeintr->end_time = end_time;
|
|
pipeintr->end_time_valid = TRUE;
|
|
cancel_timer(timer_id); /* Worry about timer if/when we come back */
|
|
}
|
|
pipeintr->max_bufflen = exp_width;
|
|
/* nothing copied to stringpool.free yet */
|
|
pipeintr->bytes_read = 0;
|
|
pipeintr->bytes2read = bytes2read;
|
|
pipeintr->add_bytes = add_bytes;
|
|
pipeintr->bytes_count = bytes_count;
|
|
rm_ptr->mupintr = TRUE;
|
|
(TREF(pipefifo_interrupt))++;
|
|
PIPE_DEBUG(PRINTF(" %d utf1.1 stream outofband\n", pid); DEBUGPIPEFLUSH);
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
chset = io_ptr->ichset; /* UTF16 will have changed to UTF16BE or UTF16LE */
|
|
}
|
|
|
|
if (0 <= status && bytes2read && rm_ptr->bom_buf_cnt > rm_ptr->bom_buf_off)
|
|
{
|
|
PIPE_DEBUG(PRINTF("2: status: %d bytes2read: %d bom_buf_cnt: %d bom_buf_off: %d\n",
|
|
status, bytes2read, rm_ptr->bom_buf_cnt, rm_ptr->bom_buf_off);
|
|
DEBUGPIPEFLUSH);
|
|
from_bom = MIN((rm_ptr->bom_buf_cnt - rm_ptr->bom_buf_off), bytes2read);
|
|
memcpy(rm_ptr->inbuf_pos, &rm_ptr->bom_buf[rm_ptr->bom_buf_off], from_bom);
|
|
rm_ptr->bom_buf_off += from_bom;
|
|
rm_ptr->inbuf_pos += from_bom;
|
|
bytes2read -= from_bom; /* now in buffer */
|
|
bytes_read = from_bom;
|
|
char_bytes_read += from_bom;
|
|
rm_ptr->file_pos += from_bom; /* If there is no BOM increment file position */
|
|
status = 0;
|
|
}
|
|
|
|
|
|
if ((0 <= status && 0 < bytes2read))
|
|
{
|
|
PIPE_DEBUG(PRINTF("3: status: %d bytes2read: %d rm_ptr->utf_start_pos: %d "
|
|
"rm_ptr->utf_tot_bytes_in_buffer: %d\n",
|
|
status, bytes2read, rm_ptr->utf_start_pos,
|
|
rm_ptr->utf_tot_bytes_in_buffer); DEBUGPIPEFLUSH);
|
|
/* If it is a pipe and at least one character is read, a timer with timer_id
|
|
will be started. It is canceled later in this routine if not expired
|
|
prior to return */
|
|
if (rm_ptr->utf_start_pos == rm_ptr->utf_tot_bytes_in_buffer)
|
|
{
|
|
DEBUG_ONLY(memset(rm_ptr->utf_tmp_buffer, 0, CHUNK_SIZE));
|
|
rm_ptr->utf_start_pos = rm_ptr->utf_tot_bytes_in_buffer = 0;
|
|
/* Read CHUNK_SIZE bytes from device into the temporary buffer. By doing this
|
|
* one-byte reads can be avoided when in UTF mode.
|
|
*/
|
|
DOREADRLTO2(fildes, rm_ptr->utf_tmp_buffer, CHUNK_SIZE, out_of_time, &blocked_in,
|
|
rm_ptr->pipe, flags, status, &utf_tot_bytes_read,
|
|
timer_id, &msec_timeout, pipe_zero_timeout, pipe_or_fifo, pipe_or_fifo);
|
|
|
|
PIPE_DEBUG(PRINTF("4: read chunk status: %d utf_tot_bytes_read: %d\n",
|
|
status, utf_tot_bytes_read); DEBUGPIPEFLUSH);
|
|
/* if status is -1, then total number of bytes read will be stored
|
|
* in utf_tot_bytes_read. */
|
|
/* if chunk read returned some bytes then ignore outofband. We won't try a
|
|
read again until bytes are processed*/
|
|
if (pipe_or_fifo && outofband && (0 >= status))
|
|
{
|
|
PIPE_DEBUG(PRINTF(" %d utf2 stream outofband\n", pid); DEBUGPIPEFLUSH);
|
|
PUSH_MV_STENT(MVST_ZINTDEV);
|
|
mv_chain->mv_st_cont.mvs_zintdev.io_ptr = io_ptr;
|
|
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_count;
|
|
mv_chain->mv_st_cont.mvs_zintdev.buffer_valid = TRUE;
|
|
pipeintr->who_saved = pipewhich_readfl;
|
|
if (0 < msec_timeout && NO_M_TIMEOUT != msec_timeout)
|
|
{
|
|
pipeintr->end_time = end_time;
|
|
pipeintr->end_time_valid = TRUE;
|
|
/* Worry about timer if/when we come back */
|
|
cancel_timer(timer_id);
|
|
}
|
|
pipeintr->max_bufflen = exp_width;
|
|
/* streaming mode uses bytes_count to show how many bytes are in *temp,
|
|
but the interrupt re-entrant code uses bytes_read */
|
|
pipeintr->bytes_read = bytes_count;
|
|
pipeintr->bytes2read = bytes2read;
|
|
pipeintr->char_count = char_count;
|
|
pipeintr->add_bytes = add_bytes;
|
|
pipeintr->bytes_count = bytes_count;
|
|
PIPE_DEBUG(PRINTF("utf2 stream outofband "
|
|
"char_bytes_read %d add_bytes %d bytes_count %d\n",
|
|
char_bytes_read, add_bytes, bytes_count); DEBUGPIPEFLUSH);
|
|
rm_ptr->mupintr = TRUE;
|
|
/* Don't step on our parade in the interrupt */
|
|
stringpool.free += bytes_count;
|
|
(TREF(pipefifo_interrupt))++;
|
|
outofband_action(FALSE);
|
|
GTMASSERT; /* Should *never* return from outofband_action */
|
|
return FALSE; /* For the compiler.. */
|
|
}
|
|
|
|
if (-1 == status)
|
|
{
|
|
rm_ptr->utf_tot_bytes_in_buffer = utf_tot_bytes_read;
|
|
tot_bytes_read = utf_tot_bytes_read;
|
|
if (!rm_ptr->pipe && !rm_ptr->fifo)
|
|
status = utf_tot_bytes_read;
|
|
}
|
|
else
|
|
rm_ptr->utf_tot_bytes_in_buffer = status;
|
|
|
|
} else if (pipe_zero_timeout)
|
|
out_of_time = FALSE; /* reset out_of_time for pipe as no actual read is done */
|
|
|
|
|
|
if (0 <= rm_ptr->utf_tot_bytes_in_buffer)
|
|
{
|
|
min_bytes_to_copy = MIN(bytes2read,
|
|
(rm_ptr->utf_tot_bytes_in_buffer - rm_ptr->utf_start_pos));
|
|
assert(0 <= min_bytes_to_copy);
|
|
assert(CHUNK_SIZE >= min_bytes_to_copy);
|
|
assert(rm_ptr->utf_start_pos <= rm_ptr->utf_tot_bytes_in_buffer);
|
|
/* If we have data in buffer, copy it to inbuf_pos */
|
|
if (0 < min_bytes_to_copy)
|
|
{
|
|
/* If min_bytes_to_copy == 1, avoid memcpy by direct assignment */
|
|
if (1 == min_bytes_to_copy)
|
|
*rm_ptr->inbuf_pos = rm_ptr->utf_tmp_buffer[rm_ptr->utf_start_pos];
|
|
else
|
|
{
|
|
memcpy(rm_ptr->inbuf_pos,
|
|
rm_ptr->utf_tmp_buffer + rm_ptr->utf_start_pos,
|
|
min_bytes_to_copy);
|
|
}
|
|
}
|
|
/* Increment utf_start_pos */
|
|
rm_ptr->utf_start_pos += min_bytes_to_copy;
|
|
/* Set status appropriately so that the following code can continue as if
|
|
* a one byte read happened. In case of a negative status, preserve the status
|
|
* as-is.
|
|
*/
|
|
status = (0 <= status) ? min_bytes_to_copy : status;
|
|
}
|
|
}
|
|
if (0 <= status)
|
|
{
|
|
rm_ptr->inbuf_pos += status;
|
|
bytes_read += status; /* bytes read this pass */
|
|
char_bytes_read += status; /* bytes for this character */
|
|
tot_bytes_read += bytes_read; /* total bytes read this command */
|
|
rm_ptr->file_pos += status;
|
|
PIPE_DEBUG(PRINTF("5: status: %d bytes2read: %d bytes_read: %d char_bytes_read: "
|
|
"%d tot_bytes_read: %d\n", status, bytes2read, bytes_read,
|
|
char_bytes_read, tot_bytes_read); DEBUGPIPEFLUSH);
|
|
if (0 < bytes2read && 0 == status)
|
|
{ /* EOF on read */
|
|
if (0 == char_bytes_read)
|
|
break; /* nothing read for this char so treat as EOF */
|
|
assert(1 < (bytes2read + bytes_read)); /* incomplete character */
|
|
SETZACANCELTIMER;
|
|
iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free),
|
|
bytes_read, char_start, rm_ptr->inbuf_pos);
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
UTF8_BADCHAR(bytes_read, char_start, rm_ptr->inbuf_pos,
|
|
chset_names[chset].len, chset_names[chset].addr);
|
|
} else if (status < bytes2read)
|
|
{
|
|
bytes2read -= status;
|
|
continue;
|
|
}
|
|
if (CHSET_UTF8 == chset)
|
|
{
|
|
PIPE_DEBUG(PRINTF("6: char_bytes_read: %d\n", char_bytes_read); DEBUGPIPEFLUSH);
|
|
if (1 == char_bytes_read)
|
|
{
|
|
add_bytes = UTF8_MBFOLLOW(char_start);
|
|
if (0 < add_bytes)
|
|
{
|
|
bytes2read = add_bytes;
|
|
PIPE_DEBUG(PRINTF("7: bytes2read: %d\n",
|
|
bytes2read); DEBUGPIPEFLUSH);
|
|
continue;
|
|
} else if (-1 == add_bytes)
|
|
{
|
|
SETZACANCELTIMER;
|
|
iorm_readfl_badchar(v,
|
|
(int)((unsigned char *)temp - stringpool.free),
|
|
char_bytes_read,
|
|
char_start, (char_start + char_bytes_read));
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
UTF8_BADCHAR(char_bytes_read, char_start,
|
|
char_start + char_bytes_read, 0, NULL);
|
|
}
|
|
if (u32_line_term[U32_LT_LF] == *char_start)
|
|
if (rm_ptr->crlast)
|
|
{ /* ignore LF following CR */
|
|
rm_ptr->crlast = FALSE;
|
|
rm_ptr->inbuf_pos = char_start;
|
|
bytes2read = 1; /* reset */
|
|
bytes_read = char_bytes_read = 0; /* start fresh */
|
|
tot_bytes_read--;
|
|
continue;
|
|
} else
|
|
{
|
|
line_term_seen = TRUE;
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
if (!rdone)
|
|
break;
|
|
}
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
if (u32_line_term[U32_LT_CR] == *char_start)
|
|
{
|
|
rm_ptr->crlast = TRUE;
|
|
line_term_seen = TRUE;
|
|
if (!rdone)
|
|
break;
|
|
} else
|
|
rm_ptr->crlast = FALSE;
|
|
if (u32_line_term[U32_LT_FF] == *char_start)
|
|
{
|
|
line_term_seen = TRUE;
|
|
if (!rdone)
|
|
break;
|
|
}
|
|
*temp++ = *char_start;
|
|
PIPE_DEBUG(PRINTF("8: move *char_start to *temp\n"); DEBUGPIPEFLUSH);
|
|
} else
|
|
{
|
|
PIPE_DEBUG(PRINTF("9: char_bytes_read: %d add_bytes: %d\n",
|
|
char_bytes_read, add_bytes); DEBUGPIPEFLUSH);
|
|
|
|
assert(char_bytes_read == (add_bytes + 1));
|
|
nextmb = UTF8_MBTOWC(char_start, rm_ptr->inbuf_pos, utf_code);
|
|
if (WEOF == utf_code)
|
|
{ /* invalid mb char */
|
|
SETZACANCELTIMER;
|
|
iorm_readfl_badchar(v,
|
|
(int)((unsigned char *)temp - stringpool.free),
|
|
char_bytes_read,
|
|
char_start, rm_ptr->inbuf_pos);
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
UTF8_BADCHAR(char_bytes_read, char_start,
|
|
rm_ptr->inbuf_pos, 0, NULL);
|
|
}
|
|
assert(nextmb == rm_ptr->inbuf_pos);
|
|
rm_ptr->inbuf_off = nextmb; /* mark as read */
|
|
rm_ptr->crlast = FALSE;
|
|
if (u32_line_term[U32_LT_NL] == utf_code ||
|
|
u32_line_term[U32_LT_LS] == utf_code ||
|
|
u32_line_term[U32_LT_PS] == utf_code)
|
|
{
|
|
line_term_seen = TRUE;
|
|
if (!rdone)
|
|
break;
|
|
}
|
|
memcpy(temp, char_start, char_bytes_read);
|
|
PIPE_DEBUG(PRINTF("10: move char_bytes_read: %d to *temp\n",
|
|
char_bytes_read); DEBUGPIPEFLUSH);
|
|
temp += char_bytes_read;
|
|
}
|
|
bytes_count += char_bytes_read;
|
|
PIPE_DEBUG(PRINTF("11: bytes_count: %d \n", bytes_count); DEBUGPIPEFLUSH);
|
|
if (bytes_count > MAX_STRLEN)
|
|
{ /* need to leave bytes for this character in buffer */
|
|
bytes_count -= char_bytes_read;
|
|
rm_ptr->inbuf_off = char_start;
|
|
rm_ptr->file_pos -= char_bytes_read;
|
|
break;
|
|
}
|
|
} else if (CHSET_UTF16BE == chset || CHSET_UTF16LE == chset)
|
|
{
|
|
if (2 == char_bytes_read)
|
|
{
|
|
if (CHSET_UTF16BE == chset)
|
|
add_bytes = UTF16BE_MBFOLLOW(char_start, rm_ptr->inbuf_pos);
|
|
else
|
|
add_bytes = UTF16LE_MBFOLLOW(char_start, rm_ptr->inbuf_pos);
|
|
if (1 < add_bytes)
|
|
{ /* UTF16xE_MBFOLLOW returns 1 or 3 if valid */
|
|
bytes2read = add_bytes - 1;
|
|
continue;
|
|
} else if (-1 == add_bytes)
|
|
{ /* not valid */
|
|
SETZACANCELTIMER;
|
|
iorm_readfl_badchar(v,
|
|
(int)((unsigned char *)temp - stringpool.free),
|
|
char_bytes_read,
|
|
char_start, rm_ptr->inbuf_pos);
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
UTF8_BADCHAR(char_bytes_read, char_start, rm_ptr->inbuf_pos,
|
|
chset_names[chset].len, chset_names[chset].addr);
|
|
}
|
|
}
|
|
assert(char_bytes_read == (add_bytes + 1));
|
|
if (CHSET_UTF16BE == chset)
|
|
nextmb = UTF16BE_MBTOWC(char_start, rm_ptr->inbuf_pos, utf_code);
|
|
else
|
|
nextmb = UTF16LE_MBTOWC(char_start, rm_ptr->inbuf_pos, utf_code);
|
|
if (WEOF == utf_code)
|
|
{ /* invalid mb char */
|
|
SETZACANCELTIMER;
|
|
iorm_readfl_badchar(v, (int)((unsigned char *)temp - stringpool.free),
|
|
char_bytes_read, char_start, rm_ptr->inbuf_pos);
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
UTF8_BADCHAR(char_bytes_read, char_start, rm_ptr->inbuf_pos,
|
|
chset_names[chset].len, chset_names[chset].addr);
|
|
}
|
|
assert(nextmb == rm_ptr->inbuf_pos);
|
|
if (u32_line_term[U32_LT_LF] == utf_code)
|
|
{
|
|
if (rm_ptr->crlast)
|
|
{ /* ignore LF following CR */
|
|
rm_ptr->crlast = FALSE;
|
|
rm_ptr->inbuf_pos = char_start;
|
|
bytes2read = 2; /* reset */
|
|
bytes_read = char_bytes_read = 0; /* start fresh */
|
|
tot_bytes_read -= 2;
|
|
continue;
|
|
}
|
|
}
|
|
rm_ptr->inbuf_off = rm_ptr->inbuf_pos; /* mark as read */
|
|
if (u32_line_term[U32_LT_CR] == utf_code)
|
|
rm_ptr->crlast = TRUE;
|
|
else
|
|
rm_ptr->crlast = FALSE;
|
|
for (ltind = 0; 0 < u32_line_term[ltind]; ltind++)
|
|
if (u32_line_term[ltind] == utf_code)
|
|
{
|
|
line_term_seen = TRUE;
|
|
break;
|
|
}
|
|
if (line_term_seen && !rdone)
|
|
break; /* out of do loop */
|
|
temp_start = temp;
|
|
temp = (char *)UTF8_WCTOMB(utf_code, temp_start);
|
|
bytes_count += (int4)(temp - temp_start);
|
|
if (bytes_count > MAX_STRLEN)
|
|
{ /* need to leave bytes for this character in buffer */
|
|
bytes_count -= (int4)(temp - temp_start);
|
|
rm_ptr->inbuf_off = char_start;
|
|
rm_ptr->file_pos -= char_bytes_read;
|
|
break;
|
|
}
|
|
} else
|
|
GTMASSERT;
|
|
char_count++;
|
|
char_start = rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf;
|
|
bytes_read = char_bytes_read = 0;
|
|
bytes2read = (CHSET_UTF8 == chset) ? 1 : 2;
|
|
} else
|
|
{
|
|
inchar = 0;
|
|
if (errno == 0)
|
|
{
|
|
tot_bytes_read = 0;
|
|
status = 0;
|
|
} else if (EINTR == errno)
|
|
{
|
|
if (out_of_time)
|
|
status = -2;
|
|
else
|
|
continue; /* Ignore interrupt if not our wakeup */
|
|
}
|
|
break;
|
|
}
|
|
} while (char_count < width && bytes_count < MAX_STRLEN);
|
|
}
|
|
}
|
|
PIPE_DEBUG(PRINTF(" %d notoutofband, %d %d\n", pid, status, line_term_seen); DEBUGPIPEFLUSH);
|
|
real_errno = errno;
|
|
if (TRUE == do_clearerr)
|
|
clearerr(filstr);
|
|
memcpy(rm_ptr->dollar_device, "0", SIZEOF("0"));
|
|
io_ptr->dollar.za = 0;
|
|
/* On error, getc() returns EOF while read() returns -1. Both code paths converge here. Thankfully EOF is -1 on all
|
|
* platforms that we know of so it is enough to check for -1 status here. Assert that below.
|
|
*/
|
|
assert(EOF == -1);
|
|
if ((-1 == status) && (EINTR != real_errno))
|
|
{
|
|
v->str.len = 0;
|
|
v->str.addr = (char *)stringpool.free; /* ensure valid address */
|
|
if (EAGAIN != real_errno)
|
|
{
|
|
/* Need to cancel the timer before taking the error return. Otherwise, it will be
|
|
canceled under the (timed) clause below. */
|
|
if (timed && !out_of_time)
|
|
cancel_timer(timer_id);
|
|
io_ptr->dollar.za = 9;
|
|
/* save error in $device */
|
|
DOLLAR_DEVICE_SET(rm_ptr, real_errno);
|
|
rts_error(VARLSTCNT(1) real_errno);
|
|
}
|
|
ret = FALSE;
|
|
}
|
|
|
|
if (timed)
|
|
{
|
|
/* If a timer was started then msec_timeout will be non-zero so the cancel_timer
|
|
check is done in the else clause. */
|
|
if (msec_timeout == 0)
|
|
{
|
|
if (!rm_ptr->pipe || FALSE == blocked_in)
|
|
{
|
|
FCNTL3(fildes, F_SETFL, flags, fcntl_res);
|
|
if (0 > fcntl_res)
|
|
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, LEN_AND_LIT("fcntl"), CALLFROM, errno);
|
|
}
|
|
if ((rm_ptr->pipe && (0 == status)) || (rm_ptr->fifo && (0 == status || real_errno == EAGAIN)))
|
|
ret = FALSE;
|
|
} else
|
|
{
|
|
if (out_of_time)
|
|
ret = FALSE;
|
|
else
|
|
cancel_timer(timer_id);
|
|
}
|
|
}
|
|
|
|
if (status == 0 && tot_bytes_read == 0)
|
|
{
|
|
v->str.len = 0;
|
|
v->str.addr = (char *)stringpool.free; /* ensure valid address */
|
|
UNICODE_ONLY(v->str.char_len = 0;)
|
|
|
|
/* on end of file set $za to 9 */
|
|
len = SIZEOF(ONE_COMMA_DEV_DET_EOF);
|
|
memcpy(rm_ptr->dollar_device, ONE_COMMA_DEV_DET_EOF, len);
|
|
io_ptr->dollar.za = 9;
|
|
|
|
if ((TRUE == io_ptr->dollar.zeof) && (RM_READ == saved_lastop))
|
|
rts_error(VARLSTCNT(1) ERR_IOEOF);
|
|
|
|
io_ptr->dollar.zeof = TRUE;
|
|
*dollarx_ptr = 0;
|
|
(*dollary_ptr)++;
|
|
if (io_ptr->error_handler.len > 0)
|
|
rts_error(VARLSTCNT(1) ERR_IOEOF);
|
|
if ((pipe_zero_timeout || rm_ptr->fifo) && out_of_time)
|
|
{
|
|
ret = TRUE;
|
|
out_of_time = FALSE;
|
|
}
|
|
} else
|
|
{
|
|
if (rm_ptr->pipe)
|
|
{
|
|
if ((tot_bytes_read > 0) || (out_of_time && blocked_in))
|
|
ret = TRUE;
|
|
else
|
|
ret = FALSE;
|
|
}
|
|
if (!utf_active || !rm_ptr->fixed)
|
|
{ /* if Unicode and fixed, already setup the mstr */
|
|
v->str.len = bytes_count;
|
|
v->str.addr = (char *)stringpool.free;
|
|
UNICODE_ONLY(v->str.char_len = char_count;)
|
|
if (!utf_active && !rm_ptr->fixed)
|
|
char_count = bytes_count;
|
|
}
|
|
if (!rm_ptr->fixed && line_term_seen)
|
|
{
|
|
*dollarx_ptr = 0;
|
|
(*dollary_ptr)++;
|
|
} else
|
|
{
|
|
*dollarx_ptr += char_count;
|
|
if (*dollarx_ptr >= io_ptr->width && io_ptr->wrap)
|
|
{
|
|
*dollary_ptr += (*dollarx_ptr / io_ptr->width);
|
|
if(io_ptr->length)
|
|
*dollary_ptr %= io_ptr->length;
|
|
*dollarx_ptr %= io_ptr->width;
|
|
}
|
|
}
|
|
}
|
|
assert(FALSE == rm_ptr->mupintr);
|
|
return (rm_ptr->pipe && out_of_time) ? FALSE : ret;
|
|
}
|