int move_cursor_left(int col, int num_cols) { /* ------------------------------------------------------- * moves cursor left by num_cols columns. if col is leftmost, * then it goes back to the end of the previous line * returns 0 if success, != 0 if error * ------------------------------------------------------- */ int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes; int ret; if (0 == num_cols) ret = 0; else if (0 > num_cols) ret = move_cursor_right(col, -num_cols); else if (0 < col) { ret = write_loop(fildes, (unsigned char *)CURSOR_LEFT, MIN(col, num_cols)); num_cols -= MIN(col, num_cols); if (num_cols) { DOWRITERC(fildes, CURSOR_UP, strlen(CURSOR_UP), ret); if (0 > ret) return -1; ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, io_curr_device.in->width - num_cols); } } else { DOWRITERC(fildes, CURSOR_UP, strlen(CURSOR_UP), ret); if (0 > ret) return -1; ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, io_curr_device.in->width - num_cols); } return ret; }
int write_loop(int fildes, unsigned char *str, int num_times) { int i, size_required, ret; unsigned char string[1024]; unsigned char *temp = NULL; *string = '\0'; size_required = num_times * STRLEN((char *)str); if (size_required > SIZEOF(string)) { for (i = 0; i < num_times; i++) { DOWRITERC(fildes, str, strlen((char *)str), ret); if (0 > ret) return -1; } } else if (num_times) { for (i = 0; i < num_times; i++) { strcat((char *)string, (char *)str); } DOWRITERC(fildes, string, size_required, ret); if (0 > ret) return -1; } return 0; }
int move_cursor_right(int col, int num_cols) { /* ------------------------------------------------------- * moves cursor right by num_cols columns, if col is rightmost, * then it goes to the start of the next line * returns 0 if success, != 0 if error * ------------------------------------------------------- */ int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes; int ret; io_desc *io_ptr = io_curr_device.in; if (0 == num_cols) ret = 0; else if (0 > num_cols) ret = move_cursor_left(col, -num_cols); else if ((io_curr_device.in->width - num_cols) > col) ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, num_cols); else { DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret); if (0 > ret) return -1; num_cols -= (io_curr_device.in->width - col); if (num_cols) ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, num_cols); } return ret; }
void iott_flush_buffer(io_desc *io_ptr, boolean_t new_write_flag) { d_tt_struct *tt_ptr; int4 status; ssize_t write_len; error_def(ERR_NOPRINCIO); tt_ptr = io_ptr->dev_sp; if (!tt_ptr->write_active) return; /* Was assert but that ended up causing endless loops -- now we just survive */ write_len = (ssize_t)(tt_ptr->tbuffp - tt_ptr->ttybuff); if (0 < write_len) { DOWRITERC(tt_ptr->fildes, tt_ptr->ttybuff, write_len, status); if (0 == status) { tt_ptr->tbuffp = tt_ptr->ttybuff; if (io_ptr == io_std_device.out) { /* ------------------------------------------------ * set prin_out_dev_failure to FALSE in case it * had been set TRUE earlier and is now working. * for eg. a write fails and the next write works. * ------------------------------------------------ */ prin_out_dev_failure = FALSE; } } else /* (0 != status) */ { tt_ptr->write_active = FALSE; /* In case we come back this-a-way */ if (io_ptr == io_std_device.out) { if (!prin_out_dev_failure) prin_out_dev_failure = TRUE; else { send_msg(VARLSTCNT(1) ERR_NOPRINCIO); /* rts_error(VARLSTCNT(1) ERR_NOPRINCIO); This causes a core dump */ stop_image_no_core(); } } xfer_set_handlers(tt_write_error_event, tt_write_error_set, status); } } tt_ptr->write_active = new_write_flag; }
int write_str(void *str832, unsigned int len, unsigned int start_x, boolean_t move, boolean_t multibyte) { /* writes a specified string starting from the current cursor position and returns the cursor back to the same place. str832 -> the string to write, may be Unicode code points len -> the length of the string start_x -> is the current cursor's column in the window. move -> whether the cursor moves or not. multibyte -> str832 is always bytes, if utf8_active then multibyte possible returns -1 if error, 0 if successful */ int number_of_lines_up, number_of_chars_left, written, ret, outlen; unsigned int cur_x; int width = io_curr_device.in->width, cur_width, line_width, this_width; int fildes = ((d_tt_struct *)((io_curr_device.in)->dev_sp))->fildes; unsigned char *str, string[TTDEF_BUF_SZ], *outptr, *outtop, *strstart, *nextptr; wint_t *str32, temp32; io_desc *io_ptr = io_curr_device.in; d_tt_struct *tt_ptr; boolean_t utf8_active, writenewline; assert(width); tt_ptr = (d_tt_struct *)io_ptr->dev_sp; utf8_active = gtm_utf8_mode ? (CHSET_M != io_ptr->ichset) : FALSE; if (utf8_active && !multibyte) str32 = (wint_t *)str832; else str = (unsigned char *)str832; if (multibyte && utf8_active) { outptr = str; outtop = str + len; } cur_x = start_x % width; /* start_x is absolute, get relative value into cur_x */ number_of_lines_up = 0; #ifdef UNICODE_SUPPORTED if (utf8_active) { cur_width = width - cur_x; for (line_width = 0; 0 < len; ) { writenewline = FALSE; if (multibyte) { /* go through mb string one line at a time */ for (strstart = outptr ; outptr < outtop; ) { nextptr = UTF8_MBTOWC(outptr, outtop, temp32); GTM_IO_WCWIDTH(temp32, this_width); if ((line_width + this_width) > cur_width) { writenewline = TRUE; break; } line_width += this_width; outptr = nextptr; } outlen = (int)(outptr - strstart); len -= outlen; } else { for (outptr = string, outtop = string + SIZEOF(string); (0 < len) && ((outptr + GTM_MB_LEN_MAX) < outtop); str32++, len--) { GTM_IO_WCWIDTH(*str32, this_width); if ((line_width + this_width) > cur_width) { writenewline = TRUE; break; } line_width += this_width; temp32 = *str32; /* preserve argument */ outptr = UTF8_WCTOMB(temp32, outptr); } outlen =(int)(outptr - string); strstart = string; } assert(!writenewline || len); assert(line_width <= cur_width); if (line_width >= cur_width) /* our write has positioned us to end of width. write new line */ writenewline = TRUE; /* either end of input, end of conversion buffer, or full line */ if (0 < outlen) { /* something to write */ DOWRITERL_A(fildes, strstart, outlen, written); if (0 > written) return -1; } if (!writenewline) { if (0 < len) continue; /* end of buffer in middle of line so no EOL */ if (0 == len) { /* last line is partial so no new line */ cur_x = cur_x + line_width; break; } } /* ------------------------------------------------------------------------- * for terminals that have the EAT_NEWLINE_GLITCH auto_margin doesn't work * even though AUTO_RIGHT_MARGIN may be 1. So you have to check both * before writing the TTEOL * ------------------------------------------------------------------------- */ if (!AUTO_RIGHT_MARGIN || EAT_NEWLINE_GLITCH) { DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret); if (0 != ret) return -1; } number_of_lines_up++; cur_x = line_width = 0; cur_width = width; } } else { #endif for ( ; 0 < len; ) { cur_width = width - cur_x; if (cur_width) { if (len < cur_width) cur_width = len; DOWRITERL_A(fildes, str, cur_width, written); if (0 > written) return -1; str += written; len -= written; cur_x += cur_width; } if ((cur_x < width) && (0 == len)) break; /* last line is partial so no new line */ /* ------------------------------------------------------------------------- * for terminals that have the EAT_NEWLINE_GLITCH auto_margin doesn't work * even though AUTO_RIGHT_MARGIN may be 1. So you have to check both * before writing the TTEOL * ------------------------------------------------------------------------- */ if (!AUTO_RIGHT_MARGIN || EAT_NEWLINE_GLITCH) { DOWRITERC(fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL), ret); if (0 != ret) return -1; } number_of_lines_up++; cur_x = 0; cur_width = width; } #ifdef UNICODE_SUPPORTED } #endif number_of_chars_left = cur_x - start_x; if (!move) { ret = write_loop(fildes, (unsigned char *)CURSOR_UP, number_of_lines_up); if (0 > ret) return -1; if (number_of_chars_left > 0) { ret = write_loop(fildes, (unsigned char *)CURSOR_LEFT, number_of_chars_left); if (0 > ret) return -1; } else { ret = write_loop(fildes, (unsigned char *)CURSOR_RIGHT, -number_of_chars_left); if (0 > ret) return -1; } } return 0; }
short iott_readfl (mval *v, int4 length, int4 timeout) /* timeout in seconds */ { boolean_t ret, nonzerotimeout, timed; uint4 mask; unsigned char inchar, *temp; #ifdef __MVS__ unsigned char asc_inchar; #endif int dx, msk_in, msk_num, outlen, rdlen, save_errno, selstat, status, width; int4 msec_timeout; /* timeout in milliseconds */ io_desc *io_ptr; d_tt_struct *tt_ptr; io_terminator outofbands; io_termmask mask_term; unsigned char *zb_ptr, *zb_top; ABS_TIME cur_time, end_time; fd_set input_fd; struct timeval input_timeval; struct timeval save_input_timeval; error_def(ERR_CTRAP); error_def(ERR_IOEOF); error_def(ERR_NOPRINCIO); assert(stringpool.free >= stringpool.base); assert(stringpool.free <= stringpool.top); io_ptr = io_curr_device.in; tt_ptr = (d_tt_struct *)(io_ptr->dev_sp); assert(dev_open == io_ptr->state); iott_flush(io_curr_device.out); width = io_ptr->width; if (stringpool.free + length > stringpool.top) stp_gcol (length); outlen = 0; /* --------------------------------------------------------- * zb_ptr is be used to fill-in the value of $zb as we go * If we drop-out with error or otherwise permaturely, * consider $zb to be null. * --------------------------------------------------------- */ zb_ptr = io_ptr->dollar.zb; zb_top = zb_ptr + sizeof(io_ptr->dollar.zb) - 1; *zb_ptr = 0; io_ptr->esc_state = START; io_ptr->dollar.za = 0; io_ptr->dollar.zeof = FALSE; v->str.len = 0; dx = (int)io_ptr->dollar.x; ret = TRUE; temp = stringpool.free; mask = tt_ptr->term_ctrl; mask_term = tt_ptr->mask_term; if (mask & TRM_NOTYPEAHD) TCFLUSH(tt_ptr->fildes, TCIFLUSH, status); if (mask & TRM_READSYNC) { DOWRITERC(tt_ptr->fildes, &dc1, 1, status); if (0 != status) { io_ptr->dollar.za = 9; rts_error(VARLSTCNT(1) status); } } nonzerotimeout = FALSE; if (NO_M_TIMEOUT == timeout) { timed = FALSE; input_timeval.tv_sec = 100; msec_timeout = NO_M_TIMEOUT; } else { timed = TRUE; input_timeval.tv_sec = timeout; msec_timeout = timeout2msec(timeout); if (!msec_timeout) iott_mterm(io_ptr); else { nonzerotimeout = TRUE; sys_get_curr_time(&cur_time); add_int_to_abs_time(&cur_time, msec_timeout, &end_time); } } input_timeval.tv_usec = 0; do { if (outofband) { outlen = 0; if (!msec_timeout) iott_rterm(io_ptr); outofband_action(FALSE); break; } errno = 0; FD_ZERO(&input_fd); FD_SET(tt_ptr->fildes, &input_fd); assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd)); /* the checks for EINTR below are valid and should not be converted to EINTR * wrapper macros, since the select/read is not retried on EINTR. */ save_input_timeval = input_timeval; /* take a copy and pass it because select() below might change it */ selstat = select(tt_ptr->fildes + 1, (void *)&input_fd, (void *)NULL, (void *)NULL, &save_input_timeval); if (selstat < 0) { if (EINTR != errno) goto term_error; } else if (0 == selstat) { if (timed) { ret = FALSE; break; } continue; /* select() timeout; keep going */ } else if (0 < (rdlen = read(tt_ptr->fildes, &inchar, 1))) /* This read is protected */ { assert(0 != FD_ISSET(tt_ptr->fildes, &input_fd)); /* -------------------------------------------------- * set prin_in_dev_failure to FALSE to indicate that * input device is working now. * -------------------------------------------------- */ prin_in_dev_failure = FALSE; if (tt_ptr->canonical) { if (0 == inchar) { /* -------------------------------------- * This means that the device has hungup * -------------------------------------- */ io_ptr->dollar.zeof = TRUE; io_ptr->dollar.x = 0; io_ptr->dollar.za = 9; io_ptr->dollar.y++; if (io_ptr->error_handler.len > 0) rts_error(VARLSTCNT(1) ERR_IOEOF); break; } else io_ptr->dollar.zeof = FALSE; } if (mask & TRM_CONVERT) NATIVE_CVT2UPPER(inchar, inchar); GETASCII(asc_inchar,inchar); if ((dx >= width) && io_ptr->wrap && !(mask & TRM_NOECHO)) { DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); dx = 0; } if ((' ' > INPUT_CHAR) && (tt_ptr->enbld_outofbands.mask & (1 << INPUT_CHAR))) { outlen = 0; io_ptr->dollar.za = 9; std_dev_outbndset(INPUT_CHAR); /* it needs ASCII? */ outofband = 0; if (!msec_timeout) iott_rterm(io_ptr); rts_error(VARLSTCNT(3) ERR_CTRAP, 1, ctrap_action_is); break; } if ((0 != (mask & TRM_ESCAPE)) && ((NATIVE_ESC == inchar) || (START != io_ptr->esc_state))) { if (zb_ptr >= zb_top) { /* $zb overflow */ io_ptr->dollar.za = 2; break; } *zb_ptr++ = inchar; iott_escape(zb_ptr - 1, zb_ptr, io_ptr); *(zb_ptr - 1) = INPUT_CHAR; /* need to store ASCII value */ if (FINI == io_ptr->esc_state) break; if (BADESC == io_ptr->esc_state) { /* Escape sequence failed parse */ io_ptr->dollar.za = 2; break; } /* -------------------------------------------------------------------- * In escape sequence...do not process further, but get next character * -------------------------------------------------------------------- */ } else { /* SIMPLIFY THIS! */ msk_num = (uint4)INPUT_CHAR / NUM_BITS_IN_INT4; msk_in = (1 << ((uint4)INPUT_CHAR % NUM_BITS_IN_INT4)); if (msk_in & mask_term.mask[msk_num]) { *zb_ptr++ = INPUT_CHAR; break; } if (((int)inchar == tt_ptr->ttio_struct->c_cc[VERASE]) && !(mask & TRM_PASTHRU)) { if ((0< outlen) && (0 < dx)) { outlen--; dx--; *temp--; if (!(mask & TRM_NOECHO)) { DOWRITERC(tt_ptr->fildes, eraser, sizeof(eraser), status); if (0 != status) goto term_error; } } } else { if (!(mask & TRM_NOECHO)) { DOWRITERC(tt_ptr->fildes, &inchar, 1, status); if (0 != status) goto term_error; } *temp++ = inchar; outlen++; dx++; } } } else if (0 == rdlen) { if (0 < selstat) { /* this should be the only possibility */ io_ptr->dollar.zeof = TRUE; io_ptr->dollar.x = 0; io_ptr->dollar.za = 0; io_ptr->dollar.y++; 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(); } } if (io_ptr->dollar.zeof) { io_ptr->dollar.za = 9; rts_error(VARLSTCNT(1) ERR_IOEOF); } else { io_ptr->dollar.zeof = TRUE; io_ptr->dollar.za = 0; if (0 < io_ptr->error_handler.len) rts_error(VARLSTCNT(1) ERR_IOEOF); } break; } if (0 == errno) { /* eof */ io_ptr->dollar.zeof = TRUE; io_ptr->dollar.x = 0; io_ptr->dollar.za = 0; io_ptr->dollar.y++; if (0 < io_ptr->error_handler.len) rts_error(VARLSTCNT(1) ERR_IOEOF); break; } } else if (EINTR != errno) /* rdlen < 0 */ goto term_error; if (nonzerotimeout) { sys_get_curr_time(&cur_time); cur_time = sub_abs_time(&end_time, &cur_time); if (0 > cur_time.at_sec) { ret = FALSE; break; } input_timeval.tv_sec = cur_time.at_sec; input_timeval.tv_usec = cur_time.at_usec; } } while (outlen < length); *zb_ptr++ = 0; if (!msec_timeout) { iott_rterm(io_ptr); if (0 == outlen) ret = FALSE; } if (mask & TRM_READSYNC) { DOWRITERC(tt_ptr->fildes, &dc3, 1, status); if (0 != status) { io_ptr->dollar.za = 9; rts_error(VARLSTCNT(1) status); } } if (outofband) { v->str.len = 0; io_ptr->dollar.za = 9; return(FALSE); } v->str.len = outlen; v->str.addr = (char *)stringpool.free; if (!(mask & TRM_NOECHO)) { if ((io_ptr->dollar.x += v->str.len) >= io_ptr->width && io_ptr->wrap) { io_ptr->dollar.y += (io_ptr->dollar.x / io_ptr->width); if (io_ptr->length) io_ptr->dollar.y %= io_ptr->length; io_ptr->dollar.x %= io_ptr->width; if (0 == io_ptr->dollar.x) DOWRITE(tt_ptr->fildes, NATIVE_TTEOL, strlen(NATIVE_TTEOL)); } } return ((short)ret); term_error: save_errno = errno; io_ptr->dollar.za = 9; if (!msec_timeout) iott_rterm(io_ptr); rts_error(VARLSTCNT(1) save_errno); return FALSE; }
void iott_use(io_desc *iod, mval *pp) { boolean_t flush_input; char dc1, *ttab; d_tt_struct *temp_ptr, *tt_ptr; int p_offset, fil_type, save_errno, status; int4 length, width; io_desc *d_in, *d_out; io_termmask mask_term; struct sigaction act; struct termios t; uint4 mask_in; unsigned char ch, len; boolean_t ch_set; p_offset = 0; assert(iod->state == dev_open); ESTABLISH_GTMIO_CH(&iod->pair, ch_set); iott_flush(iod); tt_ptr = (d_tt_struct *)iod->dev_sp; if (*(pp->str.addr + p_offset) != iop_eol) { if (tt_ptr->mupintr) if (dollar_zininterrupt) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_ZINTRECURSEIO); else { /* The interrupted read was not properly resumed so clear it now */ tt_ptr->mupintr = FALSE; tt_ptr->tt_state_save.who_saved = ttwhichinvalid; io_find_mvstent(iod, TRUE); } status = tcgetattr(tt_ptr->fildes, &t); if (0 != status) { save_errno = errno; ISSUE_NOPRINCIO_IF_NEEDED_TT(io_curr_device.out); rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TCGETATTR, 1, tt_ptr->fildes, save_errno); } flush_input = FALSE; d_in = iod->pair.in; d_out = iod->pair.out; temp_ptr = (d_tt_struct *)d_in->dev_sp; mask_in = temp_ptr->term_ctrl; mask_term = temp_ptr->mask_term; while (*(pp->str.addr + p_offset) != iop_eol) { switch (ch = *(pp->str.addr + p_offset++)) { case iop_canonical: tt_ptr->canonical = TRUE; t.c_lflag |= ICANON; break; case iop_nocanonical: tt_ptr->canonical = FALSE; t.c_lflag &= ~(ICANON); break; case iop_empterm: tt_ptr->ext_cap |= TT_EMPTERM; break; case iop_noempterm: tt_ptr->ext_cap &= ~TT_EMPTERM; break; case iop_cenable: if (!ctrlc_on) { /* if it's already cenable, no need to change */ temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; if (tt_ptr->fildes == temp_ptr->fildes) { /* if this is $PRINCIPAL make sure the ctrlc_handler is enabled */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = ctrlc_handler_ptr; sigaction(SIGINT, &act, 0); ctrlc_on = TRUE; } } break; case iop_nocenable: if (ctrlc_on) { /* if it's already nocenable, no need to change */ temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; if (tt_ptr->fildes == temp_ptr->fildes) { /* if this is $PRINCIPAL may disable the ctrlc_handler */ if (0 == (CTRLC_MSK & tt_ptr->enbld_outofbands.mask)) { /* but only if ctrap=$c(3) is not active */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction(SIGINT, &act, 0); } ctrlc_on = FALSE; } } break; case iop_clearscreen: if (NULL != CLR_EOS) gtm_tputs(CLR_EOS, 1, outc); break; case iop_convert: mask_in |= TRM_CONVERT; break; case iop_noconvert: mask_in &= ~TRM_CONVERT; break; case iop_ctrap: GET_LONG(tt_ptr->enbld_outofbands.mask, pp->str.addr + p_offset); if (!ctrlc_on) { /* if cenable, ctrlc_handler active anyway, otherwise, depends on ctrap=$c(3) */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = (CTRLC_MSK & tt_ptr->enbld_outofbands.mask) ? ctrlc_handler_ptr : SIG_IGN; sigaction(SIGINT, &act, 0); } break; case iop_downscroll: if (d_out->dollar.y > 0) { d_out->dollar.y--; if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); } break; case iop_echo: mask_in &= (~TRM_NOECHO); break; case iop_noecho: mask_in |= TRM_NOECHO; break; case iop_editing: if (io_curr_device.in == io_std_device.in) { /* $PRINCIPAL only */ tt_ptr->ext_cap |= TT_EDITING; if (!tt_ptr->recall_buff.addr) { assert(tt_ptr->in_buf_sz); tt_ptr->recall_buff.addr = malloc(tt_ptr->in_buf_sz); tt_ptr->recall_size = tt_ptr->in_buf_sz; tt_ptr->recall_buff.len = 0; /* nothing in buffer */ } } break; case iop_noediting: if (io_curr_device.in == io_std_device.in) tt_ptr->ext_cap &= ~TT_EDITING; /* $PRINCIPAL only */ break; case iop_escape: mask_in |= TRM_ESCAPE; break; case iop_noescape: mask_in &= (~TRM_ESCAPE); default: break; case iop_eraseline: if (NULL != CLR_EOL) gtm_tputs(CLR_EOL, 1, outc); break; 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_filter: len = *(pp->str.addr + p_offset); ttab = pp->str.addr + p_offset + 1; if ((fil_type = namelook(filter_index, filter_names, ttab, len)) < 0) { rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TTINVFILTER); return; } switch (fil_type) { case 0: iod->write_filter |= CHAR_FILTER; break; case 1: iod->write_filter |= ESC1; break; case 2: iod->write_filter &= ~CHAR_FILTER; break; case 3: iod->write_filter &= ~ESC1; break; } break; case iop_nofilter: iod->write_filter = 0; break; case iop_flush: flush_input = TRUE; break; case iop_hostsync: t.c_iflag |= IXOFF; break; case iop_nohostsync: t.c_iflag &= ~IXOFF; break; case iop_insert: if (io_curr_device.in == io_std_device.in) tt_ptr->ext_cap &= ~TT_NOINSERT; /* $PRINCIPAL only */ break; case iop_noinsert: if (io_curr_device.in == io_std_device.in) tt_ptr->ext_cap |= TT_NOINSERT; /* $PRINCIPAL only */ break; case iop_length: GET_LONG(length, pp->str.addr + p_offset); if (0 > length) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DEVPARMNEG); d_out->length = length; break; case iop_pasthru: mask_in |= TRM_PASTHRU; break; case iop_nopasthru: mask_in &= (~TRM_PASTHRU); break; case iop_readsync: mask_in |= TRM_READSYNC; break; case iop_noreadsync: dc1 = (char)17; temp_ptr = (d_tt_struct *)io_std_device.in->dev_sp; DOWRITERC(temp_ptr->fildes, &dc1, 1, status); if (0 != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) status); mask_in &= (~TRM_READSYNC); break; case iop_terminator: memcpy(&mask_term.mask[0], (pp->str.addr + p_offset), SIZEOF(io_termmask)); temp_ptr = (d_tt_struct *)d_in->dev_sp; if (mask_term.mask[0] == NUL && mask_term.mask[1] == NUL && mask_term.mask[2] == NUL && mask_term.mask[3] == NUL && mask_term.mask[4] == NUL && mask_term.mask[5] == NUL && mask_term.mask[6] == NUL && mask_term.mask[7] == NUL) { temp_ptr->default_mask_term = TRUE; if (CHSET_UTF8 == d_in->ichset) { mask_term.mask[0] = TERM_MSK_UTF8_0; mask_term.mask[4] = TERM_MSK_UTF8_4; } else mask_term.mask[0] = TERM_MSK; } else temp_ptr->default_mask_term = FALSE; break; case iop_noterminator: temp_ptr = (d_tt_struct *)d_in->dev_sp; temp_ptr->default_mask_term = FALSE; memset(&mask_term.mask[0], 0, SIZEOF(io_termmask)); break; case iop_ttsync: t.c_iflag |= IXON; break; case iop_nottsync: t.c_iflag &= ~IXON; break; case iop_typeahead: mask_in &= (~TRM_NOTYPEAHD); break; case iop_notypeahead: mask_in |= TRM_NOTYPEAHD; break; case iop_upscroll: d_out->dollar.y++; if (d_out->length) d_out->dollar.y %= d_out->length; if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); break; case iop_width: GET_LONG(width, pp->str.addr + p_offset); if (0 > width) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_DEVPARMNEG); /* Do not allow a WIDTH of 1 if UTF mode (ICHSET or OCHSET is not M) */ if ((1 == width) && ((CHSET_M != d_in->ochset) || (CHSET_M != d_in->ichset))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_WIDTHTOOSMALL); if (0 == width) { d_out->wrap = FALSE; d_out->width = TTDEF_PG_WIDTH; } else { d_out->width = width; d_out->wrap = TRUE; } break; case iop_wrap: d_out->wrap = TRUE; break; case iop_nowrap: d_out->wrap = FALSE; break; case iop_x: GET_LONG(d_out->dollar.x, pp->str.addr + p_offset); if (0 > (int4)d_out->dollar.x) d_out->dollar.x = 0; if (d_out->dollar.x > d_out->width && d_out->wrap) { d_out->dollar.y += (d_out->dollar.x / d_out->width); if (d_out->length) d_out->dollar.y %= d_out->length; d_out->dollar.x %= d_out->width; } if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); break; case iop_y: GET_LONG(d_out->dollar.y, pp->str.addr + p_offset); if (0 > (int4)d_out->dollar.y) d_out->dollar.y = 0; if (d_out->length) d_out->dollar.y %= d_out->length; if (NULL != CURSOR_ADDRESS) gtm_tputs(gtm_tparm(CURSOR_ADDRESS, d_out->dollar.y, d_out->dollar.x), 1, outc); 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 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 break; } } p_offset += ((IOP_VAR_SIZE == io_params_size[ch]) ? (unsigned char)*(pp->str.addr + p_offset) + 1 : io_params_size[ch]); } temp_ptr = (d_tt_struct *)d_in->dev_sp; Tcsetattr(tt_ptr->fildes, TCSANOW, &t, status, save_errno); if (0 != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_TCSETATTR, 1, tt_ptr->fildes, save_errno); temp_ptr->term_ctrl = mask_in; memcpy(&temp_ptr->mask_term, &mask_term, SIZEOF(io_termmask)); if (flush_input) { TCFLUSH(tt_ptr->fildes, TCIFLUSH, status); if (0 != status) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, LIT_AND_LEN("tcflush input"), CALLFROM, errno); } } else if (tt_ptr->mupintr && !dollar_zininterrupt) { /* The interrupted read was not properly resumed so clear it now */ tt_ptr->mupintr = FALSE; tt_ptr->tt_state_save.who_saved = ttwhichinvalid; io_find_mvstent(iod, TRUE); /* clear mv stack entry */ } REVERT_GTMIO_CH(&iod->pair, ch_set); return; }
int gtm_trigger_complink(gv_trigger_t *trigdsc, boolean_t dolink) { char rtnname[GTM_PATH_MAX + 1], rtnname_template[GTM_PATH_MAX + 1]; char objname[GTM_PATH_MAX + 1]; char zcomp_parms[(GTM_PATH_MAX * 2) + SIZEOF(mident_fixed) + SIZEOF(OBJECT_PARM) + SIZEOF(NAMEOFRTN_PARM)]; mstr save_zsource; int rtnfd, rc, lenrtnname, lenobjname, len, alphnum_len, retry, save_errno; char *mident_suffix_p1, *mident_suffix_p2, *mident_suffix_top, *namesub1, *namesub2, *zcomp_parms_ptr; mval zlfile, zcompprm; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; DBGTRIGR_ONLY(memcpy(rtnname, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len)); DBGTRIGR_ONLY(rtnname[trigdsc->rtn_desc.rt_name.len] = 0); DBGTRIGR((stderr, "gtm_trigger_complink: (Re)compiling trigger %s\n", rtnname)); ESTABLISH_RET(gtm_trigger_complink_ch, ((0 == error_condition) ? TREF(dollar_zcstatus) : error_condition )); /* Verify there are 2 available chars for uniqueness */ assert((MAX_MIDENT_LEN - TRIGGER_NAME_RESERVED_SPACE) >= (trigdsc->rtn_desc.rt_name.len)); assert(NULL == trigdsc->rtn_desc.rt_adr); gtm_trigger_comp_prev_run_time = run_time; run_time = TRUE; /* Required by compiler */ /* Verify the routine name set by MUPIP TRIGGER and read by gvtr_db_read_hasht() is not in use */ if (NULL != find_rtn_hdr(&trigdsc->rtn_desc.rt_name)) { /* Ooops .. need name to be more unique.. */ /* Though variable definitions are conventionally done at the function entry, the reason alphanumeric_table * definition is done here is to minimize the time taken to initialize the below table in the most common case * (i.e. no trigger name collisions). */ char alphanumeric_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '\0'}; alphnum_len = STR_LIT_LEN(alphanumeric_table); namesub1 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++; /* If WBTEST_HELPOUT_TRIGNAMEUNIQ is defined, set alphnum_len to 1. This way, we make the maximum * possible combinations for the uniqe trigger names to be 3 which is significantly lesser than * the actual number of combinations (62x62 = 3844). For eg., if ^a is a global having triggers defined * in 4 global directories, then the possible unique trigger names are a#1# ; a#1#A ; a#1#AA. */ GTM_WHITE_BOX_TEST(WBTEST_HELPOUT_TRIGNAMEUNIQ, alphnum_len, 1); mident_suffix_top = (char *)alphanumeric_table + alphnum_len; /* Phase 1. See if any single character can add uniqueness */ for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top; mident_suffix_p1++) { *namesub1 = *mident_suffix_p1; if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name)) break; } if (mident_suffix_p1 == mident_suffix_top) { /* Phase 2. Phase 1 could not find uniqueness .. Find it with 2 char variations */ namesub2 = trigdsc->rtn_desc.rt_name.addr + trigdsc->rtn_desc.rt_name.len++; for (mident_suffix_p1 = (char *)alphanumeric_table; mident_suffix_p1 < mident_suffix_top; mident_suffix_p1++) { /* First char loop */ for (mident_suffix_p2 = (char *)alphanumeric_table; mident_suffix_p2 < mident_suffix_top; mident_suffix_p2++) { /* 2nd char loop */ *namesub1 = *mident_suffix_p1; *namesub2 = *mident_suffix_p2; if (NULL == find_rtn_hdr(&trigdsc->rtn_desc.rt_name)) { mident_suffix_p1 = mident_suffix_top + 1; /* Break out of both loops */ break; } } } if (mident_suffix_p1 == mident_suffix_top) { /* Phase 3: Punt */ assert(WBTEST_HELPOUT_TRIGNAMEUNIQ == gtm_white_box_test_case_number); rts_error(VARLSTCNT(5) ERR_TRIGNAMEUNIQ, 3, trigdsc->rtn_desc.rt_name.len - 2, trigdsc->rtn_desc.rt_name.addr, alphnum_len * alphnum_len); } } } /* Write trigger execute string out to temporary file and compile it */ assert(MAX_XECUTE_LEN >= trigdsc->xecute_str.str.len); rc = SNPRINTF(rtnname_template, GTM_PATH_MAX, "%s/trgtmpXXXXXX", DEFAULT_GTM_TMP); assert(0 < rc); /* Note rc is return code aka length - we expect a non-zero length */ assert(GTM_PATH_MAX >= rc); /* The mkstemp() routine is known to bogus-fail for no apparent reason at all especially on AIX 6.1. In the event * this shortcoming plagues other platforms as well, we add a low-cost retry wrapper. */ retry = MAX_MKSTEMP_RETRIES; do { strcpy(rtnname, rtnname_template); rtnfd = mkstemp(rtnname); } while ((-1 == rtnfd) && (EEXIST == errno) && (0 < --retry)); if (-1 == rtnfd) { save_errno = errno; assert(FALSE); rts_error(VARLSTCNT(12) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("mkstemp()"), CALLFROM, ERR_TEXT, 2, RTS_ERROR_TEXT(rtnname), save_errno); } assert(0 < rtnfd); /* Verify file descriptor */ rc = 0; # ifdef GEN_TRIGCOMPFAIL_ERROR { /* Used ONLY to generate an error in a trigger compile by adding some junk in a previous line */ DOWRITERC(rtnfd, ERROR_CAUSING_JUNK, strlen(ERROR_CAUSING_JUNK), rc); /* BYPASSOK */ if (0 != rc) { UNLINK(rtnname); rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc); } } # endif DOWRITERC(rtnfd, trigdsc->xecute_str.str.addr, trigdsc->xecute_str.str.len, rc); if (0 != rc) { UNLINK(rtnname); rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc); } if (NULL == memchr(trigdsc->xecute_str.str.addr, '\n', trigdsc->xecute_str.str.len)) { DOWRITERC(rtnfd, NEWLINE, strlen(NEWLINE), rc); /* BYPASSOK */ if (0 != rc) { UNLINK(rtnname); rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("write()"), CALLFROM, rc); } } CLOSEFILE(rtnfd, rc); if (0 != rc) { UNLINK(rtnname); rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("close()"), CALLFROM, rc); } assert(MAX_MIDENT_LEN > trigdsc->rtn_desc.rt_name.len); zcomp_parms_ptr = zcomp_parms; lenrtnname = STRLEN(rtnname); MEMCPY_LIT(zcomp_parms_ptr, NAMEOFRTN_PARM); zcomp_parms_ptr += STRLEN(NAMEOFRTN_PARM); memcpy(zcomp_parms_ptr, trigdsc->rtn_desc.rt_name.addr, trigdsc->rtn_desc.rt_name.len); zcomp_parms_ptr += trigdsc->rtn_desc.rt_name.len; MEMCPY_LIT(zcomp_parms_ptr, OBJECT_PARM); zcomp_parms_ptr += STRLEN(OBJECT_PARM); strcpy(objname, rtnname); /* Make copy of rtnname to become object name */ strcat(objname, OBJECT_FTYPE); /* Turn into object file reference */ lenobjname = lenrtnname + STRLEN(OBJECT_FTYPE); memcpy(zcomp_parms_ptr, objname, lenobjname); zcomp_parms_ptr += lenobjname; *zcomp_parms_ptr++ = ' '; memcpy(zcomp_parms_ptr, rtnname, lenrtnname); zcomp_parms_ptr += lenrtnname; *zcomp_parms_ptr = '\0'; /* Null tail */ len = INTCAST(zcomp_parms_ptr - zcomp_parms); assert((SIZEOF(zcomp_parms) - 1) > len); /* Verify no overflow */ zcompprm.mvtype = MV_STR; zcompprm.str.addr = zcomp_parms; zcompprm.str.len = len; /* Backup dollar_zsource so trigger doesn't show */ PUSH_MV_STENT(MVST_MSAV); mv_chain->mv_st_cont.mvs_msav.v = dollar_zsource; mv_chain->mv_st_cont.mvs_msav.addr = &dollar_zsource; TREF(trigger_compile) = TRUE; /* Set flag so compiler knows this is a special trigger compile */ op_zcompile(&zcompprm, FALSE); /* Compile but don't require a .m file extension */ TREF(trigger_compile) = FALSE; /* compile_source_file() establishes handler so always returns */ if (0 != TREF(dollar_zcstatus)) { /* Someone err'd.. */ run_time = gtm_trigger_comp_prev_run_time; REVERT; UNLINK(objname); /* Remove files before return error */ UNLINK(rtnname); return ERR_TRIGCOMPFAIL; } if (dolink) { /* Link is optional as MUPIP TRIGGER doesn't need link */ zlfile.mvtype = MV_STR; zlfile.str.addr = objname; zlfile.str.len = lenobjname; /* Specifying literal_null for a second arg (as opposed to NULL or 0) allows us to specify * linking the object file (no compilation or looking for source). The 2nd arg is parms for * recompilation and is non-null in an explicit zlink which we need to emulate. */ # ifdef GEN_TRIGLINKFAIL_ERROR UNLINK(objname); /* delete object before it can be used */ # endif op_zlink(&zlfile, (mval *)&literal_null); /* need cast due to "extern const" attributes */ /* No return here if link fails for some reason */ trigdsc->rtn_desc.rt_adr = find_rtn_hdr(&trigdsc->rtn_desc.rt_name); if (NULL == trigdsc->rtn_desc.rt_adr) GTMASSERT; /* Can't find routine we just put there? Catastrophic if happens */ /* Replace the randomly generated source name with the constant "GTM Trigger" */ trigdsc->rtn_desc.rt_adr->src_full_name.addr = GTM_TRIGGER_SOURCE_NAME; trigdsc->rtn_desc.rt_adr->src_full_name.len = STRLEN(GTM_TRIGGER_SOURCE_NAME); trigdsc->rtn_desc.rt_adr->trigr_handle = trigdsc; /* Back pointer to trig def */ } if (MVST_MSAV == mv_chain->mv_st_type && &dollar_zsource == mv_chain->mv_st_cont.mvs_msav.addr) { /* Top mv_stent is one we pushed on there - restore dollar_zsource and get rid of it */ dollar_zsource = mv_chain->mv_st_cont.mvs_msav.v; POP_MV_STENT(); } else assert(FALSE); /* This mv_stent should be the one we just pushed */ /* Remove temporary files created */ UNLINK(objname); /* Delete the object file first since rtnname is the unique key */ UNLINK(rtnname); /* Delete the source file */ run_time = gtm_trigger_comp_prev_run_time; REVERT; return 0; }