sfmode.c revision 7c2fbfb345896881c631598ee3852ce9ce33fb07
/***********************************************************************
* *
* 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 <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* Phong Vo <kpv@research.att.com> *
* *
***********************************************************************/
#include "sfhdr.h"
static char* Version = "\n@(#)$Id: sfio (AT&T Research) 2008-07-17 $\0\n";
/* Functions to set a given stream to some desired mode
**
** Written by Kiem-Phong Vo.
**
** Modifications:
** 06/27/1990 (first version)
** 01/06/1991
** 07/08/1991
** 06/18/1992
** 02/02/1993
** 05/25/1993
** 02/07/1994
** 05/21/1996
** 08/01/1997
** 08/01/1998 (extended formatting)
** 09/09/1999 (thread-safe)
** 02/01/2001 (adaptive buffering)
** 05/31/2002 (multi-byte handling in sfvprintf/vscanf)
** 09/06/2002 (SF_IOINTR flag)
** 11/15/2002 (%#c for sfvprintf)
** 05/31/2003 (sfsetbuf(f,f,align_size) to set alignment for data)
** (%I1d is fixed to handle "signed char" correctly)
** 01/01/2004 Porting issues to various platforms resolved.
** 06/01/2008 Allowing notify() at entering/exiting thread-safe routines.
*/
/* the below is for protecting the application from SIGPIPE */
#if _PACKAGE_ast
#include <sig.h>
#include <wait.h>
#define Sfsignal_f Sig_handler_t
#else
#include <signal.h>
typedef void(* Sfsignal_f)_ARG_((int));
#endif
static int _Sfsigp = 0; /* # of streams needing SIGPIPE protection */
/* done at exiting time */
#if __STD_C
static void _sfcleanup(void)
#else
static void _sfcleanup()
#endif
{
reg Sfpool_t* p;
reg Sfio_t* f;
reg int n;
reg int pool;
f = (Sfio_t*)Version; /* shut compiler warning */
/* set this so that no more buffering is allowed for write streams */
_Sfexiting = 1001;
sfsync(NIL(Sfio_t*));
for(p = &_Sfpool; p; p = p->next)
{ for(n = 0; n < p->n_sf; ++n)
{ if(!(f = p->sf[n]) || SFFROZEN(f) )
continue;
SFLOCK(f,0);
SFMTXLOCK(f);
/* let application know that we are leaving */
(void)SFRAISE(f, SF_ATEXIT, NIL(Void_t*));
if(f->flags&SF_STRING)
continue;
/* from now on, write streams are unbuffered */
pool = f->mode&SF_POOL;
f->mode &= ~SF_POOL;
if((f->flags&SF_WRITE) && !(f->mode&SF_WRITE))
(void)_sfmode(f,SF_WRITE,1);
if(((f->bits&SF_MMAP) && f->data) ||
((f->mode&SF_WRITE) && f->next == f->data) )
(void)SFSETBUF(f,NIL(Void_t*),0);
f->mode |= pool;
SFMTXUNLOCK(f);
SFOPEN(f,0);
}
}
}
/* put into discrete pool */
#if __STD_C
int _sfsetpool(Sfio_t* f)
#else
int _sfsetpool(f)
Sfio_t* f;
#endif
{
reg Sfpool_t* p;
reg Sfio_t** array;
reg int n, rv;
if(!_Sfcleanup)
{ _Sfcleanup = _sfcleanup;
(void)atexit(_sfcleanup);
}
if(!(p = f->pool) )
p = f->pool = &_Sfpool;
POOLMTXENTER(p);
rv = -1;
if(p->n_sf >= p->s_sf)
{ if(p->s_sf == 0) /* initialize pool array */
{ p->s_sf = sizeof(p->array)/sizeof(p->array[0]);
p->sf = p->array;
}
else /* allocate a larger array */
{ n = (p->sf != p->array ? p->s_sf : (p->s_sf/4 + 1)*4) + 4;
if(!(array = (Sfio_t**)malloc(n*sizeof(Sfio_t*))) )
goto done;
/* move old array to new one */
memcpy((Void_t*)array,(Void_t*)p->sf,p->n_sf*sizeof(Sfio_t*));
if(p->sf != p->array)
free((Void_t*)p->sf);
p->sf = array;
p->s_sf = n;
}
}
/* always add at end of array because if this was done during some sort
of walk thru all streams, we'll want the new stream to be seen.
*/
p->sf[p->n_sf++] = f;
rv = 0;
done:
POOLMTXRETURN(p, rv);
}
/* create an auxiliary buffer for sfgetr/sfreserve/sfputr */
#if __STD_C
Sfrsrv_t* _sfrsrv(reg Sfio_t* f, reg ssize_t size)
#else
Sfrsrv_t* _sfrsrv(f,size)
reg Sfio_t* f;
reg ssize_t size;
#endif
{
Sfrsrv_t *rsrv, *rs;
/* make buffer if nothing yet */
size = ((size + SF_GRAIN-1)/SF_GRAIN)*SF_GRAIN;
if(!(rsrv = f->rsrv) || size > rsrv->size)
{ if(!(rs = (Sfrsrv_t*)malloc(size+sizeof(Sfrsrv_t))))
size = -1;
else
{ if(rsrv)
{ if(rsrv->slen > 0)
memcpy(rs,rsrv,sizeof(Sfrsrv_t)+rsrv->slen);
free(rsrv);
}
f->rsrv = rsrv = rs;
rsrv->size = size;
rsrv->slen = 0;
}
}
if(rsrv && size > 0)
rsrv->slen = 0;
return size >= 0 ? rsrv : NIL(Sfrsrv_t*);
}
#ifdef SIGPIPE
#if __STD_C
static void ignoresig(int sig)
#else
static void ignoresig(sig)
int sig;
#endif
{
signal(sig, ignoresig);
}
#endif
#if __STD_C
int _sfpopen(reg Sfio_t* f, int fd, int pid, int stdio)
#else
int _sfpopen(f, fd, pid, stdio)
reg Sfio_t* f;
int fd;
int pid;
int stdio; /* stdio popen() does not reset SIGPIPE handler */
#endif
{
reg Sfproc_t* p;
if(f->proc)
return 0;
if(!(p = f->proc = (Sfproc_t*)malloc(sizeof(Sfproc_t))) )
return -1;
p->pid = pid;
p->size = p->ndata = 0;
p->rdata = NIL(uchar*);
p->file = fd;
p->sigp = (!stdio && pid >= 0 && (f->flags&SF_WRITE)) ? 1 : 0;
#ifdef SIGPIPE /* protect from broken pipe signal */
if(p->sigp)
{ Sfsignal_f handler;
(void)vtmtxlock(_Sfmutex);
if((handler = signal(SIGPIPE, ignoresig)) != SIG_DFL &&
handler != ignoresig)
signal(SIGPIPE, handler); /* honor user handler */
_Sfsigp += 1;
(void)vtmtxunlock(_Sfmutex);
}
#endif
return 0;
}
#if __STD_C
int _sfpclose(reg Sfio_t* f)
#else
int _sfpclose(f)
reg Sfio_t* f; /* stream to close */
#endif
{
Sfproc_t* p;
int pid, status;
if(!(p = f->proc))
return -1;
f->proc = NIL(Sfproc_t*);
if(p->rdata)
free(p->rdata);
if(p->pid < 0)
status = 0;
else
{ /* close the associated stream */
if(p->file >= 0)
CLOSE(p->file);
/* wait for process termination */
#if _PACKAGE_ast
sigcritical(SIG_REG_EXEC|SIG_REG_PROC);
#endif
while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR)
;
if(pid == -1)
status = -1;
#if _PACKAGE_ast
sigcritical(0);
#endif
#ifdef SIGPIPE
(void)vtmtxlock(_Sfmutex);
if(p->sigp && (_Sfsigp -= 1) <= 0)
{ Sfsignal_f handler;
if((handler = signal(SIGPIPE,SIG_DFL)) != SIG_DFL &&
handler != ignoresig)
signal(SIGPIPE,handler); /* honor user handler */
_Sfsigp = 0;
}
(void)vtmtxunlock(_Sfmutex);
#endif
}
free(p);
return status;
}
#if __STD_C
static int _sfpmode(Sfio_t* f, int type)
#else
static int _sfpmode(f,type)
Sfio_t* f;
int type;
#endif
{
Sfproc_t* p;
if(!(p = f->proc) )
return -1;
if(type == SF_WRITE)
{ /* save unread data */
p->ndata = f->endb-f->next;
if(p->ndata > p->size)
{ if(p->rdata)
free((char*)p->rdata);
if((p->rdata = (uchar*)malloc(p->ndata)) )
p->size = p->ndata;
else
{ p->size = 0;
return -1;
}
}
if(p->ndata > 0)
memcpy((Void_t*)p->rdata,(Void_t*)f->next,p->ndata);
f->endb = f->data;
}
else
{ /* restore read data */
if(p->ndata > f->size) /* may lose data!!! */
p->ndata = f->size;
if(p->ndata > 0)
{ memcpy((Void_t*)f->data,(Void_t*)p->rdata,p->ndata);
f->endb = f->data+p->ndata;
p->ndata = 0;
}
}
/* switch file descriptor */
if(p->pid >= 0)
{ type = f->file;
f->file = p->file;
p->file = type;
}
return 0;
}
#if __STD_C
int _sfmode(reg Sfio_t* f, reg int wanted, reg int local)
#else
int _sfmode(f, wanted, local)
reg Sfio_t* f; /* change r/w mode and sync file pointer for this stream */
reg int wanted; /* desired mode */
reg int local; /* a local call */
#endif
{
reg int n;
Sfoff_t addr;
reg int rv = 0;
SFONCE(); /* initialize mutexes */
if(wanted&SF_SYNCED) /* for (SF_SYNCED|SF_READ) stream, just junk data */
{ wanted &= ~SF_SYNCED;
if((f->mode&(SF_SYNCED|SF_READ)) == (SF_SYNCED|SF_READ) )
{ f->next = f->endb = f->endr = f->data;
f->mode &= ~SF_SYNCED;
}
}
if((!local && SFFROZEN(f)) || (!(f->flags&SF_STRING) && f->file < 0))
{ if(local || !f->disc || !f->disc->exceptf)
{ local = 1;
goto err_notify;
}
for(;;)
{ if((rv = (*f->disc->exceptf)(f,SF_LOCKED,0,f->disc)) < 0)
return rv;
if((!local && SFFROZEN(f)) ||
(!(f->flags&SF_STRING) && f->file < 0) )
{ if(rv == 0)
{ local = 1;
goto err_notify;
}
else continue;
}
else break;
}
}
if(f->mode&SF_GETR)
{ f->mode &= ~SF_GETR;
#ifdef MAP_TYPE
if((f->bits&SF_MMAP) && (f->tiny[0] += 1) >= (4*SF_NMAP) )
{ /* turn off mmap to avoid page faulting */
sfsetbuf(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
f->tiny[0] = 0;
}
else
#endif
if(f->getr)
{ f->next[-1] = f->getr;
f->getr = 0;
}
}
if(f->mode&SF_STDIO) /* synchronizing with stdio pointers */
(*_Sfstdsync)(f);
if(f->disc == _Sfudisc && wanted == SF_WRITE &&
sfclose((*_Sfstack)(f,NIL(Sfio_t*))) < 0 )
{ local = 1;
goto err_notify;
}
if(f->mode&SF_POOL)
{ /* move to head of pool */
if(f == f->pool->sf[0] || (*_Sfpmove)(f,0) < 0 )
{ local = 1;
goto err_notify;
}
f->mode &= ~SF_POOL;
}
SFLOCK(f,local);
/* buffer initialization */
wanted &= SF_RDWR;
if(f->mode&SF_INIT)
{
if(!f->pool && _sfsetpool(f) < 0)
{ rv = -1;
goto done;
}
if(wanted == 0)
goto done;
if(wanted != (int)(f->mode&SF_RDWR) && !(f->flags&wanted) )
goto err_notify;
if((f->flags&SF_STRING) && f->size >= 0 && f->data)
{ f->mode &= ~SF_INIT;
f->extent = ((f->flags&SF_READ) || (f->bits&SF_BOTH)) ?
f->size : 0;
f->here = 0;
f->endb = f->data + f->size;
f->next = f->endr = f->endw = f->data;
if(f->mode&SF_READ)
f->endr = f->endb;
else f->endw = f->endb;
}
else
{ n = f->flags;
(void)SFSETBUF(f,f->data,f->size);
f->flags |= (n&SF_MALLOC);
}
}
if(wanted == (int)SFMODE(f,1))
goto done;
switch(SFMODE(f,1))
{
case SF_WRITE: /* switching to SF_READ */
if(wanted == 0 || wanted == SF_WRITE)
break;
if(!(f->flags&SF_READ) )
goto err_notify;
else if(f->flags&SF_STRING)
{ SFSTRSIZE(f);
f->endb = f->data+f->extent;
f->mode = SF_READ;
break;
}
/* reset buffer */
if(f->next > f->data && SFFLSBUF(f,-1) < 0)
goto err_notify;
if(f->size == 0)
{ /* unbuffered */
f->data = f->tiny;
f->size = sizeof(f->tiny);
}
f->next = f->endr = f->endw = f->endb = f->data;
f->mode = SF_READ|SF_LOCK;
/* restore saved read data for coprocess */
if(f->proc && _sfpmode(f,wanted) < 0)
goto err_notify;
break;
case (SF_READ|SF_SYNCED): /* a previously sync-ed read stream */
if(wanted != SF_WRITE)
{ /* just reset the pointers */
f->mode = SF_READ|SF_LOCK;
/* see if must go with new physical location */
if((f->flags&(SF_SHARE|SF_PUBLIC)) == (SF_SHARE|SF_PUBLIC) &&
(addr = SFSK(f,0,SEEK_CUR,f->disc)) != f->here)
{
#ifdef MAP_TYPE
if((f->bits&SF_MMAP) && f->data)
{ SFMUNMAP(f,f->data,f->endb-f->data);
f->data = NIL(uchar*);
}
#endif
f->endb = f->endr = f->endw = f->next = f->data;
f->here = addr;
}
else
{ addr = f->here + (f->endb - f->next);
if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
goto err_notify;
f->here = addr;
}
break;
}
/* fall thru */
case SF_READ: /* switching to SF_WRITE */
if(wanted != SF_WRITE)
break;
else if(!(f->flags&SF_WRITE))
goto err_notify;
else if(f->flags&SF_STRING)
{ f->endb = f->data+f->size;
f->mode = SF_WRITE|SF_LOCK;
break;
}
/* save unread data before switching mode */
if(f->proc && _sfpmode(f,wanted) < 0)
goto err_notify;
/* reset buffer and seek pointer */
if(!(f->mode&SF_SYNCED) )
{ n = f->endb - f->next;
if(f->extent >= 0 && (n > 0 || (f->data && (f->bits&SF_MMAP))) )
{ /* reset file pointer */
addr = f->here - n;
if(SFSK(f,addr,SEEK_SET,f->disc) < 0)
goto err_notify;
f->here = addr;
}
}
f->mode = SF_WRITE|SF_LOCK;
#ifdef MAP_TYPE
if(f->bits&SF_MMAP)
{ if(f->data)
SFMUNMAP(f,f->data,f->endb-f->data);
(void)SFSETBUF(f,(Void_t*)f->tiny,(size_t)SF_UNBOUND);
}
#endif
if(f->data == f->tiny)
{ f->endb = f->data = f->next = NIL(uchar*);
f->size = 0;
}
else f->endb = (f->next = f->data) + f->size;
break;
default: /* unknown case */
err_notify:
if((wanted &= SF_RDWR) == 0 && (wanted = f->flags&SF_RDWR) == SF_RDWR)
wanted = SF_READ;
/* set errno for operations that access wrong stream type */
if(wanted != (f->mode&SF_RDWR) && f->file >= 0)
errno = EBADF;
if(_Sfnotify) /* notify application of the error */
(*_Sfnotify)(f, wanted, (void*)((long)f->file));
rv = -1;
break;
}
done:
SFOPEN(f,local);
return rv;
}