fis-gtm/sr_unix/iorm_use.c

461 lines
14 KiB
C
Raw Normal View History

/****************************************************************
* *
* 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. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_string.h"
#include <errno.h>
#include "gtm_unistd.h"
#include "gtm_stat.h"
#include "gtm_stdio.h"
#include "gtm_iconv.h"
#include "copy.h"
#include "io.h"
#include "iormdef.h"
#include "io_params.h"
#include "iosp.h"
#include "eintr_wrappers.h"
#include "stringpool.h"
#ifdef UNICODE_SUPPORTED
#include "gtm_conv.h"
#include "gtm_utf8.h"
#endif
/* Only want to do fstat() once on this file, not on evry use.
*/
#define FSTAT_CHECK \
if (!fstat_done) \
{ \
FSTAT_FILE(rm_ptr->fildes, &statbuf, fstat_res); \
if (-1 == fstat_res) \
{ \
save_errno = errno; \
rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("fstat"), CALLFROM, save_errno); \
} \
mode = mode1 = statbuf.st_mode; \
fstat_done = TRUE; \
}
#define IS_PADCHAR_VALID(chset, padchar) (IS_ASCII(padchar))
typedef struct
{
unsigned short mem;
unsigned short grp;
} uic_struct;
LITREF unsigned char io_params_size[];
GBLREF boolean_t gtm_utf8_mode;
#ifdef UNICODE_SUPPORTED
GBLREF UConverter *chset_desc[];
LITREF mstr chset_names[];
#endif
error_def(ERR_DEVPARMNEG);
error_def(ERR_RMWIDTHPOS);
error_def(ERR_RMWIDTHTOOBIG);
error_def(ERR_RECSIZENOTEVEN);
error_def(ERR_SYSCALL);
error_def(ERR_WIDTHTOOSMALL);
error_def(ERR_PADCHARINVALID);
error_def(ERR_IOERROR);
void iorm_use(io_desc *iod, mval *pp)
{
boolean_t seen_wrap, fstat_done;
unsigned char c;
short mode, mode1;
int4 length, width, width_bytes, recordsize, padchar;
int fstat_res, save_errno;
d_rm_struct *rm_ptr;
struct stat statbuf;
int p_offset;
mstr chset_mstr;
boolean_t ichset_specified, ochset_specified, chset_allowed;
gtm_chset_t width_chset, temp_chset;
p_offset = 0;
rm_ptr = (d_rm_struct *)iod->dev_sp;
seen_wrap = fstat_done = FALSE;
ichset_specified = ochset_specified = chset_allowed = FALSE;
if (ZOS_ONLY(TRUE ||) gtm_utf8_mode)
chset_allowed = TRUE;
while (*(pp->str.addr + p_offset) != iop_eol)
{
assert((params) *(pp->str.addr + p_offset) < (params)n_iops);
switch (c = *(pp->str.addr + p_offset++))
{
case iop_exception:
iod->error_handler.len = *(pp->str.addr + p_offset);
iod->error_handler.addr = (char *)(pp->str.addr + p_offset + 1);
s2pool(&iod->error_handler);
break;
case iop_fixed:
if (iod->state != dev_open)
rm_ptr->fixed = TRUE;
break;
case iop_nofixed:
if (iod->state != dev_open)
{
rm_ptr->fixed = FALSE;
if (CHSET_BINARY == iod->ichset)
{
ichset_specified = FALSE;
iod->ichset = CHSET_M;
}
if (CHSET_BINARY == iod->ochset)
{
ochset_specified = FALSE;
iod->ochset = CHSET_M;
}
}
break;
case iop_length:
GET_LONG(length, (pp->str.addr + p_offset));
if (length < 0)
rts_error(VARLSTCNT(1) ERR_DEVPARMNEG);
iod->length = length;
break;
case iop_w_protection:
FSTAT_CHECK;
mode &= ~(0x07);
mode |= *(pp->str.addr + p_offset);
break;
case iop_g_protection:
FSTAT_CHECK;
mode &= ~(0x07 << 3);
mode |= *(pp->str.addr + p_offset) << 3;
break;
case iop_s_protection:
case iop_o_protection:
FSTAT_CHECK;
mode &= ~(0x07 << 6);
mode |= *(pp->str.addr + p_offset) << 6;
break;
case iop_pad:
if (iod->state != dev_open)
{
GET_LONG(padchar, (pp->str.addr + p_offset));
if (!IS_PADCHAR_VALID(iod->ochset, padchar))
rts_error(VARLSTCNT(2) ERR_PADCHARINVALID, 0);
rm_ptr->padchar = padchar;
}
break;
case iop_readonly:
rm_ptr->noread = TRUE;
break;
case iop_noreadonly:
rm_ptr->noread = FALSE;
break;
case iop_writeonly:
rm_ptr->write_only = TRUE;
break;
case iop_nowriteonly:
rm_ptr->write_only = FALSE;
break;
case iop_recordsize:
if (dev_open != iod->state || (!IS_UTF_CHSET(iod->ichset) && !IS_UTF_CHSET(iod->ochset)) ||
(!rm_ptr->done_1st_read && !rm_ptr->done_1st_write))
{ /* only if not open, not UTF, or no reads or writes yet */
GET_LONG(recordsize, (pp->str.addr + p_offset));
if (recordsize <= 0)
rts_error(VARLSTCNT(1) ERR_RMWIDTHPOS);
else if (MAX_STRLEN < recordsize)
rts_error(VARLSTCNT(1) ERR_RMWIDTHTOOBIG);
rm_ptr->recordsize = recordsize;
rm_ptr->def_recsize = FALSE;
}
break;
case iop_rewind:
if (iod->state == dev_open && !rm_ptr->fifo && !rm_ptr->pipe && 1 != rm_ptr->fildes)
{
iorm_flush(iod);
if (lseek(rm_ptr->fildes, (off_t)0, SEEK_SET) == -1)
{
rts_error(VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"),
RTS_ERROR_LITERAL("REWIND"), CALLFROM, errno);
}
if (fseek(rm_ptr->filstr, (long)0, SEEK_SET) == -1) /* Rewind the input stream */
{
rts_error(VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("fseek"),
RTS_ERROR_LITERAL("REWIND"), CALLFROM, errno);
}
iod->dollar.zeof = FALSE;
iod->dollar.y = 0;
iod->dollar.x = 0;
rm_ptr->lastop = RM_NOOP;
rm_ptr->done_1st_read = rm_ptr->done_1st_write = rm_ptr->crlast = FALSE;
rm_ptr->out_bytes = rm_ptr->bom_buf_cnt = rm_ptr->bom_buf_off = 0;
rm_ptr->inbuf_pos = rm_ptr->inbuf_off = rm_ptr->inbuf;
rm_ptr->file_pos = 0;
/* Reset temporary buffer so that the next read starts afresh */
if (IS_UTF_CHSET(iod->ichset))
{
DEBUG_ONLY(memset(rm_ptr->utf_tmp_buffer, 0, CHUNK_SIZE));
rm_ptr->utf_start_pos = 0;
rm_ptr->utf_tot_bytes_in_buffer = 0;
}
}
break;
case iop_stream:
if (iod->state != dev_open)
{
rm_ptr->stream = TRUE;
if (CHSET_BINARY == iod->ichset)
{
ichset_specified = FALSE;
iod->ichset = CHSET_M;
}
if (CHSET_BINARY == iod->ochset)
{
ochset_specified = FALSE;
iod->ochset = CHSET_M;
}
}
break;
case iop_truncate:
if (!rm_ptr->fifo && !rm_ptr->pipe && 1 != rm_ptr->fildes)
{
int ftruncate_res;
if (fseek(rm_ptr->filstr, (long)rm_ptr->file_pos, SEEK_SET) == -1)
{
rts_error(VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("fseek"),
RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno);
}
if (lseek(rm_ptr->fildes, (off_t)rm_ptr->file_pos, SEEK_SET) == -1)
{
rts_error(VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("lseek"),
RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno);
}
FTRUNCATE(rm_ptr->fildes, (off_t)rm_ptr->file_pos, ftruncate_res);
if (0 != ftruncate_res)
{
rts_error(VARLSTCNT(9) ERR_IOERROR, 7, RTS_ERROR_LITERAL("ftruncate"),
RTS_ERROR_LITERAL("TRUNCATE"), CALLFROM, errno);
}
iod->dollar.zeof = TRUE;
/* Reset temporary buffer so that the next read can start afresh */
if (IS_UTF_CHSET(iod->ichset))
{
rm_ptr->inbuf_top = rm_ptr->inbuf_off = rm_ptr->inbuf_pos = rm_ptr->inbuf;
rm_ptr->out_bytes = rm_ptr->bom_buf_cnt = rm_ptr->bom_buf_off = 0;
DEBUG_ONLY(memset(rm_ptr->utf_tmp_buffer, 0, CHUNK_SIZE));
rm_ptr->utf_start_pos = 0;
rm_ptr->utf_tot_bytes_in_buffer = 0;
if (0 == rm_ptr->file_pos)
{
/* If the truncate size is zero reset done_1st_read and done_1st_write
* to FALSE.
*/
rm_ptr->done_1st_read = rm_ptr->done_1st_write = FALSE;
}
}
}
break;
case iop_uic:
{
unsigned char *ch, ct, *end;
int chown_res;
uic_struct uic;
ch = (unsigned char *)pp->str.addr + p_offset;
ct = *ch++;
end = ch + ct;
uic.grp = uic.mem = 0;
while ((*ch != ',') && (ch < end))
uic.mem = (10 * uic.mem) + (*ch++ - '0');
if (*ch == ',')
{
while (++ch < end)
uic.grp = (10 * uic.grp) + (*ch - '0');
}
CHG_OWNER(iod->trans_name->dollar_io, uic.mem, uic.grp, chown_res);
if (-1 == chown_res)
rts_error(VARLSTCNT(1) errno);
break;
}
case iop_width:
assert(iod->state == dev_open);
GET_LONG(width, (pp->str.addr + p_offset));
if (0 > width || (0 == width && !IS_UTF_CHSET(iod->ochset)))
rts_error(VARLSTCNT(1) ERR_RMWIDTHPOS);
else if (MAX_STRLEN < width)
rts_error(VARLSTCNT(1) ERR_RMWIDTHTOOBIG);
/* Do not allow a WIDTH of 1 if either ICHSET or OCHSET is UTF-* */
if ((1 == width) && gtm_utf8_mode && ((IS_UTF_CHSET(iod->ochset)) || (IS_UTF_CHSET(iod->ichset))))
rts_error(VARLSTCNT(1) ERR_WIDTHTOOSMALL);
if (IS_UTF_CHSET(iod->ochset) && rm_ptr->fixed)
iorm_flush(iod); /* need to flush current record first */
rm_ptr->def_width = FALSE;
if (0 == width)
{
iod->width = rm_ptr->recordsize; /* Effectively eliminates consideration of width */
if (!seen_wrap)
iod->wrap = FALSE;
} else
{
iod->width = width;
iod->wrap = TRUE;
}
break;
case iop_wrap:
if (dev_open != iod->state)
{
if (CHSET_BINARY == iod->ichset || CHSET_BINARY == iod->ochset)
break; /* ignore wrap if BINARY specified */
} else
{ /* already open so check what conversion is in use */
#ifdef __MVS__
if (CHSET_BINARY == iod->process_chset)
break;
#endif
}
iod->wrap = TRUE;
seen_wrap = TRUE; /* Don't allow WIDTH=0 to override WRAP */
break;
case iop_nowrap:
iod->wrap = FALSE;
break;
case iop_ipchset:
{
#ifdef KEEP_zOS_EBCDIC
if ( (iconv_t)0 != iod->input_conv_cd )
{
ICONV_CLOSE_CD(iod->input_conv_cd);
}
SET_CODE_SET(iod->in_code_set, (char *)(pp->str.addr + p_offset + 1));
if (DEFAULT_CODE_SET != iod->in_code_set)
ICONV_OPEN_CD(iod->input_conv_cd, (char *)(pp->str.addr + p_offset + 1),
INSIDE_CH_SET);
#endif
if (chset_allowed && (dev_open != iod->state))
{
chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1);
chset_mstr.len = *(pp->str.addr + p_offset);
SET_ENCODING(temp_chset, &chset_mstr);
if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset))
break; /* ignore UTF chsets if not utf8_mode */
iod->ichset = temp_chset;
ichset_specified = TRUE;
}
}
break;
case iop_opchset:
{
#ifdef KEEP_zOS_EBCDIC
if ( (iconv_t) 0 != iod->output_conv_cd )
{
ICONV_CLOSE_CD(iod->output_conv_cd);
}
SET_CODE_SET(iod->out_code_set, (char *)(pp->str.addr + p_offset + 1));
if (DEFAULT_CODE_SET != iod->out_code_set)
ICONV_OPEN_CD(iod->output_conv_cd, INSIDE_CH_SET,
(char *)(pp->str.addr + p_offset + 1));
#endif
if (chset_allowed && (dev_open != iod->state))
{
chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1);
chset_mstr.len = *(pp->str.addr + p_offset);
SET_ENCODING(temp_chset, &chset_mstr);
if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset))
break; /* ignore UTF chsets if not utf8_mode */
iod->ochset = temp_chset;
ochset_specified = TRUE;
if (gtm_utf8_mode && !IS_PADCHAR_VALID(iod->ochset, rm_ptr->padchar))
rts_error(VARLSTCNT(2) ERR_PADCHARINVALID, 0);
}
}
break;
case iop_chset:
{
if (chset_allowed && (dev_open != iod->state))
{
chset_mstr.addr = (char *)(pp->str.addr + p_offset + 1);
chset_mstr.len = *(pp->str.addr + p_offset);
SET_ENCODING(temp_chset, &chset_mstr);
if (!gtm_utf8_mode && IS_UTF_CHSET(temp_chset))
break; /* ignore UTF chsets if not utf8_mode */
iod->ochset = temp_chset;
if (gtm_utf8_mode && !IS_PADCHAR_VALID(iod->ochset, rm_ptr->padchar))
rts_error(VARLSTCNT(2) ERR_PADCHARINVALID, 0);
iod->ichset = iod->ochset;
ochset_specified = ichset_specified = TRUE;
}
}
break;
case iop_m:
if (chset_allowed && (dev_open != iod->state))
{
iod->ichset = iod->ochset = CHSET_M;
ichset_specified = ochset_specified = TRUE;
}
break;
case iop_utf8:
case iop_utf16:
case iop_utf16be:
case iop_utf16le:
if (gtm_utf8_mode && dev_open != iod->state)
{
iod->ichset = iod->ochset =
(iop_utf8 == c) ? CHSET_UTF8 :
(iop_utf16 == c) ? CHSET_UTF16 :
(iop_utf16be == c) ? CHSET_UTF16BE : CHSET_UTF16LE;
ichset_specified = ochset_specified = TRUE;
}
break;
default:
break;
}
p_offset += ((IOP_VAR_SIZE == io_params_size[c]) ?
(unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[c]);
}
if (dev_open != iod->state)
{
if (!ichset_specified)
{
#ifdef __MVS__
iod->is_ichset_default = TRUE;
#endif
iod->ichset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M;
}
if (!ochset_specified)
{
#ifdef __MVS__
iod->is_ochset_default = TRUE;
#endif
iod->ochset = (gtm_utf8_mode) ? CHSET_UTF8 : CHSET_M;
}
if ((CHSET_M != iod->ichset) && (CHSET_UTF16 != iod->ichset) && (CHSET_MAX_IDX > iod->ichset))
get_chset_desc(&chset_names[iod->ichset]);
if ((CHSET_M != iod->ochset) && (CHSET_UTF16 != iod->ochset) && (CHSET_MAX_IDX > iod->ochset))
get_chset_desc(&chset_names[iod->ochset]);
/* If ICHSET or OCHSET is of type UTF-16, check that RECORDSIZE is even */
if (gtm_utf8_mode && (IS_UTF16_CHSET(iod->ichset) || IS_UTF16_CHSET(iod->ochset)))
{
if (rm_ptr->def_recsize)
{ /* DEF_RM_RECORDSIZE is currently an odd number (32K-1). Round it down
* to be a multiple of 4 bytes since a UTF-16 char can be 2 or 4 bytes */
assert(DEF_RM_RECORDSIZE == 32767);
rm_ptr->recordsize = ROUND_DOWN2(rm_ptr->recordsize, 4);
} else if (0 != rm_ptr->recordsize % 2)
rts_error(VARLSTCNT(3) ERR_RECSIZENOTEVEN, 1, rm_ptr->recordsize);
}
}
if (fstat_done && mode != mode1)
{ /* if the mode has been changed by the qualifiers, reset it */
if (-1 == CHMOD(iod->trans_name->dollar_io, mode))
rts_error(VARLSTCNT(1) errno);
}
return;
}