/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #if defined(__STDPP__directive) && defined(__STDPP__hide) __STDPP__directive pragma pp:hide getpagesize #else #define getpagesize ______getpagesize #endif #include "sfhdr.h" #if defined(__STDPP__directive) && defined(__STDPP__hide) __STDPP__directive pragma pp:nohide getpagesize #else #undef getpagesize #endif #if _lib_getpagesize _BEGIN_EXTERNS_ extern int getpagesize _ARG_((void)); _END_EXTERNS_ #endif /* Set a (new) buffer for a stream. ** If size < 0, it is assigned a suitable value depending on the ** kind of stream. The actual buffer size allocated is dependent ** on how much memory is available. ** ** Written by Kiem-Phong Vo. */ #if !_sys_stat struct stat { int st_mode; int st_size; }; #undef sysfstatf #define sysfstatf(fd,st) (-1) #endif /*_sys_stat*/ static int setlinemode() { char* astsfio; char* endw; static int modes = -1; static const char sf_line[] = "SF_LINE"; static const char sf_wcwidth[] = "SF_WCWIDTH"; #define ISSEPAR(c) ((c) == ',' || (c) == ' ' || (c) == '\t') if (modes < 0) { modes = 0; if(astsfio = getenv("_AST_SFIO_OPTIONS")) { for(; *astsfio != 0; astsfio = endw) { while(ISSEPAR(*astsfio) ) *astsfio++; for(endw = astsfio; *endw && !ISSEPAR(*endw); ++endw) ; if((endw-astsfio) == (sizeof(sf_line)-1) && strncmp(astsfio,sf_line,endw-astsfio) == 0) { if ((modes |= SF_LINE) == (SF_LINE|SF_WCWIDTH)) break; } else if((endw-astsfio) == (sizeof(sf_wcwidth)-1) && strncmp(astsfio,sf_wcwidth,endw-astsfio) == 0) { if ((modes |= SF_WCWIDTH) == (SF_LINE|SF_WCWIDTH)) break; } } } } return modes; } #if __STD_C Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size) #else Void_t* sfsetbuf(f,buf,size) Sfio_t* f; /* stream to be buffered */ Void_t* buf; /* new buffer */ size_t size; /* buffer size, -1 for default size */ #endif { int sf_malloc, oflags, init, okmmap, local; ssize_t bufsize, blksz; Sfdisc_t* disc; sfstat_t st; uchar* obuf = NIL(uchar*); ssize_t osize = 0; SFMTXDECL(f); SFONCE(); SFMTXENTER(f,NIL(Void_t*)); GETLOCAL(f,local); if(size == 0 && buf) { /* special case to get buffer info */ _Sfi = f->val = (f->bits&SF_MMAP) ? (f->endb-f->data) : f->size; SFMTXRETURN(f, (Void_t*)f->data); } /* cleanup actions already done, don't allow write buffering any more */ if(_Sfexiting && !(f->flags&SF_STRING) && (f->mode&SF_WRITE)) { buf = NIL(Void_t*); size = 0; } if((init = f->mode&SF_INIT) ) { if(!f->pool && _sfsetpool(f) < 0) SFMTXRETURN(f, NIL(Void_t*)); } else if((f->mode&SF_RDWR) != SFMODE(f,local) && _sfmode(f,0,local) < 0) SFMTXRETURN(f, NIL(Void_t*)); if(init) f->mode = (f->mode&SF_RDWR)|SF_LOCK; else { int rv; /* make sure there is no hidden read data */ if(f->proc && (f->flags&SF_READ) && (f->mode&SF_WRITE) && _sfmode(f,SF_READ,local) < 0) SFMTXRETURN(f, NIL(Void_t*)); /* synchronize first */ SFLOCK(f,local); rv = SFSYNC(f); SFOPEN(f,local); if(rv < 0) SFMTXRETURN(f, NIL(Void_t*)); /* turn off the SF_SYNCED bit because buffer is changing */ f->mode &= ~SF_SYNCED; } SFLOCK(f,local); if((Sfio_t*)buf != f) blksz = -1; else /* setting alignment size only */ { blksz = (ssize_t)size; if(!init) /* stream already initialized */ { obuf = f->data; osize = f->size; goto done; } else /* initialize stream as if in the default case */ { buf = NIL(Void_t*); size = (size_t)SF_UNBOUND; } } bufsize = 0; oflags = f->flags; /* see if memory mapping is possible (see sfwrite for SF_BOTH) */ okmmap = (buf || (f->flags&SF_STRING) || (f->flags&SF_RDWR) == SF_RDWR) ? 0 : 1; /* save old buffer info */ #ifdef MAP_TYPE if(f->bits&SF_MMAP) { if(f->data) { SFMUNMAP(f,f->data,f->endb-f->data); f->data = NIL(uchar*); } } else #endif if(f->data == f->tiny) { f->data = NIL(uchar*); f->size = 0; } obuf = f->data; osize = f->size; f->flags &= ~SF_MALLOC; f->bits &= ~SF_MMAP; /* pure read/string streams must have a valid string */ if((f->flags&(SF_RDWR|SF_STRING)) == SF_RDSTR && (size == (size_t)SF_UNBOUND || !buf)) size = 0; /* set disc to the first discipline with a seekf */ for(disc = f->disc; disc; disc = disc->disc) if(disc->seekf) break; if((init || local) && !(f->flags&SF_STRING)) { /* ASSERT(f->file >= 0) */ st.st_mode = 0; /* if has discipline, set size by discipline if possible */ if(!_sys_stat || disc) { if((f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,disc)) < 0) goto unseekable; else { Sfoff_t e; if((e = SFSK(f,(Sfoff_t)0,SEEK_END,disc)) >= 0) f->extent = e > f->here ? e : f->here; (void)SFSK(f,f->here,SEEK_SET,disc); goto setbuf; } } /* get file descriptor status */ if(sysfstatf((int)f->file,&st) < 0) f->here = -1; else { #if _sys_stat && _stat_blksize /* preferred io block size */ f->blksz = (size_t)st.st_blksize; #endif bufsize = 64 * 1024; if(S_ISDIR(st.st_mode) || (Sfoff_t)st.st_size < (Sfoff_t)SF_GRAIN) okmmap = 0; if(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) f->here = SFSK(f,(Sfoff_t)0,SEEK_CUR,f->disc); else f->here = -1; #if O_TEXT /* no memory mapping with O_TEXT because read()/write() alter data stream */ if(okmmap && f->here >= 0 && (sysfcntlf((int)f->file,F_GETFL,0) & O_TEXT) ) okmmap = 0; #endif } if(init) f->flags |= setlinemode(); if(f->here >= 0) { f->extent = (Sfoff_t)st.st_size; /* seekable std-devices are share-public by default */ if(f == sfstdin || f == sfstdout || f == sfstderr) f->flags |= SF_SHARE|SF_PUBLIC; } else { unseekable: f->extent = -1; f->here = 0; if(init) { if(S_ISCHR(st.st_mode) ) { int oerrno = errno; bufsize = SF_GRAIN; /* set line mode for terminals */ if(!(f->flags&(SF_LINE|SF_WCWIDTH)) && isatty(f->file)) f->flags |= SF_LINE|SF_WCWIDTH; #if _sys_stat else /* special case /dev/null */ { reg int dev, ino; dev = (int)st.st_dev; ino = (int)st.st_ino; if(sysstatf(DEVNULL,&st) >= 0 && dev == (int)st.st_dev && ino == (int)st.st_ino) SFSETNULL(f); } #endif errno = oerrno; } /* initialize side buffer for r+w unseekable streams */ if(!f->proc && (f->bits&SF_BOTH) ) (void)_sfpopen(f,-1,-1,1); } } /* set page size, this is also the desired default buffer size */ if(_Sfpage <= 0) { #if _lib_getpagesize if((_Sfpage = (size_t)getpagesize()) <= 0) #endif _Sfpage = SF_PAGE; } } #ifdef MAP_TYPE if(okmmap && size && (f->mode&SF_READ) && f->extent >= 0 ) { /* see if we can try memory mapping */ if(!disc) for(disc = f->disc; disc; disc = disc->disc) if(disc->readf) break; if(!disc) { f->bits |= SF_MMAP; if(size == (size_t)SF_UNBOUND) { if(bufsize > _Sfpage) size = bufsize * SF_NMAP; else size = _Sfpage * SF_NMAP; if(size > 256*1024) size = 256*1024; } } } #endif /* get buffer space */ setbuf: if(size == (size_t)SF_UNBOUND) { /* define a default size suitable for block transfer */ if(init && osize > 0) size = osize; else if(f == sfstderr && (f->mode&SF_WRITE)) size = 0; else if(f->flags&SF_STRING ) size = SF_GRAIN; else if((f->flags&SF_READ) && !(f->bits&SF_BOTH) && f->extent > 0 && f->extent < (Sfoff_t)_Sfpage ) size = (((size_t)f->extent + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN; else if((ssize_t)(size = _Sfpage) < bufsize) size = bufsize; buf = NIL(Void_t*); } sf_malloc = 0; if(size > 0 && !buf && !(f->bits&SF_MMAP)) { /* try to allocate a buffer */ if(obuf && size == (size_t)osize && init) { buf = (Void_t*)obuf; obuf = NIL(uchar*); sf_malloc = (oflags&SF_MALLOC); } if(!buf) { /* do allocation */ while(!buf && size > 0) { if((buf = (Void_t*)malloc(size)) ) break; else size /= 2; } if(size > 0) sf_malloc = SF_MALLOC; } } if(size == 0 && !(f->flags&SF_STRING) && !(f->bits&SF_MMAP) && (f->mode&SF_READ)) { /* use the internal buffer */ size = sizeof(f->tiny); buf = (Void_t*)f->tiny; } /* set up new buffer */ f->size = size; f->next = f->data = f->endr = f->endw = (uchar*)buf; f->endb = (f->mode&SF_READ) ? f->data : f->data+size; if(f->flags&SF_STRING) { /* these fields are used to test actual size - see sfseek() */ f->extent = (!sf_malloc && ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ) ? size : 0; f->here = 0; /* read+string stream should have all data available */ if((f->mode&SF_READ) && !sf_malloc) f->endb = f->data+size; } f->flags = (f->flags & ~SF_MALLOC)|sf_malloc; if(obuf && obuf != f->data && osize > 0 && (oflags&SF_MALLOC)) { free((Void_t*)obuf); obuf = NIL(uchar*); } done: _Sfi = f->val = obuf ? osize : 0; /* blksz is used for aligning disk block boundary while reading data to ** optimize data transfer from disk (eg, via direct I/O). blksz can be ** at most f->size/2 so that data movement in buffer can be optimized. ** blksz should also be a power-of-2 for optimal disk seeks. */ if(blksz <= 0 || (blksz & (blksz-1)) != 0 ) blksz = SF_GRAIN; while(blksz > f->size/2) blksz /= 2; f->blksz = blksz; SFOPEN(f,local); SFMTXRETURN(f, (Void_t*)obuf); }