/* fifo.c */


#include "fifo.h"
#include "nalloc.h"

#define NULL 0

static NALLOC *na = NULL;

FIFO *fi_open(fi, size)
    FIFO *fi;
    int size;
{
  FIFO *ifi = fi;
  if (!fi)
    fi = (FIFO *) malloc(sizeof(FIFO));
  fi->maxblk = size / FI_BSIZE;
  fi->hoff = fi->toff = 0;
  fi->blocks = 0;
  if(!na)
    na = na_open(sizeof(FI_BLK));
  fi->head = fi->tail = (FI_BLK *) na_get(na);
  fi->flags = (ifi) ? 0 : FI_FREE;
  return (fi);
}

void fi_close(fi)
    FIFO *fi;
{
  FI_BLK *t, *t1;
  fi->tail->next = NULL;
  for (t = fi->head; t; t = t1) {
    t1 = t->next;
    na_free(na, t);
  }
  if (fi->flags & FI_FREE)
    free(fi);
}

void fi_flush(fi)
    FIFO *fi;
{
  FI_BLK *t, *t1;
  fi->tail->next = NULL;
  for (t = fi->head; t; t = t1) {
    t1 = t->next;
    na_free(na, t);
  }
  fi->head = fi->tail = (FI_BLK *) na_get(na);
  fi->toff = fi->hoff = 0;
}
/* write returns 0 for total failure or 1 for success no inbetweens */
int fi_write(fi, buff, size)
    FIFO *fi;
    char *buff;
    int size;
{
  if (fi->blocks > fi->maxblk)
    return (0);
  while (size) {
    int asize;
    asize = (size > (FI_BSIZE - fi->toff)) ? FI_BSIZE - fi->toff : size;
    memcpy(&fi->tail->blk[fi->toff], buff, asize);
    fi->toff += asize;
    buff += asize;
    size -= asize;
    /* see if we need to add a new block */
    if (fi->toff == FI_BSIZE) {
      fi->tail = (fi->tail->next = (FI_BLK *) na_get(na));
      fi->blocks++;
      fi->toff = 0;
    }
  }
  /* used by fi_gets to know if fifo has been updated */
  fi->flags |= FI_CHANGE;
  return (1);
}
/* remove # of characters from fifo with no read */
void fi_munch(fi, size)
    FIFO *fi;
    int size;
{
  while (size && ((fi->head != fi->tail) || (fi->toff != fi->hoff))) {
    int asize = ((fi->head == fi->tail) ? fi->toff : FI_BSIZE) - fi->hoff;
    if (asize > size)
      asize = size;
    fi->hoff += asize;
    size -= asize;
    /* if head at end of block then move to next and free it */
    if (fi->hoff == FI_BSIZE) {
      FI_BLK *next = fi->head->next;
      fi->hoff = 0;
      na_free(na, fi->head);
      fi->blocks--;
      fi->head = next;
    }
  }
}
/* returns a pointer + # of available chars, munch # of chars used after */
int fi_rread(fi, buf)
    FIFO *fi;
    char **buf;
{
  *buf = &fi->head->blk[fi->hoff];
  return (((fi->head == fi->tail) ? fi->toff : FI_BSIZE) - fi->hoff);
}
/*
 * get a string from fifo terminated by \n, returns pointer on sucess or 0 on
 * failure, Note: \n is not included in string
 */
char *fi_gets(fi, buff, size)
    FIFO *fi;
    char *buff;
    int size;
{
  char *s;
  FI_BLK *blk;
  int off;
  int munch = 0;
  size--;
  /* don't try again until fifo updated */
  if ((fi->flags & FI_FAILED) && !(fi->flags & FI_CHANGE))
    return (0);
  fi->flags &= ~(FI_FAILED & FI_CHANGE);
  /* scan fifo for \n */
  s = buff;
  blk = fi->head;
  off = fi->hoff;
  while (size) {
    int asize = ((blk == fi->tail) ? fi->toff : FI_BSIZE) - off;
    if (asize > size)
      asize = size;
    size -= asize;
    while (asize) {
      char c;
      c = *s++ = blk->blk[off++];
      munch++;
      asize--;
      /* finally found it */
      if (c == '\n') {
	s[-1] = 0;
	fi_munch(fi, munch);
	return (buff);
      }
    }
    /* no luck */
    if (size && (blk == fi->tail)) {
      fi->flags |= FI_FAILED;
      return (0);
    }
    /* next block */
    blk = blk->next;
    off = 0;
  }
  *s++ = 0;
  fi_munch(fi, munch);
  return (buff);
}