/* ** 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_halfduplex_t *cross; const char *tag; sio_rc_t rc_with_data, rc_no_data; al_t *al; 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; sio_labelnum_t label_data; sio_labelnum_t label_error; sio_labelnum_t label_eof; }; #define SIO_LABEL_DATA(sio) ((al_label_t)&(sio)->label_data) #define SIO_LABEL_ERROR(sio) ((al_label_t)&(sio)->label_error) #define SIO_LABEL_EOF(sio) ((al_label_t)&(sio)->label_eof) 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) { sio_rc_t rc; sio_halfduplex_t *h; /* * call stage and direct data upstream/downstream * according to response code * * if stage directs SIO_OK, chose default direction * depending on data in assembly line * * if we the stage does not return a direction, * simply end the code * * if we drop off the chain, simply result SIO_OK * */ rc = SIO_UPSTREAM; h = chain; while (h != NULL) { rc = h->func(sio, h->al, h->stage->userdata); /* chose default direction */ if (rc == SIO_OK) { if (al_bytes(h->al) > 0) rc = h->rc_with_data; else rc = h->rc_no_data; } if (rc == SIO_UPSTREAM) h = NEXT(h,hd); else if (rc == SIO_DOWNSTREAM) h = PREV(h,hd); else if (rc == SIO_XSTREAM) h = h->cross; else break; } if (h == NULL) rc = SIO_OK; return rc; } /**************************************************************************/ 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); sio->label_data = SIO_LN_DATA; sio->label_error = SIO_LN_ERROR; sio->label_eof = SIO_LN_EOF; *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; /* 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 = NULL; sios->rw = SIO_MODE_INVALID; sios->reader.func = sios->module->input; sios->reader.stage = sios; sios->writer.func = sios->module->output; sios->writer.stage = sios; sios->reader.cross = &sios->writer; sios->writer.cross = &sios->reader; sios->reader.tag = "reader"; sios->writer.tag = "writer"; /* default rules */ sios->reader.rc_with_data = SIO_DOWNSTREAM; sios->reader.rc_no_data = SIO_UPSTREAM; sios->writer.rc_with_data = SIO_UPSTREAM; sios->writer.rc_no_data = SIO_DOWNSTREAM; rc = sios->module->init(sio, &sios->userdata); if (rc != SIO_OK) { free(sios); return SIO_RC(rc); } *siosp = sios; return SIO_RC(rc); } sio_rc_t sio_configure_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; } static sio_rc_t sio_create_al(sio_t *sio, sio_mode_t rw) { al_rc_t arc; int freereader = 0; if (rw == SIO_MODE_READ || rw == SIO_MODE_READWRITE) { if (ISEMPTY(&sio->readers,hd)) { arc = al_create(&sio->readers.al); if (arc != AL_OK) return SIO_ERR_INT; freereader = 1; } } if (rw == SIO_MODE_WRITE || rw == SIO_MODE_READWRITE) { if (ISEMPTY(&sio->writers,hd)) { arc = al_create(&sio->writers.al); if (arc != AL_OK) { if (freereader) al_destroy(sio->readers.al); return SIO_ERR_INT; } } } return SIO_OK; } static sio_rc_t sio_destroy_al(sio_t *sio, sio_mode_t rw) { if (rw == SIO_MODE_READ || rw == SIO_MODE_READWRITE) { if (ISEMPTY(&sio->readers,hd)) { al_destroy(sio->readers.al); sio->readers.al = NULL; } } if (rw == SIO_MODE_WRITE || rw == SIO_MODE_READWRITE) { if (ISEMPTY(&sio->writers,hd)) { al_destroy(sio->writers.al); sio->writers.al = NULL; } } return SIO_OK; } sio_rc_t sio_attach(sio_t *sio, sio_stage_t *sios, sio_mode_t rw) { sio_rc_t rc; int freereader = 0; /* argument sanity check(s) */ if (sio == NULL || sios == NULL) return SIO_RC(SIO_ERR_ARG); switch (rw) { case SIO_MODE_READ: case SIO_MODE_WRITE: case SIO_MODE_READWRITE: break; default: return SIO_RC(SIO_ERR_ARG); } /* is module already attached ? */ if (sios->rw != SIO_MODE_INVALID) return SIO_RC(SIO_ERR_ARG); /* create assembly lines (if aready existing) */ rc = sio_create_al(sio, rw); if (rc != SIO_OK) return SIO_RC(rc); if (rw == SIO_MODE_READ || rw == SIO_MODE_READWRITE) { rc = sios->module->openr(sio, sio->readers.al, sios->userdata); if (rc != SIO_OK) { sio_destroy_al(sio, rw); return SIO_RC(rc); } ADDTAIL(&sio->readers,hd,&sios->reader); freereader = 1; } if (rw == SIO_MODE_WRITE || rw == SIO_MODE_READWRITE) { rc = sios->module->openw(sio, sio->writers.al, sios->userdata); if (rc != SIO_OK) { if (freereader) { REMOVE(&sio->readers,hd,&sios->reader); sios->module->closer(sio, sio->readers.al, sios->userdata); } sio_destroy_al(sio, rw); return SIO_RC(rc); } ADDTAIL(&sio->writers,hd,&sios->writer); } sios->reader.al = sio->readers.al; sios->writer.al = sio->writers.al; 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: case SIO_MODE_WRITE: case SIO_MODE_READWRITE: break; default: return SIO_RC(SIO_ERR_ARG); } rc = SIO_OK; if (sios->rw == SIO_MODE_WRITE || sios->rw == SIO_MODE_READWRITE) { REMOVE(&sio->writers,hd,&sios->writer); rc = sios->module->closew(sio, sio->writers.al, sios->userdata); } if (sios->rw == SIO_MODE_READ || sios->rw == SIO_MODE_READWRITE) { REMOVE(&sio->readers,hd,&sios->reader); if (rc == SIO_OK) rc = sios->module->closer(sio, sio->readers.al, sios->userdata); else /* XXX - double error handling ? */ sios->module->closer(sio, sio->readers.al, sios->userdata); } sios->writer.al = NULL; sios->reader.al = NULL; sio_destroy_al(sio, sios->rw); sios->rw = SIO_MODE_INVALID; return SIO_RC(rc); } sio_rc_t sio_input(sio_t *sio, al_t *al, size_t limit) { sio_rc_t rc; sio_halfduplex_t *h; al_t *src; size_t n; /* argument sanity check(s) */ if (sio == NULL || al == NULL) return SIO_RC(SIO_ERR_ARG); h = HEAD(&sio->readers,hd); if (h == NULL) return SIO_RC(SIO_ERR_ARG); src = h->al; n = al_bytes(src); if (n == 0) { rc = sio_strategy(sio, h); 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_output(sio_t *sio, al_t *al) { sio_rc_t rc; al_rc_t arc; sio_halfduplex_t *h; al_t *dst; size_t n; /* argument sanity check(s) */ if (sio == NULL || al == NULL) return SIO_RC(SIO_ERR_ARG); h = HEAD(&sio->writers,hd); if (h == NULL) return SIO_RC(SIO_ERR_ARG); dst = h->al; n = al_bytes(dst); arc = al_splice(dst, n, 0, al, NULL); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); rc = sio_strategy(sio, h); return SIO_RC(rc); } sio_rc_t sio_push(sio_t *sio) { sio_rc_t rc; al_rc_t arc; sio_halfduplex_t *h; al_t *dst; char eof = '\0'; /* argument sanity check(s) */ if (sio == NULL) return SIO_RC(SIO_ERR_ARG); h = HEAD(&sio->writers,hd); if (h == NULL) return SIO_RC(SIO_ERR_ARG); dst = h->al; arc = al_append_bytes(dst, &eof, 1, SIO_LABEL_EOF(sio)); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); rc = sio_strategy(sio, h); 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; if (n == 0) return SIO_OK; arc = al_create(&al); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); rc = sio_input(sio, al, n); if (rc == SIO_OK) arc = al_flatten(al, 0, n, AL_FORWARD_SPAN, SIO_LABEL_DATA(sio), dst, actualp); 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; if (n == 0) return SIO_OK; arc = al_create(&al); if (arc != AL_OK) return SIO_RC(SIO_ERR_INT); arc = al_append_bytes(al, src, n, SIO_LABEL_DATA(sio)); 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; case SIO_XSTREAM: mess = "Invoke Crossstream Stage"; break; default: mess = "Invalid Result Code"; break; } return mess; } sio_rc_t sio_label(sio_t *sio, sio_labelnum_t ln, al_label_t *p) { void *label; switch (ln) { case SIO_LN_DATA: label = SIO_LABEL_DATA(sio); break; case SIO_LN_ERROR: label = SIO_LABEL_ERROR(sio); break; case SIO_LN_EOF: label = SIO_LABEL_EOF(sio); break; default: return SIO_ERR_ARG; } *p = label; return SIO_OK; }