#include "cvs.h"
#include "buffer.h"
#include "pagealign_alloc.h"
#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
# include <sys/socket.h>
# if !defined( EIO )
# define EIO EBADPOS
# endif
static void buf_default_memory_error (struct buffer *);
static struct buffer_data *get_buffer_data (void);
struct buffer *
buf_initialize (type_buf_input input,
type_buf_output output,
type_buf_flush flush,
type_buf_block block,
type_buf_get_fd get_fd,
type_buf_shutdown shutdown,
type_buf_memory_error memory_error,
void *closure)
{
struct buffer *buf;
buf = xmalloc (sizeof (struct buffer));
buf->data = NULL;
buf->last = NULL;
buf->nonblocking = false;
buf->input = input;
buf->output = output;
buf->flush = flush;
buf->block = block;
buf->get_fd = get_fd;
buf->shutdown = shutdown;
buf->memory_error = memory_error ? memory_error : buf_default_memory_error;
buf->closure = closure;
return buf;
}
void
buf_free (struct buffer *buf)
{
if (buf->closure != NULL)
{
free (buf->closure);
buf->closure = NULL;
}
buf_free_data (buf);
free (buf);
}
struct buffer *
buf_nonio_initialize( void (*memory) (struct buffer *) )
{
return buf_initialize (NULL, NULL, NULL, NULL, NULL, NULL, memory, NULL);
}
static void
buf_default_memory_error (struct buffer *buf)
{
error (1, 0, "out of memory");
}
static struct buffer_data *
get_buffer_data (void)
{
struct buffer_data *ret;
ret = xmalloc (sizeof (struct buffer_data));
ret->text = pagealign_xalloc (BUFFER_DATA_SIZE);
return ret;
}
int
buf_empty (buf)
struct buffer *buf;
{
buf_input_data (buf, NULL);
return buf_empty_p (buf);
}
int
buf_empty_p (struct buffer *buf)
{
struct buffer_data *data;
for (data = buf->data; data != NULL; data = data->next)
if (data->size > 0)
return 0;
return 1;
}
# if defined (SERVER_FLOWCONTROL) || defined (PROXY_SUPPORT)
int
buf_count_mem (struct buffer *buf)
{
struct buffer_data *data;
int mem = 0;
for (data = buf->data; data != NULL; data = data->next)
mem += BUFFER_DATA_SIZE;
return mem;
}
# endif
void
buf_output (struct buffer *buf, const char *data, size_t len)
{
if (buf->data != NULL
&& (((buf->last->text + BUFFER_DATA_SIZE)
- (buf->last->bufp + buf->last->size))
>= len))
{
memcpy (buf->last->bufp + buf->last->size, data, len);
buf->last->size += len;
return;
}
while (1)
{
struct buffer_data *newdata;
newdata = get_buffer_data ();
if (newdata == NULL)
{
(*buf->memory_error) (buf);
return;
}
if (buf->data == NULL)
buf->data = newdata;
else
buf->last->next = newdata;
newdata->next = NULL;
buf->last = newdata;
newdata->bufp = newdata->text;
if (len <= BUFFER_DATA_SIZE)
{
newdata->size = len;
memcpy (newdata->text, data, len);
return;
}
newdata->size = BUFFER_DATA_SIZE;
memcpy (newdata->text, data, BUFFER_DATA_SIZE);
data += BUFFER_DATA_SIZE;
len -= BUFFER_DATA_SIZE;
}
}
void
buf_output0 (struct buffer *buf, const char *string)
{
buf_output (buf, string, strlen (string));
}
void
buf_append_char (struct buffer *buf, int ch)
{
if (buf->data != NULL
&& (buf->last->text + BUFFER_DATA_SIZE
!= buf->last->bufp + buf->last->size))
{
*(buf->last->bufp + buf->last->size) = ch;
++buf->last->size;
}
else
{
char b;
b = ch;
buf_output (buf, &b, 1);
}
}
static inline void
buf_free_datas (struct buffer_data *first, struct buffer_data *last)
{
struct buffer_data *b, *n, *p;
b = first;
do
{
p = b;
n = b->next;
pagealign_free (b->text);
free (b);
b = n;
} while (p != last);
}
int
buf_send_output (struct buffer *buf)
{
assert (buf->output != NULL);
while (buf->data != NULL)
{
struct buffer_data *data;
data = buf->data;
if (data->size > 0)
{
int status;
size_t nbytes;
status = (*buf->output) (buf->closure, data->bufp, data->size,
&nbytes);
if (status != 0)
{
buf_free_data (buf);
return status;
}
if (nbytes != data->size)
{
assert (buf->nonblocking);
data->size -= nbytes;
data->bufp += nbytes;
return 0;
}
}
buf->data = data->next;
buf_free_datas (data, data);
}
buf->last = NULL;
return 0;
}
int
buf_flush (struct buffer *buf, bool block)
{
int nonblocking;
int status;
assert (buf->flush != NULL);
nonblocking = buf->nonblocking;
if (nonblocking && block)
{
status = set_block (buf);
if (status != 0)
return status;
}
status = buf_send_output (buf);
if (status == 0)
status = (*buf->flush) (buf->closure);
if (nonblocking && block)
{
int blockstat;
blockstat = set_nonblock (buf);
if (status == 0)
status = blockstat;
}
return status;
}
int
set_nonblock (struct buffer *buf)
{
int status;
if (buf->nonblocking)
return 0;
assert (buf->block != NULL);
status = (*buf->block) (buf->closure, 0);
if (status != 0)
return status;
buf->nonblocking = true;
return 0;
}
int
set_block (struct buffer *buf)
{
int status;
if (! buf->nonblocking)
return 0;
assert (buf->block != NULL);
status = (*buf->block) (buf->closure, 1);
if (status != 0)
return status;
buf->nonblocking = false;
return 0;
}
int
buf_send_counted (struct buffer *buf)
{
int size;
struct buffer_data *data;
size = 0;
for (data = buf->data; data != NULL; data = data->next)
size += data->size;
data = get_buffer_data ();
if (data == NULL)
{
(*buf->memory_error) (buf);
return ENOMEM;
}
data->next = buf->data;
buf->data = data;
if (buf->last == NULL)
buf->last = data;
data->bufp = data->text;
data->size = sizeof (int);
*((int *) data->text) = size;
return buf_send_output (buf);
}
int
buf_send_special_count (struct buffer *buf, int count)
{
struct buffer_data *data;
data = get_buffer_data ();
if (data == NULL)
{
(*buf->memory_error) (buf);
return ENOMEM;
}
data->next = buf->data;
buf->data = data;
if (buf->last == NULL)
buf->last = data;
data->bufp = data->text;
data->size = sizeof (int);
*((int *) data->text) = count;
return buf_send_output (buf);
}
void
buf_append_data (struct buffer *buf, struct buffer_data *data,
struct buffer_data *last)
{
if (data != NULL)
{
if (buf->data == NULL)
buf->data = data;
else
buf->last->next = data;
buf->last = last;
}
}
# ifdef PROXY_SUPPORT
void
buf_copy_data (struct buffer *buf, struct buffer_data *data,
struct buffer_data *last)
{
struct buffer_data *first, *new, *cur, *prev;
assert (buf);
assert (data);
prev = first = NULL;
cur = data;
while (1)
{
new = get_buffer_data ();
if (!new) error (1, errno, "Failed to allocate buffer data.");
if (!first) first = new;
memcpy (new->text, cur->bufp, cur->size);
new->bufp = new->text;
new->size = cur->size;
new->next = NULL;
if (prev) prev->next = new;
if (cur == last) break;
prev = new;
cur = cur->next;
}
buf_append_data (buf, first, new);
}
# endif
void
buf_free_data (struct buffer *buffer)
{
if (buf_empty_p (buffer)) return;
buf_free_datas (buffer->data, buffer->last);
buffer->data = buffer->last = NULL;
}
void
buf_append_buffer (struct buffer *to, struct buffer *from)
{
struct buffer_data *n;
buf_append_data (to, from->data, from->last);
n = from->data;
while (n)
{
if (n == from->last) break;
n = n->next;
}
from->data = NULL;
from->last = NULL;
}
int
buf_read_file (FILE *f, long int size, struct buffer_data **retp,
struct buffer_data **lastp)
{
int status;
*retp = NULL;
*lastp = NULL;
while (size > 0)
{
struct buffer_data *data;
int get;
data = get_buffer_data ();
if (data == NULL)
{
status = -2;
goto error_return;
}
if (*retp == NULL)
*retp = data;
else
(*lastp)->next = data;
data->next = NULL;
*lastp = data;
data->bufp = data->text;
data->size = 0;
if (size > BUFFER_DATA_SIZE)
get = BUFFER_DATA_SIZE;
else
get = size;
errno = EIO;
if (fread (data->text, get, 1, f) != 1)
{
status = errno;
goto error_return;
}
data->size += get;
size -= get;
}
return 0;
error_return:
if (*retp != NULL)
buf_free_datas (*retp, (*lastp)->next);
return status;
}
int
buf_read_file_to_eof (FILE *f, struct buffer_data **retp,
struct buffer_data **lastp)
{
int status;
*retp = NULL;
*lastp = NULL;
while (!feof (f))
{
struct buffer_data *data;
int get, nread;
data = get_buffer_data ();
if (data == NULL)
{
status = -2;
goto error_return;
}
if (*retp == NULL)
*retp = data;
else
(*lastp)->next = data;
data->next = NULL;
*lastp = data;
data->bufp = data->text;
data->size = 0;
get = BUFFER_DATA_SIZE;
errno = EIO;
nread = fread (data->text, 1, get, f);
if (nread == 0 && !feof (f))
{
status = errno;
goto error_return;
}
data->size = nread;
}
return 0;
error_return:
if (*retp != NULL)
buf_free_datas (*retp, (*lastp)->next);
return status;
}
int
buf_chain_length (struct buffer_data *buf)
{
int size = 0;
while (buf)
{
size += buf->size;
buf = buf->next;
}
return size;
}
int
buf_length (struct buffer *buf)
{
return buf_chain_length (buf->data);
}
int
buf_input_data (struct buffer *buf, size_t *countp)
{
assert (buf->input != NULL);
if (countp != NULL)
*countp = 0;
while (1)
{
int status;
size_t get, nbytes;
if (buf->data == NULL
|| (buf->last->bufp + buf->last->size
== buf->last->text + BUFFER_DATA_SIZE))
{
struct buffer_data *data;
data = get_buffer_data ();
if (data == NULL)
{
(*buf->memory_error) (buf);
return -2;
}
if (buf->data == NULL)
buf->data = data;
else
buf->last->next = data;
data->next = NULL;
buf->last = data;
data->bufp = data->text;
data->size = 0;
}
get = ((buf->last->text + BUFFER_DATA_SIZE)
- (buf->last->bufp + buf->last->size));
status = (*buf->input) (buf->closure,
buf->last->bufp + buf->last->size,
0, get, &nbytes);
if (status != 0)
return status;
buf->last->size += nbytes;
if (countp != NULL)
*countp += nbytes;
if (nbytes < get)
{
return 0;
}
}
}
int
buf_read_line (struct buffer *buf, char **line, size_t *lenp)
{
return buf_read_short_line (buf, line, lenp, SIZE_MAX);
}
int
buf_read_short_line (struct buffer *buf, char **line, size_t *lenp,
size_t max)
{
assert (buf->input != NULL);
*line = NULL;
while (1)
{
size_t len, finallen, predicted_len;
struct buffer_data *data;
char *nl;
len = 0;
for (data = buf->data; data != NULL; data = data->next)
{
nl = memchr (data->bufp, '\012', data->size);
if (nl != NULL)
{
finallen = nl - data->bufp;
if (xsum (len, finallen) >= max) return -2;
len += finallen;
break;
}
else if (xsum (len, data->size) >= max) return -2;
len += data->size;
}
if (data != NULL)
{
char *p;
struct buffer_data *nldata;
p = xmalloc (len + 1);
if (p == NULL)
return -2;
*line = p;
nldata = data;
data = buf->data;
while (data != nldata)
{
struct buffer_data *next;
memcpy (p, data->bufp, data->size);
p += data->size;
next = data->next;
buf_free_datas (data, data);
data = next;
}
memcpy (p, data->bufp, finallen);
p[finallen] = '\0';
data->size -= finallen + 1;
data->bufp = nl + 1;
buf->data = data;
if (lenp != NULL)
*lenp = len;
return 0;
}
predicted_len = 0;
while (1)
{
int status;
size_t size, nbytes;
char *mem;
if (buf->data == NULL
|| (buf->last->bufp + buf->last->size
== buf->last->text + BUFFER_DATA_SIZE))
{
data = get_buffer_data ();
if (data == NULL)
{
(*buf->memory_error) (buf);
return -2;
}
if (buf->data == NULL)
buf->data = data;
else
buf->last->next = data;
data->next = NULL;
buf->last = data;
data->bufp = data->text;
data->size = 0;
}
mem = buf->last->bufp + buf->last->size;
size = (buf->last->text + BUFFER_DATA_SIZE) - mem;
status = (*buf->input) (buf->closure, mem, 1, size, &nbytes);
if (status != 0)
return status;
predicted_len += nbytes;
buf->last->size += nbytes;
if (nbytes == 1)
{
if (*mem == '\012')
break;
}
else
{
if (memchr (mem, '\012', nbytes) != NULL)
break;
}
if (xsum (len, predicted_len) >= max) return -2;
}
}
}
int
buf_read_data (struct buffer *buf, size_t want, char **retdata, size_t *got)
{
assert (buf->input != NULL);
while (buf->data != NULL && buf->data->size == 0)
{
struct buffer_data *next;
next = buf->data->next;
buf_free_datas (buf->data, buf->data);
buf->data = next;
if (next == NULL)
buf->last = NULL;
}
if (buf->data == NULL)
{
struct buffer_data *data;
int status;
size_t get, nbytes;
data = get_buffer_data ();
if (data == NULL)
{
(*buf->memory_error) (buf);
return -2;
}
buf->data = data;
buf->last = data;
data->next = NULL;
data->bufp = data->text;
data->size = 0;
if (want < BUFFER_DATA_SIZE)
get = want;
else
get = BUFFER_DATA_SIZE;
status = (*buf->input) (buf->closure, data->bufp, get,
BUFFER_DATA_SIZE, &nbytes);
if (status != 0)
return status;
data->size = nbytes;
}
*retdata = buf->data->bufp;
if (want < buf->data->size)
{
*got = want;
buf->data->size -= want;
buf->data->bufp += want;
}
else
{
*got = buf->data->size;
buf->data->size = 0;
}
return 0;
}
void
buf_copy_lines (struct buffer *outbuf, struct buffer *inbuf, int command)
{
while (1)
{
struct buffer_data *data;
struct buffer_data *nldata;
char *nl;
int len;
nldata = NULL;
nl = NULL;
for (data = inbuf->data; data != NULL; data = data->next)
{
nl = memchr (data->bufp, '\n', data->size);
if (nl != NULL)
{
nldata = data;
break;
}
}
if (nldata == NULL)
{
return;
}
buf_append_char (outbuf, command);
buf_append_char (outbuf, ' ');
if (inbuf->data != nldata)
{
for (data = inbuf->data; data->next != nldata; data = data->next);
data->next = NULL;
buf_append_data (outbuf, inbuf->data, data);
inbuf->data = nldata;
}
len = nl + 1 - nldata->bufp;
if (len == nldata->size)
{
inbuf->data = nldata->next;
if (inbuf->data == NULL)
inbuf->last = NULL;
nldata->next = NULL;
buf_append_data (outbuf, nldata, nldata);
}
else
{
buf_output (outbuf, nldata->bufp, len);
nldata->bufp += len;
nldata->size -= len;
}
}
}
int
buf_copy_counted (struct buffer *outbuf, struct buffer *inbuf, int *special)
{
*special = 0;
while (1)
{
struct buffer_data *data;
int need;
union
{
char intbuf[sizeof (int)];
int i;
} u;
char *intp;
int count;
struct buffer_data *start;
int startoff;
struct buffer_data *stop;
int stopwant;
need = sizeof (int);
intp = u.intbuf;
for (data = inbuf->data; data != NULL; data = data->next)
{
if (data->size >= need)
{
memcpy (intp, data->bufp, need);
break;
}
memcpy (intp, data->bufp, data->size);
intp += data->size;
need -= data->size;
}
if (data == NULL)
{
return need;
}
count = u.i;
start = data;
startoff = need;
if (count < 0)
{
stop = start;
stopwant = 0;
}
else
{
need = count - (start->size - startoff);
if (need <= 0)
{
stop = start;
stopwant = count;
}
else
{
for (data = start->next; data != NULL; data = data->next)
{
if (need <= data->size)
break;
need -= data->size;
}
if (data == NULL)
{
return need;
}
stop = data;
stopwant = need;
}
}
start->bufp += startoff;
start->size -= startoff;
if (start->size == 0)
start = start->next;
if (stop->size == stopwant)
{
stop = stop->next;
stopwant = 0;
}
while (inbuf->data != start)
{
data = inbuf->data;
inbuf->data = data->next;
buf_free_datas (data, data);
}
if (count < 0)
{
*special = count;
return 0;
}
if (start != stop)
{
for (data = start; data->next != stop; data = data->next);
inbuf->data = stop;
data->next = NULL;
buf_append_data (outbuf, start, data);
}
if (stopwant > 0)
{
buf_output (outbuf, stop->bufp, stopwant);
stop->bufp += stopwant;
stop->size -= stopwant;
}
}
}
int
buf_get_fd (struct buffer *buf)
{
if (buf->get_fd)
return (*buf->get_fd) (buf->closure);
return -1;
}
int
buf_shutdown (struct buffer *buf)
{
if (buf->shutdown) return (*buf->shutdown) (buf);
return 0;
}
# define PACKET_SLOP (100)
struct packetizing_buffer
{
struct buffer *buf;
int (*inpfn) (void *fnclosure, const char *input, char *output,
size_t size);
int (*outfn) (void *fnclosure, const char *input, char *output,
size_t size, size_t *translated);
void *fnclosure;
bool translated;
size_t holdsize;
char *holdbuf;
size_t holdbufsize;
char *holddata;
};
static int packetizing_buffer_input (void *, char *, size_t, size_t, size_t *);
static int packetizing_buffer_output (void *, const char *, size_t, size_t *);
static int packetizing_buffer_flush (void *);
static int packetizing_buffer_block (void *, bool);
static int packetizing_buffer_get_fd (void *);
static int packetizing_buffer_shutdown (struct buffer *);
struct buffer *
packetizing_buffer_initialize (struct buffer *buf,
int (*inpfn) (void *, const char *, char *,
size_t),
int (*outfn) (void *, const char *, char *,
size_t, size_t *),
void *fnclosure,
void (*memory) (struct buffer *))
{
struct packetizing_buffer *pb;
pb = xmalloc (sizeof *pb);
memset (pb, 0, sizeof *pb);
pb->buf = buf;
pb->inpfn = inpfn;
pb->outfn = outfn;
pb->fnclosure = fnclosure;
if (inpfn != NULL)
{
pb->holdbufsize = BUFFER_DATA_SIZE + PACKET_SLOP + 2;
pb->holdbuf = xmalloc (pb->holdbufsize);
}
return buf_initialize (inpfn != NULL ? packetizing_buffer_input : NULL,
inpfn != NULL ? NULL : packetizing_buffer_output,
inpfn != NULL ? NULL : packetizing_buffer_flush,
packetizing_buffer_block,
packetizing_buffer_get_fd,
packetizing_buffer_shutdown,
memory,
pb);
}
static int
packetizing_buffer_input (void *closure, char *data, size_t need, size_t size,
size_t *got)
{
struct packetizing_buffer *pb = closure;
*got = 0;
if (pb->holdsize > 0 && pb->translated)
{
size_t copy;
copy = pb->holdsize;
if (copy > size)
{
memcpy (data, pb->holddata, size);
pb->holdsize -= size;
pb->holddata += size;
*got = size;
return 0;
}
memcpy (data, pb->holddata, copy);
pb->holdsize = 0;
pb->translated = false;
data += copy;
need -= copy;
size -= copy;
*got = copy;
}
while (need > 0 || *got == 0)
{
int status;
size_t get, nread, count, tcount;
char *bytes;
static char *stackoutbuf = NULL;
char *inbuf, *outbuf;
if (!stackoutbuf)
stackoutbuf = xmalloc (BUFFER_DATA_SIZE + PACKET_SLOP);
if (pb->holdsize < 2)
{
get = 2 - pb->holdsize;
status = buf_read_data (pb->buf, get, &bytes, &nread);
if (status != 0)
{
if (status == -2)
status = ENOMEM;
return status;
}
if (nread == 0)
{
return 0;
}
if (get == 1)
pb->holdbuf[1] = bytes[0];
else
{
pb->holdbuf[0] = bytes[0];
if (nread < 2)
{
pb->holdsize = 1;
continue;
}
pb->holdbuf[1] = bytes[1];
}
pb->holdsize = 2;
}
count = (((pb->holdbuf[0] & 0xff) << 8)
+ (pb->holdbuf[1] & 0xff));
if (count + 2 > pb->holdbufsize)
{
char *n;
n = xrealloc (pb->holdbuf, count + 2);
if (n == NULL)
{
(*pb->buf->memory_error) (pb->buf);
return ENOMEM;
}
pb->holdbuf = n;
pb->holdbufsize = count + 2;
}
get = count - (pb->holdsize - 2);
status = buf_read_data (pb->buf, get, &bytes, &nread);
if (status != 0)
{
if (status == -2)
status = ENOMEM;
return status;
}
if (nread == 0)
{
return 0;
}
if (nread < get)
{
memcpy (pb->holdbuf + pb->holdsize, bytes, nread);
pb->holdsize += nread;
continue;
}
if (pb->holdsize == 2)
{
inbuf = bytes;
}
else
{
memcpy (pb->holdbuf + pb->holdsize, bytes, nread);
inbuf = pb->holdbuf + 2;
}
if (count <= BUFFER_DATA_SIZE + PACKET_SLOP)
outbuf = stackoutbuf;
else
{
outbuf = xmalloc (count);
if (outbuf == NULL)
{
(*pb->buf->memory_error) (pb->buf);
return ENOMEM;
}
}
status = (*pb->inpfn) (pb->fnclosure, inbuf, outbuf, count);
if (status != 0)
return status;
tcount = ((outbuf[0] & 0xff) << 8) + (outbuf[1] & 0xff);
if (tcount > count)
error (1, 0, "Input translation failure");
if (tcount > size)
{
memcpy (data, outbuf + 2, size);
*got += size;
pb->holdsize = tcount - size;
memcpy (pb->holdbuf, outbuf + 2 + size, tcount - size);
pb->holddata = pb->holdbuf;
pb->translated = true;
if (outbuf != stackoutbuf)
free (outbuf);
return 0;
}
memcpy (data, outbuf + 2, tcount);
if (outbuf != stackoutbuf)
free (outbuf);
pb->holdsize = 0;
data += tcount;
need -= tcount;
size -= tcount;
*got += tcount;
}
return 0;
}
static int
packetizing_buffer_output (void *closure, const char *data, size_t have,
size_t *wrote)
{
struct packetizing_buffer *pb = closure;
static char *inbuf = NULL;
static char *stack_outbuf = NULL;
struct buffer_data *outdata = NULL;
char *outbuf;
size_t size, translated;
int status;
assert (have <= BUFFER_DATA_SIZE);
if (!inbuf)
{
inbuf = xmalloc (BUFFER_DATA_SIZE + 2);
stack_outbuf = xmalloc (BUFFER_DATA_SIZE + PACKET_SLOP + 4);
}
inbuf[0] = (have >> 8) & 0xff;
inbuf[1] = have & 0xff;
memcpy (inbuf + 2, data, have);
size = have + 2;
if (size + PACKET_SLOP + 2 > BUFFER_DATA_SIZE)
outbuf = stack_outbuf;
else
{
outdata = get_buffer_data ();
if (outdata == NULL)
{
(*pb->buf->memory_error) (pb->buf);
return ENOMEM;
}
outdata->next = NULL;
outdata->bufp = outdata->text;
outbuf = outdata->text;
}
status = (*pb->outfn) (pb->fnclosure, inbuf, outbuf + 2, size,
&translated);
if (status != 0)
return status;
assert (translated <= size + PACKET_SLOP);
outbuf[0] = (translated >> 8) & 0xff;
outbuf[1] = translated & 0xff;
if (outbuf == stack_outbuf)
buf_output (pb->buf, outbuf, translated + 2);
else
{
outdata->size = translated + 2;
buf_append_data (pb->buf, outdata, outdata);
}
*wrote = have;
return buf_send_output (pb->buf);
}
static int
packetizing_buffer_flush (void *closure)
{
struct packetizing_buffer *pb = closure;
return buf_flush (pb->buf, 0);
}
static int
packetizing_buffer_block (void *closure, bool block)
{
struct packetizing_buffer *pb = closure;
if (block)
return set_block (pb->buf);
else
return set_nonblock (pb->buf);
}
static int
packetizing_buffer_get_fd (void *closure)
{
struct packetizing_buffer *cb = closure;
return buf_get_fd (cb->buf);
}
static int
packetizing_buffer_shutdown (struct buffer *buf)
{
struct packetizing_buffer *pb = buf->closure;
return buf_shutdown (pb->buf);
}
struct fd_buffer
{
int fd;
int blocking;
pid_t child_pid;
cvsroot_t *root;
};
static int fd_buffer_input (void *, char *, size_t, size_t, size_t *);
static int fd_buffer_output (void *, const char *, size_t, size_t *);
static int fd_buffer_flush (void *);
static int fd_buffer_block (void *, bool);
static int fd_buffer_get_fd (void *);
static int fd_buffer_shutdown (struct buffer *);
struct buffer *
fd_buffer_initialize (int fd, pid_t child_pid, cvsroot_t *root, bool input,
void (*memory) (struct buffer *))
{
struct fd_buffer *n;
n = xmalloc (sizeof *n);
n->fd = fd;
n->child_pid = child_pid;
n->root = root;
fd_buffer_block (n, true);
return buf_initialize (input ? fd_buffer_input : NULL,
input ? NULL : fd_buffer_output,
input ? NULL : fd_buffer_flush,
fd_buffer_block, fd_buffer_get_fd,
fd_buffer_shutdown,
memory,
n);
}
static int
fd_buffer_input (void *closure, char *data, size_t need, size_t size,
size_t *got)
{
struct fd_buffer *fb = closure;
int nbytes;
assert (need <= size);
*got = 0;
if (fb->blocking)
{
int status;
fd_set readfds;
status = fd_buffer_block (fb, false);
if (status != 0) return status;
FD_ZERO (&readfds);
FD_SET (fb->fd, &readfds);
do
{
int numfds;
do {
numfds = fd_select (fb->fd + 1, &readfds, NULL, NULL, NULL);
if (numfds < 0 && errno != EINTR)
{
status = errno;
goto block_done;
}
} while (numfds < 0);
nbytes = read (fb->fd, data + *got, size - *got);
if (nbytes == 0)
{
if (*got)
{
status = 0;
break;
}
else
{
status = -1;
break;
}
}
if (nbytes < 0)
{
if (!blocking_error (errno))
{
status = errno;
break;
}
}
*got += nbytes;
} while (*got < need);
block_done:
if (status == 0 || status == -1)
{
int newstatus;
newstatus = fd_buffer_block (fb, true);
if (newstatus) status = newstatus;
}
return status;
}
nbytes = read (fb->fd, data, size);
if (nbytes > 0)
{
*got = nbytes;
return 0;
}
if (nbytes == 0)
return -1;
if (blocking_error (errno))
return 0;
return errno;
}
static int
fd_buffer_output (void *closure, const char *data, size_t have, size_t *wrote)
{
struct fd_buffer *fd = closure;
*wrote = 0;
while (have > 0)
{
int nbytes;
nbytes = write (fd->fd, data, have);
if (nbytes <= 0)
{
if (! fd->blocking
&& (nbytes == 0 || blocking_error (errno)))
{
return 0;
}
if (nbytes == 0)
return EIO;
return errno;
}
*wrote += nbytes;
data += nbytes;
have -= nbytes;
}
return 0;
}
static int
fd_buffer_flush (void *closure)
{
return 0;
}
static struct stat devnull;
static int devnull_set = -1;
static int
fd_buffer_block (void *closure, bool block)
{
struct fd_buffer *fb = closure;
# if defined (F_GETFL) && defined (O_NONBLOCK) && defined (F_SETFL)
int flags;
flags = fcntl (fb->fd, F_GETFL, 0);
if (flags < 0)
return errno;
if (block)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
if (fcntl (fb->fd, F_SETFL, flags) < 0)
{
struct stat sb;
int save_errno = errno;
bool isdevnull = false;
if (devnull_set == -1)
devnull_set = stat ("/dev/null", &devnull);
if (devnull_set >= 0)
isdevnull = (fstat (fb->fd, &sb) >= 0
&& sb.st_dev == devnull.st_dev
&& sb.st_ino == devnull.st_ino
&& sb.st_mode == devnull.st_mode
&& sb.st_uid == devnull.st_uid
&& sb.st_gid == devnull.st_gid
&& sb.st_size == devnull.st_size
&& sb.st_blocks == devnull.st_blocks
&& sb.st_blksize == devnull.st_blksize);
if (isdevnull)
errno = 0;
else
{
errno = save_errno;
return errno;
}
}
# endif
fb->blocking = block;
return 0;
}
static int
fd_buffer_get_fd (void *closure)
{
struct fd_buffer *fb = closure;
return fb->fd;
}
static int
fd_buffer_shutdown (struct buffer *buf)
{
struct fd_buffer *fb = buf->closure;
struct stat s;
bool closefd, statted;
if (fstat (fb->fd, &s) == -1) statted = false;
else statted = true;
closefd = statted;
if (buf->flush)
{
buf_flush (buf, 1);
buf->flush = NULL;
}
if (buf->input)
{
# ifdef SHUTDOWN_SERVER
if (fb->root && fb->root->method != server_method)
# endif
# ifndef NO_SOCKET_TO_FD
{
if (statted && S_ISSOCK (s.st_mode))
shutdown (fb->fd, 0);
}
# endif
# ifdef START_RSH_WITH_POPEN_RW
else if (fb->root && pclose (fb->fd) == EOF)
{
error (1, errno, "closing connection to %s",
fb->root->hostname);
closefd = false;
}
# endif
buf->input = NULL;
}
else if (buf->output)
{
# ifdef SHUTDOWN_SERVER
if (fb->root && fb->root->method == server_method)
SHUTDOWN_SERVER (fb->fd);
else
# endif
# ifndef NO_SOCKET_TO_FD
if (statted && S_ISSOCK (s.st_mode))
shutdown (fb->fd, 1);
# else
{
}
# endif
buf->output = NULL;
}
if (statted && closefd && close (fb->fd) == -1)
{
if (server_active)
{
}
# ifdef CLIENT_SUPPORT
else if (fb->root)
error (1, errno, "closing down connection to %s",
fb->root->hostname);
# endif
error (0, errno, "closing down buffer");
}
if (fb->child_pid)
{
int w;
do
w = waitpid (fb->child_pid, NULL, 0);
while (w == -1 && errno == EINTR);
if (w == -1)
error (1, errno, "waiting for process %d", fb->child_pid);
}
free (buf->closure);
buf->closure = NULL;
return 0;
}
#endif