char: remove explicit_be_open from CharDriverState

It's only used in qmp_chardev_add(), so use a create() argument instead.

Also switched to typedef functions for CharDriverParse/CharDriverCreate.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20161022100951.19562-7-marcandre.lureau@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Marc-André Lureau 2016-10-22 13:09:43 +03:00 committed by Paolo Bonzini
parent ebf4c54d4b
commit 82878dac6f
8 changed files with 59 additions and 32 deletions

View File

@ -566,6 +566,7 @@ static void baum_free(struct CharDriverState *chr)
static CharDriverState *chr_baum_init(const char *id, static CharDriverState *chr_baum_init(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevCommon *common = backend->u.braille.data; ChardevCommon *common = backend->u.braille.data;

View File

@ -151,6 +151,7 @@ static QemuInputHandler msmouse_handler = {
static CharDriverState *qemu_chr_open_msmouse(const char *id, static CharDriverState *qemu_chr_open_msmouse(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevCommon *common = backend->u.msmouse.data; ChardevCommon *common = backend->u.msmouse.data;
@ -164,7 +165,7 @@ static CharDriverState *qemu_chr_open_msmouse(const char *id,
chr->chr_write = msmouse_chr_write; chr->chr_write = msmouse_chr_write;
chr->chr_free = msmouse_chr_free; chr->chr_free = msmouse_chr_free;
chr->chr_accept_input = msmouse_chr_accept_input; chr->chr_accept_input = msmouse_chr_accept_input;
chr->explicit_be_open = true; *be_opened = false;
mouse = g_new0(MouseState, 1); mouse = g_new0(MouseState, 1);
mouse->hs = qemu_input_handler_register((DeviceState *)mouse, mouse->hs = qemu_input_handler_register((DeviceState *)mouse,

View File

@ -112,6 +112,7 @@ static void testdev_free(struct CharDriverState *chr)
static CharDriverState *chr_testdev_init(const char *id, static CharDriverState *chr_testdev_init(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
TestdevCharState *testdev; TestdevCharState *testdev;

View File

@ -109,7 +109,6 @@ struct CharDriverState {
char *filename; char *filename;
int logfd; int logfd;
int be_open; int be_open;
int explicit_be_open;
int is_mux; int is_mux;
guint fd_in_tag; guint fd_in_tag;
bool replay; bool replay;
@ -474,10 +473,15 @@ void qemu_chr_set_feature(CharDriverState *chr,
CharDriverFeature feature); CharDriverFeature feature);
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename);
typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend,
Error **errp);
typedef CharDriverState *CharDriverCreate(const char *id,
ChardevBackend *backend,
ChardevReturn *ret, bool *be_opened,
Error **errp);
void register_char_driver(const char *name, ChardevBackendKind kind, void register_char_driver(const char *name, ChardevBackendKind kind,
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), CharDriverParse *parse, CharDriverCreate *create);
CharDriverState *(*create)(const char *id, ChardevBackend *backend,
ChardevReturn *ret, Error **errp));
extern int term_escape_char; extern int term_escape_char;

View File

@ -509,6 +509,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
static CharDriverState *qemu_chr_open_null(const char *id, static CharDriverState *qemu_chr_open_null(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -519,7 +520,7 @@ static CharDriverState *qemu_chr_open_null(const char *id,
return NULL; return NULL;
} }
chr->chr_write = null_chr_write; chr->chr_write = null_chr_write;
chr->explicit_be_open = true; *be_opened = false;
return chr; return chr;
} }
@ -836,7 +837,9 @@ static void mux_set_focus(MuxDriver *d, int focus)
static CharDriverState *qemu_chr_open_mux(const char *id, static CharDriverState *qemu_chr_open_mux(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, Error **errp) ChardevReturn *ret,
bool *be_opened,
Error **errp)
{ {
ChardevMux *mux = backend->u.mux.data; ChardevMux *mux = backend->u.mux.data;
CharDriverState *chr, *drv; CharDriverState *chr, *drv;
@ -868,7 +871,7 @@ static CharDriverState *qemu_chr_open_mux(const char *id,
/* only default to opened state if we've realized the initial /* only default to opened state if we've realized the initial
* set of muxes * set of muxes
*/ */
chr->explicit_be_open = muxes_realized ? 0 : 1; *be_opened = muxes_realized;
chr->is_mux = 1; chr->is_mux = 1;
if (!qemu_chr_fe_init(&d->chr, drv, errp)) { if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
qemu_chr_free(chr); qemu_chr_free(chr);
@ -1280,6 +1283,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out,
static CharDriverState *qemu_chr_open_pipe(const char *id, static CharDriverState *qemu_chr_open_pipe(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevHostdev *opts = backend->u.pipe.data; ChardevHostdev *opts = backend->u.pipe.data;
@ -1362,6 +1366,7 @@ static void qemu_chr_free_stdio(struct CharDriverState *chr)
static CharDriverState *qemu_chr_open_stdio(const char *id, static CharDriverState *qemu_chr_open_stdio(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevStdio *opts = backend->u.stdio.data; ChardevStdio *opts = backend->u.stdio.data;
@ -1609,6 +1614,7 @@ static void pty_chr_free(struct CharDriverState *chr)
static CharDriverState *qemu_chr_open_pty(const char *id, static CharDriverState *qemu_chr_open_pty(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -1645,7 +1651,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id,
chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_update_read_handler = pty_chr_update_read_handler;
chr->chr_free = pty_chr_free; chr->chr_free = pty_chr_free;
chr->chr_add_watch = pty_chr_add_watch; chr->chr_add_watch = pty_chr_add_watch;
chr->explicit_be_open = true; *be_opened = false;
s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd));
s->timer_tag = 0; s->timer_tag = 0;
@ -1845,6 +1851,7 @@ static void qemu_chr_free_tty(CharDriverState *chr)
static CharDriverState *qemu_chr_open_tty_fd(int fd, static CharDriverState *qemu_chr_open_tty_fd(int fd,
ChardevCommon *backend, ChardevCommon *backend,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -1975,6 +1982,7 @@ static void pp_free(CharDriverState *chr)
static CharDriverState *qemu_chr_open_pp_fd(int fd, static CharDriverState *qemu_chr_open_pp_fd(int fd,
ChardevCommon *backend, ChardevCommon *backend,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -2047,6 +2055,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
static CharDriverState *qemu_chr_open_pp_fd(int fd, static CharDriverState *qemu_chr_open_pp_fd(int fd,
ChardevCommon *backend, ChardevCommon *backend,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -2058,7 +2067,7 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd,
chr->opaque = (void *)(intptr_t)fd; chr->opaque = (void *)(intptr_t)fd;
chr->chr_write = null_chr_write; chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl; chr->chr_ioctl = pp_ioctl;
chr->explicit_be_open = true; *be_opened = false;
return chr; return chr;
} }
#endif #endif
@ -2387,6 +2396,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename,
static CharDriverState *qemu_chr_open_pipe(const char *id, static CharDriverState *qemu_chr_open_pipe(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevHostdev *opts = backend->u.pipe.data; ChardevHostdev *opts = backend->u.pipe.data;
@ -2433,6 +2443,7 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out,
static CharDriverState *qemu_chr_open_win_con(const char *id, static CharDriverState *qemu_chr_open_win_con(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevCommon *common = backend->u.console.data; ChardevCommon *common = backend->u.console.data;
@ -2578,6 +2589,7 @@ static void win_stdio_free(CharDriverState *chr)
static CharDriverState *qemu_chr_open_stdio(const char *id, static CharDriverState *qemu_chr_open_stdio(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -2753,6 +2765,7 @@ static void udp_chr_free(CharDriverState *chr)
static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
ChardevCommon *backend, ChardevCommon *backend,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr = NULL; CharDriverState *chr = NULL;
@ -2772,7 +2785,7 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc,
chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_update_read_handler = udp_chr_update_read_handler;
chr->chr_free = udp_chr_free; chr->chr_free = udp_chr_free;
/* be isn't opened until we get a connection */ /* be isn't opened until we get a connection */
chr->explicit_be_open = true; *be_opened = false;
return chr; return chr;
} }
@ -3504,6 +3517,7 @@ static void ringbuf_chr_free(struct CharDriverState *chr)
static CharDriverState *qemu_chr_open_ringbuf(const char *id, static CharDriverState *qemu_chr_open_ringbuf(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevRingbuf *opts = backend->u.ringbuf.data; ChardevRingbuf *opts = backend->u.ringbuf.data;
@ -4032,17 +4046,14 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend,
typedef struct CharDriver { typedef struct CharDriver {
const char *name; const char *name;
ChardevBackendKind kind; ChardevBackendKind kind;
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); CharDriverParse *parse;
CharDriverState *(*create)(const char *id, ChardevBackend *backend, CharDriverCreate *create;
ChardevReturn *ret, Error **errp);
} CharDriver; } CharDriver;
static GSList *backends; static GSList *backends;
void register_char_driver(const char *name, ChardevBackendKind kind, void register_char_driver(const char *name, ChardevBackendKind kind,
void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp), CharDriverParse *parse, CharDriverCreate *create)
CharDriverState *(*create)(const char *id, ChardevBackend *backend,
ChardevReturn *ret, Error **errp))
{ {
CharDriver *s; CharDriver *s;
@ -4429,6 +4440,7 @@ QemuOptsList qemu_chardev_opts = {
static CharDriverState *qmp_chardev_open_file(const char *id, static CharDriverState *qmp_chardev_open_file(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevFile *file = backend->u.file.data; ChardevFile *file = backend->u.file.data;
@ -4464,6 +4476,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
static CharDriverState *qmp_chardev_open_serial(const char *id, static CharDriverState *qmp_chardev_open_serial(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevHostdev *serial = backend->u.serial.data; ChardevHostdev *serial = backend->u.serial.data;
@ -4488,6 +4501,7 @@ static int qmp_chardev_open_file_source(char *src, int flags,
static CharDriverState *qmp_chardev_open_file(const char *id, static CharDriverState *qmp_chardev_open_file(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevFile *file = backend->u.file.data; ChardevFile *file = backend->u.file.data;
@ -4522,6 +4536,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id,
static CharDriverState *qmp_chardev_open_serial(const char *id, static CharDriverState *qmp_chardev_open_serial(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevHostdev *serial = backend->u.serial.data; ChardevHostdev *serial = backend->u.serial.data;
@ -4533,7 +4548,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
return NULL; return NULL;
} }
qemu_set_nonblock(fd); qemu_set_nonblock(fd);
return qemu_chr_open_tty_fd(fd, common, errp); return qemu_chr_open_tty_fd(fd, common, be_opened, errp);
} }
#endif #endif
@ -4541,6 +4556,7 @@ static CharDriverState *qmp_chardev_open_serial(const char *id,
static CharDriverState *qmp_chardev_open_parallel(const char *id, static CharDriverState *qmp_chardev_open_parallel(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevHostdev *parallel = backend->u.parallel.data; ChardevHostdev *parallel = backend->u.parallel.data;
@ -4551,7 +4567,7 @@ static CharDriverState *qmp_chardev_open_parallel(const char *id,
if (fd < 0) { if (fd < 0) {
return NULL; return NULL;
} }
return qemu_chr_open_pp_fd(fd, common, errp); return qemu_chr_open_pp_fd(fd, common, be_opened, errp);
} }
#endif #endif
@ -4580,6 +4596,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
static CharDriverState *qmp_chardev_open_socket(const char *id, static CharDriverState *qmp_chardev_open_socket(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
CharDriverState *chr; CharDriverState *chr;
@ -4656,7 +4673,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
chr->chr_add_watch = tcp_chr_add_watch; chr->chr_add_watch = tcp_chr_add_watch;
chr->chr_update_read_handler = tcp_chr_update_read_handler; chr->chr_update_read_handler = tcp_chr_update_read_handler;
/* be isn't opened until we get a connection */ /* be isn't opened until we get a connection */
chr->explicit_be_open = true; *be_opened = false;
chr->filename = SocketAddress_to_str("disconnected:", chr->filename = SocketAddress_to_str("disconnected:",
addr, is_listen, is_telnet); addr, is_listen, is_telnet);
@ -4712,6 +4729,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id,
static CharDriverState *qmp_chardev_open_udp(const char *id, static CharDriverState *qmp_chardev_open_udp(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevUdp *udp = backend->u.udp.data; ChardevUdp *udp = backend->u.udp.data;
@ -4724,7 +4742,7 @@ static CharDriverState *qmp_chardev_open_udp(const char *id,
object_unref(OBJECT(sioc)); object_unref(OBJECT(sioc));
return NULL; return NULL;
} }
return qemu_chr_open_udp(sioc, common, errp); return qemu_chr_open_udp(sioc, common, be_opened, errp);
} }
@ -4748,6 +4766,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
Error *local_err = NULL; Error *local_err = NULL;
GSList *i; GSList *i;
CharDriver *cd; CharDriver *cd;
bool be_opened = true;
chr = qemu_chr_find(id); chr = qemu_chr_find(id);
if (chr) { if (chr) {
@ -4759,7 +4778,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
cd = i->data; cd = i->data;
if (cd->kind == backend->type) { if (cd->kind == backend->type) {
chr = cd->create(id, backend, ret, &local_err); chr = cd->create(id, backend, ret, &be_opened, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
goto out_error; goto out_error;
@ -4778,7 +4797,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
if (!chr->filename) { if (!chr->filename) {
chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]); chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
} }
if (!chr->explicit_be_open) { if (be_opened) {
qemu_chr_be_event(chr, CHR_EVENT_OPENED); qemu_chr_be_event(chr, CHR_EVENT_OPENED);
} }
QTAILQ_INSERT_TAIL(&chardevs, chr, next); QTAILQ_INSERT_TAIL(&chardevs, chr, next);

View File

@ -282,7 +282,6 @@ static CharDriverState *chr_open(const char *subtype,
chr->chr_add_watch = spice_chr_add_watch; chr->chr_add_watch = spice_chr_add_watch;
chr->chr_free = spice_chr_free; chr->chr_free = spice_chr_free;
chr->chr_set_fe_open = set_fe_open; chr->chr_set_fe_open = set_fe_open;
chr->explicit_be_open = true;
chr->chr_accept_input = spice_chr_accept_input; chr->chr_accept_input = spice_chr_accept_input;
QLIST_INSERT_HEAD(&spice_chars, s, next); QLIST_INSERT_HEAD(&spice_chars, s, next);
@ -293,6 +292,7 @@ static CharDriverState *chr_open(const char *subtype,
static CharDriverState *qemu_chr_open_spice_vmc(const char *id, static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data; ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
@ -311,6 +311,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
return NULL; return NULL;
} }
*be_opened = false;
return chr_open(type, spice_vmc_set_fe_open, common, errp); return chr_open(type, spice_vmc_set_fe_open, common, errp);
} }
@ -318,6 +319,7 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
static CharDriverState *qemu_chr_open_spice_port(const char *id, static CharDriverState *qemu_chr_open_spice_port(const char *id,
ChardevBackend *backend, ChardevBackend *backend,
ChardevReturn *ret, ChardevReturn *ret,
bool *be_opened,
Error **errp) Error **errp)
{ {
ChardevSpicePort *spiceport = backend->u.spiceport.data; ChardevSpicePort *spiceport = backend->u.spiceport.data;
@ -335,6 +337,7 @@ static CharDriverState *qemu_chr_open_spice_port(const char *id,
if (!chr) { if (!chr) {
return NULL; return NULL;
} }
*be_opened = false;
s = chr->opaque; s = chr->opaque;
s->sin.portname = g_strdup(name); s->sin.portname = g_strdup(name);

View File

@ -2079,10 +2079,6 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
s->chr = chr; s->chr = chr;
chr->opaque = s; chr->opaque = s;
chr->chr_set_echo = text_console_set_echo; chr->chr_set_echo = text_console_set_echo;
/* console/chardev init sometimes completes elsewhere in a 2nd
* stage, so defer OPENED events until they are fully initialized
*/
chr->explicit_be_open = true;
if (display_state) { if (display_state) {
text_console_do_init(chr, display_state); text_console_do_init(chr, display_state);
@ -2093,8 +2089,13 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp)
static VcHandler *vc_handler = text_console_init; static VcHandler *vc_handler = text_console_init;
static CharDriverState *vc_init(const char *id, ChardevBackend *backend, static CharDriverState *vc_init(const char *id, ChardevBackend *backend,
ChardevReturn *ret, Error **errp) ChardevReturn *ret, bool *be_opened,
Error **errp)
{ {
/* console/chardev init sometimes completes elsewhere in a 2nd
* stage, so defer OPENED events until they are fully initialized
*/
*be_opened = false;
return vc_handler(backend->u.vc.data, errp); return vc_handler(backend->u.vc.data, errp);
} }

View File

@ -1685,9 +1685,6 @@ static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp)
/* Temporary, until gd_vc_vte_init runs. */ /* Temporary, until gd_vc_vte_init runs. */
chr->opaque = g_new0(VirtualConsole, 1); chr->opaque = g_new0(VirtualConsole, 1);
/* defer OPENED events until our vc is fully initialized */
chr->explicit_be_open = true;
vcs[nb_vcs++] = chr; vcs[nb_vcs++] = chr;
return chr; return chr;