#include <X11/Xtrans.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#ifndef Lynx
#include <sys/param.h>
#ifndef __UNIXOS2__
#include <sys/uio.h>
#endif
#else
#include <uio.h>
#endif
#include "FSproto.h"
#include "clientstr.h"
#include "X11/Xpoll.h"
#include "osdep.h"
#include "globals.h"
#include "dispatch.h"
#if defined(EAGAIN) && defined(EWOULDBLOCK)
#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
#else
#ifdef EAGAIN
#define ETEST(err) (err == EAGAIN)
#else
#define ETEST(err) (err == EWOULDBLOCK)
#endif
#endif
extern fd_set ClientsWithInput;
extern fd_set ClientsWriteBlocked;
extern fd_set OutputPending;
extern long OutputBufferSize;
extern int ConnectionTranslation[];
extern Bool AnyClientsWriteBlocked;
extern Bool NewOutputPending;
static int timesThisConnection = 0;
static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
static OsCommPtr AvailableInput = (OsCommPtr) NULL;
static ConnectionInputPtr AllocateInputBuffer(void);
static ConnectionOutputPtr AllocateOutputBuffer(void);
#define MAX_TIMES_PER 10
#define yield_control() \
{ isItTimeToYield = TRUE; \
timesThisConnection = 0; }
#define yield_control_no_input() \
{ yield_control(); \
FD_CLR(fd, &ClientsWithInput); }
#define yield_control_death() \
{ timesThisConnection = 0; }
#define request_length(req, client) \
((int)((client)->swapped ? lswaps((req)->length) : (req)->length) << 2)
int
ReadRequest(ClientPtr client)
{
OsCommPtr oc;
ConnectionInputPtr oci;
fsReq *request;
int fd,
result,
gotnow,
needed = 0;
if (client == NULL)
return -1;
oc = (OsCommPtr) client->osPrivate;
if (oc == NULL)
return -1;
oci = oc->input;
fd = oc->fd;
if (oci != NULL && fd < 0)
return -1;
if (AvailableInput) {
if (AvailableInput != oc) {
ConnectionInputPtr aci = AvailableInput->input;
if (aci->size > BUFWATERMARK) {
fsfree(aci->buffer);
fsfree(aci);
} else {
aci->next = FreeInputs;
FreeInputs = aci;
}
AvailableInput->input = (ConnectionInputPtr) NULL;
}
AvailableInput = (OsCommPtr) NULL;
}
if (!oci) {
if ((oci = FreeInputs ) != (ConnectionInputPtr) 0) {
FreeInputs = oci->next;
} else if (!(oci = AllocateInputBuffer())) {
yield_control_death();
return -1;
}
oc->input = oci;
}
oci->bufptr += oci->lenLastReq;
gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
#ifdef WORD64
if ((oci->bufptr - oci->buffer) & 7 && gotnow > 0)
{
memmove( oci->buffer, oci->bufptr, gotnow);
oci->bufptr = oci->buffer;
oci->bufcnt = gotnow;
}
#endif
request = (fsReq *) oci->bufptr;
if ((gotnow < SIZEOF(fsReq)) ||
(gotnow < (needed = request_length(request, client)))) {
oci->lenLastReq = 0;
if ((gotnow < SIZEOF(fsReq)) || needed == 0)
needed = SIZEOF(fsReq);
else if (needed > MAXBUFSIZE) {
yield_control_death();
return -1;
}
if ((gotnow == 0) ||
((oci->bufptr - oci->buffer + needed) > oci->size))
{
if ((gotnow > 0) && (oci->bufptr != oci->buffer))
memmove( oci->buffer, oci->bufptr, gotnow);
if (needed > oci->size) {
char *ibuf;
ibuf = (char *) fsrealloc(oci->buffer, needed);
if (!ibuf) {
yield_control_death();
return -1;
}
oci->size = needed;
oci->buffer = ibuf;
}
oci->bufptr = oci->buffer;
oci->bufcnt = gotnow;
}
if (oc->trans_conn == NULL) {
yield_control_death();
return -1;
}
result = _FontTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
oci->size - oci->bufcnt);
if (result <= 0) {
#if !(defined(SVR4) && defined(i386) && !defined(sun))
if ((result < 0) && ETEST(errno)) {
yield_control_no_input();
return 0;
} else
#endif
{
yield_control_death();
return -1;
}
}
oci->bufcnt += result;
gotnow += result;
if ((oci->size > BUFWATERMARK) &&
(oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
char *ibuf;
ibuf = (char *) fsrealloc(oci->buffer, BUFSIZE);
if (ibuf) {
oci->size = BUFSIZE;
oci->buffer = ibuf;
oci->bufptr = ibuf + oci->bufcnt - gotnow;
}
}
request = (fsReq *) oci->bufptr;
if ((gotnow < SIZEOF(fsReq)) ||
(gotnow < (needed = request_length(request, client)))) {
yield_control_no_input();
return 0;
}
}
if (needed == 0)
needed = SIZEOF(fsReq);
oci->lenLastReq = needed;
if (gotnow >= needed + SIZEOF(fsReq)) {
request = (fsReq *) (oci->bufptr + needed);
if (gotnow >= needed + request_length(request, client))
FD_SET(fd, &ClientsWithInput);
else
yield_control_no_input();
} else {
if (gotnow == needed)
AvailableInput = oc;
yield_control_no_input();
}
if (++timesThisConnection >= MAX_TIMES_PER)
yield_control();
client->requestBuffer = (pointer) oci->bufptr;
return needed;
}
Bool
InsertFakeRequest(ClientPtr client, char *data, int count)
{
OsCommPtr oc = (OsCommPtr) client->osPrivate;
ConnectionInputPtr oci = oc->input;
int fd = oc->fd;
fsReq *request;
int gotnow,
moveup;
if (AvailableInput) {
if (AvailableInput != oc) {
register ConnectionInputPtr aci = AvailableInput->input;
if (aci->size > BUFWATERMARK) {
fsfree(aci->buffer);
fsfree(aci);
} else {
aci->next = FreeInputs;
FreeInputs = aci;
}
AvailableInput->input = (ConnectionInputPtr) NULL;
}
AvailableInput = (OsCommPtr) NULL;
}
if (!oci) {
if ((oci = FreeInputs) != (ConnectionInputPtr) 0)
FreeInputs = oci->next;
else if (!(oci = AllocateInputBuffer()))
return FALSE;
oc->input = oci;
}
oci->bufptr += oci->lenLastReq;
oci->lenLastReq = 0;
gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
if ((gotnow + count) > oci->size) {
char *ibuf;
ibuf = (char *) fsrealloc(oci->buffer, gotnow + count);
if (!ibuf)
return FALSE;
oci->size = gotnow + count;
oci->buffer = ibuf;
oci->bufptr = ibuf + oci->bufcnt - gotnow;
}
moveup = count - (oci->bufptr - oci->buffer);
if (moveup > 0) {
if (gotnow > 0)
memmove( oci->bufptr + moveup, oci->bufptr, gotnow);
oci->bufptr += moveup;
oci->bufcnt += moveup;
}
memmove( oci->bufptr - count, data, count);
oci->bufptr -= count;
request = (fsReq *) oci->bufptr;
gotnow += count;
if ((gotnow >= SIZEOF(fsReq)) &&
(gotnow >= request_length(request, client)))
FD_SET(fd, &ClientsWithInput);
else
yield_control_no_input();
return TRUE;
}
void
ResetCurrentRequest(ClientPtr client)
{
OsCommPtr oc = (OsCommPtr) client->osPrivate;
ConnectionInputPtr oci = oc->input;
int fd = oc->fd;
fsReq *request;
int gotnow;
if (AvailableInput == oc)
AvailableInput = (OsCommPtr) NULL;
oci->lenLastReq = 0;
request = (fsReq *) oci->bufptr;
gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
if ((gotnow >= SIZEOF(fsReq)) &&
(gotnow >= request_length(request, client))) {
FD_SET(fd, &ClientsWithInput);
yield_control();
} else {
yield_control_no_input();
}
}
int
FlushClient(
ClientPtr client,
OsCommPtr oc,
char *extraBuf,
int extraCount,
int padsize)
{
ConnectionOutputPtr oco = oc->output;
int fd = oc->fd;
struct iovec iov[3];
char padBuffer[3];
long written;
long notWritten;
long todo;
if (!oco)
return 0;
written = 0;
notWritten = oco->count + extraCount + padsize;
todo = notWritten;
while (notWritten) {
long before = written;
long remain = todo;
int i = 0;
long len;
#define InsertIOV(pointer, length) \
len = (length) - before; \
if (len > remain) \
len = remain; \
if (len <= 0) { \
before = (-len); \
} else { \
iov[i].iov_len = len; \
iov[i].iov_base = (pointer) + before; \
i++; \
remain -= len; \
before = 0; \
}
InsertIOV((char *) oco->buf, oco->count);
InsertIOV(extraBuf, extraCount);
InsertIOV(padBuffer, padsize);
errno = 0;
if (oc->trans_conn && (len = _FontTransWritev(oc->trans_conn, iov, i)) >= 0) {
written += len;
notWritten -= len;
todo = notWritten;
} else if (ETEST(errno)
#ifdef SUNSYSV
|| (errno == 0)
#endif
#ifdef EMSGSIZE
|| ((errno == EMSGSIZE) && (todo == 1))
#endif
)
{
FD_SET(fd, &ClientsWriteBlocked);
AnyClientsWriteBlocked = TRUE;
if (written < oco->count) {
if (written > 0) {
oco->count -= written;
memmove( (char *) oco->buf, (char *) oco->buf + written,
oco->count);
written = 0;
}
} else {
written -= oco->count;
oco->count = 0;
}
if (notWritten > oco->size) {
unsigned char *obuf;
obuf = (unsigned char *) fsrealloc(oco->buf,
notWritten + OutputBufferSize);
if (!obuf) {
if (oc->trans_conn)
_FontTransClose(oc->trans_conn);
oc->trans_conn = NULL;
MarkClientException(client);
oco->count = 0;
return -1;
}
oco->size = notWritten + OutputBufferSize;
oco->buf = obuf;
}
if ((len = extraCount - written) > 0) {
memmove( (char *) oco->buf + oco->count,
extraBuf + written, len);
}
oco->count = notWritten;
return extraCount;
}
#ifdef EMSGSIZE
else if (errno == EMSGSIZE)
{
todo >>= 1;
}
#endif
else
{
if (oc->trans_conn)
_FontTransClose(oc->trans_conn);
oc->trans_conn = NULL;
MarkClientException(client);
oco->count = 0;
return -1;
}
}
oco->count = 0;
if (AnyClientsWriteBlocked) {
FD_CLR(fd, &ClientsWriteBlocked);
if (!XFD_ANYSET(&ClientsWriteBlocked))
AnyClientsWriteBlocked = FALSE;
}
if (oco->size > BUFWATERMARK) {
fsfree(oco->buf);
fsfree(oco);
} else {
oco->next = FreeOutputs;
FreeOutputs = oco;
}
oc->output = (ConnectionOutputPtr) NULL;
return extraCount;
}
void
FlushAllOutput(void)
{
int index, base;
fd_mask mask;
OsCommPtr oc;
ClientPtr client;
if (!NewOutputPending)
return;
NewOutputPending = FALSE;
for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) {
mask = OutputPending.fds_bits[base];
OutputPending.fds_bits[base] = 0;
while (mask) {
index = ffs(mask) - 1;
mask &= ~lowbit(mask);
if ((index = ConnectionTranslation[(base << 5) + index]) == 0)
continue;
client = clients[index];
if (client->clientGone == CLIENT_GONE)
continue;
oc = (OsCommPtr) client->osPrivate;
if (FD_ISSET(oc->fd, &ClientsWithInput)) {
FD_SET(oc->fd, &OutputPending);
NewOutputPending = TRUE;
} else {
(void) FlushClient(client, oc, (char *) NULL, 0, 0);
}
}
}
}
static int
write_to_client_internal(ClientPtr client, int count, char *buf, int padBytes)
{
OsCommPtr oc = (OsCommPtr) client->osPrivate;
ConnectionOutputPtr oco = oc->output;
if (!count)
return 0;
if (!oco) {
if ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
FreeOutputs = oco->next;
} else if (!(oco = AllocateOutputBuffer())) {
_FontTransClose(oc->trans_conn);
oc->trans_conn = NULL;
MarkClientException(client);
return -1;
}
oc->output = oco;
}
if (oco->count + count + padBytes > oco->size) {
FD_CLR(oc->fd, &OutputPending);
NewOutputPending = FALSE;
return FlushClient(client, oc, buf, count, padBytes);
}
NewOutputPending = TRUE;
FD_SET(oc->fd, &OutputPending);
memmove( (char *) oco->buf + oco->count, buf, count);
oco->count += count + padBytes;
return count;
}
void
WriteToClientUnpadded(ClientPtr client, int count, char *buf)
{
write_to_client_internal(client, count, buf, 0);
}
static int padlength[4] = {0, 3, 2, 1};
void
WriteToClient(ClientPtr client, int count, char *buf)
{
int flag = 0;
if (NULL == buf) {
flag = -1;
buf = (char *)fsalloc(count); memset(buf, 0, count);
}
write_to_client_internal(client, count, buf, padlength[count & 3]);
if (flag)
fsfree(buf);
}
static ConnectionInputPtr
AllocateInputBuffer(void)
{
register ConnectionInputPtr oci;
oci = (ConnectionInputPtr) fsalloc(sizeof(ConnectionInput));
if (!oci)
return (ConnectionInputPtr) NULL;
oci->buffer = (char *) fsalloc(BUFSIZE);
if (!oci->buffer) {
fsfree(oci);
return (ConnectionInputPtr) NULL;
}
oci->next = 0;
oci->size = BUFSIZE;
oci->bufptr = oci->buffer;
oci->bufcnt = 0;
oci->lenLastReq = 0;
return oci;
}
static ConnectionOutputPtr
AllocateOutputBuffer(void)
{
register ConnectionOutputPtr oco;
oco = (ConnectionOutputPtr) fsalloc(sizeof(ConnectionOutput));
if (!oco)
return (ConnectionOutputPtr) NULL;
oco->buf = (unsigned char *) fsalloc(BUFSIZE);
if (!oco->buf) {
fsfree(oco);
return (ConnectionOutputPtr) NULL;
}
oco->size = BUFSIZE;
oco->count = 0;
return oco;
}
void
FreeOsBuffers(OsCommPtr oc)
{
register ConnectionInputPtr oci;
register ConnectionOutputPtr oco;
if (AvailableInput == oc)
AvailableInput = (OsCommPtr) NULL;
if ((oci = oc->input) != (ConnectionInputPtr) 0) {
if (FreeInputs) {
fsfree(oci->buffer);
fsfree(oci);
} else {
FreeInputs = oci;
oci->next = (ConnectionInputPtr) NULL;
oci->bufptr = oci->buffer;
oci->bufcnt = 0;
oci->lenLastReq = 0;
}
}
if ((oco = oc->output) != (ConnectionOutputPtr) 0) {
if (FreeOutputs) {
fsfree(oco->buf);
fsfree(oco);
} else {
FreeOutputs = oco;
oco->next = (ConnectionOutputPtr) NULL;
oco->count = 0;
}
}
}
void
ResetOsBuffers(void)
{
register ConnectionInputPtr oci;
register ConnectionOutputPtr oco;
while ((oci = FreeInputs) != (ConnectionInputPtr) 0) {
FreeInputs = oci->next;
fsfree(oci->buffer);
fsfree(oci);
}
while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) {
FreeOutputs = oco->next;
fsfree(oco->buf);
fsfree(oco);
}
}