Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

sfmode.c

#include    "sfhdr.h"
static char*      Version = "\n@(#)sfio (AT&T Labs - kpv) 1999-08-05\0\n";

#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

/*    Switch the given stream to a desired mode
**
**    Written by Kiem-Phong Vo (06/27/90)
*/

#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;

      sfsync(NIL(Sfio_t*));

      for(p = &_Sfpool; p; p = p->next)
      {     for(n = 0; n < p->n_sf; ++n)
            {     f = p->sf[n];

                  if(SFFROZEN(f))
                        continue;

                  SFLOCK(f,0);

                  /* 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;

                  SFOPEN(f,0);
            }
      }

      /* set this so that no more buffering is allowed */
      _Sfexiting = 1001;
}

/* 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;

      if(p->mode&SF_LOCK) /* only one manipulator at a time */
            return -1;

      p->mode |= SF_LOCK;
      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:
      p->mode &= ~SF_LOCK;
      return rv;
}

/* to create auxiliary buffer */
#define SF_RSRV   128
static Sfrsrv_t*  _Sfrsrv;

#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; /* closing if size < 0 */
#endif
{
      reg Sfrsrv_t      *last, *frs;

      for(last = NIL(Sfrsrv_t*), frs = _Sfrsrv; frs; last = frs, frs = frs->next)
            if(frs->sf == f)
                  break;
      if(frs)
      {     /* unhook it from current position */
            if(last)
                  last->next = frs->next;
            else  _Sfrsrv = frs->next;
      }

      if(size < 0)      /* closing stream */
      {     if(frs)
                  free(frs);
            return NIL(Sfrsrv_t*);
      }

      /* inserting, make buffer if nothing yet */
      size = ((size + SF_RSRV-1)/SF_RSRV)*SF_RSRV;
      if(!frs || size > frs->size)
      {     if(!(last = (Sfrsrv_t*)malloc(size+sizeof(Sfrsrv_t))))
                  size = -1;
            else
            {     if(frs)
                  {     if(frs->slen > 0)
                              memcpy(last,frs,sizeof(Sfrsrv_t)+frs->slen);
                        free(frs);
                  }
                  frs = last;
                  frs->size = size;
                  frs->slen = 0;
            }
      }

      if(frs)
      {     if(size > 0)
                  frs->slen = 0;
            frs->sf = f;
            frs->next = _Sfrsrv;
            _Sfrsrv = frs;
      }

      return size >= 0 ? frs : 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

#define SF_POPEN_INIT   0     /* not checked yet                  */
#define SF_POPEN_SET    1     /* set to ignoresig                 */
#define SF_POPEN_USER   2     /* user override -- leave it alone  */

/* to keep track of process or unseekable read/write streams */
typedef struct _sfp_
{     struct _sfp_*     next; /* link list                  */
      int         pid;  /* process id                 */
      Sfio_t*           sf;   /* stream associated with     */
      uchar*            rdata;      /* read data being cached     */
      int         ndata;      /* size of cached data        */
      int         size; /* buffer size                */
      int         file; /* saved file descriptor      */
      int         type; /* 0 or SF_POPEN_SET          */
} Sfpopen_t;
static struct
{     Sfpopen_t*  head; /* linked list of processes   */
      int         state;      /* signal handler state       */
      int         set;  /* # of SF_POPEN_SET  streams */
} Sfpopen = {NIL(Sfpopen_t*), SF_POPEN_INIT, 0};

#if __STD_C
int _sfpopen(reg Sfio_t* f, int fd, int pid)
#else
int _sfpopen(f, fd, pid)
reg Sfio_t* f;
int         fd;
int         pid;
#endif
{
      reg Sfpopen_t* p = (Sfpopen_t*)Version;   /* shut up unuse warning */

      for(p = Sfpopen.head; p; p = p->next)
            if(p->sf == f)
                  return 0;

      if(!(p = (Sfpopen_t*)malloc(sizeof(Sfpopen_t))) )
            return -1;

      p->next = Sfpopen.head; Sfpopen.head = p;
      p->pid = pid;
      p->sf = f;
      p->size = p->ndata = 0;
      p->rdata = NIL(uchar*);
      p->file = fd;
      p->type = (pid >= 0 && !(f->bits&SF_STDIO) && (f->flags&SF_WRITE)) ?
                  SF_POPEN_SET : 0;

      f->bits = (f->bits & ~SF_STDIO) | SF_PROCESS;

#ifdef SIGPIPE    /* protect from broken pipe signal */
      if(p->type == SF_POPEN_SET)
      {     Sfsignal_f  handler;

            Sfpopen.set += 1; /* reference count */

            if(Sfpopen.state == SF_POPEN_INIT)
            {     if((handler = signal(SIGPIPE, ignoresig)) == SIG_DFL ||
                      handler == ignoresig)
                        Sfpopen.state = SF_POPEN_SET;
                  else
                  {     Sfpopen.state = SF_POPEN_USER;
                        signal(SIGPIPE, handler);
                  }
            }
      }
#endif

      return 0;
}

#if __STD_C
int _sfpclose(reg Sfio_t* f)
#else
int _sfpclose(f)
reg Sfio_t* f;    /* stream to close */
#endif
{
      reg Sfpopen_t     *last, *p;
      int         pid, status;

      if(!f)
      {     /* before exec-ing, close all pipe ends */
            for(p = Sfpopen.head; p; p = p->next)
            {     if(p->file >= 0)
                        CLOSE(p->file);
                  CLOSE(p->sf->file);
            }
            return 0;
      }

      /* find the associated process structure */
      for(last = NIL(Sfpopen_t*), p = Sfpopen.head; p ; last = p, p = p->next)
            if(f == p->sf)
                  break;
      if(!p)
            return -1;

      f->bits &= ~SF_PROCESS;
      (void)sfclose(f);

      /* delete from process table */
      if(last)
            last->next = p->next;
      else  Sfpopen.head = p->next;
      if(p->rdata)
            free(p->rdata);

      if(p->pid < 0)
      {     free(p);
            return 0;
      }

      /* close the associated streams */
      if(p->file >= 0)
            CLOSE(p->file);

      /* wait for process termination */
#if _PACKAGE_ast
      sigcritical(1);
#endif
      while ((pid = waitpid(p->pid,&status,0)) == -1 && errno == EINTR)
            ;
#if _PACKAGE_ast
      sigcritical(0);
#endif

#ifdef SIGPIPE
      if(p->type == SF_POPEN_SET)
      {     Sfsignal_f  handler;

            if((Sfpopen.set -= 1) == 0 && Sfpopen.state == SF_POPEN_SET)
            {     if((handler = signal(SIGPIPE,SIG_DFL)) != ignoresig)
                  {     Sfpopen.state = SF_POPEN_USER;
                        signal(SIGPIPE,handler);
                  }
                  else  Sfpopen.state = SF_POPEN_INIT;
            }
      }
#endif

      free(p);
      return (pid == -1 ? -1 : status);
}

#if __STD_C
static int _sfpmode(Sfio_t* f, int type)
#else
static int _sfpmode(f,type)
Sfio_t*     f;
int   type;
#endif
{     reg Sfpopen_t     *last, *p;

      /* find the associated process structure. We must be careful here
         with respect to a stacked stream since it takes on the identity
         of the stack base.
      */
      for(last = NIL(Sfpopen_t*), p = Sfpopen.head; p; last = p, p = p->next)
            if((f->push ? f->push : f) == p->sf)
                  break;
      if(!p)
            return -1;

      /* move-to-front heuristic */
      if(last)
      {     last->next = p->next;
            p->next = Sfpopen.head;
            Sfpopen.head = p;
      }

      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
void _sfswap(Sfio_t* f1, Sfio_t* f2, int stack)
#else
void _sfswap(f1, f2, stack)
Sfio_t*     f1;
Sfio_t*     f2;
int   stack;
#endif
{
      reg Sfrsrv_t      *r, *r1, *r2;
      reg Sfpopen_t     *p, *p1, *p2;

      /* switch sfreserve or sfgetr identifier */
      r1 = r2 = NIL(Sfrsrv_t*);
      for(r = _Sfrsrv; r; r = r->next)
      {     if(r->sf == f1)
                  r1 = r;
            if(r->sf == f2)
                  r2 = r;
      }
      if(r1)
            r1->sf = f2;
      if(r2)
            r2->sf = f1;

      /* if called from sfstack, we already did this */
      if(!stack)
      {     /* and for sfpopen buffer */
            p1 = p2 = NIL(Sfpopen_t*);
            for(p = Sfpopen.head; p; p = p->next)
            {     if(p->sf == f1)
                        p1 = p;
                  if(p->sf == f2)
                        p2 = p;
            }
            if(p1)
                  p1->sf = f2;
            if(p2)
                  p2->sf = f1;
      }
}

#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;

      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 */
            (*_Sfstdio)(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->bits&SF_PROCESS) && _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,1,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,0,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->bits&SF_PROCESS) && _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,0,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(_Sfnotify)
            {     if((wanted &= SF_RDWR) == 0)
                        wanted = f->flags&SF_RDWR;
                  (*_Sfnotify)(f,wanted,f->file);
            }
            rv = -1;
            break;
      }

done:
      SFOPEN(f,local);
      return rv;
}

Generated by  Doxygen 1.6.0   Back to index