mirror of https://github.com/buserror/mii_emu.git
140 lines
3.3 KiB
C
140 lines
3.3 KiB
C
/*
|
|
* mish_input.c
|
|
*
|
|
* Copyright (C) 2020 Michel Pollet <buserror@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include "mish_priv.h"
|
|
#include "mish_priv_line.h"
|
|
|
|
#ifdef MISH_INPUT_TEST
|
|
#define D(_w) _w
|
|
#else
|
|
#define D(_w)
|
|
#endif
|
|
|
|
void
|
|
_mish_input_init(
|
|
mish_p m,
|
|
mish_input_p in,
|
|
int fd)
|
|
{
|
|
TAILQ_INIT(&in->backlog);
|
|
// fprintf(stderr, "%s %d\n", __func__, fd);
|
|
in->fd = fd;
|
|
in->line = NULL;
|
|
FD_SET(in->fd, &m->select.read);
|
|
if (in->fd >= m->select.max - 1)
|
|
m->select.max = in->fd +1;
|
|
|
|
int flags = fcntl(fd, F_GETFL, NULL);
|
|
if (flags == 1) {
|
|
// perror("mish: input F_GETFL");
|
|
flags = 0;
|
|
}
|
|
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
|
|
perror("mish: input F_SETFL");
|
|
}
|
|
|
|
void
|
|
_mish_input_clear(
|
|
mish_p m,
|
|
mish_input_p in)
|
|
{
|
|
mish_line_p l;
|
|
while ((l = TAILQ_FIRST(&in->backlog)) != NULL) {
|
|
TAILQ_REMOVE(&in->backlog, l, self);
|
|
free(l);
|
|
}
|
|
if (in->line)
|
|
free(in->line);
|
|
in->line = NULL;
|
|
if (in->fd != -1) {
|
|
FD_CLR(in->fd, &m->select.read);
|
|
FD_CLR(in->fd, &m->select.write);
|
|
close(in->fd);
|
|
in->fd = -1;
|
|
}
|
|
}
|
|
|
|
int
|
|
_mish_input_read(
|
|
mish_p m,
|
|
fd_set * fds,
|
|
mish_input_p in)
|
|
{
|
|
if (!FD_ISSET(in->fd, fds))
|
|
return 0;
|
|
do {
|
|
if (_mish_line_reserve(&in->line, 80)) {
|
|
D(printf(" reserve bailed us\n");)
|
|
break;
|
|
}
|
|
ssize_t rd = read(in->fd,
|
|
in->line->line + in->line->len,
|
|
in->line->size - in->line->len - 1);
|
|
if (rd == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
|
|
break;
|
|
if (rd <= 0) {
|
|
close(in->fd);
|
|
FD_CLR(in->fd, &m->select.read);
|
|
FD_CLR(in->fd, &m->select.write);
|
|
in->fd = -1;
|
|
printf(MISH_COLOR_RED "mish: telnet: disconnected"
|
|
MISH_COLOR_RESET "\n");
|
|
return -1;
|
|
}
|
|
in->line->len += rd;
|
|
} while (1);
|
|
uint8_t * s = (uint8_t*) in->line->line + in->line->done;
|
|
uint8_t * d = s;
|
|
int added = in->line->len - in->line->done;
|
|
D(printf(" buffer added %d done %d len %d size %d\n", added,
|
|
(int)in->line->done, (int)in->line->len, (int)in->line->size);
|
|
if (in->line->done)
|
|
printf(" buffer: '%.*s'\n", in->line->done, in->line->line);)
|
|
/*
|
|
* This loop re-parse the data, passes it to the optional handler,
|
|
* and then store processed lines into the backlog. This is not super
|
|
* optimal in the case there isn't a process_char() callback, so perhaps
|
|
* I should make another less 'generic' input parser that doesn't copy
|
|
* stuff around (in place, often).
|
|
*/
|
|
while (added) {
|
|
int r = MISH_IN_STORE;
|
|
if (in->process_char)
|
|
r = in->process_char(m, in, *s);
|
|
else
|
|
r = *s == '\n' ? MISH_IN_SPLIT : MISH_IN_STORE;
|
|
|
|
if (r == MISH_IN_STORE || r == MISH_IN_SPLIT) {
|
|
*d++ = *s;
|
|
in->line->done++;
|
|
}
|
|
if (r == MISH_IN_SPLIT) {
|
|
D(printf(" split size %d remains %d : '%.*s'\n", in->line->done,
|
|
(int)added, in->line->done-1, in->line->line);)
|
|
_mish_line_add(&in->backlog, in->line->line, in->line->done);
|
|
d = (uint8_t*)in->line->line;
|
|
in->line->len = in->line->done = 0;
|
|
}
|
|
s++; added--;
|
|
}
|
|
*d = 0; // NUL terminate for debug purpose!
|
|
D(printf(" exit added %d done %d len %d size %d\n", added,
|
|
(int)in->line->done, (int)in->line->len, (int)in->line->size);
|
|
in->line->len = in->line->done;
|
|
if (in->line->done)
|
|
printf(" buffer: '%s'\n", in->line->line);)
|
|
|
|
return TAILQ_FIRST(&in->backlog) != NULL;
|
|
}
|