summaryrefslogtreecommitdiff
path: root/src/lib/libast/sfio/sfmode.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/lib/libast/sfio/sfmode.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/lib/libast/sfio/sfmode.c')
-rw-r--r--src/lib/libast/sfio/sfmode.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/src/lib/libast/sfio/sfmode.c b/src/lib/libast/sfio/sfmode.c
new file mode 100644
index 0000000..97182fb
--- /dev/null
+++ b/src/lib/libast/sfio/sfmode.c
@@ -0,0 +1,596 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1985-2011 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* 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 Labs - Research) 2009-09-15 $\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.
+** 09/15/2008 Add sfwalk().
+*/
+
+/* 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->data &&
+ ((f->bits&SF_MMAP) ||
+ ((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;
+}