/***************************************************************************
 *
 * Copyright (c) 1998-1999 Niels Möller
 * Copyright (c) 1999 BalaBit Computing
 * 
 * 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 of the License, 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; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Inspired by nsyslog, originally written by Darren Reed.
 *
 * $Id: read_line.c,v 1.5 2004/08/05 10:34:49 bazsi Exp $
 *
 ***************************************************************************/

#include <string.h>

#include "io.h"
#include "read_line.h"

#include "werror.h"
#include "xalloc.h"

#define CLASS_DEFINE
#include "read_line.h.x"
#undef CLASS_DEFINE

#include "read_line.c.x"

/* CLASS:
   (class
     (name read_line)
     (super read_handler)
     (vars
       (handler object line_handler)

       ; Line buffer       
       (pos simple UINT32)
       (buffer array UINT8 MAX_LINE)))
*/

/* CLASS:
   (class
     (name string_read)
     (super abstract_read)
     (vars
       (line object read_line)
       (index simple UINT32)))
*/

static int do_string_read(struct abstract_read **r,
			  UINT32 length, UINT8 *buffer)
{
  CAST(string_read, closure, *r);
  
  UINT32 left = closure->line->pos - closure->index;
  UINT32 to_read = LIBOL_MIN(length, left);

  memcpy(buffer, closure->line->buffer + closure->index, to_read);
  closure->index += to_read;

  return to_read;
}

static int do_read_line(struct read_handler **h,
			struct abstract_read *read)
{
  CAST(read_line, closure, *h);
  
  UINT8 *eol;
  UINT32 length;
  struct read_handler *next = NULL;
  int n;

  n = A_READ(read, MAX_LINE - closure->pos, closure->buffer + closure->pos);

  switch(n)
    {
    case 0:
      return ST_OK | ST_GOON;
    case A_FAIL:
      /* Fall throw */
    case A_EOF:
      return ST_FAIL | ST_CLOSE;
    }

  closure->pos += n;

  /* Loop over all received lines */
  
  eol = memchr(closure->buffer, '\0', closure->pos);
  if (eol == NULL)
	  eol = memchr(closure->buffer, '\n', closure->pos);
  while (eol)
    {
      /* eol points at the newline character. end points at the
       * character terminating the line, which may be a carriage
       * return preceeding the newline. */
      UINT8 *end = eol;
      int res;

      if ( (eol > closure->buffer)
	   && (eol[-1] == '\r'))
	end--;
      
      length = end - closure->buffer;
      
      res = PROCESS_LINE(closure->handler, &next, length, closure->buffer);
      {
	/* Remove line from buffer */
	/* Number of characters that have been processed */
	UINT32 done = eol - closure->buffer + 1;
	UINT32 left = closure->pos - done;
	
	memcpy(closure->buffer, closure->buffer + done, left);
	closure->pos = left;
      }

      if (ST_CLOSEDP(res))
	return res;
      
      if (next)
	{
	  /* Read no more lines. Instead, pass remaining data to next,
	   * and install the new read-handler. */
	  if (closure->pos)
	    {
	      int res;
	      
	      struct string_read read =
	      { { STACK_HEADER, do_string_read },
		NULL,
		0 };
	      read.line = closure;
	      while(next && (read.index < closure->pos))
		{
		  res = READ_HANDLER(next, &read.super);
		  if (ST_CLOSEDP(res))
		    return res;
		}
	    }
	  /* No data left */
	  KILL(closure);
	  *h = next;
	  return ST_OK | ST_GOON;
	}
      else
	if (!closure->handler)
	  {
	    /* Fail */
	    return ST_FAIL | ST_CLOSE;
	  }
	  
      eol = memchr(closure->buffer, '\0', closure->pos);
      if (eol == NULL)
        eol = memchr(closure->buffer, '\n', closure->pos);
    }     
  
  /* Partial line */
  if (closure->pos == MAX_LINE)
    {
      werror("Received too long a line\n");
      return ST_FAIL | ST_CLOSE;
    }
  return ST_OK | ST_GOON;
}

struct read_handler *make_read_line(struct line_handler *handler)
{
  NEW(read_line, closure);

  closure->super.handler = do_read_line;
  closure->pos = 0;

  closure->handler = handler;

  return &closure->super;
}

  


syntax highlighted by Code2HTML, v. 0.9.1