1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 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(reg Sfio_t* f, reg Sfdisc_t* disc) 108 #else 109 Sfdisc_t* sfdisc(f,disc) 110 reg Sfio_t* f; 111 reg Sfdisc_t* disc; 112 #endif 113 { 114 reg Sfdisc_t *d, *rdisc; 115 reg Sfread_f oreadf; 116 reg Sfwrite_f owritef; 117 reg Sfseek_f oseekf; 118 ssize_t n; 119 reg Dccache_t *dcca = NIL(Dccache_t*); 120 121 SFMTXSTART(f, NIL(Sfdisc_t*)); 122 123 if((f->flags&SF_READ) && f->proc && (f->mode&SF_WRITE) ) 124 { /* make sure in read mode to check for read-ahead data */ 125 if(_sfmode(f,SF_READ,0) < 0) 126 SFMTXRETURN(f, NIL(Sfdisc_t*)); 127 } 128 else if((f->mode&SF_RDWR) != f->mode && _sfmode(f,0,0) < 0) 129 SFMTXRETURN(f, NIL(Sfdisc_t*)); 130 131 SFLOCK(f,0); 132 rdisc = NIL(Sfdisc_t*); 133 134 /* disallow popping while there is cached data */ 135 if(!disc && f->disc && f->disc->disc && f->disc->disc->readf == _dccaread ) 136 goto done; 137 138 /* synchronize before switching to a new discipline */ 139 if(!(f->flags&SF_STRING)) 140 { (void)SFSYNC(f); /* do a silent buffer synch */ 141 if((f->mode&SF_READ) && (f->mode&SF_SYNCED) ) 142 { f->mode &= ~SF_SYNCED; 143 f->endb = f->next = f->endr = f->endw = f->data; 144 } 145 146 /* if there is buffered data, ask app before proceeding */ 147 if(((f->mode&SF_WRITE) && (n = f->next-f->data) > 0) || 148 ((f->mode&SF_READ) && (n = f->endb-f->next) > 0) ) 149 { int rv = 0; 150 if(rv == 0 && f->disc && f->disc->exceptf) /* ask current discipline */ 151 { SFOPEN(f,0); 152 rv = (*f->disc->exceptf)(f, SF_DBUFFER, &n, f->disc); 153 SFLOCK(f,0); 154 } 155 if(rv == 0 && disc && disc->exceptf) /* ask discipline being pushed */ 156 { SFOPEN(f,0); 157 rv = (*disc->exceptf)(f, SF_DBUFFER, &n, disc); 158 SFLOCK(f,0); 159 } 160 if(rv < 0) 161 goto done; 162 } 163 164 /* trick the new discipline into processing already buffered data */ 165 if((f->mode&SF_READ) && n > 0 && disc && disc->readf ) 166 { if(!(dcca = (Dccache_t*)malloc(sizeof(Dccache_t)+n)) ) 167 goto done; 168 memclear(dcca, sizeof(Dccache_t)); 169 170 dcca->disc.readf = _dccaread; 171 dcca->disc.exceptf = _dccaexcept; 172 173 /* move buffered data into the temp discipline */ 174 dcca->data = ((uchar*)dcca) + sizeof(Dccache_t); 175 dcca->endb = dcca->data + n; 176 memcpy(dcca->data, f->next, n); 177 f->endb = f->next = f->endr = f->endw = f->data; 178 } 179 } 180 181 /* save old readf, writef, and seekf to see if stream need reinit */ 182 #define GETDISCF(func,iof,type) \ 183 { for(d = f->disc; d && !d->iof; d = d->disc) ; \ 184 func = d ? d->iof : NIL(type); \ 185 } 186 GETDISCF(oreadf,readf,Sfread_f); 187 GETDISCF(owritef,writef,Sfwrite_f); 188 GETDISCF(oseekf,seekf,Sfseek_f); 189 190 if(disc == SF_POPDISC) 191 { /* popping, warn the being popped discipline */ 192 if(!(d = f->disc) ) 193 goto done; 194 disc = d->disc; 195 if(d->exceptf) 196 { SFOPEN(f,0); 197 if((*(d->exceptf))(f,SF_DPOP,(Void_t*)disc,d) < 0 ) 198 goto done; 199 SFLOCK(f,0); 200 } 201 f->disc = disc; 202 rdisc = d; 203 } 204 else 205 { /* pushing, warn being pushed discipline */ 206 do 207 { /* loop to handle the case where d may pop itself */ 208 d = f->disc; 209 if(d && d->exceptf) 210 { SFOPEN(f,0); 211 if( (*(d->exceptf))(f,SF_DPUSH,(Void_t*)disc,d) < 0 ) 212 goto done; 213 SFLOCK(f,0); 214 } 215 } while(d != f->disc); 216 217 /* make sure we are not creating an infinite loop */ 218 for(; d; d = d->disc) 219 if(d == disc) 220 goto done; 221 222 /* set new disc */ 223 if(dcca) /* insert the discipline with cached data */ 224 { dcca->disc.disc = f->disc; 225 disc->disc = &dcca->disc; 226 } 227 else disc->disc = f->disc; 228 f->disc = disc; 229 rdisc = disc; 230 } 231 232 if(!(f->flags&SF_STRING) ) 233 { /* this stream may have to be reinitialized */ 234 reg int reinit = 0; 235 #define DISCF(dst,iof,type) (dst ? dst->iof : NIL(type)) 236 #define REINIT(oiof,iof,type) \ 237 if(!reinit) \ 238 { for(d = f->disc; d && !d->iof; d = d->disc) ; \ 239 if(DISCF(d,iof,type) != oiof) \ 240 reinit = 1; \ 241 } 242 243 REINIT(oreadf,readf,Sfread_f); 244 REINIT(owritef,writef,Sfwrite_f); 245 REINIT(oseekf,seekf,Sfseek_f); 246 247 if(reinit) 248 { SETLOCAL(f); 249 f->bits &= ~SF_NULL; /* turn off /dev/null handling */ 250 if((f->bits&SF_MMAP) || (f->mode&SF_INIT)) 251 sfsetbuf(f,NIL(Void_t*),(size_t)SF_UNBOUND); 252 else if(f->data == f->tiny) 253 sfsetbuf(f,NIL(Void_t*),0); 254 else 255 { int flags = f->flags; 256 sfsetbuf(f,(Void_t*)f->data,f->size); 257 f->flags |= (flags&SF_MALLOC); 258 } 259 } 260 } 261 262 done : 263 SFOPEN(f,0); 264 SFMTXRETURN(f, rdisc); 265 } 266