256 lines
8.5 KiB
C
256 lines
8.5 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. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
/* iosocket_write.c */
|
|
|
|
#include "mdef.h"
|
|
|
|
#include <errno.h>
|
|
#include "gtm_socket.h"
|
|
#include "gtm_inet.h"
|
|
#include "gtm_stdio.h"
|
|
#include "gtm_string.h"
|
|
|
|
#include "io.h"
|
|
#include "iotcpdef.h"
|
|
#include "iotcproutine.h"
|
|
#include "gt_timer.h"
|
|
#include "iosocketdef.h"
|
|
#include "dollarx.h"
|
|
#include "gtm_conv.h"
|
|
#include "gtm_utf8.h"
|
|
#include "stringpool.h"
|
|
#include "send_msg.h"
|
|
#include "error.h"
|
|
|
|
GBLREF io_pair io_curr_device;
|
|
#ifdef UNIX
|
|
GBLREF io_pair io_std_device;
|
|
GBLREF bool prin_out_dev_failure;
|
|
#endif
|
|
|
|
GBLREF tcp_library_struct tcp_routines;
|
|
GBLREF mstr chset_names[];
|
|
GBLREF UConverter *chset_desc[];
|
|
GBLREF spdesc stringpool;
|
|
|
|
error_def(ERR_SOCKWRITE);
|
|
error_def(ERR_TEXT);
|
|
error_def(ERR_CURRSOCKOFR);
|
|
error_def(ERR_ZFF2MANY);
|
|
error_def(ERR_DELIMSIZNA);
|
|
error_def(ERR_ZINTRECURSEIO);
|
|
UNIX_ONLY(error_def(ERR_NOPRINCIO);)
|
|
|
|
void iosocket_write(mstr *v)
|
|
{
|
|
iosocket_write_real(v, TRUE);
|
|
}
|
|
|
|
void iosocket_write_real(mstr *v, boolean_t convert_output)
|
|
{
|
|
io_desc *iod;
|
|
mstr tempv;
|
|
char *out, *c_ptr, *c_top;
|
|
int in_b_len, b_len, status, new_len, c_len, mb_len;
|
|
int flags;
|
|
d_socket_struct *dsocketptr;
|
|
socket_struct *socketptr;
|
|
|
|
DBGSOCK2((stdout, "socwrite: ************************** Top of iosocket_write\n"));
|
|
iod = io_curr_device.out;
|
|
assert(gtmsocket == iod->type);
|
|
dsocketptr = (d_socket_struct *)iod->dev_sp;
|
|
socketptr = dsocketptr->socket[dsocketptr->current_socket];
|
|
if (dsocketptr->n_socket <= dsocketptr->current_socket)
|
|
{
|
|
rts_error(VARLSTCNT(4) ERR_CURRSOCKOFR, 2, dsocketptr->current_socket, dsocketptr->n_socket);
|
|
return;
|
|
}
|
|
if (dsocketptr->mupintr)
|
|
rts_error(VARLSTCNT(1) ERR_ZINTRECURSEIO);
|
|
#ifdef MSG_NOSIGNAL
|
|
flags = MSG_NOSIGNAL; /* return EPIPE instead of SIGPIPE */
|
|
#else
|
|
flags = 0;
|
|
#endif
|
|
socketptr->lastop = TCP_WRITE;
|
|
if (socketptr->first_write)
|
|
{ /* First WRITE, do following
|
|
1. Transition to UTF16BE if ochset is UTF16 and WRITE a BOM
|
|
2. Convert ZFF into ochset format so we don't need to convert every time ZFF is output
|
|
3. Convert DELIMITER 0 to OCHSET format to avoid repeated conversions of DELIM0 on output
|
|
*/
|
|
if (CHSET_UTF16 == iod->ochset)
|
|
{
|
|
DBGSOCK2((stdout, "socwrite: First write UTF16 -- writing BOM\n"));
|
|
iod->ochset = CHSET_UTF16BE; /* per Unicode standard, assume big endian when endian
|
|
format is unspecified */
|
|
get_chset_desc(&chset_names[iod->ochset]);
|
|
DOTCPSEND(socketptr->sd, UTF16BE_BOM, UTF16BE_BOM_LEN, flags, status);
|
|
DBGSOCK2((stdout, "socwrite: TCP send of BOM-BE with rc %d\n", status));
|
|
if (0 != status)
|
|
{
|
|
SOCKERROR(iod, dsocketptr, socketptr, ERR_SOCKWRITE, status);
|
|
return;
|
|
}
|
|
#ifdef UNIX
|
|
else if (iod == io_std_device.out)
|
|
prin_out_dev_failure = FALSE;
|
|
#endif
|
|
}
|
|
if (CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset) /* need conversion of ZFF and DELIM0 */
|
|
{
|
|
if (0 < socketptr->zff.len)
|
|
{
|
|
new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], &socketptr->zff, NULL,
|
|
NULL);
|
|
if (MAX_ZFF_LEN < new_len)
|
|
rts_error(VARLSTCNT(4) ERR_ZFF2MANY, 2, new_len, MAX_ZFF_LEN);
|
|
if (NULL != socketptr->zff.addr) /* we rely on newsocket.zff.addr being set to NULL
|
|
in iosocket_create() */
|
|
socketptr->zff.addr = (char *)malloc(MAX_ZFF_LEN);
|
|
socketptr->zff.len = new_len;
|
|
UNICODE_ONLY(socketptr->zff.char_len = 0); /* don't care */
|
|
memcpy(socketptr->zff.addr, stringpool.free, new_len);
|
|
}
|
|
|
|
if (0 < socketptr->n_delimiter)
|
|
{
|
|
new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset],
|
|
&socketptr->delimiter[0], NULL, NULL);
|
|
if (MAX_DELIM_LEN < new_len)
|
|
{
|
|
rts_error(VARLSTCNT(1) ERR_DELIMSIZNA);
|
|
return;
|
|
}
|
|
socketptr->odelimiter0.len = new_len;
|
|
UNICODE_ONLY(socketptr->odelimiter0.char_len = socketptr->delimiter[0].char_len);
|
|
socketptr->odelimiter0.addr = malloc(new_len);
|
|
memcpy(socketptr->odelimiter0.addr, stringpool.free, new_len);
|
|
}
|
|
}
|
|
socketptr->first_write = FALSE;
|
|
}
|
|
memcpy(dsocketptr->dollar_device, "0", SIZEOF("0"));
|
|
if (CHSET_M != iod->ochset)
|
|
{ /* For ochset == UTF-8, validate the output,
|
|
* For ochset == UTF-16[B|L]E, convert the output (and validate during conversion)
|
|
*/
|
|
if (CHSET_UTF8 == iod->ochset)
|
|
{
|
|
UTF8_LEN_STRICT(v->addr, v->len); /* triggers badchar error for invalid sequence */
|
|
tempv = *v;
|
|
} else
|
|
{
|
|
assert(CHSET_UTF16BE == iod->ochset || CHSET_UTF16LE == iod->ochset);
|
|
/* Certain types of writes (calls from iosocket_wteol or _wtff) already have their output
|
|
converted. Converting again just wrecks it so avoid that when necessary.
|
|
*/
|
|
if (convert_output)
|
|
{
|
|
new_len = gtm_conv(chset_desc[CHSET_UTF8], chset_desc[iod->ochset], v, NULL, NULL);
|
|
tempv.addr = (char *)stringpool.free;
|
|
tempv.len = new_len;
|
|
/* Since there is no dependence on string pool between now and when we send the data,
|
|
we won't bother "protecting" the stringpool value. This space can be used again
|
|
by whomever needs it without us forcing a garbage collection due to IO reformat.
|
|
*/
|
|
/* stringpool.free += new_len; */
|
|
} else
|
|
tempv = *v;
|
|
}
|
|
} else
|
|
tempv = *v;
|
|
if (0 != (in_b_len = tempv.len))
|
|
{
|
|
DBGSOCK2((stdout, "socwrite: starting output loop (%d bytes) - iodwidth: %d wrap: %d\n",
|
|
in_b_len, iod->width, iod->wrap));
|
|
for (out = tempv.addr; ; out += b_len)
|
|
{
|
|
DBGSOCK2((stdout, "socwrite: ---------> Top of write loop $x: %d $y: %d in_b_len: %d\n",
|
|
iod->dollar.x, iod->dollar.y, in_b_len));
|
|
if (!iod->wrap)
|
|
b_len = in_b_len;
|
|
else
|
|
{
|
|
if ((iod->dollar.x >= iod->width) && (START == iod->esc_state))
|
|
{
|
|
/* Should this really be iosocket_Wteol() (for FILTER)? IF we call iosocket_wteol(),
|
|
* there will be recursion iosocket_Write -> iosocket_Wteol ->iosocket_Write */
|
|
if (0 < socketptr->n_delimiter)
|
|
{
|
|
DOTCPSEND(socketptr->sd, socketptr->odelimiter0.addr, socketptr->odelimiter0.len,
|
|
(socketptr->urgent ? MSG_OOB : 0) | flags, status);
|
|
DBGSOCK2((stdout, "socwrite: TCP send of %d byte delimiter with rc %d\n",
|
|
socketptr->odelimiter0.len, status));
|
|
if (0 != status)
|
|
{
|
|
SOCKERROR(iod, dsocketptr, socketptr, ERR_SOCKWRITE, status);
|
|
return;
|
|
}
|
|
#ifdef UNIX
|
|
else if (iod == io_std_device.out)
|
|
prin_out_dev_failure = FALSE;
|
|
#endif
|
|
}
|
|
iod->dollar.y++;
|
|
iod->dollar.x = 0;
|
|
DBGSOCK2((stdout, "socwrite: $x > width - wrote delimiter: %d $x: %d $y: %d\n",
|
|
(0 < socketptr->n_delimiter), iod->dollar.x, iod->dollar.y));
|
|
}
|
|
if ((START != iod->esc_state) || ((int)(iod->dollar.x + in_b_len) <= (int)iod->width))
|
|
{ /* enough room even in the worst case, i.e., if width - dollar.x can accommodate in_b_len chars,
|
|
* it certainly can accommodate in_b_len bytes */
|
|
b_len = in_b_len;
|
|
} else
|
|
{
|
|
c_len = iod->width - iod->dollar.x;
|
|
for (c_ptr = out, c_top = out + in_b_len, b_len = 0;
|
|
(c_ptr < c_top) && c_len--;
|
|
b_len += mb_len, c_ptr += mb_len)
|
|
{
|
|
mb_len = (CHSET_M == iod->ochset) ? 0 :
|
|
(CHSET_UTF8 == iod->ochset) ? UTF8_MBFOLLOW(c_ptr) :
|
|
(CHSET_UTF16BE == iod->ochset) ? UTF16BE_MBFOLLOW(c_ptr, c_top) :
|
|
UTF16LE_MBFOLLOW(c_ptr, c_top);
|
|
assert(-1 != mb_len);
|
|
mb_len++;
|
|
}
|
|
DBGSOCK2((stdout, "socwrite: computing string length in chars: in_b_len: %d mb_len: %d\n",
|
|
in_b_len, mb_len));
|
|
}
|
|
}
|
|
assert(0 != b_len);
|
|
DOTCPSEND(socketptr->sd, out, b_len, (socketptr->urgent ? MSG_OOB : 0) | flags, status);
|
|
DBGSOCK2((stdout, "socwrite: TCP data send of %d bytes with rc %d\n", b_len, status));
|
|
if (0 != status)
|
|
{
|
|
SOCKERROR(iod, dsocketptr, socketptr, ERR_SOCKWRITE, status);
|
|
return;
|
|
}
|
|
#ifdef UNIX
|
|
else if (iod == io_std_device.out)
|
|
prin_out_dev_failure = FALSE;
|
|
#endif
|
|
dollarx(iod, (uchar_ptr_t)out, (uchar_ptr_t)out + b_len);
|
|
DBGSOCK2((stdout, "socwrite: $x/$y updated by dollarx(): $x: %d $y: %d filter: %d escape: %d\n",
|
|
iod->dollar.x, iod->dollar.y, iod->write_filter, iod->esc_state));
|
|
in_b_len -= b_len;
|
|
if (0 >= in_b_len)
|
|
break;
|
|
}
|
|
iod->dollar.za = 0;
|
|
}
|
|
DBGSOCK2((stdout, "socwrite: <--------- Leaving iosocket_write\n"));
|
|
return;
|
|
}
|