/* $Id: linebuf.h,v 1.1.1.1 1995/11/30 07:47:38 dm Exp $ */

/*
 *
 * Copyright (c) 1995 David Mazieres (maziere1@das.harvard.edu)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
 *  This file is just a wrapper around fgets() to make it usable.
 */

/*  Stress-test.  Increase this later. */
#define LINEBUF_SIZE 16

typedef struct {
  char *buf;
  unsigned int size;
  int lineno;
  const char *filename;
  FILE *stream;
  void (*errfun) (const char *, ...);
} Linebuf;

static inline Linebuf *
Linebuf_alloc (const char *filename, void (*errfun) (const char *, ...))
{
  Linebuf *lb;

  if (! (lb = malloc (sizeof (*lb)))) {
    if (errfun)
      (*errfun) ("linebuf (%s): malloc failed\n", lb->filename);
    return (NULL);
  }

  if (filename) {
    lb->filename = filename;
    if (! (lb->stream = fopen (filename, "r"))) {
      free (lb);
      if (errfun)
	(*errfun) ("%s: %s\n", filename, sys_errlist[errno]);
      return (NULL);
    }
  }
  else {
    lb->filename = "(stdin)";
    lb->stream = stdin;
  }

  if (! (lb->buf = malloc (lb->size = LINEBUF_SIZE))) {
    if (errfun)
      (*errfun) ("linebuf (%s): malloc failed\n", lb->filename);
    free (lb);
    return (NULL);
  }
  lb->errfun = errfun;
  lb->lineno = 0;
  return (lb);
}

static inline void
Linebuf_free (Linebuf *lb)
{
  fclose (lb->stream);
  free (lb->buf);
  free (lb);
}

static inline void
Linebuf_restart (Linebuf *lb)
{
  clearerr (lb->stream);
  rewind (lb->stream);
  lb->lineno = 0;
}

static inline int
Linebuf_lineno (Linebuf *lb)
{
  return (lb->lineno);
}

static inline char *
getline (Linebuf *lb)
{
  int n = 0;

  lb->lineno++;
  for (;;) {
    /* Read a line */
    if (! fgets (&lb->buf[n], lb->size - n, lb->stream)) {
      if (ferror (lb->stream) && lb->errfun)
	(*lb->errfun) ("%s: %s\n", lb->filename, sys_errlist[errno]);
      return (NULL);
    }
    n = strlen (lb->buf);

    /* Return it or an error if it fits */
    if (n > 0 && lb->buf[n-1] == '\n') {
      lb->buf[n-1] = '\0';
      return (lb->buf);
    }
    if (n != lb->size - 1) {
      if (lb->errfun)
	(*lb->errfun) ("%s: skipping incomplete last line\n", lb->filename);
      return (NULL);
    }

    /* Double the buffer if we need more space */
    if (! (lb->buf = realloc (lb->buf, (lb->size *= 2)))) {
      if (lb->errfun)
	(*lb->errfun) ("linebuf (%s): realloc failed\n", lb->filename);
      return (NULL);
    }
  }
}
