1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #include "sfhdr.h" 23 24 /* Add a new discipline to the discipline stack. Each discipline 25 ** provides alternative I/O functions that are analogues of the 26 ** system calls. 27 ** 28 ** When the application fills or flushes the stream buffer, data 29 ** will be processed through discipline functions. A case deserving 30 ** consideration is stacking a discipline onto a read stream. Each 31 ** discipline operation implies buffer synchronization so the stream 32 ** buffer should be empty. However, a read stream representing an 33 ** unseekable device (eg, a pipe) may not be synchronizable. In that 34 ** case, any buffered data must then be fed to the new discipline 35 ** to preserve data processing semantics. This is done by creating 36 ** a temporary discipline to cache such buffered data and feed 37 ** them to the new discipline when its readf() asks for new data. 38 ** Care must then be taken to remove this temporary discipline 39 ** when it runs out of cached data. 40 ** 41 ** Written by Kiem-Phong Vo 42 */ 43 44 typedef struct _dccache_s 45 { Sfdisc_t disc; 46 uchar* data; 47 uchar* endb; 48 } Dccache_t; 49 50 #if __STD_C 51 static int _dccaexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc) 52 #else 53 static int _dccaexcept(f,type,val,disc) 54 Sfio_t* f; 55 int type; 56 Void_t* val; 57 Sfdisc_t* disc; 58 #endif 59 { 60 if(disc && type == SF_FINAL) 61 free(disc); 62 return 0; 63 } 64 65 #if __STD_C 66 static ssize_t _dccaread(Sfio_t* f, Void_t* buf, size_t size, Sfdisc_t* disc) 67 #else 68 static ssize_t _dccaread(f, buf, size, disc) 69 Sfio_t* f; 70 Void_t* buf; 71 size_t size; 72 Sfdisc_t* disc; 73 #endif 74 { 75 ssize_t sz; 76 Sfdisc_t *prev; 77 Dccache_t *dcca; 78 79 if(!f) /* bad stream */ 80 return -1; 81 82 /* make sure that this is on the discipline stack */ 83 for(prev = f->disc; prev; prev = prev->disc) 84 if(prev->disc == disc) 85 break; 86 if(!prev) 87 return -1; 88 89 if(size <= 0) /* nothing to do */ 90 return size; 91 92 /* read from available data */ 93 dcca = (Dccache_t*)disc; 94 if((sz = dcca->endb - dcca->data) > (ssize_t)size) 95 sz = (ssize_t)size; 96 memcpy(buf, dcca->data, sz); 97 98 if((dcca->data += sz) >= dcca->endb) /* free empty cache */ 99 { prev->disc = disc->disc; 100 free(disc); 101 } 102 103 return sz; 104 } 105 106 #if __STD_C 107 Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc) 108 #else 109 Sfdisc_t* sfdisc(f,disc) 110 Sfio_t* f; 111 Sfdisc_t* disc; 112 #endif 113 { 114 Sfdisc_t *d, *rdisc; 115 Sfread_f oreadf; 116 Sfwrite_f owritef; 117 Sfseek_f oseekf; 118 ssize_t n; 119 Dccache_t *dcca = NIL(Dccache_t*); 120 SFMTXDECL(f); 121 122 SFMTXENTER(f, NIL(Sfdisc_t*)); 123 124 if((Sfio_t*)disc == f) /* special case to get the top discipline */ 125 SFMTXRETURN(f,f->disc); 126 127 if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) ) 128 { /* make sure in read mode to check for read-ahead data */ 129 if(_sfmode(f,SF_READ,0) < 0) 130 SFMTXRETURN(f, NIL(Sfdisc_t*)); 131 } 132 else 133 { if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0) 134 SFMTXRETURN(f, NIL(Sfdisc_t*)); 135 } 136 137 SFLOCK(f,0); 138 rdisc = NIL(Sfdisc_t*); 139 140 /* disallow popping while there is cached data */ 141 if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread ) 142 goto done; 143 144 /* synchronize before switching to a new discipline */ 145 if(!(f->flags&SF_STRING)) 146 { (void)SFSYNC(f); /* do a silent buffer synch */ 147 if((f->mode&SF_READ) && (f->mode&SF_SYNCED) ) 148 { f->mode &= ~SF_SYNCED; 149 f->endb = f->next = f->endr = f->endw = f->data; 150 } 151 152 /* if there is buffered data, ask app before proceeding */ 153 if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) || 154 ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) ) 155 { int rv = 0; 156 if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */ 157 { SFOPEN(f,0); 158 rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc); 159 SFLOCK(f,0); 160 } 161 if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */ 162 { SFOPEN(f,0); 163 rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc); 164 SFLOCK(f,0); 165 } 166 if(rv < 0) 167 goto done; 168 } 169 170 /* trick the new discipline into processing already buffered data */ 171 if((f->mode&SF_READ) && n > 0 && disc && disc->readf ) 172 { if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) ) 173 goto done; 174 memclear(dcca, sizeof(Dccache_t)); 175 176 dcca->disc.readf = _dccaread; 177 dcca->disc.exceptf = _dccaexcept; 178 179 /* move buffered data into the temp discipline */ 180 dcca->data = ((uchar*)dcca) + sizeof(Dccache_t); 181 dcca->endb = dcca->data + n; 182 memcpy(dcca->data, f->next, n); 183 f->endb = f->next = f->endr = f->endw = f->data; 184 } 185 } 186 187 /* save old readf, writef, and seekf to see if stream need reinit */ 188 #define GETDISCF(func,iof,type) \ 189 { for(d = f->disc; d && !d->iof; d = d->disc) ; \ 190 func = d ? d->iof : NIL(type); \ 191 } 192 GETDISCF(oreadf,readf,Sfread_f); 193 GETDISCF(owritef,writef,Sfwrite_f); 194 GETDISCF(oseekf,seekf,Sfseek_f); 195 196 if(disc == SF_POPDISC) 197 { /* popping, warn the being popped discipline */ 198 if(!(d = f->disc) ) 199 goto done; 200 disc = d->disc; 201 if(d->exceptf) 202 { SFOPEN(f,0); 203 if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 ) 204 goto done; 205 SFLOCK(f,0); 206 } 207 f->disc = disc; 208 rdisc = d; 209 } 210 else 211 { /* pushing, warn being pushed discipline */ 212 do 213 { /* loop to handle the case where d may pop itself */ 214 d = f->disc; 215 if(d && d->exceptf) 216 { SFOPEN(f,0); 217 if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 ) 218 goto done; 219 SFLOCK(f,0); 220 } 221 } while(d != f->disc); 222 223 /* make sure we are not creating an infinite loop */ 224 for(; d; d = d->disc) 225 if(d == disc) 226 goto done; 227 228 /* set new disc */ 229 if(dcca) /* insert the discipline with cached data */ 230 { dcca->disc.disc = f->disc; 231 disc->disc = &dcca->disc; 232 } 233 else disc->disc = f->disc; 234 f->disc = disc; 235 rdisc = disc; 236 } 237 238 if(!(f->flags&SF_STRING) ) 239 { /* this stream may have to be reinitialized */ 240 reg int reinit = 0; 241 #define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type)) 242 #define REINIT(oiof,iof,type) \ 243 if(!reinit) \ 244 { for(d = f->disc; d && !d->iof; d = d->disc) ; \ 245 if(DISCF(d,iof,type) != oiof) \ 246 reinit = 1; \ 247 } 248 249 REINIT(oreadf,readf,Sfread_f); 250 REINIT(owritef,writef,Sfwrite_f); 251 REINIT(oseekf,seekf,Sfseek_f); 252 253 if(reinit) 254 { SETLOCAL(f); 255 f->bits &= ~SF_NULL; /* turn off /dev/null handling */ 256 if((f->bits&SF_MMAP) || (f->mode&SF_INIT)) 257 sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND); 258 else if(f->data == f->tiny) 259 sfsetbuf(f,NIL(Void_t*),0); 260 else 261 { int flags = f->flags; 262 sfsetbuf(f,(Void_t*)f->data,f->size); 263 f->flags |= (flags&SF_MALLOC); 264 } 265 } 266 } 267 268 done : 269 SFOPEN(f,0); 270 SFMTXRETURN(f, rdisc); 271 } 272