fis-gtm/sr_unix/zshow_devices.c

708 lines
21 KiB
C

/****************************************************************
* *
* 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 "gtm_socket.h"
#include "gtm_inet.h"
#include "gtm_stdio.h"
#include "mlkdef.h"
#include "zshow.h"
#include "io.h"
#include "iottdef.h"
#include "trmdef.h"
#include "iormdef.h"
#include "iotcpdef.h"
#include "gt_timer.h"
#include "iosocketdef.h"
#include "zshow_params.h"
#include "nametabtyp.h"
#include "mvalconv.h"
#ifdef __MVS__
#include "gtm_zos_io.h"
#include <_Ccsid.h>
#endif
#define ZS_ONE_OUT(V,TEXT) ((V)->str.len = 1, (V)->str.addr = (TEXT), zshow_output(output,&(V)->str))
#define ZS_STR_OUT(V,TEXT) ((V)->str.len = SIZEOF((TEXT)) - 1, (V)->str.addr = (TEXT), zshow_output(output,&(V)->str))
#define ZS_VAR_STR_OUT(V,TEXT) ((V)->str.len = STRLEN((TEXT)), (V)->str.addr = (TEXT), zshow_output(output,&(V)->str))
#define ZS_PARM_SP(V,TEXT) ((V)->str.len = dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \
zshow_param_index[(TEXT)].offset ].len, \
(V)->str.addr = (char *)dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \
zshow_param_index[(TEXT)].offset ].name, zshow_output(output,&(V)->str), ZS_ONE_OUT((V),space_text))
#define ZS_PARM_EQU(V,TEXT) ((V)->str.len = dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \
zshow_param_index[(TEXT)].offset ].len, \
(V)->str.addr = (char *)dev_param_names[dev_param_index[zshow_param_index[(TEXT)].letter] + \
zshow_param_index[(TEXT)].offset ].name, zshow_output(output,&(V)->str), ZS_ONE_OUT((V),equal_text))
static readonly char space_text[] = {' '};
LITREF mstr chset_names[];
LITREF nametabent dev_param_names[];
LITREF unsigned char dev_param_index[];
LITREF zshow_index zshow_param_index[];
GBLREF bool ctrlc_on;
GBLREF io_log_name *io_root_log_name;
GBLREF io_pair *io_std_device;
GBLREF boolean_t gtm_utf8_mode;
void zshow_devices(zshow_out *output)
{
io_log_name *l; /* logical name pointer */
mval v;
mval m;
d_rm_struct *rm_ptr;
d_tt_struct *tt_ptr;
d_socket_struct *dsocketptr;
socket_struct *socketptr;
io_termmask *mask_out;
int4 i, j, ii, jj;
boolean_t first;
unsigned char delim_buff_sm[MAX_DELIM_LEN];
unsigned short delim_len_sm;
char delim_mstr_buff[(MAX_DELIM_LEN * MAX_ZWR_EXP_RATIO) + 11];
mstr delim;
int delim_len, tmpport;
static readonly char space8_text[] = " ";
static readonly char filchar_text[] = "CHARACTERS";
static readonly char filesc_text[] = "ESCAPES";
static readonly char terminal_text[] = "TERMINAL ";
static readonly char magtape_text[] = "MAGTAPE ";
static readonly char rmsfile_text[] = "RMS ";
static readonly char fifo_text[] = "FIFO ";
static readonly char pipe_text[] = "PIPE ";
static readonly char mailbox_text[] = "MAILBOX ";
static readonly char dollarc_text[] = "$C(";
static readonly char equal_text[] = {'='};
static readonly char comma_text[] = {','};
static readonly char quote_text[] = {'"'};
static readonly char lparen_text[] = {'('};
static readonly char rparen_text[] = {')'};
static readonly char lb_text[] = {'['};
static readonly char rb_text[] = {']'};
static readonly char devop[] = "OPEN ";
static readonly char devcl[] = "CLOSED ";
static readonly char interrupt_text[] = "ZINTERRUPT ";
/* gtmsocket specific */
static readonly char at_text[] = {'@'};
static readonly char delimiter_text[] = "DELIMITER ";
static readonly char nodelimiter_text[] = "NODELIMITER ";
static readonly char local_text[] = "LOCAL=";
static readonly char remote_text[] = "REMOTE=";
static readonly char total_text[] = "TOTAL=";
static readonly char current_text[] = "CURRENT=";
static readonly char passive_text[] = "PASSIVE ";
static readonly char active_text[] = "ACTIVE ";
static readonly char socket_text[] = "SOCKET";
static readonly char descriptor_text[] = "DESC=";
static readonly char trap_text[] = "TRAP ";
static readonly char notrap_text[] = "NOTRAP ";
static readonly char zdelay_text[] = "ZDELAY ";
static readonly char znodelay_text[] = "ZNODELAY ";
static readonly char zbfsize_text[] = "ZBFSIZE=";
static readonly char zibfsize_text[] = "ZIBFSIZE=";
static readonly char port_text[] = "PORT=";
static readonly char ichset_text[] = "ICHSET=";
static readonly char ochset_text[] = "OCHSET=";
#ifdef __MVS__
static readonly char filetag_text[] = "FILETAG=";
static readonly char untagged_text[] = "UNTAGGED";
static readonly char ebcdic_text[] = "EBCDIC";
static readonly char binary_text[] = "BINARY";
static readonly char processchset_text[] = "CHSET=";
static readonly char text_text[] = " TEXT";
char csname[_CSNAME_LEN_MAX + 1], *csptr;
#endif
static readonly char zsh_socket_state[][10] =
{ "CONNECTED"
,"LISTENING"
,"BOUND"
,"CREATED"
};
static readonly char morereadtime_text[] = "MOREREADTIME=";
v.mvtype = MV_STR;
for (l = io_root_log_name; l != 0; l = l->next)
{
if (l->iod->trans_name == l)
{
/* if it is an rm type we don't want to output the device if it is the stderr
device for a pipe device */
if ((rm_ptr = (d_rm_struct*)l->iod->dev_sp) && rm == l->iod->type && rm_ptr->pipe && rm_ptr->stderr_parent)
continue;
v.str.addr = &l->dollar_io[0];
v.str.len = l->len;
zshow_output(output,&v.str);
ZS_ONE_OUT(&v, space_text);
if (l->iod->state == dev_open)
{
ZS_STR_OUT(&v, devop);
switch(l->iod->type)
{
case tt:
ZS_STR_OUT(&v, terminal_text);
tt_ptr = (d_tt_struct*)l->iod->dev_sp;
if (!ctrlc_on && io_std_device->out == l->iod) /* and standard input */
{ ZS_PARM_SP(&v, zshow_nocene);
}
if (tt_ptr->enbld_outofbands.mask)
{ ZS_PARM_EQU(&v, zshow_ctra);
ZS_STR_OUT(&v,dollarc_text);
first = TRUE;
for ( i = 1, j = 0; j < 32 ; j++,i = i * 2)
{ if (i & tt_ptr->enbld_outofbands.mask)
{ if (!first)
{ ZS_ONE_OUT(&v, comma_text);
}else
{ first = FALSE;
}
MV_FORCE_MVAL(&m,j);
mval_write(output,&m,FALSE);
}
}
ZS_ONE_OUT(&v, rparen_text);
ZS_ONE_OUT(&v, space_text);
}
if ((int4)(tt_ptr->term_ctrl) & TRM_NOECHO)
{
ZS_PARM_SP(&v, zshow_noecho);
}
if (tt_ptr->term_ctrl & TRM_PASTHRU)
{
ZS_PARM_SP(&v, zshow_past);
} else
{
ZS_PARM_SP(&v, zshow_nopast);
}
if (!(tt_ptr->term_ctrl & TRM_ESCAPE))
{
ZS_PARM_SP(&v, zshow_noesca);
}
if (tt_ptr->term_ctrl & TRM_READSYNC)
{
ZS_PARM_SP(&v, zshow_reads);
} else
{
ZS_PARM_SP(&v, zshow_noreads);
}
if (tt_ptr->term_ctrl & TRM_NOTYPEAHD)
{
ZS_PARM_SP(&v, zshow_notype);
} else
{
ZS_PARM_SP(&v, zshow_type);
}
if (!l->iod->wrap)
{
ZS_PARM_SP(&v, zshow_nowrap);
}
mask_out = &tt_ptr->mask_term;
if (!tt_ptr->default_mask_term)
{
ZS_PARM_EQU(&v, zshow_term);
ZS_STR_OUT(&v,dollarc_text);
first = TRUE;
for ( i = 0; i < 8 ;i++)
{
for ( j = 0; j < 32; j++)
if (mask_out->mask[i] & (1 << j))
{
if (!first)
{
ZS_ONE_OUT(&v, comma_text);
} else
first = FALSE;
MV_FORCE_MVAL(&m,i * 32 + j);
mval_write(output,&m,FALSE);
}
}
ZS_ONE_OUT(&v, rparen_text);
ZS_ONE_OUT(&v, space_text);
}
ZS_PARM_EQU(&v, zshow_width);
MV_FORCE_MVAL(&m,(int)l->iod->width);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
ZS_PARM_EQU(&v, zshow_leng);
MV_FORCE_MVAL(&m,(int)l->iod->pair.out->length);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
if (l->iod->write_filter)
{
bool twoparms = FALSE;
ZS_PARM_EQU(&v, zshow_fil);
if (l->iod->write_filter & CHAR_FILTER)
{
if (l->iod->write_filter & ESC1)
{
twoparms = TRUE;
ZS_ONE_OUT(&v,lparen_text);
}
ZS_STR_OUT(&v,filchar_text);
if (twoparms)
{
ZS_ONE_OUT(&v, comma_text);
ZS_ONE_OUT(&v, space_text);
}
}
if (l->iod->write_filter & ESC1)
ZS_STR_OUT(&v,filesc_text);
if (twoparms)
ZS_ONE_OUT(&v,rparen_text);
ZS_ONE_OUT(&v, space_text);
}
if (TT_EDITING & tt_ptr->ext_cap)
ZS_PARM_SP(&v, zshow_edit);
if (TT_NOINSERT & tt_ptr->ext_cap)
ZS_PARM_SP(&v, zshow_noinse);
if (TT_EMPTERM & tt_ptr->ext_cap)
ZS_PARM_SP(&v, zshow_empterm);
if (tt_ptr->canonical)
ZS_STR_OUT(&v, "CANONICAL ");
switch(l->iod->ichset)
{
case CHSET_M:
if (gtm_utf8_mode)
{
ZS_STR_OUT(&v, ichset_text);
zshow_output(output, &chset_names[l->iod->ichset]);
ZS_ONE_OUT(&v, space_text);
}
break;
case CHSET_UTF8:
assert(gtm_utf8_mode);
break;
default:
GTMASSERT;
}
switch(l->iod->ochset)
{
case CHSET_M:
if (gtm_utf8_mode)
{
ZS_STR_OUT(&v, ochset_text);
zshow_output(output, &chset_names[l->iod->ochset]);
ZS_ONE_OUT(&v, space_text);
}
break;
case CHSET_UTF8:
assert(gtm_utf8_mode);
break;
default:
GTMASSERT;
}
if (tt_ptr->mupintr)
ZS_STR_OUT(&v, interrupt_text);
break;
case rm:
/* we go to rm_ptr above for the rm type */
if (rm_ptr->fifo)
ZS_STR_OUT(&v,fifo_text);
else if (!rm_ptr->pipe)
{
ZS_STR_OUT(&v,rmsfile_text);
if (rm_ptr->follow)
{
ZS_PARM_SP(&v, zshow_follow);
}
}
else
{
ZS_STR_OUT(&v,pipe_text);
if (rm_ptr->dev_param_pairs.num_pairs)
{
int ignore_stderr = FALSE;
/* if one of the dev_param_pairs[i]->name is the
STDERR then we don't want to output it if the
device is closed. We'll check them all even though
it is currently the last one - just to be safe. */
if (rm_ptr->stderr_child && (dev_open !=
rm_ptr->stderr_child->state))
ignore_stderr = TRUE;
for ( i = 0; i < rm_ptr->dev_param_pairs.num_pairs; i++ )
{
if (TRUE == ignore_stderr &&
0 == STRCMP(rm_ptr->dev_param_pairs.pairs[i].name,
"STDERR="))
continue;
ZS_VAR_STR_OUT(
&v,rm_ptr->dev_param_pairs.pairs[i].name);
ZS_VAR_STR_OUT(
&v,rm_ptr->dev_param_pairs.pairs[i].definition);
ZS_ONE_OUT(&v, space_text);
}
}
if (rm_ptr->independent)
{
ZS_PARM_SP(&v, zshow_independent);
}
if (rm_ptr->parse)
{
ZS_PARM_SP(&v, zshow_parse);
}
}
if (rm_ptr->fixed)
{
ZS_PARM_SP(&v, zshow_fixed);
}
if (rm_ptr->noread)
{
ZS_PARM_SP(&v, zshow_read);
}
if (gtm_utf8_mode && (IS_UTF_CHSET(l->iod->ichset) || IS_UTF_CHSET(l->iod->ochset)))
{
if (!rm_ptr->def_recsize)
{
ZS_PARM_EQU(&v, zshow_rec);
MV_FORCE_MVAL(&m, (int)rm_ptr->recordsize);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
}
if (!rm_ptr->def_width)
{
ZS_PARM_EQU(&v, zshow_width);
MV_FORCE_MVAL(&m, (int)l->iod->width);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
}
}
else if (l->iod->width != DEF_RM_WIDTH)
{
ZS_PARM_EQU(&v, zshow_rec);
MV_FORCE_MVAL(&m,(int)l->iod->width);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
}
if (!l->iod->wrap)
{
ZS_PARM_SP(&v, zshow_nowrap);
}
switch(l->iod->ichset)
{
case CHSET_M:
if (gtm_utf8_mode)
{
ZS_STR_OUT(&v, ichset_text);
zshow_output(output, &chset_names[l->iod->ichset]);
ZS_ONE_OUT(&v, space_text);
}
break;
case CHSET_UTF8:
assert(gtm_utf8_mode);
break;
case CHSET_UTF16:
case CHSET_UTF16BE:
case CHSET_UTF16LE:
assert(gtm_utf8_mode);
ZS_STR_OUT(&v, ichset_text);
zshow_output(output, &chset_names[l->iod->ichset]);
ZS_ONE_OUT(&v, space_text);
break;
default:
GTMASSERT;
}
switch(l->iod->ochset)
{
case CHSET_M:
if (gtm_utf8_mode)
{
ZS_STR_OUT(&v, ochset_text);
zshow_output(output, &chset_names[l->iod->ochset]);
ZS_ONE_OUT(&v, space_text);
}
break;
case CHSET_UTF8:
assert(gtm_utf8_mode);
break;
case CHSET_UTF16:
case CHSET_UTF16BE:
case CHSET_UTF16LE:
assert(gtm_utf8_mode);
ZS_STR_OUT(&v, ochset_text);
zshow_output(output, &chset_names[l->iod->ochset]);
ZS_ONE_OUT(&v, space_text);
break;
default:
GTMASSERT;
}
#ifdef __MVS__
if (TAG_ASCII != l->iod->file_tag)
{
ZS_STR_OUT(&v, filetag_text);
switch ((unsigned int)l->iod->file_tag)
{
case TAG_UNTAGGED:
ZS_STR_OUT(&v, untagged_text);
break;
case TAG_EBCDIC:
ZS_STR_OUT(&v, ebcdic_text);
break;
case TAG_BINARY:
ZS_STR_OUT(&v, binary_text);
break;
default:
if (-1 == __toCSName((__ccsid_t)l->iod->file_tag, csname))
{ /* no name so output number */
csptr = (char *)i2asc((uchar_ptr_t)csname,
(unsigned int)l->iod->file_tag);
*csptr = '\0'; /* terminate */
}
ZS_VAR_STR_OUT(&v, csname);
}
if (l->iod->text_flag)
ZS_STR_OUT(&v, text_text);
ZS_ONE_OUT(&v, space_text);
}
if (l->iod->file_chset != l->iod->process_chset &&
(!(0 == l->iod->file_chset && CHSET_ASCII == l->iod->process_chset) &&
!(CHSET_ASCII == l->iod->file_chset && CHSET_M == l->iod->process_chset)))
{ /* suppress default cases */
ZS_STR_OUT(&v, processchset_text);
zshow_output(output, &chset_names[l->iod->process_chset]);
ZS_ONE_OUT(&v, space_text);
}
#endif
break;
case gtmsocket:
delim.addr = delim_mstr_buff;
delim_len = 0;
ZS_STR_OUT(&v, socket_text);
dsocketptr = (d_socket_struct *)l->iod->dev_sp;
ZS_ONE_OUT(&v, space_text);
ZS_STR_OUT(&v, total_text);
MV_FORCE_MVAL(&m, (int)dsocketptr->n_socket);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
ZS_STR_OUT(&v, current_text);
MV_FORCE_MVAL(&m, (int)dsocketptr->current_socket);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
if (dsocketptr->mupintr)
ZS_STR_OUT(&v, interrupt_text);
output->flush = TRUE;
zshow_output(output, 0);
for(ii = 0; ii < dsocketptr->n_socket; ii++)
{
/* output each socket */
socketptr = dsocketptr->socket[ii];
ZS_STR_OUT(&v, space8_text);
/* socket handle */
ZS_STR_OUT(&v, socket_text);
ZS_ONE_OUT(&v, lb_text);
MV_FORCE_MVAL(&m, ii);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, rb_text);
ZS_ONE_OUT(&v, equal_text);
v.str.addr = socketptr->handle;
v.str.len = socketptr->handle_len;
zshow_output(output, &v.str);
ZS_ONE_OUT(&v, space_text);
/* socket descriptor */
ZS_STR_OUT(&v, descriptor_text);
MV_FORCE_MVAL(&m, socketptr->sd);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
/* socket state */
ZS_STR_OUT(&v, zsh_socket_state[socketptr->state]);
ZS_ONE_OUT(&v, space_text);
/* socket IO mode */ switch(l->iod->ichset)
{
case CHSET_M:
if (gtm_utf8_mode)
{
ZS_STR_OUT(&v, ichset_text);
zshow_output(output, &chset_names[l->iod->ichset]);
ZS_ONE_OUT(&v, space_text);
}
break;
case CHSET_UTF8:
assert(gtm_utf8_mode);
break;
case CHSET_UTF16:
case CHSET_UTF16BE:
case CHSET_UTF16LE:
assert(gtm_utf8_mode);
ZS_STR_OUT(&v, ichset_text);
zshow_output(output, &chset_names[l->iod->ichset]);
ZS_ONE_OUT(&v, space_text);
break;
default:
GTMASSERT;
}
switch(l->iod->ochset)
{
case CHSET_M:
if (gtm_utf8_mode)
{
ZS_STR_OUT(&v, ochset_text);
zshow_output(output, &chset_names[l->iod->ochset]);
ZS_ONE_OUT(&v, space_text);
}
break;
case CHSET_UTF8:
assert(gtm_utf8_mode);
break;
case CHSET_UTF16:
case CHSET_UTF16BE:
case CHSET_UTF16LE:
assert(gtm_utf8_mode);
ZS_STR_OUT(&v, ochset_text);
zshow_output(output, &chset_names[l->iod->ochset]);
ZS_ONE_OUT(&v, space_text);
break;
default:
GTMASSERT;
}
/* socket type */
if (socketptr->passive)
{
ZS_STR_OUT(&v, passive_text);
} else
{
ZS_STR_OUT(&v, active_text);
}
ZS_ONE_OUT(&v, space_text);
/* error trapping */
if (socketptr->ioerror)
{
ZS_STR_OUT(&v, trap_text);
} else
{
ZS_STR_OUT(&v, notrap_text);
}
ZS_ONE_OUT(&v, space_text);
/* address + port */
if (socketptr->passive)
{
ZS_STR_OUT(&v, port_text);
tmpport = (int)socketptr->local.port;
MV_FORCE_MVAL(&m, tmpport);
mval_write(output, &m, FALSE);
} else
{
ZS_STR_OUT(&v, remote_text);
if (NULL != socketptr->remote.saddr_ip)
{
v.str.addr = socketptr->remote.saddr_ip;
v.str.len = STRLEN(socketptr->remote.saddr_ip);
} else
{
v.str.addr = "";
v.str.len = 0;
}
zshow_output(output, &v.str);
ZS_ONE_OUT(&v, at_text);
tmpport = (int)socketptr->remote.port;
MV_FORCE_MVAL(&m, tmpport);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
if (NULL != socketptr->local.saddr_ip)
{
ZS_STR_OUT(&v, local_text);
v.str.addr = socketptr->local.saddr_ip;
v.str.len = STRLEN(socketptr->local.saddr_ip);
zshow_output(output, &v.str);
ZS_ONE_OUT(&v, at_text);
tmpport = (int)socketptr->local.port;
MV_FORCE_MVAL(&m, tmpport);
mval_write(output, &m, FALSE);
}
}
ZS_ONE_OUT(&v, space_text);
output->flush = TRUE;
zshow_output(output, 0);
ZS_STR_OUT(&v, space8_text);
ZS_STR_OUT(&v, space8_text);
/* zdelay */
if (socketptr->nodelay)
{
ZS_STR_OUT(&v, znodelay_text);
} else
{
ZS_STR_OUT(&v, zdelay_text);
}
ZS_ONE_OUT(&v, space_text);
/* zbfsize */
ZS_STR_OUT(&v, zbfsize_text);
MV_FORCE_MVAL(&m, (int4)(socketptr->buffer_size));
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
/* izbfsize */
ZS_STR_OUT(&v, zibfsize_text);
MV_FORCE_MVAL(&m, socketptr->bufsiz);
mval_write(output, &m, FALSE);
ZS_ONE_OUT(&v, space_text);
/* delimiters */
if (socketptr->n_delimiter > 0)
{
output->flush = TRUE;
zshow_output(output, 0);
ZS_STR_OUT(&v, space8_text);
ZS_STR_OUT(&v, space8_text);
ZS_STR_OUT(&v, delimiter_text);
for (jj = 0; jj < socketptr->n_delimiter; jj++)
{
delim_len_sm = socketptr->delimiter[jj].len;
memcpy(delim_buff_sm,
socketptr->delimiter[jj].addr, delim_len_sm);
format2zwr(delim_buff_sm, delim_len_sm,
(uchar_ptr_t)delim.addr, &delim_len);
delim.len = (unsigned short)delim_len;
assert(SIZEOF(delim_mstr_buff) >= delim_len);
zshow_output(output, &delim);
ZS_ONE_OUT(&v, space_text);
}
} else
{
ZS_STR_OUT(&v, nodelimiter_text);
}
/* readmoretime */
if (DEFAULT_MOREREAD_TIMEOUT != socketptr->moreread_timeout)
{
ZS_STR_OUT(&v, morereadtime_text);
MV_FORCE_MVAL(&m, (int)socketptr->moreread_timeout);
mval_write(output, &m, FALSE);
}
output->flush = TRUE;
zshow_output(output, 0);
}
default:
v.str.len = 0;
break;
}
if (l->iod->error_handler.len)
{
ZS_PARM_EQU(&v, zshow_exce);
ZS_ONE_OUT(&v, quote_text);
v.str = l->iod->error_handler;
zshow_output(output, &v.str);
output->flush = TRUE;
ZS_ONE_OUT(&v, quote_text);
} else
{ output->flush = TRUE;
zshow_output(output, 0);
}
} else
{ output->flush = TRUE;
ZS_STR_OUT(&v, devcl);
}
}
}
}