/**************************************************************** * * * Copyright 2001, 2013 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 #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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_RMWIDTHPOS); else if (MAX_STRLEN < recordsize) rts_error_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_RMWIDTHPOS); else if (MAX_STRLEN < width) rts_error_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) 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; case iop_follow: if (!rm_ptr->fifo && !rm_ptr->pipe) rm_ptr->follow = TRUE; break; case iop_nofollow: if (!rm_ptr->fifo && !rm_ptr->pipe) rm_ptr->follow = FALSE; 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_csa(CSA_ARG(NULL) 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_csa(CSA_ARG(NULL) VARLSTCNT(1) errno); } return; }