#include <assert.h>
#include "cvs.h"
#include "buffer.h"
#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
#ifdef HAVE_WINSOCK_H
# include <winsock.h>
#else
# include <sys/socket.h>
#endif
#if !defined (EIO)
#define EIO EBADPOS
#endif
static struct buffer_data *free_buffer_data;
static void buf_default_memory_error PROTO ((struct buffer *));
static void allocate_buffer_datas PROTO((void));
static struct buffer_data *get_buffer_data PROTO((void));
struct buffer *
buf_initialize (input, output, flush, block, shutdown, memory, closure)
int (*input) PROTO((void *, char *, int, int, int *));
int (*output) PROTO((void *, const char *, int, int *));
int (*flush) PROTO((void *));
int (*block) PROTO((void *, int));
int (*shutdown) PROTO((struct buffer *));
void (*memory) PROTO((struct buffer *));
void *closure;
{
struct buffer *buf;
buf = (struct buffer *) xmalloc (sizeof (struct buffer));
buf->data = NULL;
buf->last = NULL;
buf->nonblocking = 0;
buf->input = input;
buf->output = output;
buf->flush = flush;
buf->block = block;
buf->shutdown = shutdown;
buf->memory_error = memory ? memory : buf_default_memory_error;
buf->closure = closure;
return buf;
}
void
buf_free (buf)
struct buffer *buf;
{
if (buf->closure != NULL)
{
free (buf->closure);
buf->closure = NULL;
}
if (buf->data != NULL)
{
buf->last->next = free_buffer_data;
free_buffer_data = buf->data;
}
free (buf);
}
struct buffer *
buf_nonio_initialize (memory)
void (*memory) PROTO((struct buffer *));
{
return (buf_initialize
((int (*) PROTO((void *, char *, int, int, int *))) NULL,
(int (*) PROTO((void *, const char *, int, int *))) NULL,
(int (*) PROTO((void *))) NULL,
(int (*) PROTO((void *, int))) NULL,
(int (*) PROTO((struct buffer *))) NULL,
memory,
(void *) NULL));
}
static void
buf_default_memory_error (buf)
struct buffer *buf;
{
error (1, 0, "out of memory");
}
static void
allocate_buffer_datas ()
{
struct buffer_data *alc;
char *space;
int i;
#define ALLOC_COUNT (16)
alc = xmalloc (ALLOC_COUNT * sizeof (struct buffer_data));
space = (char *) valloc (ALLOC_COUNT * BUFFER_DATA_SIZE);
if (!space)
{
free (alc);
return;
}
for (i = 0; i < ALLOC_COUNT; i++, alc++, space += BUFFER_DATA_SIZE)
{
alc->next = free_buffer_data;
free_buffer_data = alc;
alc->text = space;
}
}
static struct buffer_data *
get_buffer_data ()
{
struct buffer_data *ret;
if (free_buffer_data == NULL)
{
allocate_buffer_datas ();
if (free_buffer_data == NULL)
return NULL;
}
ret = free_buffer_data;
free_buffer_data = ret->next;
return ret;
}
int
buf_empty (buf)
struct buffer *buf;
{
buf_input_data (buf, NULL);
return buf_empty_p (buf);
}
int
buf_empty_p (buf)
struct buffer *buf;
{
struct buffer_data *data;
for (data = buf->data; data != NULL; data = data->next)
if (data->size > 0)
return 0;
return 1;
}
#ifdef SERVER_FLOWCONTROL
int
buf_count_mem (buf)
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 (buf, data, len)
struct buffer *buf;
const char *data;
int 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 (buf, string)
struct buffer *buf;
const char *string;
{
buf_output (buf, string, strlen (string));
}
void
buf_append_char (buf, ch)
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);
}
}
int
buf_send_output (buf)
struct buffer *buf;
{
if (buf->output == NULL)
abort ();
while (buf->data != NULL)
{
struct buffer_data *data;
data = buf->data;
if (data->size > 0)
{
int status, nbytes;
status = (*buf->output) (buf->closure, data->bufp, data->size,
&nbytes);
if (status != 0)
{
buf->last->next = free_buffer_data;
free_buffer_data = buf->data;
buf->data = NULL;
buf->last = NULL;
return status;
}
if (nbytes != data->size)
{
assert (buf->nonblocking);
data->size -= nbytes;
data->bufp += nbytes;
return 0;
}
}
buf->data = data->next;
data->next = free_buffer_data;
free_buffer_data = data;
}
buf->last = NULL;
return 0;
}
int
buf_flush (buf, block)
struct buffer *buf;
int block;
{
int nonblocking;
int status;
if (buf->flush == NULL)
abort ();
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 (buf)
struct buffer *buf;
{
int status;
if (buf->nonblocking)
return 0;
if (buf->block == NULL)
abort ();
status = (*buf->block) (buf->closure, 0);
if (status != 0)
return status;
buf->nonblocking = 1;
return 0;
}
int
set_block (buf)
struct buffer *buf;
{
int status;
if (! buf->nonblocking)
return 0;
if (buf->block == NULL)
abort ();
status = (*buf->block) (buf->closure, 1);
if (status != 0)
return status;
buf->nonblocking = 0;
return 0;
}
int
buf_send_counted (buf)
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 (buf, 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 (buf, data, last)
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;
}
}
void
buf_append_buffer (to, from)
struct buffer *to;
struct buffer *from;
{
buf_append_data (to, from->data, from->last);
from->data = NULL;
from->last = NULL;
}
int
buf_read_file (f, size, retp, lastp)
FILE *f;
long 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)
{
(*lastp)->next = free_buffer_data;
free_buffer_data = *retp;
}
return status;
}
int
buf_read_file_to_eof (f, retp, lastp)
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)
{
(*lastp)->next = free_buffer_data;
free_buffer_data = *retp;
}
return status;
}
int
buf_chain_length (buf)
struct buffer_data *buf;
{
int size = 0;
while (buf)
{
size += buf->size;
buf = buf->next;
}
return size;
}
int
buf_length (buf)
struct buffer *buf;
{
return buf_chain_length (buf->data);
}
int
buf_input_data (buf, countp)
struct buffer *buf;
int *countp;
{
if (buf->input == NULL)
abort ();
if (countp != NULL)
*countp = 0;
while (1)
{
int get;
int status, 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 (buf, line, lenp)
struct buffer *buf;
char **line;
int *lenp;
{
if (buf->input == NULL)
abort ();
*line = NULL;
while (1)
{
int len, finallen = 0;
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;
len += finallen;
break;
}
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;
data->next = free_buffer_data;
free_buffer_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;
}
while (1)
{
int size, status, 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;
buf->last->size += nbytes;
if (nbytes == 1)
{
if (*mem == '\012')
break;
}
else
{
if (memchr (mem, '\012', nbytes) != NULL)
break;
}
}
}
}
int
buf_read_data (buf, want, retdata, got)
struct buffer *buf;
int want;
char **retdata;
int *got;
{
if (buf->input == NULL)
abort ();
while (buf->data != NULL && buf->data->size == 0)
{
struct buffer_data *next;
next = buf->data->next;
buf->data->next = free_buffer_data;
free_buffer_data = buf->data;
buf->data = next;
if (next == NULL)
buf->last = NULL;
}
if (buf->data == NULL)
{
struct buffer_data *data;
int get, status, 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 (outbuf, inbuf, command)
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 (outbuf, inbuf, special)
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;
data->next = free_buffer_data;
free_buffer_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_shutdown (buf)
struct buffer *buf;
{
if (buf->shutdown)
return (*buf->shutdown) (buf);
return 0;
}
static int stdio_buffer_input PROTO((void *, char *, int, int, int *));
static int stdio_buffer_output PROTO((void *, const char *, int, int *));
static int stdio_buffer_flush PROTO((void *));
static int stdio_buffer_shutdown PROTO((struct buffer *buf));
struct stdio_buffer_closure
{
FILE *fp;
int child_pid;
};
struct buffer *
stdio_buffer_initialize (fp, child_pid, input, memory)
FILE *fp;
int child_pid;
int input;
void (*memory) PROTO((struct buffer *));
{
struct stdio_buffer_closure *bc = xmalloc (sizeof (*bc));
bc->fp = fp;
bc->child_pid = child_pid;
return buf_initialize (input ? stdio_buffer_input : NULL,
input ? NULL : stdio_buffer_output,
input ? NULL : stdio_buffer_flush,
(int (*) PROTO((void *, int))) NULL,
stdio_buffer_shutdown,
memory,
(void *) bc);
}
FILE *
stdio_buffer_get_file (buf)
struct buffer *buf;
{
struct stdio_buffer_closure *bc;
assert(buf->shutdown == stdio_buffer_shutdown);
bc = (struct stdio_buffer_closure *) buf->closure;
return(bc->fp);
}
static int
stdio_buffer_input (closure, data, need, size, got)
void *closure;
char *data;
int need;
int size;
int *got;
{
struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure;
int nbytes;
if (need == 0 || need == 1)
{
int ch;
ch = getc (bc->fp);
if (ch == EOF)
{
if (feof (bc->fp))
return -1;
else if (errno == 0)
return EIO;
else
return errno;
}
*data = ch;
*got = 1;
return 0;
}
nbytes = fread (data, 1, need, bc->fp);
if (nbytes == 0)
{
*got = 0;
if (feof (bc->fp))
return -1;
else if (errno == 0)
return EIO;
else
return errno;
}
*got = nbytes;
return 0;
}
static int
stdio_buffer_output (closure, data, have, wrote)
void *closure;
const char *data;
int have;
int *wrote;
{
struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure;
*wrote = 0;
while (have > 0)
{
int nbytes;
nbytes = fwrite (data, 1, have, bc->fp);
if (nbytes != have)
{
if (errno == 0)
return EIO;
else
return errno;
}
*wrote += nbytes;
have -= nbytes;
data += nbytes;
}
return 0;
}
static int
stdio_buffer_flush (closure)
void *closure;
{
struct stdio_buffer_closure *bc = (struct stdio_buffer_closure *) closure;
if (fflush (bc->fp) != 0)
{
if (errno == 0)
return EIO;
else
return errno;
}
return 0;
}
static int
stdio_buffer_shutdown (buf)
struct buffer *buf;
{
struct stdio_buffer_closure *bc = buf->closure;
struct stat s;
int closefp = 1;
assert (fstat (fileno (bc->fp), &s) != -1);
if (buf->flush)
{
buf_flush (buf, 1);
buf->flush = NULL;
}
if (buf->input)
{
# ifdef SHUTDOWN_SERVER
if (current_parsed_root->method != server_method)
# endif
# ifndef NO_SOCKET_TO_FD
{
if (S_ISSOCK (s.st_mode))
shutdown (fileno (bc->fp), 0);
}
# endif
# ifdef START_RSH_WITH_POPEN_RW
else if (pclose (bc->fp) == EOF)
{
error (1, errno, "closing connection to %s",
current_parsed_root->hostname);
closefp = 0;
}
# endif
buf->input = NULL;
}
else if (buf->output)
{
# ifdef SHUTDOWN_SERVER
if (current_parsed_root->method == server_method)
SHUTDOWN_SERVER (fileno (bc->fp));
else
# endif
# ifndef NO_SOCKET_TO_FD
if (S_ISSOCK (s.st_mode))
shutdown (fileno (bc->fp), 1);
# else
{
}
# endif
buf->output = NULL;
}
if (closefp && fclose (bc->fp) == EOF)
{
if (0
# ifdef SERVER_SUPPORT
|| server_active
# endif
)
{
}
# ifdef CLIENT_SUPPORT
else
error (1, errno,
"closing down connection to %s",
current_parsed_root->hostname);
# endif
}
if (bc->child_pid)
{
int w;
do
w = waitpid (bc->child_pid, (int *) 0, 0);
while (w == -1 && errno == EINTR);
if (w == -1)
error (1, errno, "waiting for process %d", bc->child_pid);
}
return 0;
}
#define PACKET_SLOP (100)
struct packetizing_buffer
{
struct buffer *buf;
int (*inpfn) PROTO((void *fnclosure, const char *input, char *output,
int size));
int (*outfn) PROTO((void *fnclosure, const char *input, char *output,
int size, int *translated));
void *fnclosure;
int translated;
int holdsize;
char *holdbuf;
int holdbufsize;
char *holddata;
};
static int packetizing_buffer_input PROTO((void *, char *, int, int, int *));
static int packetizing_buffer_output PROTO((void *, const char *, int, int *));
static int packetizing_buffer_flush PROTO((void *));
static int packetizing_buffer_block PROTO((void *, int));
static int packetizing_buffer_shutdown PROTO((struct buffer *));
struct buffer *
packetizing_buffer_initialize (buf, inpfn, outfn, fnclosure, memory)
struct buffer *buf;
int (*inpfn) PROTO ((void *, const char *, char *, int));
int (*outfn) PROTO ((void *, const char *, char *, int, int *));
void *fnclosure;
void (*memory) PROTO((struct buffer *));
{
struct packetizing_buffer *pb;
pb = (struct packetizing_buffer *) 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_shutdown,
memory,
pb);
}
static int
packetizing_buffer_input (closure, data, need, size, got)
void *closure;
char *data;
int need;
int size;
int *got;
{
struct packetizing_buffer *pb = (struct packetizing_buffer *) closure;
*got = 0;
if (pb->holdsize > 0 && pb->translated)
{
int 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 = 0;
data += copy;
need -= copy;
size -= copy;
*got = copy;
}
while (need > 0 || *got == 0)
{
int get, status, nread, count, tcount;
char *bytes;
char stackoutbuf[BUFFER_DATA_SIZE + PACKET_SLOP];
char *inbuf, *outbuf;
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 <= sizeof stackoutbuf)
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 = 1;
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 (closure, data, have, wrote)
void *closure;
const char *data;
int have;
int *wrote;
{
struct packetizing_buffer *pb = (struct packetizing_buffer *) closure;
char inbuf[BUFFER_DATA_SIZE + 2];
char stack_outbuf[BUFFER_DATA_SIZE + PACKET_SLOP + 4];
struct buffer_data *outdata;
char *outbuf;
int size, status, translated;
if (have > BUFFER_DATA_SIZE)
{
abort ();
}
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;
if (translated > size + PACKET_SLOP)
abort ();
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 (closure)
void *closure;
{
struct packetizing_buffer *pb = (struct packetizing_buffer *) closure;
return buf_flush (pb->buf, 0);
}
static int
packetizing_buffer_block (closure, block)
void *closure;
int block;
{
struct packetizing_buffer *pb = (struct packetizing_buffer *) closure;
if (block)
return set_block (pb->buf);
else
return set_nonblock (pb->buf);
}
static int
packetizing_buffer_shutdown (buf)
struct buffer *buf;
{
struct packetizing_buffer *pb = (struct packetizing_buffer *) buf->closure;
return buf_shutdown (pb->buf);
}
#endif