14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #include "sfhdr.h"
234887Schin
244887Schin /* Add a new discipline to the discipline stack. Each discipline
254887Schin ** provides alternative I/O functions that are analogues of the
264887Schin ** system calls.
274887Schin **
284887Schin ** When the application fills or flushes the stream buffer, data
294887Schin ** will be processed through discipline functions. A case deserving
304887Schin ** consideration is stacking a discipline onto a read stream. Each
314887Schin ** discipline operation implies buffer synchronization so the stream
324887Schin ** buffer should be empty. However, a read stream representing an
334887Schin ** unseekable device (eg, a pipe) may not be synchronizable. In that
344887Schin ** case, any buffered data must then be fed to the new discipline
354887Schin ** to preserve data processing semantics. This is done by creating
364887Schin ** a temporary discipline to cache such buffered data and feed
374887Schin ** them to the new discipline when its readf() asks for new data.
384887Schin ** Care must then be taken to remove this temporary discipline
394887Schin ** when it runs out of cached data.
404887Schin **
414887Schin ** Written by Kiem-Phong Vo
424887Schin */
434887Schin
444887Schin typedef struct _dccache_s
454887Schin { Sfdisc_t disc;
464887Schin uchar* data;
474887Schin uchar* endb;
484887Schin } Dccache_t;
494887Schin
504887Schin #if __STD_C
_dccaexcept(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)514887Schin static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
524887Schin #else
534887Schin static int _dccaexcept(f,type,val,disc)
544887Schin Sfio_t* f;
554887Schin int type;
564887Schin Void_t* val;
574887Schin Sfdisc_t* disc;
584887Schin #endif
594887Schin {
604887Schin if(disc && type == SF_FINAL)
614887Schin free(disc);
624887Schin return 0;
634887Schin }
644887Schin
654887Schin #if __STD_C
_dccaread(Sfio_t * f,Void_t * buf,size_t size,Sfdisc_t * disc)664887Schin static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc)
674887Schin #else
684887Schin static ssize_t _dccaread(f, buf, size, disc)
694887Schin Sfio_t* f;
704887Schin Void_t* buf;
714887Schin size_t size;
724887Schin Sfdisc_t* disc;
734887Schin #endif
744887Schin {
754887Schin ssize_t sz;
764887Schin Sfdisc_t *prev;
774887Schin Dccache_t *dcca;
784887Schin
794887Schin if(!f) /* bad stream */
804887Schin return -1;
814887Schin
824887Schin /* make sure that this is on the discipline stack */
834887Schin for(prev = f->disc; prev; prev = prev->disc)
844887Schin if(prev->disc == disc)
854887Schin break;
864887Schin if(!prev)
874887Schin return -1;
884887Schin
894887Schin if(size <= 0) /* nothing to do */
904887Schin return size;
914887Schin
924887Schin /* read from available data */
934887Schin dcca = (Dccache_t*)disc;
944887Schin if((sz = dcca->endb - dcca->data) > (ssize_t)size)
954887Schin sz = (ssize_t)size;
964887Schin memcpy(buf, dcca->data, sz);
974887Schin
984887Schin if((dcca->data += sz) >= dcca->endb) /* free empty cache */
994887Schin { prev->disc = disc->disc;
1004887Schin free(disc);
1014887Schin }
1024887Schin
1034887Schin return sz;
1044887Schin }
1054887Schin
1064887Schin #if __STD_C
sfdisc(Sfio_t * f,Sfdisc_t * disc)1078462SApril.Chin@Sun.COM Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)
1084887Schin #else
1094887Schin Sfdisc_t* sfdisc(f,disc)
1108462SApril.Chin@Sun.COM Sfio_t* f;
1118462SApril.Chin@Sun.COM Sfdisc_t* disc;
1124887Schin #endif
1134887Schin {
1148462SApril.Chin@Sun.COM Sfdisc_t *d, *rdisc;
1158462SApril.Chin@Sun.COM Sfread_f oreadf;
1168462SApril.Chin@Sun.COM Sfwrite_f owritef;
1178462SApril.Chin@Sun.COM Sfseek_f oseekf;
1184887Schin ssize_t n;
1198462SApril.Chin@Sun.COM Dccache_t *dcca = NIL(Dccache_t*);
1208462SApril.Chin@Sun.COM SFMTXDECL(f);
1214887Schin
1228462SApril.Chin@Sun.COM SFMTXENTER(f, NIL(Sfdisc_t*));
1238462SApril.Chin@Sun.COM
1248462SApril.Chin@Sun.COM if((Sfio_t*)disc == f) /* special case to get the top discipline */
1258462SApril.Chin@Sun.COM SFMTXRETURN(f,f->disc);
1264887Schin
1274887Schin if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) )
1284887Schin { /* make sure in read mode to check for read-ahead data */
1294887Schin if(_sfmode(f,SF_READ,0) < 0)
1304887Schin SFMTXRETURN(f, NIL(Sfdisc_t*));
1314887Schin }
1328462SApril.Chin@Sun.COM else
1338462SApril.Chin@Sun.COM { if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0)
1348462SApril.Chin@Sun.COM SFMTXRETURN(f, NIL(Sfdisc_t*));
1358462SApril.Chin@Sun.COM }
1364887Schin
1374887Schin SFLOCK(f,0);
1384887Schin rdisc = NIL(Sfdisc_t*);
1394887Schin
1404887Schin /* disallow popping while there is cached data */
1414887Schin if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread )
1424887Schin goto done;
1434887Schin
1444887Schin /* synchronize before switching to a new discipline */
1454887Schin if(!(f->flags&SF_STRING))
1464887Schin { (void)SFSYNC(f); /* do a silent buffer synch */
1474887Schin if((f->mode&SF_READ) && (f->mode&SF_SYNCED) )
1484887Schin { f->mode &= ~SF_SYNCED;
1494887Schin f->endb = f->next = f->endr = f->endw = f->data;
1504887Schin }
1514887Schin
1524887Schin /* if there is buffered data, ask app before proceeding */
1534887Schin if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) ||
1544887Schin ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) )
1554887Schin { int rv = 0;
1564887Schin if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */
1574887Schin { SFOPEN(f,0);
1584887Schin rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc);
1594887Schin SFLOCK(f,0);
1604887Schin }
1614887Schin if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */
1624887Schin { SFOPEN(f,0);
1634887Schin rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc);
1644887Schin SFLOCK(f,0);
1654887Schin }
1664887Schin if(rv < 0)
1674887Schin goto done;
1684887Schin }
1694887Schin
1704887Schin /* trick the new discipline into processing already buffered data */
1714887Schin if((f->mode&SF_READ) && n > 0 && disc && disc->readf )
1724887Schin { if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) )
1734887Schin goto done;
1744887Schin memclear(dcca, sizeof(Dccache_t));
1754887Schin
1764887Schin dcca->disc.readf = _dccaread;
1774887Schin dcca->disc.exceptf = _dccaexcept;
1784887Schin
1794887Schin /* move buffered data into the temp discipline */
1804887Schin dcca->data = ((uchar*)dcca) + sizeof(Dccache_t);
1814887Schin dcca->endb = dcca->data + n;
1824887Schin memcpy(dcca->data, f->next, n);
1834887Schin f->endb = f->next = f->endr = f->endw = f->data;
1844887Schin }
1854887Schin }
1864887Schin
1874887Schin /* save old readf, writef, and seekf to see if stream need reinit */
1884887Schin #define GETDISCF(func,iof,type) \
1894887Schin { for(d = f->disc; d && !d->iof; d = d->disc) ; \
1904887Schin func = d ? d->iof : NIL(type); \
1914887Schin }
1924887Schin GETDISCF(oreadf,readf,Sfread_f);
1934887Schin GETDISCF(owritef,writef,Sfwrite_f);
1944887Schin GETDISCF(oseekf,seekf,Sfseek_f);
1954887Schin
1964887Schin if(disc == SF_POPDISC)
1974887Schin { /* popping, warn the being popped discipline */
1984887Schin if(!(d = f->disc) )
1994887Schin goto done;
2004887Schin disc = d->disc;
2014887Schin if(d->exceptf)
2024887Schin { SFOPEN(f,0);
2034887Schin if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 )
2044887Schin goto done;
2054887Schin SFLOCK(f,0);
2064887Schin }
2074887Schin f->disc = disc;
2084887Schin rdisc = d;
2094887Schin }
2104887Schin else
2114887Schin { /* pushing, warn being pushed discipline */
2124887Schin do
2134887Schin { /* loop to handle the case where d may pop itself */
2144887Schin d = f->disc;
2154887Schin if(d && d->exceptf)
2164887Schin { SFOPEN(f,0);
2174887Schin if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 )
2184887Schin goto done;
2194887Schin SFLOCK(f,0);
2204887Schin }
2214887Schin } while(d != f->disc);
2224887Schin
2234887Schin /* make sure we are not creating an infinite loop */
2244887Schin for(; d; d = d->disc)
2254887Schin if(d == disc)
2264887Schin goto done;
2274887Schin
2284887Schin /* set new disc */
2294887Schin if(dcca) /* insert the discipline with cached data */
2304887Schin { dcca->disc.disc = f->disc;
2314887Schin disc->disc = &dcca->disc;
2324887Schin }
2334887Schin else disc->disc = f->disc;
2344887Schin f->disc = disc;
2354887Schin rdisc = disc;
2364887Schin }
2374887Schin
2384887Schin if(!(f->flags&SF_STRING) )
2394887Schin { /* this stream may have to be reinitialized */
2404887Schin reg int reinit = 0;
2414887Schin #define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type))
2424887Schin #define REINIT(oiof,iof,type) \
2434887Schin if(!reinit) \
2444887Schin { for(d = f->disc; d && !d->iof; d = d->disc) ; \
2454887Schin if(DISCF(d,iof,type) != oiof) \
2464887Schin reinit = 1; \
2474887Schin }
2484887Schin
2494887Schin REINIT(oreadf,readf,Sfread_f);
2504887Schin REINIT(owritef,writef,Sfwrite_f);
2514887Schin REINIT(oseekf,seekf,Sfseek_f);
2524887Schin
2534887Schin if(reinit)
2544887Schin { SETLOCAL(f);
2554887Schin f->bits &= ~SF_NULL; /* turn off /dev/null handling */
2564887Schin if((f->bits&SF_MMAP) || (f->mode&SF_INIT))
2574887Schin sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND);
2584887Schin else if(f->data == f->tiny)
2594887Schin sfsetbuf(f,NIL(Void_t*),0);
2604887Schin else
2614887Schin { int flags = f->flags;
2624887Schin sfsetbuf(f,(Void_t*)f->data,f->size);
2634887Schin f->flags |= (flags&SF_MALLOC);
2644887Schin }
2654887Schin }
2664887Schin }
2674887Schin
2684887Schin done :
2694887Schin SFOPEN(f,0);
2704887Schin SFMTXRETURN(f, rdisc);
2714887Schin }
272