/* libsizzle/sport.c - string port handling
 
  Copyright (C) 1999, 2000 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
 
  This 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 software 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 package; see the file COPYING.  If not, write to
  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif 

#include <assert.h>
#include <string.h>
#include <stdio.h>

#if ENABLE_NLS
# include <libintl.h>
# define _(String) gettext(String)
#else
# define _(String) (String)
#endif

#include "alloc.h"
#include "node.h"
#include "capi.h"
#include "validate.h"
#include "port.h"
#include "sport.h"

/* The string port type identifier.  */
zzz_object_t zzz_sport_port_type;

zzz_scm_t
zzz_make_sport (int start_len)
{
  zzz_scm_t port;
  sport_info_t info;

  info = zzz_malloc (sizeof (struct sport_info));
  info->len = 0;
  info->size = start_len;
  info->buf = zzz_malloc (sizeof (char) * info->size);
  info->fpos = 0;
  port = zzz_make_port (zzz_sport_port_type, info);
  port_read_p (port) = 1;
  port_write_p (port) = 1;
  return port;
}

zzz_scm_t
zzz_make_sport_str (char * str)
{
  zzz_scm_t port;
  int len = strlen (str);

  port = zzz_make_sport (len);
  memmove (sport_val (port), str, len);
  sport_len (port) = len;
  port_read_p (port) = 1;
  port_write_p (port) = 1;
  return port;
}

zzz_scm_t
zzz_make_sport_str_n (char * str, unsigned len)
{
  zzz_scm_t port;

  port = zzz_make_sport (len);
  memmove (sport_val (port), str, len);
  sport_len (port) = len;
  port_read_p (port) = 1;
  port_write_p (port) = 1;
  return port;
}

zzz_scm_t
zzz_make_sport_str_n_no_copy (char * str, unsigned len)
{
  zzz_scm_t port;

  port = zzz_make_sport (0);
  sport_size (port) = len;
  sport_len (port) = len;
  sport_pos (port) = 0;
  sport_val (port) = str;
  port_read_p (port) = 1;
  return port;
}



static void
sport_putc (int ch, zzz_scm_t port)
{
  if (sport_len (port) >= sport_size (port))
    {
      sport_val (port) = zzz_realloc (sport_val (port),
				      sport_size (port) + 1024,
				      sport_size (port));
      sport_size (port) += 1024;
    }
  sport_val (port)[sport_len (port)++] = ch;
}

static int
sport_getc (zzz_scm_t port)
{
  if (sport_pos (port) >= sport_len (port))
    return EOF;
  else
    return sport_val (port)[sport_pos (port)++];
}

static void
sport_ungetc (int ch, zzz_scm_t port)
{
  if (sport_pos (port) > 0)
    sport_pos (port)--;
}

static long
sport_seek (zzz_scm_t port, long ofs, int whence)
{
  long new_ofs;

  switch (whence)
    {
    case SEEK_SET:
      new_ofs = ofs;
      break;
    case SEEK_CUR:
      new_ofs = sport_pos (port) + ofs;
      break;
    case SEEK_END:
      new_ofs = sport_len (port) + ofs;
      break;
    default:
      return sport_pos (port);
    }
  if (new_ofs < 0 || new_ofs > sport_len (port))
    return sport_pos (port);
  sport_pos (port) = new_ofs;
  return new_ofs;
}

static long
sport_tell (zzz_scm_t port)
{
  return sport_pos (port);
}

static int
sport_char_ready_p (zzz_scm_t port)
{
  return sport_pos (port) < sport_len (port);
}

static int
sport_free (zzz_scm_t port)
{
  sport_info_t info = port_info (port);
  if (info->size > 0)
    zzz_free (info->buf, info->size);
  return 0;
}

/*:doc
  open-input-string
  (open-input-string string) => string-port
  Takes @var{string} and returns an input port that delivers characters
  from the string.  The port can be closed by @code{close-input-port},
  though its storage will be reclaimed by the garbage collector if it
  becomes inaccessible.
  doc:*/
#define FUNC_NAME "open-input-string"
static result_t
open_input_string_func (zzz_scm_t env, zzz_scm_t string, zzz_scm_t * result)
{
  static char * s_func_name = FUNC_NAME;
  zzz_scm_t sport;

  CHECK_TYPE (1, string_p (string), zzz_string_type_name);
  sport = zzz_make_sport_str_n (string_val (string), string_len (string));
  port_read_p (sport) = 1;
  *result = sport;
  return RESULT_SUCCESS;
}
#undef FUNC_NAME

/*:doc
  open-output-string
  (open-output-string) => string-port
  Returns an output port that will accumulate characters for retrieval by
  @code{get-output-string}.  The port can be closed by
  @code{close-output-port}, though its storage will be reclaimed by the
  garbage collector if it becomes inaccessible.
  doc:*/
#define FUNC_NAME "open-output-string"
static result_t
open_output_string_func (zzz_scm_t env, zzz_scm_t * result)
{
  static char * s_func_name = FUNC_NAME;
  zzz_scm_t sport;

  sport = zzz_make_sport (32);
  port_write_p (sport) = 1;
  *result = sport;
  return RESULT_SUCCESS;
}
#undef FUNC_NAME

/*:doc
  get-output-string
  (get-output-string string-port) => string
  Given an output port created with @code{open-output-string}, returns a
  string consisting of the characters that have been output to the port so
  far.
  doc:*/
#define FUNC_NAME "get-output-string"
static result_t
get_output_string_func (zzz_scm_t env, zzz_scm_t sport, zzz_scm_t * result)
{
  static char * s_func_name = FUNC_NAME;

  CHECK_TYPE (1, sport_p (sport), zzz_sport_type_name);
  *result = zzz_make_string (sport_val (sport), sport_len (sport));
  return RESULT_SUCCESS;
}
#undef FUNC_NAME

result_t
zzz_init_sport (void)
{
  zzz_sport_port_type = zzz_define_port_type (sport_getc, sport_ungetc,
					      sport_putc, NULL, NULL, NULL,
					      sport_seek, sport_tell,
					      sport_char_ready_p,
					      sport_free, NULL);
  zzz_define_function ("open-input-string",
		       (zzz_prim_t) open_input_string_func, argv_1_0_0);
  zzz_define_function ("open-output-string",
		       (zzz_prim_t) open_output_string_func, argv_0_0_0);
  zzz_define_function ("get-output-string",
		       (zzz_prim_t) get_output_string_func, argv_1_0_0);
  return RESULT_SUCCESS;
}
