1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/contrib/cygtermd/sel.c
Simon Tatham 5d718ef64b Whitespace rationalisation of entire code base.
The number of people has been steadily increasing who read our source
code with an editor that thinks tab stops are 4 spaces apart, as
opposed to the traditional tty-derived 8 that the PuTTY code expects.

So I've been wondering for ages about just fixing it, and switching to
a spaces-only policy throughout the code. And I recently found out
about 'git blame -w', which should make this change not too disruptive
for the purposes of source-control archaeology; so perhaps now is the
time.

While I'm at it, I've also taken the opportunity to remove all the
trailing spaces from source lines (on the basis that git dislikes
them, and is the only thing that seems to have a strong opinion one
way or the other).
    
Apologies to anyone downstream of this code who has complicated patch
sets to rebase past this change. I don't intend it to be needed again.
2019-09-08 20:29:21 +01:00

387 lines
8.8 KiB
C

/*
* sel.c: implementation of sel.h.
*/
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include "sel.h"
#include "malloc.h"
/* ----------------------------------------------------------------------
* Chunk of code lifted from PuTTY's misc.c to manage buffers of
* data to be written to an fd.
*/
#define BUFFER_GRANULE 512
typedef struct bufchain_tag {
struct bufchain_granule *head, *tail;
size_t buffersize; /* current amount of buffered data */
} bufchain;
struct bufchain_granule {
struct bufchain_granule *next;
size_t buflen, bufpos;
char buf[BUFFER_GRANULE];
};
static void bufchain_init(bufchain *ch)
{
ch->head = ch->tail = NULL;
ch->buffersize = 0;
}
static void bufchain_clear(bufchain *ch)
{
struct bufchain_granule *b;
while (ch->head) {
b = ch->head;
ch->head = ch->head->next;
sfree(b);
}
ch->tail = NULL;
ch->buffersize = 0;
}
static size_t bufchain_size(bufchain *ch)
{
return ch->buffersize;
}
static void bufchain_add(bufchain *ch, const void *data, size_t len)
{
const char *buf = (const char *)data;
if (len == 0) return;
ch->buffersize += len;
if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {
size_t copylen = BUFFER_GRANULE - ch->tail->buflen;
if (copylen > len)
copylen = len;
memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);
buf += copylen;
len -= copylen;
ch->tail->buflen += copylen;
}
while (len > 0) {
struct bufchain_granule *newbuf;
size_t grainlen = BUFFER_GRANULE;
if (grainlen > len)
grainlen = len;
newbuf = snew(struct bufchain_granule);
newbuf->bufpos = 0;
newbuf->buflen = grainlen;
memcpy(newbuf->buf, buf, grainlen);
buf += grainlen;
len -= grainlen;
if (ch->tail)
ch->tail->next = newbuf;
else
ch->head = ch->tail = newbuf;
newbuf->next = NULL;
ch->tail = newbuf;
}
}
static void bufchain_consume(bufchain *ch, size_t len)
{
struct bufchain_granule *tmp;
assert(ch->buffersize >= len);
while (len > 0) {
size_t remlen = len;
assert(ch->head != NULL);
if (remlen >= ch->head->buflen - ch->head->bufpos) {
remlen = ch->head->buflen - ch->head->bufpos;
tmp = ch->head;
ch->head = tmp->next;
sfree(tmp);
if (!ch->head)
ch->tail = NULL;
} else
ch->head->bufpos += remlen;
ch->buffersize -= remlen;
len -= remlen;
}
}
static void bufchain_prefix(bufchain *ch, void **data, size_t *len)
{
*len = ch->head->buflen - ch->head->bufpos;
*data = ch->head->buf + ch->head->bufpos;
}
/* ----------------------------------------------------------------------
* The actual implementation of the sel interface.
*/
struct sel {
void *ctx;
sel_rfd *rhead, *rtail;
sel_wfd *whead, *wtail;
};
struct sel_rfd {
sel *parent;
sel_rfd *prev, *next;
sel_readdata_fn_t readdata;
sel_readerr_fn_t readerr;
void *ctx;
int fd;
int frozen;
};
struct sel_wfd {
sel *parent;
sel_wfd *prev, *next;
sel_written_fn_t written;
sel_writeerr_fn_t writeerr;
void *ctx;
int fd;
bufchain buf;
};
sel *sel_new(void *ctx)
{
sel *sel = snew(struct sel);
sel->ctx = ctx;
sel->rhead = sel->rtail = NULL;
sel->whead = sel->wtail = NULL;
return sel;
}
sel_wfd *sel_wfd_add(sel *sel, int fd,
sel_written_fn_t written, sel_writeerr_fn_t writeerr,
void *ctx)
{
sel_wfd *wfd = snew(sel_wfd);
wfd->written = written;
wfd->writeerr = writeerr;
wfd->ctx = ctx;
wfd->fd = fd;
bufchain_init(&wfd->buf);
wfd->next = NULL;
wfd->prev = sel->wtail;
if (sel->wtail)
sel->wtail->next = wfd;
else
sel->whead = wfd;
sel->wtail = wfd;
wfd->parent = sel;
return wfd;
}
sel_rfd *sel_rfd_add(sel *sel, int fd,
sel_readdata_fn_t readdata, sel_readerr_fn_t readerr,
void *ctx)
{
sel_rfd *rfd = snew(sel_rfd);
rfd->readdata = readdata;
rfd->readerr = readerr;
rfd->ctx = ctx;
rfd->fd = fd;
rfd->frozen = 0;
rfd->next = NULL;
rfd->prev = sel->rtail;
if (sel->rtail)
sel->rtail->next = rfd;
else
sel->rhead = rfd;
sel->rtail = rfd;
rfd->parent = sel;
return rfd;
}
size_t sel_write(sel_wfd *wfd, const void *data, size_t len)
{
bufchain_add(&wfd->buf, data, len);
return bufchain_size(&wfd->buf);
}
void sel_wfd_setfd(sel_wfd *wfd, int fd)
{
wfd->fd = fd;
}
void sel_rfd_setfd(sel_rfd *rfd, int fd)
{
rfd->fd = fd;
}
void sel_rfd_freeze(sel_rfd *rfd)
{
rfd->frozen = 1;
}
void sel_rfd_unfreeze(sel_rfd *rfd)
{
rfd->frozen = 0;
}
int sel_wfd_delete(sel_wfd *wfd)
{
sel *sel = wfd->parent;
int ret;
if (wfd->prev)
wfd->prev->next = wfd->next;
else
sel->whead = wfd->next;
if (wfd->next)
wfd->next->prev = wfd->prev;
else
sel->wtail = wfd->prev;
bufchain_clear(&wfd->buf);
ret = wfd->fd;
sfree(wfd);
return ret;
}
int sel_rfd_delete(sel_rfd *rfd)
{
sel *sel = rfd->parent;
int ret;
if (rfd->prev)
rfd->prev->next = rfd->next;
else
sel->rhead = rfd->next;
if (rfd->next)
rfd->next->prev = rfd->prev;
else
sel->rtail = rfd->prev;
ret = rfd->fd;
sfree(rfd);
return ret;
}
void sel_free(sel *sel)
{
while (sel->whead)
sel_wfd_delete(sel->whead);
while (sel->rhead)
sel_rfd_delete(sel->rhead);
sfree(sel);
}
void *sel_get_ctx(sel *sel) { return sel->ctx; }
void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; }
void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; }
void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; }
void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; }
void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; }
int sel_iterate(sel *sel, long timeout)
{
sel_rfd *rfd;
sel_wfd *wfd;
fd_set rset, wset;
int maxfd = 0;
struct timeval tv, *ptv;
char buf[65536];
int ret;
FD_ZERO(&rset);
FD_ZERO(&wset);
for (rfd = sel->rhead; rfd; rfd = rfd->next) {
if (rfd->fd >= 0 && !rfd->frozen) {
FD_SET(rfd->fd, &rset);
if (maxfd < rfd->fd + 1)
maxfd = rfd->fd + 1;
}
}
for (wfd = sel->whead; wfd; wfd = wfd->next) {
if (wfd->fd >= 0 && bufchain_size(&wfd->buf)) {
FD_SET(wfd->fd, &wset);
if (maxfd < wfd->fd + 1)
maxfd = wfd->fd + 1;
}
}
if (timeout < 0) {
ptv = NULL;
} else {
ptv = &tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = 1000 * (timeout % 1000);
}
do {
ret = select(maxfd, &rset, &wset, NULL, ptv);
} while (ret < 0 && (errno == EINTR || errno == EAGAIN));
if (ret < 0)
return errno;
/*
* Just in case one of the callbacks destroys an rfd or wfd we
* had yet to get round to, we must loop from the start every
* single time. Algorithmically irritating, but necessary
* unless we want to store the rfd structures in a heavyweight
* tree sorted by fd. And let's face it, if we cared about
* good algorithmic complexity it's not at all clear we'd be
* using select in the first place.
*/
do {
for (wfd = sel->whead; wfd; wfd = wfd->next)
if (wfd->fd >= 0 && FD_ISSET(wfd->fd, &wset)) {
void *data;
size_t len;
FD_CLR(wfd->fd, &wset);
bufchain_prefix(&wfd->buf, &data, &len);
ret = write(wfd->fd, data, len);
assert(ret != 0);
if (ret < 0) {
if (wfd->writeerr)
wfd->writeerr(wfd, errno);
} else {
bufchain_consume(&wfd->buf, len);
if (wfd->written)
wfd->written(wfd, bufchain_size(&wfd->buf));
}
break;
}
} while (wfd);
do {
for (rfd = sel->rhead; rfd; rfd = rfd->next)
if (rfd->fd >= 0 && !rfd->frozen && FD_ISSET(rfd->fd, &rset)) {
FD_CLR(rfd->fd, &rset);
ret = read(rfd->fd, buf, sizeof(buf));
if (ret < 0) {
if (rfd->readerr)
rfd->readerr(rfd, errno);
} else {
if (rfd->readdata)
rfd->readdata(rfd, buf, ret);
}
break;
}
} while (rfd);
return 0;
}