summaryrefslogtreecommitdiff
path: root/src/lib/libast/sfio/sfdisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libast/sfio/sfdisc.c')
-rw-r--r--src/lib/libast/sfio/sfdisc.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/src/lib/libast/sfio/sfdisc.c b/src/lib/libast/sfio/sfdisc.c
new file mode 100644
index 0000000..8ffc559
--- /dev/null
+++ b/src/lib/libast/sfio/sfdisc.c
@@ -0,0 +1,271 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1985-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* Phong Vo <kpv@research.att.com> *
+* *
+***********************************************************************/
+#include "sfhdr.h"
+
+/* Add a new discipline to the discipline stack. Each discipline
+** provides alternative I/O functions that are analogues of the
+** system calls.
+**
+** When the application fills or flushes the stream buffer, data
+** will be processed through discipline functions. A case deserving
+** consideration is stacking a discipline onto a read stream. Each
+** discipline operation implies buffer synchronization so the stream
+** buffer should be empty. However, a read stream representing an
+** unseekable device (eg, a pipe) may not be synchronizable. In that
+** case, any buffered data must then be fed to the new discipline
+** to preserve data processing semantics. This is done by creating
+** a temporary discipline to cache such buffered data and feed
+** them to the new discipline when its readf() asks for new data.
+** Care must then be taken to remove this temporary discipline
+** when it runs out of cached data.
+**
+** Written by Kiem-Phong Vo
+*/
+
+typedef struct _dccache_s
+{ Sfdisc_t disc;
+ uchar* data;
+ uchar* endb;
+} Dccache_t;
+
+#if __STD_C
+static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
+#else
+static int _dccaexcept(f,type,val,disc)
+Sfio_t* f;
+int type;
+Void_t* val;
+Sfdisc_t* disc;
+#endif
+{
+ if(disc && type == SF_FINAL)
+ free(disc);
+ return 0;
+}
+
+#if __STD_C
+static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
+#else
+static ssize_t _dccaread(f, buf, size, disc)
+Sfio_t* f;
+Void_t* buf;
+size_t size;
+Sfdisc_t* disc;
+#endif
+{
+ ssize_t sz;
+ Sfdisc_t *prev;
+ Dccache_t *dcca;
+
+ if(!f) /* bad stream */
+ return -1;
+
+ /* make sure that this is on the discipline stack */
+ for(prev = f->disc; prev; prev = prev->disc)
+ if(prev->disc == disc)
+ break;
+ if(!prev)
+ return -1;
+
+ if(size <= 0) /* nothing to do */
+ return size;
+
+ /* read from available data */
+ dcca = (Dccache_t*)disc;
+ if((sz = dcca->endb - dcca->data) > (ssize_t)size)
+ sz = (ssize_t)size;
+ memcpy(buf, dcca->data, sz);
+
+ if((dcca->data += sz) >= dcca->endb) /* free empty cache */
+ { prev->disc = disc->disc;
+ free(disc);
+ }
+
+ return sz;
+}
+
+#if __STD_C
+Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
+#else
+Sfdisc_t* sfdisc(f,disc)
+Sfio_t* f;
+Sfdisc_t* disc;
+#endif
+{
+ Sfdisc_t *d, *rdisc;
+ Sfread_f oreadf;
+ Sfwrite_f owritef;
+ Sfseek_f oseekf;
+ ssize_t n;
+ Dccache_t *dcca = NIL(Dccache_t*);
+ SFMTXDECL(f); /* declare a local stream variable for multithreading */
+
+ SFMTXENTER(f, NIL(Sfdisc_t*));
+
+ if((Sfio_t*)disc == f) /* special case to get the top discipline */
+ SFMTXRETURN(f,f->disc);
+
+ if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) )
+ { /* make sure in read mode to check for read-ahead data */
+ if(_sfmode(f,SF_READ,0) < 0)
+ SFMTXRETURN(f, NIL(Sfdisc_t*));
+ }
+ else
+ { if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
+ SFMTXRETURN(f, NIL(Sfdisc_t*));
+ }
+
+ SFLOCK(f,0);
+ rdisc = NIL(Sfdisc_t*);
+
+ /* disallow popping while there is cached data */
+ if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread )
+ goto done;
+
+ /* synchronize before switching to a new discipline */
+ if(!(f->flags&SF_STRING))
+ { (void)SFSYNC(f); /* do a silent buffer synch */
+ if((f->mode&SF_READ) && (f->mode&SF_SYNCED) )
+ { f->mode &= ~SF_SYNCED;
+ f->endb = f->next = f->endr = f->endw = f->data;
+ }
+
+ /* if there is buffered data, ask app before proceeding */
+ if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) ||
+ ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) )
+ { int rv = 0;
+ if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */
+ { SFOPEN(f,0);
+ rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc);
+ SFLOCK(f,0);
+ }
+ if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */
+ { SFOPEN(f,0);
+ rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc);
+ SFLOCK(f,0);
+ }
+ if(rv < 0)
+ goto done;
+ }
+
+ /* trick the new discipline into processing already buffered data */
+ if((f->mode&SF_READ) && n > 0 && disc && disc->readf )
+ { if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) )
+ goto done;
+ memclear(dcca, sizeof(Dccache_t));
+
+ dcca->disc.readf = _dccaread;
+ dcca->disc.exceptf = _dccaexcept;
+
+ /* move buffered data into the temp discipline */
+ dcca->data = ((uchar*)dcca) + sizeof(Dccache_t);
+ dcca->endb = dcca->data + n;
+ memcpy(dcca->data, f->next, n);
+ f->endb = f->next = f->endr = f->endw = f->data;
+ }
+ }
+
+ /* save old readf, writef, and seekf to see if stream need reinit */
+#define GETDISCF(func,iof,type) \
+ { for(d = f->disc; d && !d->iof; d = d->disc) ; \
+ func = d ? d->iof : NIL(type); \
+ }
+ GETDISCF(oreadf,readf,Sfread_f);
+ GETDISCF(owritef,writef,Sfwrite_f);
+ GETDISCF(oseekf,seekf,Sfseek_f);
+
+ if(disc == SF_POPDISC)
+ { /* popping, warn the being popped discipline */
+ if(!(d = f->disc) )
+ goto done;
+ disc = d->disc;
+ if(d->exceptf)
+ { SFOPEN(f,0);
+ if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 )
+ goto done;
+ SFLOCK(f,0);
+ }
+ f->disc = disc;
+ rdisc = d;
+ }
+ else
+ { /* pushing, warn being pushed discipline */
+ do
+ { /* loop to handle the case where d may pop itself */
+ d = f->disc;
+ if(d && d->exceptf)
+ { SFOPEN(f,0);
+ if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 )
+ goto done;
+ SFLOCK(f,0);
+ }
+ } while(d != f->disc);
+
+ /* make sure we are not creating an infinite loop */
+ for(; d; d = d->disc)
+ if(d == disc)
+ goto done;
+
+ /* set new disc */
+ if(dcca) /* insert the discipline with cached data */
+ { dcca->disc.disc = f->disc;
+ disc->disc = &dcca->disc;
+ }
+ else disc->disc = f->disc;
+ f->disc = disc;
+ rdisc = disc;
+ }
+
+ if(!(f->flags&SF_STRING) )
+ { /* this stream may have to be reinitialized */
+ reg int reinit = 0;
+#define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type))
+#define REINIT(oiof,iof,type) \
+ if(!reinit) \
+ { for(d = f->disc; d && !d->iof; d = d->disc) ; \
+ if(DISCF(d,iof,type) != oiof) \
+ reinit = 1; \
+ }
+
+ REINIT(oreadf,readf,Sfread_f);
+ REINIT(owritef,writef,Sfwrite_f);
+ REINIT(oseekf,seekf,Sfseek_f);
+
+ if(reinit)
+ { SETLOCAL(f);
+ f->bits &= ~SF_NULL; /* turn off /dev/null handling */
+ if((f->bits&SF_MMAP) || (f->mode&SF_INIT))
+ sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND);
+ else if(f->data == f->tiny)
+ sfsetbuf(f,NIL(Void_t*),0);
+ else
+ { int flags = f->flags;
+ sfsetbuf(f,(Void_t*)f->data,f->size);
+ f->flags |= (flags&SF_MALLOC);
+ }
+ }
+ }
+
+done :
+ SFOPEN(f,0);
+ SFMTXRETURN(f, rdisc);
+}