/* _newstre.c (emx+gcc) -- Copyright (c) 1990-1996 by Eberhard Mattes */

#include <stdio.h>
#include <stdlib.h>
#include <sys/builtin.h>        /* For <sys/fmutex.h> */
#include <sys/fmutex.h>         /* For <sys/rmutex.h> */
#include <sys/rmutex.h>
#include <emx/io.h>

#define INC     32

FILE *_newstream (void)
{
  int i;
  struct streamvec *sv, *last = NULL;

  STREAMV_LOCK;
  for (sv = &_streamvec_head; sv != NULL; sv = sv->next)
    {
      last = sv;
      for (i = 0; i < sv->n; ++i)
        if (!(sv->vec[i]._flags & (_IOOPEN|_IONEW)))
          {
            sv->vec[i]._flags = _IONEW;

            /* Initialize new members to support dynamically linked
               old executables with, say, -lvideo.  See also below. */

            sv->vec[i]._more = 0;
            sv->vec[i]._mbstate = 0;

            STREAMV_UNLOCK;
            return &sv->vec[i];
          }
    }

  /* Enlarge our tables.  As the entries must not move, we add another
     segment. */

  sv = malloc (sizeof (*sv));
  if (sv != NULL)
    {
      struct _FILE *fv;

      fv = malloc (INC * sizeof (*fv));
      if (fv == NULL)
        free (sv);
      else
        {
          for (i = 0; i < INC; ++i)
            fv[i]._flags = 0;
          sv->next = NULL;
          sv->vec = fv;
          sv->n = INC;

          /* See above. */
          fv[0]._flags = _IONEW;
          fv[0]._more = 0;
          fv[0]._mbstate = 0;

          last->next = sv;      /* Done as last step, see fflush() */

          STREAMV_UNLOCK;
          return &fv[0];
        }
    }

  STREAMV_UNLOCK;
  return NULL;
}

#if defined (__MT__)

int _setmore (FILE *stream, int lock)
{
  int i;
  struct _file2 *p;
  struct streamvec2 *sv2, *last = NULL;

  stream->_mbstate = 0;
  stream->_more = NULL;
  if (lock)
    STREAMV_LOCK;
  p = NULL;
  for (sv2 = &_streamvec2_head; sv2 != NULL; sv2 = sv2->next)
    {
      last = sv2;
      for (i = 0; i < sv2->n; ++i)
        if (sv2->vec[i].owner == NULL)
          {
            p = &sv2->vec[i];
            goto super_break;
          }
    }
super_break:
  if (p == NULL)
    {
      sv2 = malloc (sizeof (*sv2));
      if (sv2 != NULL)
        {
          struct _file2 *fv2;

          fv2 = malloc (INC * sizeof (*fv2));
          if (fv2 == NULL)
            free (sv2);
          else
            {
              for (i = 0; i < INC; ++i)
                fv2[i].owner = NULL;
              sv2->next = NULL;
              sv2->vec = fv2;
              sv2->n = INC;
              last->next = sv2;
              p = &sv2->vec[0];
            }
        }
    }
  if (p != NULL)
    p->owner = stream;
  if (lock)
    STREAMV_UNLOCK;
  if (p == NULL || _rmutex_create (&p->rsem, 0) != 0)
    {
      /* Note that stream->_more is NULL. */

      _closestream (stream);
      if (p != NULL)
        p->owner = NULL;
      return -1;
    }
  stream->_more = p;
  return 0;
}

void _setdummymore (FILE *stream, struct _file2 *more)
{
  stream->_mbstate = 0;
  stream->_more = more;
  more->owner = stream;
  _rmutex_dummy (&more->rsem);
}

#else

int _setmore (FILE *stream, int lock)
{
  stream->_mbstate = 0;
  stream->_more = NULL;
  return 0;
}

void _setdummymore (FILE *stream, struct _file2 *more)
{
  stream->_mbstate = 0;
  stream->_more = NULL;
}

#endif


void _closestream (FILE *stream)
{
  if (stream->_more != NULL && stream->_more->owner == stream)
    {
#if defined (__MT__)
      _rmutex_close (&stream->_more->rsem);
#endif
      stream->_more->owner = NULL;
    }
  stream->_flags = 0;
}
