head 1.14;
access;
symbols
SIO_0_9_3:1.14
SIO_0_9_2:1.13
SIO_0_9_1:1.12
SIO_0_9_0:1.12;
locks; strict;
comment @ * @;
1.14
date 2005.10.03.09.06.11; author rse; state Exp;
branches;
next 1.13;
1.13
date 2003.06.30.10.36.52; author rse; state Exp;
branches;
next 1.12;
1.12
date 2003.02.06.12.45.18; author mlelstv; state Exp;
branches;
next 1.11;
1.11
date 2003.02.05.16.29.05; author mlelstv; state Exp;
branches;
next 1.10;
1.10
date 2003.01.20.15.34.13; author mlelstv; state Exp;
branches;
next 1.9;
1.9
date 2003.01.06.19.04.56; author rse; state Exp;
branches;
next 1.8;
1.8
date 2002.12.18.15.51.02; author mlelstv; state Exp;
branches;
next 1.7;
1.7
date 2002.11.29.14.26.31; author mlelstv; state Exp;
branches;
next 1.6;
1.6
date 2002.11.29.13.00.18; author mlelstv; state Exp;
branches;
next 1.5;
1.5
date 2002.11.27.15.50.29; author mlelstv; state Exp;
branches;
next 1.4;
1.4
date 2002.11.24.19.40.59; author mlelstv; state Exp;
branches;
next 1.3;
1.3
date 2002.11.24.19.39.17; author mlelstv; state Exp;
branches;
next 1.2;
1.2
date 2002.11.19.22.29.20; author mlelstv; state Exp;
branches;
next 1.1;
1.1
date 2002.11.19.15.54.51; author mlelstv; state Exp;
branches;
next ;
desc
@@
1.14
log
@adjust copyright messages
@
text
@/*
** OSSP sio - Stream I/O
** Copyright (c) 2002-2005 Cable & Wireless
** Copyright (c) 2002-2005 The OSSP Project
** Copyright (c) 2002-2005 Ralf S. Engelschall
**
** This file is part of OSSP sio, a layered stream I/O library
** which can be found at http://www.ossp.org/pkg/lib/sio/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** sio_bio.c: OpenSSL BIO stage
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if ENABLE_BIO
#include
#include
#include
#include
#include "al.h"
#include "sio.h"
#include
#include
typedef enum {
INIT,
LOWER,
UPPER
} state_t;
typedef struct {
state_t state;
int isoutput;
al_t *in_buf;
al_t *out_buf;
BIO *bio;
BIO *nbio;
al_t *al_in, *al_out; /* upstream buffers */
int issink;
int freebio;
size_t inputsize;
al_label_t data_label;
al_label_t eof_label;
al_label_t error_label;
char eof;
char error;
size_t total;
int needs_input;
int eof_reached;
int write_error;
int flush_upper;
int flush_lower;
} private_t;
/********************************************************************/
static
int b_new(BIO *bi)
{
bi->init = 0;
bi->num = -1;
bi->flags = 0;
bi->ptr = NULL;
return 1;
}
static
int b_free(BIO *a)
{
if (a == NULL)
return 0;
return 1;
}
static
long b_pending(private_t *my)
{
return my ? al_bytes(my->al_in) : 0;
}
static
long b_wpending(private_t *my)
{
return my ? al_bytes(my->al_out) : 0;
}
static
long b_setflush(private_t *my)
{
if (my) my->flush_upper = 1;
return 1;
}
static
long b_geteof(private_t *my)
{
return my->eof_reached;
}
static
long b_ctrl(BIO *b, int cmd, long num, void *ptr)
{
long ret = 1;
switch (cmd) {
case BIO_CTRL_PENDING:
ret = b_pending((private_t *)b->ptr);
break;
case BIO_CTRL_WPENDING:
ret = b_wpending((private_t *)b->ptr);
break;
case BIO_CTRL_FLUSH:
ret = b_setflush((private_t *)b->ptr);
break;
case BIO_CTRL_EOF:
ret = b_geteof((private_t *)b->ptr);
break;
case BIO_C_SET_FD:
b->ptr = ptr;
b->init = 1;
ret = 1;
break;
case BIO_CTRL_RESET:
case BIO_CTRL_SET:
case BIO_CTRL_SET_CLOSE:
case BIO_CTRL_DUP:
ret = 1;
break;
case BIO_CTRL_GET_CLOSE:
case BIO_CTRL_INFO:
case BIO_CTRL_GET:
default:
ret = 0;
break;
}
return ret;
}
static
int b_read(BIO *b, char *out, int outl)
{
private_t *my = (private_t *)b->ptr;
size_t n, m;
al_label_t label;
m = 0;
if (al_bytes(my->al_in) > 0) {
al_firstlabel(my->al_in, 0, 1, AL_FORWARD, NULL, &label);
if (label == my->data_label) {
al_flatten(my->al_in, 0, outl, AL_FORWARD_SPAN, label, out, &n);
m = n;
} else {
al_flatten(my->al_in, 0, outl, AL_FORWARD_SPAN, label, NULL, &n);
my->eof_reached = 1;
m = 0;
}
al_splice(my->al_in, 0, n, NULL, NULL);
}
BIO_clear_retry_flags(b);
if (m == 0)
BIO_set_retry_read(b);
return m;
}
static
int b_write(BIO *b, const char *in, int inl)
{
private_t *my = (private_t *)b->ptr;
al_append_bytes(my->al_out, in, inl, my->data_label);
return inl;
}
static
int b_puts(BIO *b, const char *s)
{
return b_write(b, s, strlen(s));
}
static
BIO_METHOD b_method = {
BIO_TYPE_NONE,
"SIO SSL",
b_write,
b_read,
b_puts,
NULL,
b_ctrl,
b_new,
b_free,
NULL
};
/********************************************************************/
/*
* create stage
*
* allocate private instance data
*/
static
sio_rc_t siobio_init(sio_t *sio, void **up)
{
private_t *my;
my = (private_t *)malloc(sizeof(private_t));
if (my == NULL)
return SIO_ERR_MEM;
my->bio = NULL;
my->inputsize = 512;
my->issink = 0;
my->freebio = 0;
my->nbio = NULL;
my->eof = '\0';
my->error = '\0';
sio_label(sio, SIO_LN_DATA, &my->data_label);
sio_label(sio, SIO_LN_EOF, &my->eof_label);
sio_label(sio, SIO_LN_ERROR, &my->error_label);
*up = my;
return SIO_OK;
}
/*
* configure stage
*
* pass two void pointers
*/
static
sio_rc_t siobio_configure(sio_t *sio, void *u, void *obj, void *val)
{
private_t *my = (private_t *)u;
const char *name = (const char *)obj;
int v;
if (!strcmp(name, "bio")) {
my->bio = (BIO *)val;
} else if (!strcmp(name, "inputsize")) {
v = *(int *)val;
if (v <= 0)
return SIO_ERR_ARG;
my->inputsize = v;
} else if (!strcmp(name, "issink")) {
my->issink = *(int *)val;
} else if (!strcmp(name, "freebio")) {
my->freebio = *(int *)val;
} else {
return SIO_ERR_ARG;
}
return SIO_OK;
}
/*
* destroy stage
*/
static
sio_rc_t siobio_cleanup(sio_t *sio, void *u)
{
private_t *my = (private_t *)u;
free(my);
return SIO_OK;
}
static
sio_rc_t siobio_openr(sio_t *sio, al_t *al, void *u)
{
return SIO_OK;
}
static
sio_rc_t siobio_closer(sio_t *sio, al_t *al, void *u)
{
return SIO_OK;
}
static
sio_rc_t siobio_openw(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
if (my->bio == NULL)
return SIO_ERR_ARG;
if (al_create(&my->al_in) != AL_OK)
return SIO_ERR_INT;
if (al_create(&my->al_out) != AL_OK) {
al_destroy(my->al_in); my->al_in = NULL;
return SIO_ERR_INT;
}
if (al_create(&my->in_buf) != AL_OK) {
al_destroy(my->al_out); my->al_out = NULL;
al_destroy(my->al_in); my->al_in = NULL;
return SIO_ERR_INT;
}
if (al_create(&my->out_buf) != AL_OK) {
al_destroy(my->in_buf); my->in_buf = NULL;
al_destroy(my->al_out); my->al_out = NULL;
al_destroy(my->al_in); my->al_in = NULL;
return SIO_ERR_INT;
}
if (!my->issink) {
my->nbio = BIO_new(&b_method);
BIO_ctrl(my->nbio, BIO_C_SET_FD, 0, (void *)my);
BIO_push(my->bio, my->nbio);
}
my->eof_reached = 0;
my->needs_input = 0;
my->write_error = 0;
my->flush_upper = 0;
my->flush_lower = 0;
my->state = INIT;
return SIO_OK;
}
static
sio_rc_t siobio_closew(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
if (my->nbio != NULL) {
BIO_pop(my->bio);
BIO_free(my->nbio);
my->nbio = NULL;
}
al_destroy(my->out_buf); my->out_buf = NULL;
al_destroy(my->in_buf); my->in_buf = NULL;
al_destroy(my->al_out); my->al_out = NULL;
al_destroy(my->al_in); my->al_in = NULL;
return SIO_OK;
}
/********************************************************************/
/*
* auto-destructor for allocated input buffer
*/
static
void freebiobuf(char *p, size_t n, void *u)
{
free(p);
}
/*
* chunk traversal of output buffer
*
* counts my->total
* sets my->needs_input
*/
static
al_rc_t siobio_write_chunk(al_chunk_t *alc, void *u)
{
private_t *my = (private_t *)u;
int n = al_chunk_len(alc);
al_rc_t arc = AL_OK;
if (al_same_label(alc, my->data_label)) {
char *p = al_chunk_ptr(alc, 0);
int i, t;
for (t=0; tbio ? BIO_write(my->bio, p+t, n-t) : (n-t);
if (i <= 0) {
if (!BIO_should_retry(my->bio)) {
my->needs_input = 1;
if (my->eof_reached)
my->write_error = 1;
}
arc = AL_ERR_EOF;
break;
}
my->total += i;
}
} else {
my->total += n;
my->flush_lower = 1;
}
return arc;
}
/********************************************************************/
/* UPSTREAM layer */
static
sio_rc_t siobio_input_upper(private_t *my, al_t *al)
{
size_t n;
/* copy global al into input buffer */
n = al_bytes(al);
al_splice(al, 0, n, NULL, my->al_in);
/* flush output */
if (my->flush_upper)
return SIO_SCHED_CROSS;
/* transport data to lower layers */
if (al_bytes(my->al_in) <= 0)
return SIO_SCHED_UP;
if (!my->isoutput) {
my->state = LOWER;
return SIO_SCHED_LOOP;
}
return SIO_SCHED_CROSS;
}
static
sio_rc_t siobio_output_upper(private_t *my, al_t *al)
{
/* copy output buffer to global al */
my->flush_upper = 0;
al_splice(al, al_bytes(al), 0, my->al_out, NULL);
/* transport data to upper layers */
if (al_bytes(al) > 0)
return SIO_SCHED_UP;
/* lower layers signalled they need input */
if (my->needs_input) {
my->needs_input = 0;
/* place input buffer on global al so input routines
* can pick it from there again */
al_splice(al, 0, al_bytes(al), my->al_in, NULL);
return SIO_SCHED_CROSS;
}
if (my->isoutput) {
my->state = LOWER;
return SIO_SCHED_LOOP;
}
return SIO_SCHED_CROSS;
}
/* DOWNSTREAM layer */
static
sio_rc_t siobio_input_lower(private_t *my, al_t *al)
{
al_splice(al, al_bytes(al), 0, my->in_buf, NULL);
if (al_bytes(al) > 0 || my->eof_reached) {
my->state = INIT;
return SIO_SCHED_DOWN;
}
my->state = UPPER;
return SIO_SCHED_LOOP;
}
static
sio_rc_t siobio_output_lower(private_t *my, al_t *al)
{
al_splice(al, 0, al_bytes(al), NULL, my->out_buf);
if (al_bytes(my->out_buf) <= 0) {
my->state = INIT;
return SIO_SCHED_DOWN;
}
my->state = UPPER;
return SIO_SCHED_LOOP;
}
/* BIO layer */
static
void siobio_bio_read(private_t *my)
{
char *p;
int n, m;
n = BIO_pending(my->bio);
if (n <= 0 || (size_t)n > my->inputsize)
n = my->inputsize;
p = malloc(n);
assert(p != NULL);
if (my->bio) {
do {
m = BIO_read(my->bio, p, n);
} while (m <= 0 && BIO_should_retry(my->bio));
if (m == -1 && !my->eof_reached) {
my->flush_upper = 1;
m = 0;
}
} else
m = -2;
if (m < 0) {
free(p);
if (m < -1)
al_append_bytes(my->in_buf, &my->error,
sizeof(my->error), my->error_label);
else
al_append_bytes(my->in_buf, &my->eof,
sizeof(my->eof), my->eof_label);
} else if (m > 0)
al_attach_buffer(my->in_buf, p, m, my->data_label, freebiobuf, NULL);
}
static
void siobio_bio_write(private_t *my)
{
my->needs_input = 0;
my->total = 0;
al_traverse_cb(my->out_buf, 0, al_bytes(my->out_buf),
AL_FORWARD, NULL,
siobio_write_chunk, (void *)my);
al_splice(my->out_buf, 0, my->total, NULL, NULL);
if (!my->write_error && my->flush_lower) {
my->flush_upper = 1;
if (BIO_flush(my->bio) > 0)
my->flush_lower = 0;
}
}
/********************************************************************/
static
sio_rc_t siobio_input(sio_t *sio, al_t *al, void *u, sio_rc_t orc)
{
private_t *my = (private_t *)u;
sio_rc_t rc = SIO_ERR_INT;
switch (my->state) {
case INIT:
my->isoutput = 0;
my->state = LOWER;
rc = SIO_SCHED_LOOP;
break;
case LOWER:
siobio_bio_read(my);
rc = siobio_input_lower(my, al);
if (my->eof_reached) {
my->eof_reached = 0;
my->state = INIT;
al_append_bytes(al, &my->eof, sizeof(my->eof), my->eof_label);
return SIO_SCHED_DOWN;
}
break;
case UPPER:
rc = siobio_input_upper(my, al);
break;
}
return rc;
}
static
sio_rc_t siobio_output(sio_t *sio, al_t *al, void *u, sio_rc_t orc)
{
private_t *my = (private_t *)u;
sio_rc_t rc = SIO_ERR_INT;
switch (my->state) {
case INIT:
my->isoutput = 1;
my->state = LOWER;
rc = SIO_SCHED_LOOP;
break;
case LOWER:
rc = siobio_output_lower(my, al);
siobio_bio_write(my);
if (my->write_error) {
my->write_error = 0;
my->state = INIT;
al_splice(al, 0, al_bytes(al), NULL, NULL);
return SIO_SCHED_DOWN;
}
break;
case UPPER:
rc = siobio_output_upper(my, al);
break;
}
return rc;
}
static
sio_rc_t siobio_shutdown(sio_t *sio, void *u)
{
private_t *my = (private_t *)u;
if (my->freebio && my->bio != NULL) {
BIO_free(my->bio);
my->bio = NULL;
my->flush_upper = 1;
my->state = UPPER;
return SIO_OK;
}
return SIO_ERR_ARG;
}
sio_module_t sio_module_bio = {
"bio",
siobio_init,
siobio_configure,
siobio_cleanup,
siobio_openr,
siobio_closer,
siobio_openw,
siobio_closew,
siobio_input,
siobio_output,
siobio_shutdown
};
#else
const char __sio_bio_c[] = "";
#endif /* ENABLE_BIO */
@
1.13
log
@*) Correctly check the "status" return value of waitpid(3)
in the test suite.
*) Change in the test suite "%08lx" format string to "%d" because the
ts library only has a minimal formatting engine and the argument
is "int" and not "long" anyway.
*) Make sure that sio_{bio,sa,zlib}.c are not empty compilation
units (not allowed in ISO C) even if BIO, SA or ZLIB support is not
activated.
*) Changed SIZE_T_MAX fallback definition to a more portable variant
based on sizeof(size_t) instead of relying on the existance of
(non portable) UINT_MAX.
*) Added GNU autoconf checks for libnsl/libsocket under Solaris.
*) Upgraded to GNU libtool 1.5
@
text
@d3 3
a5 3
** Copyright (c) 2002-2003 Cable & Wireless Deutschland
** Copyright (c) 2002-2003 The OSSP Project
** Copyright (c) 2002-2003 Ralf S. Engelschall
@
1.12
log
@better working eof handling
read deadlock solved
default input buffer size
comments
@
text
@d644 4
d649 1
@
1.11
log
@make default no-sink operation
@
text
@d234 1
a234 1
my->nbio = NULL;
d237 1
d424 6
d434 2
a435 1
if (al_bytes(al) <= 0)
a437 2
al_splice(al, 0, al_bytes(al), NULL, my->al_in);
a441 1
d448 6
a453 4
/* flush output */
if (my->flush_upper) {
my->flush_upper = 0;
al_splice(al, al_bytes(al), 0, my->al_out, NULL);
a454 1
}
d456 1
d459 2
a468 1
d504 1
a504 1
int n;
d514 6
a519 2
n = BIO_read(my->bio, p, n);
} while (n <= 0 && BIO_should_retry(my->bio));
d521 1
a521 1
n = -2;
d523 1
a523 1
if (n < 0) {
d525 1
a525 1
if (n < -1)
d531 2
a532 2
} else if (n > 0)
al_attach_buffer(my->in_buf, p, n, my->data_label, freebiobuf, NULL);
@
1.10
log
@make external references optional
@
text
@d235 1
a235 1
my->issink = 1;
@
1.9
log
@- consistently use standard OSSP copyright message everywhere
- strip trailing whitespaces
@
text
@d31 6
d632 1
@
1.8
log
@avoid compiler warning
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d1 30
d222 1
a222 1
@
1.7
log
@eof/write_error handling
BIO_write retries now loops through scheduler to give upper
layer a chance to run.
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d510 1
a510 1
sio_rc_t rc;
d540 1
a540 1
sio_rc_t rc;
@
1.6
log
@input/output now gets another parameter where scheduler tells
them from where they methods called by passing the return value
of the previously called stage.
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@a25 2
int reader_writes;
int writer_reads;
d35 3
a37 1
int should_retry;
a39 1
int eof_reached;
d138 2
a139 1
m = -1;
a143 3
if (m < 0)
my->eof_reached = 1;
d202 1
a202 1
my->error = '\0';
d223 1
d228 4
a231 1
my->inputsize = *(int *)val;
d300 6
a305 2
my->writer_reads = 0;
my->reader_writes = 0;
d346 1
a346 1
* sets my->should_retry
d363 3
a365 3
my->should_retry = 1;
arc = AL_ERR_EOF;
break;
d367 2
a368 1
i = 0;
d370 1
a370 1
my->total += i;
d414 2
a415 2
if (my->should_retry) {
my->should_retry = 0;
d433 1
a433 1
if (al_bytes(al) > 0) {
d463 1
a463 1
if (n == 0 || n > my->inputsize)
d490 2
a491 2
my->should_retry = 0;
my->total = 0;
d497 1
a497 1
if (my->flush_lower) {
a506 7
sio_rc_t siobio_eof(private_t *my, al_t *al)
{
al_splice(al, 0, al_bytes(al), NULL, NULL);
return SIO_SCHED_DOWN;
}
static
d521 6
d551 6
@
1.5
log
@code cleanup
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d508 1
a508 1
sio_rc_t siobio_input(sio_t *sio, al_t *al, void *u)
d532 1
a532 1
sio_rc_t siobio_output(sio_t *sio, al_t *al, void *u)
@
1.4
log
@don't cause shutdown operation for BIOs that are not freed.
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d192 1
a192 1
sio_rc_t siobio_init(sio_t *sio, void **u)
d211 1
a211 1
*u = my;
@
1.3
log
@now supports full duplex BIOs. BIOs are shut down during
a BIO_free() operation, so this has to be done while the pipe
is active with the "freebio" parameter.
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d565 1
d568 1
a568 1
return SIO_OK;
@
1.2
log
@support errors, do I/O loops
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d4 1
d10 7
d19 22
a40 8
BIO *bio;
size_t inputsize;
al_label_t data_label;
al_label_t eof_label;
al_label_t error_label;
char eof;
char error;
size_t total;
d43 143
d200 5
a204 2
my->bio = NULL;
my->eof = '\0';
d231 4
d270 34
d310 13
d326 5
d336 7
d344 1
a344 1
sio_rc_t siobio_input(sio_t *sio, al_t *al, void *u)
d347 106
d460 1
d462 1
a462 1
if (p != NULL)
d466 2
d469 1
a469 1
if (p == NULL || n <= 0) {
d472 2
a473 1
al_append_bytes(al, &my->error, sizeof(my->error), my->error_label);
d475 24
a498 3
al_append_bytes(al, &my->eof, sizeof(my->eof), my->eof_label);
} else
al_attach_buffer(al, p, n, my->data_label, freebiobuf, NULL);
d500 4
d508 1
a508 1
al_rc_t siobio_write_chunk(al_chunk_t *alc, void *u)
d511 1
a511 1
al_rc_t arc = AL_OK;
d513 13
a525 18
if (al_same_label(alc, my->data_label)) {
char *p = al_chunk_ptr(alc, 0);
int n = al_chunk_len(alc);
int i, t;
for (t=0; tbio, p+t, n-t);
if (i <= 0) {
if (!BIO_should_retry(my->bio)) {
arc = AL_ERR_EOF;
break;
}
i = 0;
}
my->total += i;
}
} else {
my->total += al_chunk_len(alc);
d528 1
a528 1
return arc;
d530 1
d535 19
d555 4
a558 3
my->total = 0;
al_traverse_cb(al, 0, al_bytes(al), AL_FORWARD, my->data_label,
siobio_write_chunk, (void *)my);
d560 6
a565 1
al_splice(al, 0, al_bytes(al), NULL, NULL);
d567 1
a567 1
return SIO_SCHED_DOWN;
d580 2
a581 1
siobio_output
@
1.1
log
@wrap BIO objects from libcrypto as an endpoint
adjust test program to use sio_bio instead of sio_sa
PR:
Submitted by:
Reviewed by:
Approved by:
Obtained from:
@
text
@d15 4
a18 1
char eof;
d37 1
d39 3
a41 2
sio_label(sio, SIO_LN_DATA, &my->data_label);
sio_label(sio, SIO_LN_EOF, &my->eof_label);
a122 1
n = BIO_read(my->bio, p, n);
d124 12
a135 3
if (n == 0 && !BIO_should_read(my->bio))
al_append_bytes(al, &my->eof, sizeof(my->eof), my->eof_label);
else
d145 1
d147 19
a165 2
if (al_same_label(alc, my->data_label))
BIO_write(my->bio, al_chunk_ptr(alc, 0), al_chunk_len(alc));
d167 1
a167 1
return AL_OK;
d174 1
@