1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1985-2011 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#if defined(__STDPP__directive) && defined(__STDPP__hide) 23__STDPP__directive pragma pp:hide getpagesize 24#else 25#define getpagesize ______getpagesize 26#endif 27 28#include "sfhdr.h" 29 30#if defined(__STDPP__directive) && defined(__STDPP__hide) 31__STDPP__directive pragma pp:nohide getpagesize 32#else 33#undef getpagesize 34#endif 35 36#if _lib_getpagesize 37_BEGIN_EXTERNS_ 38extern int getpagesize _ARG_((void)); 39_END_EXTERNS_ 40#endif 41 42/* Set a (new) buffer for a stream. 43** If size < 0, it is assigned a suitable value depending on the 44** kind of stream. The actual buffer size allocated is dependent 45** on how much memory is available. 46** 47** Written by Kiem-Phong Vo. 48*/ 49 50#if !_sys_stat 51struct stat 52{ int st_mode; 53 int st_size; 54}; 55#undef sysfstatf 56#define sysfstatf(fd,st) (-1) 57#endif /*_sys_stat*/ 58 59#define SETLINEMODE 0 60 61#if SETLINEMODE 62 63static int setlinemode() 64{ char* astsfio; 65 char* endw; 66 67 static int modes = -1; 68 static const char sf_line[] = "SF_LINE"; 69 static const char sf_wcwidth[] = "SF_WCWIDTH"; 70 71#define ISSEPAR(c) ((c) == ',' || (c) == ' ' || (c) == '\t') 72 if (modes < 0) 73 { modes = 0; 74 if(astsfio = getenv("_AST_SFIO_OPTIONS")) 75 { for(; *astsfio != 0; astsfio = endw) 76 { while(ISSEPAR(*astsfio) ) 77 *astsfio++; 78 for(endw = astsfio; *endw && !ISSEPAR(*endw); ++endw) 79 ; 80 if((endw-astsfio) == (sizeof(sf_line)-1) && 81 strncmp(astsfio,sf_line,endw-astsfio) == 0) 82 { if ((modes |= SF_LINE) == (SF_LINE|SF_WCWIDTH)) 83 break; 84 } 85 else if((endw-astsfio) == (sizeof(sf_wcwidth)-1) && 86 strncmp(astsfio,sf_wcwidth,endw-astsfio) == 0) 87 { if ((modes |= SF_WCWIDTH) == (SF_LINE|SF_WCWIDTH)) 88 break; 89 } 90 } 91 } 92 } 93 return modes; 94} 95 96#endif 97 98#if __STD_C 99Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size) 100#else 101Void_t* sfsetbuf(f,buf,size) 102Sfio_t* f; /* stream to be buffered */ 103Void_t* buf; /* new buffer */ 104size_t size; /* buffer size, -1 for default size */ 105#endif 106{ 107 int sf_malloc, oflags, init, okmmap, local; 108 ssize_t bufsize, blksz; 109 Sfdisc_t* disc; 110 sfstat_t st; 111 uchar* obuf = NIL(uchar*); 112 ssize_t osize = 0; 113 SFMTXDECL(f); 114 115 SFONCE(); 116 117 SFMTXENTER(f,NIL(Void_t*)); 118 119 GETLOCAL(f,local); 120 121 if(size == 0 && buf) 122 { /* special case to get buffer info */ 123 _Sfi = f->val = (f->bits&SF_MMAP) ? (f->endb-f->data) : f->size; 124 SFMTXRETURN(f, (Void_t*)f->data); 125 } 126 127 /* cleanup actions already done, don't allow write buffering any more */ 128 if(_Sfexiting && !(f->flags&SF_STRING) && (f->mode&SF_WRITE)) 129 { buf = NIL(Void_t*); 130 size = 0; 131 } 132 133 if((init = f->mode&SF_INIT) ) 134 { if(!f->pool && _sfsetpool(f) < 0) 135 SFMTXRETURN(f, NIL(Void_t*)); 136 } 137 else if((f->mode&SF_RDWR) != SFMODE(f,local) && _sfmode(f,0,local) < 0) 138 SFMTXRETURN(f, NIL(Void_t*)); 139 140 if(init) 141 f->mode = (f->mode&SF_RDWR)|SF_LOCK; 142 else 143 { int rv; 144 145 /* make sure there is no hidden read data */ 146 if(f->proc && (f->flags&SF_READ) && (f->mode&SF_WRITE) && 147 _sfmode(f,SF_READ,local) < 0) 148 SFMTXRETURN(f, NIL(Void_t*)); 149 150 /* synchronize first */ 151 SFLOCK(f,local); rv = SFSYNC(f); SFOPEN(f,local); 152 if(rv < 0) 153 SFMTXRETURN(f, NIL(Void_t*)); 154 155 /* turn off the SF_SYNCED bit because buffer is changing */ 156 f->mode &= ~SF_SYNCED; 157 } 158 159 SFLOCK(f,local); 160 161 if((Sfio_t*)buf != f) 162 blksz = -1; 163 else /* setting alignment size only */ 164 { blksz = (ssize_t)size; 165 166 if(!init) /* stream already initialized */ 167 { obuf = f->data; 168 osize = f->size; 169 goto done; 170 } 171 else /* initialize stream as if in the default case */ 172 { buf = NIL(Void_t*); 173 size = (size_t)SF_UNBOUND; 174 } 175 } 176 177 bufsize = 0; 178 oflags = f->flags; 179 180 /* see if memory mapping is possible (see sfwrite for SF_BOTH) */ 181 okmmap = (buf || (f->flags&SF_STRING) || (f->flags&SF_RDWR) == SF_RDWR) ? 0 : 1; 182 183 /* save old buffer info */ 184#ifdef MAP_TYPE 185 if(f->bits&SF_MMAP) 186 { if(f->data) 187 { SFMUNMAP(f,f->data,f->endb-f->data); 188 f->data = NIL(uchar*); 189 } 190 } else 191#endif 192 if(f->data == f->tiny) 193 { f->data = NIL(uchar*); 194 f->size = 0; 195 } 196 obuf = f->data; 197 osize = f->size; 198 199 f->flags &= ~SF_MALLOC; 200 f->bits &= ~SF_MMAP; 201 202 /* pure read/string streams must have a valid string */ 203 if((f->flags&(SF_RDWR|SF_STRING)) == SF_RDSTR && 204 (size == (size_t)SF_UNBOUND || !buf)) 205 size = 0; 206 207 /* set disc to the first discipline with a seekf */ 208 for(disc = f->disc; disc; disc = disc->disc) 209 if(disc->seekf) 210 break; 211 212 if((init || local) && !(f->flags&SF_STRING)) 213 { /* ASSERT(f->file >= 0) */ 214 st.st_mode = 0; 215 216 /* if has discipline, set size by discipline if possible */ 217 if(!_sys_stat || disc) 218 { if((f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,disc)) < 0) 219 goto unseekable; 220 else 221 { Sfoff_t e; 222 if((e = SFSK(f,(Sfoff_t)0,SEEK_END,disc)) >= 0) 223 f->extent = e > f->here ? e : f->here; 224 (void)SFSK(f,f->here,SEEK_SET,disc); 225 goto setbuf; 226 } 227 } 228 229 /* get file descriptor status */ 230 if(sysfstatf((int)f->file,&st) < 0) 231 f->here = -1; 232 else 233 { 234#if _sys_stat && _stat_blksize /* preferred io block size */ 235 f->blksz = (size_t)st.st_blksize; 236#endif 237 bufsize = 64 * 1024; 238 if(S_ISDIR(st.st_mode) || (Sfoff_t)st.st_size < (Sfoff_t)SF_GRAIN) 239 okmmap = 0; 240 if(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) 241 f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,f->disc); 242 else f->here = -1; 243 244#if O_TEXT /* no memory mapping with O_TEXT because read()/write() alter data stream */ 245 if(okmmap && f->here >= 0 && 246 (sysfcntlf((int)f->file,F_GETFL,0) & O_TEXT) ) 247 okmmap = 0; 248#endif 249 } 250 251#if SETLINEMODE 252 if(init) 253 f->flags |= setlinemode(); 254#endif 255 256 if(f->here >= 0) 257 { f->extent = (Sfoff_t)st.st_size; 258 259 /* seekable std-devices are share-public by default */ 260 if(f == sfstdin || f == sfstdout || f == sfstderr) 261 f->flags |= SF_SHARE|SF_PUBLIC; 262 } 263 else 264 { 265 unseekable: 266 f->extent = -1; 267 f->here = 0; 268 269 if(init) 270 { if(S_ISCHR(st.st_mode) ) 271 { int oerrno = errno; 272 273 bufsize = SF_GRAIN; 274 275 /* set line mode for terminals */ 276 if(!(f->flags&(SF_LINE|SF_WCWIDTH)) && isatty(f->file)) 277 f->flags |= SF_LINE|SF_WCWIDTH; 278#if _sys_stat 279 else /* special case /dev/null */ 280 { reg int dev, ino; 281 static int null_checked, null_dev, null_ino; 282 dev = (int)st.st_dev; 283 ino = (int)st.st_ino; 284 if(!null_checked) 285 { if(sysstatf(DEVNULL,&st) < 0) 286 null_checked = -1; 287 else 288 { null_checked = 1; 289 null_dev = (int)st.st_dev; 290 null_ino = (int)st.st_ino; 291 } 292 } 293 if(null_checked >= 0 && dev == null_dev && ino == null_ino) 294 SFSETNULL(f); 295 } 296#endif 297 errno = oerrno; 298 } 299 300 /* initialize side buffer for r+w unseekable streams */ 301 if(!f->proc && (f->bits&SF_BOTH) ) 302 (void)_sfpopen(f,-1,-1,1); 303 } 304 } 305 306 /* set page size, this is also the desired default buffer size */ 307 if(_Sfpage <= 0) 308 { 309#if _lib_getpagesize 310 if((_Sfpage = (size_t)getpagesize()) <= 0) 311#endif 312 _Sfpage = SF_PAGE; 313 } 314 } 315 316#ifdef MAP_TYPE 317 if(okmmap && size && (f->mode&SF_READ) && f->extent >= 0 ) 318 { /* see if we can try memory mapping */ 319 if(!disc) 320 for(disc = f->disc; disc; disc = disc->disc) 321 if(disc->readf) 322 break; 323 if(!disc) 324 { f->bits |= SF_MMAP; 325 if(size == (size_t)SF_UNBOUND) 326 { if(bufsize > _Sfpage) 327 size = bufsize * SF_NMAP; 328 else size = _Sfpage * SF_NMAP; 329 if(size > 256*1024) 330 size = 256*1024; 331 } 332 } 333 } 334#endif 335 336 /* get buffer space */ 337setbuf: 338 if(size == (size_t)SF_UNBOUND) 339 { /* define a default size suitable for block transfer */ 340 if(init && osize > 0) 341 size = osize; 342 else if(f == sfstderr && (f->mode&SF_WRITE)) 343 size = 0; 344 else if(f->flags&SF_STRING ) 345 size = SF_GRAIN; 346 else if((f->flags&SF_READ) && !(f->bits&SF_BOTH) && 347 f->extent > 0 && f->extent < (Sfoff_t)_Sfpage ) 348 size = (((size_t)f->extent + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN; 349 else if((ssize_t)(size = _Sfpage) < bufsize) 350 size = bufsize; 351 352 buf = NIL(Void_t*); 353 } 354 355 sf_malloc = 0; 356 if(size > 0 && !buf && !(f->bits&SF_MMAP)) 357 { /* try to allocate a buffer */ 358 if(obuf && size == (size_t)osize && init) 359 { buf = (Void_t*)obuf; 360 obuf = NIL(uchar*); 361 sf_malloc = (oflags&SF_MALLOC); 362 } 363 if(!buf) 364 { /* do allocation */ 365 while(!buf && size > 0) 366 { if((buf = (Void_t*)malloc(size)) ) 367 break; 368 else size /= 2; 369 } 370 if(size > 0) 371 sf_malloc = SF_MALLOC; 372 } 373 } 374 375 if(size == 0 && !(f->flags&SF_STRING) && !(f->bits&SF_MMAP) && (f->mode&SF_READ)) 376 { /* use the internal buffer */ 377 size = sizeof(f->tiny); 378 buf = (Void_t*)f->tiny; 379 } 380 381 /* set up new buffer */ 382 f->size = size; 383 f->next = f->data = f->endr = f->endw = (uchar*)buf; 384 f->endb = (f->mode&SF_READ) ? f->data : f->data+size; 385 if(f->flags&SF_STRING) 386 { /* these fields are used to test actual size - see sfseek() */ 387 f->extent = (!sf_malloc && 388 ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ) ? size : 0; 389 f->here = 0; 390 391 /* read+string stream should have all data available */ 392 if((f->mode&SF_READ) && !sf_malloc) 393 f->endb = f->data+size; 394 } 395 396 f->flags = (f->flags & ~SF_MALLOC)|sf_malloc; 397 398 if(obuf && obuf != f->data && osize > 0 && (oflags&SF_MALLOC)) 399 { free((Void_t*)obuf); 400 obuf = NIL(uchar*); 401 } 402 403done: 404 _Sfi = f->val = obuf ? osize : 0; 405 406 /* blksz is used for aligning disk block boundary while reading data to 407 ** optimize data transfer from disk (eg, via direct I/O). blksz can be 408 ** at most f->size/2 so that data movement in buffer can be optimized. 409 ** blksz should also be a power-of-2 for optimal disk seeks. 410 */ 411 if(blksz <= 0 || (blksz & (blksz-1)) != 0 ) 412 blksz = SF_GRAIN; 413 while(blksz > f->size/2) 414 blksz /= 2; 415 f->blksz = blksz; 416 417 SFOPEN(f,local); 418 419 SFMTXRETURN(f, (Void_t*)obuf); 420} 421