/* ** OSSP sio -- stream I/O ** Copyright (c) 2002 The OSSP Project ** Copyright (c) 2002 Cable & Wireless Deutschland ** Copyright (c) 2002 Ralf S. Engelschall ** Copyright (c) 2002 Michael van Elst ** ** This file is part of OSSP sio, a library implementing layered I/O ** ** 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.c: stream I/O library implementation */ #include #include #include "al.h" #include "sio.h" #include "sio_module.h" #include "list.h" /****************************************************************************/ /* unique library identifier */ const char sio_id[] = "OSSP sio"; /* support for OSSP ex based exception throwing */ #ifdef WITH_EX #include "ex.h" #define SIO_RC(rv) \ ( (rv) != SIO_OK && (ex_catching && !ex_shielding) \ ? (ex_throw(sio_id, NULL, (rv)), (rv)) : (rv) ) #else #define SIO_RC(rv) (rv) #endif /* WITH_EX */ struct sio_halfduplex_st; typedef struct sio_halfduplex_st sio_halfduplex_t; struct sio_halfduplex_st { NODE(sio_halfduplex_t) hd; sio_stage_t *stage; sio_rc_t (*func)(sio_t *, al_t *, void *); }; struct sio_st { struct { LIST(sio_halfduplex_t) hd; al_t *al; } readers; struct { LIST(sio_halfduplex_t) hd; al_t *al; } writers; }; struct sio_stage_st { sio_halfduplex_t reader; sio_halfduplex_t writer; void *userdata; sio_module_t *module; sio_mode_t rw; }; /****************************************************************************/ static sio_rc_t sio_strategy(sio_t *sio, sio_halfduplex_t *chain, al_t *al) { sio_rc_t rc; sio_halfduplex_t *h; h = chain; while (h != NULL) { rc = h->func(sio, al, h->stage->userdata); if (rc == SIO_UPSTREAM) h = NEXT(h,hd); else if (rc == SIO_DOWNSTREAM) h = NEXT(h,hd); else break; } return SIO_OK; } /**************************************************************************/ sio_rc_t sio_create(sio_t **siop) { sio_t *sio; /* argument sanity check(s) */ if (siop == NULL) return SIO_RC(SIO_ERR_ARG); sio = (sio_t *)malloc(sizeof(sio_t)); if (sio == NULL) return SIO_RC(SIO_ERR_MEM); LISTINIT(&sio->readers,hd); LISTINIT(&sio->writers,hd); *siop = sio; return SIO_OK; } sio_rc_t sio_destroy(sio_t *sio) { /* argument sanity check(s) */ if (sio == NULL) return SIO_RC(SIO_ERR_ARG); free(sio); return SIO_OK; } sio_rc_t sio_create_stage(sio_t *sio, sio_module_t *siom, sio_stage_t **siosp) { sio_rc_t rc; sio_stage_t *sios; void *u; /* argument sanity check(s) */ if (sio == NULL || siom == NULL || siosp == NULL) return SIO_RC(SIO_ERR_ARG); sios = (sio_stage_t *)malloc(sizeof(sio_stage_t)); if (sios == NULL) return SIO_RC(SIO_ERR_MEM); NODEINIT(&sios->reader,hd); NODEINIT(&sios->writer,hd); sios->module = siom; sios->userdata = u; sios->rw = SIO_MODE_INVALID; sios->reader.func = siom->input; sios->reader.stage = sios; sios->writer.func = siom->output; sios->writer.stage = sios; rc = sios->module->init(sio, sios->userdata); return SIO_RC(rc); } sio_rc_t sio_cofigure_stage(sio_t *sio, sio_stage_t *sios, void *obj, void *value) { sio_rc_t rc; /* argument sanity check(s) */ if (sio == NULL || sios == NULL) return SIO_RC(SIO_ERR_ARG); rc = sios->module->configure(sio, sios->userdata, obj, value); return SIO_RC(rc); } sio_rc_t sio_destroy_stage(sio_t *sio, sio_stage_t *sios) { sio_rc_t rc; /* argument sanity check(s) */ if (sio == NULL || sios == NULL) return SIO_RC(SIO_ERR_ARG); rc = sios->module->cleanup(sio, sios->userdata); free(sios); return SIO_OK; } sio_rc_t sio_attach(sio_t *sio, sio_stage_t *sios, sio_mode_t rw) { sio_rc_t rc; /* argument sanity check(s) */ if (sio == NULL || sios == NULL) return SIO_RC(SIO_ERR_ARG); /* is module already attached ? */ if (sios->rw != SIO_MODE_INVALID) return SIO_RC(SIO_ERR_ARG); /* prepare module for being attached */ rc = sios->module->open(sio, sios->userdata); if (rc != SIO_OK) return SIO_RC(rc); switch (rw) { case SIO_MODE_READ: ADDTAIL(&sio->readers,hd,&sios->reader); break; case SIO_MODE_WRITE: ADDTAIL(&sio->writers,hd,&sios->writer); break; case SIO_MODE_READWRITE: ADDTAIL(&sio->readers,hd,&sios->reader); ADDTAIL(&sio->writers,hd,&sios->writer); break; default: return SIO_RC(SIO_ERR_ARG); } /* Remember the lists that sios has been attached to */ sios->rw = rw; return SIO_OK; } sio_rc_t sio_detach(sio_t *sio, sio_stage_t *sios) { sio_rc_t rc; /* argument sanity check(s) */ if (sio == NULL || sios == NULL) return SIO_RC(SIO_ERR_ARG); switch (sios->rw) { case SIO_MODE_READ: REMOVE(&sio->readers,hd,&sios->reader); break; case SIO_MODE_WRITE: REMOVE(&sio->writers,hd,&sios->writer); break; case SIO_MODE_READWRITE: REMOVE(&sio->readers,hd,&sios->reader); REMOVE(&sio->writers,hd,&sios->writer); break; default: return SIO_RC(SIO_ERR_ARG); break; } rc = sios->module->close(sio, sios->userdata); return SIO_RC(rc); } sio_rc_t sio_input(sio_t *sio, al_t *al, size_t limit) { sio_rc_t rc; al_t *src = sio->readers.al; size_t n; /* argument sanity check(s) */ if (sio == NULL || al == NULL) return SIO_RC(SIO_ERR_ARG); n = al_bytes(src); if (n == 0) { rc = sio_strategy(sio, HEAD(&sio->readers,hd), src); if (rc != SIO_OK) return SIO_RC(rc); n = al_bytes(src); if (n == 0) return SIO_RC(SIO_ERR_EOF); } if (n > limit) n = limit; (void) al_splice(src, 0, n, NULL, al); /* XXX - error handling ? */ return SIO_OK; } sio_rc_t sio_discard(sio_t *sio) { sio_rc_t rc; al_t *src = sio->readers.al; size_t n; /* argument sanity check(s) */ if (sio == NULL) return SIO_RC(SIO_ERR_ARG); while ((n = al_bytes(src)) > 0) { rc = sio_strategy(sio, HEAD(&sio->readers,hd), src); if (rc != SIO_OK) break; } if (rc == SIO_ERR_EOF) return SIO_OK; return SIO_RC(rc); } sio_rc_t sio_output(sio_t *sio, al_t *al) { sio_rc_t rc; al_t *dst = sio->writers.al; size_t n; /* argument sanity check(s) */ if (sio == NULL || al == NULL) return SIO_RC(SIO_ERR_ARG); n = al_bytes(dst); al_splice(dst, n, 0, al, NULL); rc = sio_strategy(sio, HEAD(&sio->writers,hd), dst); return SIO_RC(rc); } sio_rc_t sio_flush(sio_t *sio) { sio_rc_t rc; al_t *dst = sio->writers.al; size_t n; /* argument sanity check(s) */ if (sio == NULL) return SIO_RC(SIO_ERR_ARG); while ((n = al_bytes(dst)) > 0) { rc = sio_strategy(sio, HEAD(&sio->writers,hd), sio->writers.al); if (rc != SIO_OK) break; } return SIO_RC(rc); } sio_rc_t sio_read(sio_t *sio, char *dst, size_t n, size_t *actualp) { al_rc_t arc; sio_rc_t rc; al_t *al; arc = al_create(&al); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); rc = sio_input(sio, al, n); if (rc == AL_OK) { arc = al_flatten(al, 0, n, dst, actualp); if (arc != AL_OK) rc = SIO_ERR_INT; } arc = al_destroy(al); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); return SIO_RC(rc); } sio_rc_t sio_write(sio_t *sio, char *src, size_t n, size_t *actualp) { al_rc_t arc; sio_rc_t rc; al_t *al; arc = al_create(&al); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); arc = al_append_bytes(al, src, n); if (arc != AL_OK) rc = SIO_ERR_INT; else rc = sio_output(sio, al); arc = al_destroy(al); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); return SIO_RC(rc); } const char *sio_error(sio_rc_t rc) { const char *mess; switch (rc) { case SIO_OK: mess = "Everything Ok"; break; case SIO_ERR_ARG: mess = "Invalid Argument"; break; case SIO_ERR_MEM: mess = "Not Enough Memory"; break; case SIO_ERR_EOF: mess = "End Of Data"; break; case SIO_ERR_SYS: mess = "Operating System Error"; break; case SIO_ERR_INT: mess = "Internal Error"; break; case SIO_UPSTREAM: mess = "Invoke Upstream Stage"; break; case SIO_DOWNSTREAM: mess = "Invoke Downstream Stage"; break; default: mess = "Invalid Result Code"; break; } return mess; }