/* GNet - Networking library
 * Copyright (C) 2000  David Helder
 * Copyright (C) 2000  Andrew Lanoix
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the 
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 */


#include "gnet-private.h"
#include "gnet.h"


/**
 * gnet_io_channel_writen
 * @channel: channel to write to
 * @buffer: buffer to read from
 * @length: length of @buffer
 * @bytes_writtenp: pointer to integer in which to store the
 *   number of bytes written
 * 
 * Writes all @length bytes in @buffer to @channel.  If
 * @bytes_writtenp is set, the number of bytes written is stored in
 * the integer it points to.  @bytes_writtenp will be less than
 * @length if the connection closed or an error occured.
 *
 * This function is essentially a wrapper around g_io_channel_write().
 * The problem with g_io_channel_write() is that it may not write all
 * the bytes and will return a short count even when there was not an
 * error.  This is rare, but possible, and often difficult to detect
 * when it does happen.
 *
 * Returns: %G_IO_ERROR_NONE if successful; something else otherwise.
 * The number of bytes written is stored in the integer pointed to by
 * @bytes_writtenp.
 *
 **/
GIOError
gnet_io_channel_writen (GIOChannel* channel, 
			gpointer    buffer, 
			gsize       length,
			gsize*      bytes_writtenp)
{
  gsize nleft;
  gsize nwritten;
  gchar* ptr;
  GIOError error = G_IO_ERROR_NONE;

  g_return_val_if_fail (channel, G_IO_ERROR_INVAL);
  g_return_val_if_fail (bytes_writtenp, G_IO_ERROR_INVAL);

  ptr = buffer;
  nleft = length;

  while (nleft > 0)
    {
      if ((error = g_io_channel_write(channel, ptr, nleft, &nwritten))
	  != G_IO_ERROR_NONE)
	{
	  if (error == G_IO_ERROR_AGAIN)
	    nwritten = 0;
	  else
	    break;
	}

      nleft -= nwritten;
      ptr += nwritten;
    }

  *bytes_writtenp = (length - nleft);

  return error;
}


/**
 * gnet_io_channel_readn:
 * @channel: channel to read from
 * @buffer: buffer to write to
 * @length: length of the buffer
 * @bytes_readp: pointer to integer for the function to store the 
 * number of of bytes read
 *
 * Read exactly @length bytes from @channel into @buffer.  If
 * @bytes_readp is set, the number of bytes read is stored in the
 * integer it points to.  @bytes_readp will be less than @length if the
 * end-of-file was reached or an error occured.
 *
 * This function is essentially a wrapper around g_io_channel_read().
 * The problem with g_io_channel_read() is that it may not read all
 * the bytes requested and will return a short count even when there
 * was not an error (this is rare, but it can happen and is often
 * difficult to detect when it does).
 *
 * Returns: %G_IO_ERROR_NONE if everything is ok; something else
 * otherwise.  Also, returns the number of bytes read by modifying the
 * integer pointed to by @bytes_readp.
 *
 **/
GIOError
gnet_io_channel_readn (GIOChannel* channel, 
		       gpointer    buffer, 
		       gsize       length,
		       gsize*      bytes_readp)
{
  gsize nleft;
  gsize nread;
  gchar* ptr;
  GIOError error = G_IO_ERROR_NONE;

  g_return_val_if_fail (channel, G_IO_ERROR_INVAL);
  g_return_val_if_fail (bytes_readp, G_IO_ERROR_INVAL);

  ptr = buffer;
  nleft = length;

  while (nleft > 0)
    {
      if ((error = g_io_channel_read(channel, ptr, nleft, &nread))
	  !=  G_IO_ERROR_NONE)
	{
	  if (error == G_IO_ERROR_AGAIN)
	    nread = 0;
	  else
	    break;
	}
      else if (nread == 0)
	break;

      nleft -= nread;
      ptr += nread;
    }

  *bytes_readp = (length - nleft);

  return error;
}


/**
 * gnet_io_channel_readline
 * @channel: channel to read from
 * @buffer: buffer to write to
 * @length: length of the buffer
 * @bytes_readp: pointer to integer in which to store the 
 *   number of of bytes read
 *
 * Read a line from the channel.  The line will be NULL-terminated and
 * include the newline character.  If there is not enough room for the
 * line, the line is truncated to fit in the buffer.
 * 
 * Warnings:
 * 
 * 1. If the buffer is full and the last character is not a newline,
 * the line was truncated.  Do not assume the buffer ends with a
 * newline.
 *
 * 2. @bytes_readp is the number of bytes put in the buffer.  It
 * includes the terminating NULL character.
 * 
 * 3. NULL characters can appear in the line before the terminating
 * NULL (e.g., "Hello world\0\n").  If this matters,
 * check the string length of the buffer against the bytes read.
 *
 * I hope this isn't too confusing.  Usually the function works as you
 * expect it to if you have a big enough buffer.  If you have the
 * Stevens book, you should be familiar with the semantics.
 *
 * Returns: %G_IO_ERROR_NONE if everything is ok; something else
 * otherwise.  The number of bytes read is stored in the integer
 * pointed to by @bytes_readp (this number includes the newline).  If
 * an error is returned, the contents of @buffer and @bytes_readp are
 * undefined.
 * 
 **/
GIOError
gnet_io_channel_readline (GIOChannel* channel, 
			  gchar*      buffer, 
			  gsize       length,
			  gsize*      bytes_readp)
{
  gsize n, rc;
  gchar c, *ptr;
  GIOError error = G_IO_ERROR_NONE;

  g_return_val_if_fail (channel, G_IO_ERROR_INVAL);
  g_return_val_if_fail (bytes_readp, G_IO_ERROR_INVAL);

  ptr = buffer;

  for (n = 1; n < length; ++n)
    {
    try_again:
      error = gnet_io_channel_readn(channel, &c, 1, &rc);

      if (error == G_IO_ERROR_NONE && rc == 1)		/* read 1 char */
	{
	  *ptr++ = c;
	  if (c == '\n')
	    break;
	}
      else if (error == G_IO_ERROR_NONE && rc == 0)	/* read EOF */
	{
	  if (n == 1)	/* no data read */
	    {
	      *bytes_readp = 0;
	      return G_IO_ERROR_NONE;
	    }
	  else		/* some data read */
	    break;
	}
      else
	{
	  if (error == G_IO_ERROR_AGAIN)
	    goto try_again;

	  return error;
	}
    }

  *ptr = 0;
  *bytes_readp = n;

  return error;
}



/**
 * gnet_io_channel_readline_strdup
 * @channel: channel to read from
 * @bufferp: pointer to gchar* in which to store the new buffer
 * @bytes_readp: pointer to integer in which to store the 
 *   number of of bytes read
 *
 * Read a line from the channel.  The line will be null-terminated and
 * include the newline character.  Similarly to g_strdup_printf, a
 * buffer large enough to hold the string will be allocated.
 * 
 * Warnings:
 * 
 * 1. If the last character of the buffer is not a newline, the line
 * was truncated by EOF.  Do not assume the buffer ends with a
 * newline.
 *
 * 2. @bytes_readp is actually the number of bytes put in the buffer.
 * It includes the terminating NULL character.
 * 
 * 3. NULL characters can appear in the line before the terminating
 * null (e.g., "Hello world\0\n").  If this matters, check the string
 * length of the buffer against the bytes read.
 *
 * Returns: %G_IO_ERROR_NONE if everything is ok; something else
 * otherwise.  The number of bytes read is stored in the integer
 * pointed to by @bytes_readp (this number includes the newline).  The
 * data pointer is stored in the pointer pointed to by @bufferp.  This
 * data is caller-owned.  If an error is returned, the contents of
 * @bufferp and @bytes_readp are undefined.
 *
 **/
GIOError
gnet_io_channel_readline_strdup (GIOChannel* channel, 
				 gchar**     bufferp, 
				 gsize*      bytes_readp)
{
  gsize rc, n, length;
  gchar c, *ptr, *buf;
  GIOError error = G_IO_ERROR_NONE;

  g_return_val_if_fail (channel, G_IO_ERROR_INVAL);
  g_return_val_if_fail (bytes_readp, G_IO_ERROR_INVAL);

  length = 100;
  buf = (gchar *)g_malloc(length);
  ptr = buf;
  n = 1;

  while (1)
    {
    try_again:
      error = gnet_io_channel_readn(channel, &c, 1, &rc);

      if (error == G_IO_ERROR_NONE && rc == 1)          /* read 1 char */
        {
          *ptr++ = c;
          if (c == '\n')
            break;
        }
      else if (error == G_IO_ERROR_NONE && rc == 0)     /* read EOF */
        {
          if (n == 1)   /* no data read */
            {
              *bytes_readp = 0;
	      *bufferp = NULL;
	      g_free(buf);

              return G_IO_ERROR_NONE;
            }
          else          /* some data read */
            break;
        }
      else
        {
          if (error == G_IO_ERROR_AGAIN)
            goto try_again;

          g_free(buf);

          return error;
        }

      ++n;

      if (n >= length)
        {
          length *= 2;
          buf = g_realloc(buf, length);
          ptr = buf + n - 1;
        }
    }

  *ptr = 0;
  *bufferp = buf;
  *bytes_readp = n;

  return error;
}


syntax highlighted by Code2HTML, v. 0.9.1