/***************************************************************************
 *
 * Copyright (c) 2000, 2001 BalaBit IT Ltd, Budapest, Hungary
 *
 * 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.
 *
 * $Id: tokenize.c,v 1.1.2.2 2001/09/19 19:22:17 bazsi Exp $
 *
 ***************************************************************************/

#include <zorp/tokenize.h>

#include <stdarg.h>
#include <string.h>

void z_token_list_free(ZTokenList *list);

/*
   Reads one line from the given ZStream, and cuts it into tokens.
   A line ends with a '\n' character, and tokens are delimited by
   exactly one space. A token may contain space or '\n' escaped
   by a backslash.
*/

gboolean
z_read_token_line(ZStream *stream, ZTokenList **list, gint *count)
{
  GString *str = NULL;
  char *element;
  gchar a;
  int ret, protect = FALSE, bytes_read;

  *list = NULL;
  *count = 0;
  str = g_string_sized_new(32);

  while (1)
    {
      ret = z_stream_read(stream, &a, 1, &bytes_read);
      if (ret != G_IO_ERROR_NONE)
	{
	  z_token_list_free(*list);
	  return FALSE;
	}
      if (bytes_read != 1)
	a = '\n';
      if ((a == '\n' || a == ' ') && !protect)
	{
	  element = g_new0(char, str->len + 1);
	  strncpy(element, str->str, str->len);
	  *list = g_list_append(*list, element);
	  (*count)++;
	  g_string_assign(str, "");
	}
      else if (a == '\\' && !protect)
	{
	  protect = 1;
	}
      else
	{
	  g_string_append_c(str, a);
	  protect = 0;
	}
      if (a == '\n')
	break;
    }
  return TRUE;
}

/*
 *  Returns wether a character needs to be protected. (see definition of a
 *  token line before the read_token_line function)
 */
static int
z_token_is_protected(char a)
{
  if (a == '\\' || a == ' ' || a == '\n')
    return 1;
  return 0;
}

/*
 *  appends a atring to 'str', and protects with a backslash each character
 *  that is told to be protected by the is_protected function.
 */
void
z_token_append_protected(GString *gstr, char *str)
{
  int cnt;
  gboolean protect = FALSE;

  cnt = 0;
  while (str[cnt])
    {
      if (!protect && z_token_is_protected(str[cnt]))
	{
	  g_string_append_c(gstr, '\\');
	  protect = TRUE;
	}
      else
	{
	  g_string_append_c(gstr, str[cnt++]);
	  protect = FALSE;
	}
    }
}

/*
 *  writes a token line to 'channel'. 'fmt' describes the last paramters.
 *  a character 's' in 'fmt' means a simple string.
 *  a character 'a' in 'fmt' means an array of strings, where the last
 *     value in the array is NULL, and each string is a token
 *  a character 'g' in 'fmt' means a GList, where each element is a pointer
 *     to a string and each string is a token.
 */
gboolean
z_write_token_line(ZStream *stream, char *fmt, ...)
{
  GString *str;
  char *current;
  GList *list;
  char **array;
  int i, fcnt;
  int ret, bytes_written;
  va_list args;

  va_start(args, fmt);
  
  str = g_string_sized_new(0);

  fcnt = 0;
  while (fmt[fcnt])
    {
      switch (fmt[fcnt])
	{
	case 's':
	  current = va_arg(args, char *);
	  z_token_append_protected(str, current);
	  if (fmt[fcnt + 1])
	    g_string_append_c(str, ' ');
	  else
	    g_string_append_c(str, '\n');
	  break;
	case 'g':
	  list = va_arg(args, ZTokenList *);
	  while (list)
	    {
	      current = list->data;
	      z_token_append_protected(str, current);

	      if (list->next)
		g_string_append_c(str, ' ');
	      else if (fmt[fcnt + 1])
		g_string_append_c(str, ' ');
	      else
		g_string_append_c(str, '\n');
	      list = list->next;
	    }
	  break;
	case 'a':
	  i = 0;
	  array = va_arg(args, char **);
	  while (array[i])
	    {
	      current = array[i];
	      z_token_append_protected(str, current);

	      if (array[i + 1])
		g_string_append_c(str, ' ');
	      else if (fmt[fcnt + 1])
		g_string_append_c(str, ' ');
	      else
		g_string_append_c(str, '\n');
	      i++;
	    }
	  break;
	default:
	  break;
	}
      fcnt++;
    }
  va_end(args);

  ret = z_stream_write(stream, str->str, str->len, &bytes_written);
  g_string_free(str, TRUE);
  
  if (ret != G_IO_ERROR_NONE)
    return FALSE;
  return TRUE;
}

/*
 *  'frontend' for g_free accepting two parameters, but the secount is
 *  discarded. Needed when useing g_list_foreach.
 */
static void
z_token_free(gpointer str, gpointer a)
{
  g_free(str);
}

/*
 *  Frees a list created by the read_token_line function.
 *  (or any other list, that has elements which are pointers.)
 *  It frees all the elements as well.
 */
void
z_token_list_free(ZTokenList *list)
{
  g_list_foreach(list, z_token_free, NULL);
  g_list_free(list);
}
