/* libsizzle/port.c - port functions
 
  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 <stdio.h>
#include <errno.h>
#include <string.h>

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

#include "alloc.h"
#include "node.h"
#include "eval.h"
#include "read.h"
#include "list.h"
#include "capi.h"
#include "fdport.h"
#include "port.h"

/* The default mode when creating files.  */
int zzz_default_file_mode;

/* The ports for standard input, standard output and standard error.  */
zzz_scm_t zzz_stdin_port;
zzz_scm_t zzz_stdout_port;
zzz_scm_t zzz_stderr_port;

/* The current values for the input, output and error ports are kept
   in thee variables.  */
zzz_scm_t zzz_current_in_port;
zzz_scm_t zzz_current_out_port;
zzz_scm_t zzz_current_err_port;

/* Tagged type for port objects.  */
zzz_object_t zzz_tagged_type_port;



/* Maximum number of port types allowed to register.  */
#define MAX_PORT_TYPE_COUNT 16

/* All registered port types are kept in this table.  */
static struct port_type zzz_port_types[MAX_PORT_TYPE_COUNT];

/* Number of currently registered port types.  */
static unsigned port_type_count = 0;



unsigned
zzz_define_port_type (int (*port_getc) (zzz_scm_t port),
		      void (*port_ungetc) (int c, zzz_scm_t port),
		      void (*port_putc) (int c, zzz_scm_t port),
		      void (*port_puts) (zzz_scm_t port, char * buf,
					 unsigned len),
		      void (*port_flush) (zzz_scm_t port),
		      void (*port_close) (zzz_scm_t port),
		      long (*port_seek) (zzz_scm_t port, long offset,
					 int whence),
		      long (*port_tell) (zzz_scm_t port),
		      int (*port_char_ready_p) (zzz_scm_t port),
		      int (*port_free) (zzz_scm_t port),
		      int (*port_mark) (zzz_scm_t port))
{
  assert (port_type_count < MAX_PORT_TYPE_COUNT);
  zzz_port_types[port_type_count].port_getc = port_getc;
  zzz_port_types[port_type_count].port_ungetc = port_ungetc;
  zzz_port_types[port_type_count].port_putc = port_putc;
  zzz_port_types[port_type_count].port_puts = port_puts;
  zzz_port_types[port_type_count].port_flush = port_flush;
  zzz_port_types[port_type_count].port_close = port_close;
  zzz_port_types[port_type_count].port_seek = port_seek;
  zzz_port_types[port_type_count].port_tell = port_tell;
  zzz_port_types[port_type_count].port_char_ready_p = port_char_ready_p;
  zzz_port_types[port_type_count].port_free = port_free;
  zzz_port_types[port_type_count].port_mark = port_mark;
  port_type_count++;
  return port_type_count - 1;
}

result_t
zzz_port_putc (int ch, zzz_scm_t port, zzz_scm_t * result)
{
  /* Perform some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open: "), -1), port, NULL));
    return RESULT_EXCEPTION;
    }
  if (!port_write_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open for writing: "), -1),
	  port, NULL));
    return RESULT_EXCEPTION;
    }
  /* Put the character to the port using its `putc()' port function.  */
  if (zzz_port_types[port_ptype (port)].port_putc)
    zzz_port_types[port_ptype (port)].port_putc (ch, port);

  /* Line-buffered ports are flushed when a newline is written to
     them.  */
  if (port_flags (port) & PORT_FLAGS_LINEBUF && ch == '\n')
    {
      if (zzz_port_types[port_ptype (port)].port_flush)
	zzz_port_types[port_ptype (port)].port_flush (port);
    }
  return RESULT_SUCCESS;
}

result_t
zzz_port_getc (zzz_scm_t port, int * ch, zzz_scm_t *result)
{
  int c;

  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open: "), -1), port, NULL));
    return RESULT_EXCEPTION;
    }
  if (!port_read_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open for reading: "), -1),
	  port, NULL));
    return RESULT_EXCEPTION;
    }
  /* Read one character by calling the port's `getc()' function, if it
     has one, otherwise simply return EOF.  */
  if (zzz_port_types[port_ptype (port)].port_getc)
    {
      /* Get the character, possibly restarting interrupted system
         calls.  */
      ZZZ_SYSCALL (c, zzz_port_types[port_ptype (port)].port_getc (port));
      /* Update column/line number.  */
      if (c == '\n')
	{
	  port_line_number (port)++;
	  port_saved_col (port) = port_col_number (port);
	  port_col_number (port) = 0;
	}
      else if (c != EOF)
	port_col_number (port)++;
    }
  else
    c = EOF;
  /* Return the character in the reference parameter `ch'.  */
  *ch = c;
  return RESULT_SUCCESS;
}

result_t
zzz_port_ungetc (int ch, zzz_scm_t port, zzz_scm_t * result)
{
  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open: "), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  if (!port_read_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open for reading: "), -1),
	  port, NULL));
      return RESULT_EXCEPTION;
    }

  /* Put the character back into the port by calling the port's
     `ungetc()' function.  */
  if (zzz_port_types[port_ptype (port)].port_ungetc)
    zzz_port_types[port_ptype (port)].port_ungetc (ch, port);

  /* Reset the column/line numbers.  This is a little bit tricky if a
     newline is pushed back, because we have to get the column number
     at the end of the previous line.  This is what the structure
     field `saved_col' is used for. 
     XXX: Column counting only works for pushing only *one* character
     back. */
  if (ch == '\n')
    {
      if (port_saved_col (port) >= 0)
	port_col_number (port) = port_saved_col (port);
      port_line_number (port)--;
      port_saved_col (port) = -1;
    }
  else if (ch != EOF)
    {
      port_col_number (port)--;
      if (port_col_number (port) < 0)
	{
	  if (port_saved_col (port) >= 0)
	    port_col_number (port) = port_saved_col (port);
	  port_line_number (port)--;
	  port_saved_col (port) = -1;
	}
    }
  return RESULT_SUCCESS;
}

result_t
zzz_port_close (zzz_scm_t port, zzz_scm_t * result)
{
  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open: "), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  /* Flush possibly buffered port data.  */
  if (zzz_port_types[port_ptype (port)].port_flush)
    zzz_port_types[port_ptype (port)].port_flush (port);

  /* Close the port using its `close' function.  */
  if (zzz_port_types[port_ptype (port)].port_close)
    zzz_port_types[port_ptype (port)].port_close (port);
  /* Mark the port as closed.  */
  port_open_p (port) = 0;
  return RESULT_SUCCESS;
}

result_t
zzz_port_seek (zzz_scm_t port, long ofs, int whence, long * res_ofs,
	       zzz_scm_t * result)
{
  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open"), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  /* Seek to the given position, calling the port's seek function, if
     any.  */
  if (zzz_port_types[port_ptype (port)].port_seek)
    *res_ofs = zzz_port_types[port_ptype (port)].port_seek (port, ofs, whence);
  else
    *res_ofs = 0;
  return RESULT_SUCCESS;
}

result_t
zzz_port_tell (zzz_scm_t port, long * res_ofs, zzz_scm_t * result)
{
  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open"), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  /* Tell the port offset from the port's tell function.  */
  if (zzz_port_types[port_ptype (port)].port_tell)
    *res_ofs = zzz_port_types[port_ptype (port)].port_tell (port);
  else
    *res_ofs = 0;
  return RESULT_SUCCESS;
}

result_t
zzz_port_flush (zzz_scm_t port, zzz_scm_t * result)
{
  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open"), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  /* Flush the port, using its `flush' function (if set).  */
  if (zzz_port_types[port_ptype (port)].port_flush)
    zzz_port_types[port_ptype (port)].port_flush (port);
  return RESULT_SUCCESS;
}

result_t
zzz_port_char_ready_p (zzz_scm_t port, int * ready, zzz_scm_t *result)
{
  /* Some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open: "), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  if (!port_read_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open for reading: "), -1),
	  port, NULL));
      return RESULT_EXCEPTION;
    }
  /* Check whether data is available by calling the port's
     `char_ready_p' function.  */
  if (zzz_port_types[port_ptype (port)].port_char_ready_p)
    *ready = zzz_port_types[port_ptype (port)].port_char_ready_p (port);
  else
    *ready = 0;
  return RESULT_SUCCESS;
}

result_t
zzz_port_puts (zzz_scm_t port, char * buf, int len, zzz_scm_t * result)
{
  char * orig_buf = buf;
  int orig_len = len;

  /* Perform some sanity checks.  */
  assert (port_p (port));
  if (!port_open_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open: "), -1), port, NULL));
      return RESULT_EXCEPTION;
    }
  if (!port_write_p (port))
    {
      *result = zzz_make_misc_error_exception
	(zzz_make_list
	 (zzz_make_string (_("port not open for writing: "), -1),
	  port, NULL));
      return RESULT_EXCEPTION;
    }

  if (zzz_port_types[port_ptype (port)].port_puts)
    {
      /* Put the string to the port using its `puts()' port function.  */
      zzz_port_types[port_ptype (port)].port_puts (port, buf, len);
    }
  else if (zzz_port_types[port_ptype (port)].port_putc)
    {
      /* If no `puts()' function is defined, but a `putc()', then
         write out the string byte by byte.  */
      while (len-- > 0)
	  zzz_port_types[port_ptype (port)].port_putc (*buf++, port);
    }
  /* Line-buffered ports are flushed when a newline is written to
     them.  */
  if ((port_flags (port) & PORT_FLAGS_LINEBUF) &&
      memchr (orig_buf, '\n', orig_len))
    {
      if (zzz_port_types[port_ptype (port)].port_flush)
	zzz_port_types[port_ptype (port)].port_flush (port);
    }
  return RESULT_SUCCESS;
}

result_t
zzz_port_print (zzz_scm_t port, zzz_scm_t cell, zzz_print_state_t state,
		zzz_scm_t * result)
{
  result_t err;
  static char buffer[256];
  int len;

  /* First, check and print immediate values: The empty list and
     fixnums.  */
  if (null_p (cell))
    return zzz_port_puts (port, "()", 2, result);
  else if (fixnum_p (cell))
    {
      len = sprintf (buffer, "%ld", fixnum_val (cell));
      return zzz_port_puts (port, buffer, len, result);
    }
  else if (immediate_p (cell))
    {
      /* Print the constant immediate values.  */
      switch ((zzz_object_t) cell)
	{
	case imm_false:
	  return zzz_port_puts (port, "#f", 2, result);
	case imm_true:
	  return zzz_port_puts (port, "#t", 2, result);
	case imm_unspecified:
	  return zzz_port_puts (port, "#<unspecified>", 14, result);
	case imm_undefined:
	  return zzz_port_puts (port, "#<unspecified>", 14, result);
	case imm_eof_object:
	  return zzz_port_puts (port, "#<eof-object>", 13, result);

	  /* Immediate code objects are printed specially, because I want
	     to distinguish them from the original form names.  That helps
	     debugging and controlling syntactic form memoization.  */
	case imm_if_then:
	case imm_if_then_else:
	  return zzz_port_puts (port, "@#if", 4, result);
	  break;
	case imm_begin:
	  return zzz_port_puts (port, "@#begin", 7, result);
	  break;
	case imm_let:
	  return zzz_port_puts (port, "@#let", 5, result);
	  break;
	case imm_letrec:
	  return zzz_port_puts (port, "@#letrec", 8, result);
	  break;
	case imm_letstar:
	  return zzz_port_puts (port, "@#let*", 6, result);
	  break;
	case imm_set_x:
	  return zzz_port_puts (port, "@#set!", 6, result);
	  break;
	case imm_define:
	  return zzz_port_puts (port, "@#define", 8, result);
	  break;
	case imm_and:
	  return zzz_port_puts (port, "@#and", 5, result);
	  break;
	case imm_or:
	  return zzz_port_puts (port, "@#or", 4, result);
	  break;
	case imm_cond:
	  return zzz_port_puts (port, "@#cond", 6, result);
	  break;
	case imm_case:
	  return zzz_port_puts (port, "@#case", 6, result);
	  break;
	case imm_do:
	  return zzz_port_puts (port, "@#do", 4, result);
	  break;
	case imm_named_let:
	  return zzz_port_puts (port, "@#named-let", 11, result);
	  break;
	case imm_quote:
	  return zzz_port_puts (port, "@#quote", 7, result);
	  break;
	case imm_quasiquote:
	  return zzz_port_puts (port, "@#quasiquote", 12, result);
	  break;
	case imm_lambda:
	  return zzz_port_puts (port, "@#lambda", 8, result);
	  break;
	case imm_definefunc:
	  return zzz_port_puts (port, "@#definefunc", 12, result);
	  break;
	default:
	  abort ();
	}
    }
  else if (cons_p (cell))
    {
      int circular = 0;
      int counter = 5;

      /* Lists like (quote o) etc. are printed in their short form
	 like this: 'o */
      if (zzz_eq (car (cell), zzz_quote_sym) && cons_p (cdr (cell)))
	{
	  if ((err = zzz_port_putc ('\'', port, result)) != RESULT_SUCCESS)
	    return err;
	  return zzz_port_print (port, car (cdr (cell)), state, result);
	}
      if (zzz_eq (car (cell), zzz_quasiquote_sym) && cons_p (cdr (cell)))
	{
	  if ((err = zzz_port_putc ('`', port, result)) != RESULT_SUCCESS)
	    return err;
	  return zzz_port_print (port, car (cdr (cell)), state, result);
	}
      if (zzz_eq (car (cell), zzz_unquote_sym) && cons_p (cdr (cell)))
	{
	  if ((err = zzz_port_putc (',', port, result)) != RESULT_SUCCESS)
	    return err;
	  return zzz_port_print (port, car (cdr (cell)), state, result);
	}
      if (zzz_eq (car (cell), zzz_unquote_splicing_sym) &&
	  cons_p (cdr (cell)))
	{
	  if ((err = zzz_port_putc (',', port, result)) != RESULT_SUCCESS)
	    return err;
	  if ((err = zzz_port_putc ('@', port, result)) != RESULT_SUCCESS)
	    return err;
	  return zzz_port_print (port, car (cdr (cell)), state, result);
	}

      /* Check whether the given list contains circles.  */
      circular = zzz_list_length (cell) == -2;

      if ((err = zzz_port_putc ('(', port, result)) != RESULT_SUCCESS)
	return err;

      /* As long, as the list is not exhausted, keep printing
	 elements.  */
      while (cons_p (cell))
	{
	  /* Print the list element recursively.  */
	  if ((err = zzz_port_print (port, car (cell), state, result)) !=
	      RESULT_SUCCESS)
	    return err;

	  /* Advance to next element in the list.  */
	  cell = cdr (cell);

	  /* Only the first few elements of a circular list are
	     printed, followed by an ellipsis.  */
	  if (circular && counter-- <= 0)
	    {
	      if ((err = zzz_port_puts (port, " ...", 4, result)) !=
		  RESULT_SUCCESS)
		return err;
	      break;
	    }
	  /* All but the last element have a trailing space
	     character.  */
	  if (!null_p (cell))
	    {
	      if ((err = zzz_port_putc (' ', port, result)) !=
		  RESULT_SUCCESS)
		return err;
	    }
	}
      /* Dotted pair?  Then print the cdr with a preceding dot.  */
      if (!circular && !null_p (cell))
	{
	  if ((err = zzz_port_putc ('.', port, result)) != RESULT_SUCCESS)
	    return err;
	  if ((err = zzz_port_putc (' ', port, result)) != RESULT_SUCCESS)
	    return err;
	  if ((err = zzz_port_print (port, cell, state, result)) !=
	      RESULT_SUCCESS)
	    return err;
	}
      if ((err = zzz_port_putc (')', port, result)) != RESULT_SUCCESS)
	return err;
    }
  else if (tagged_p (cell))
    {
      /* Sanity check: Is the type of the tagged cell valid?  */
      assert (tagged_type (cell) > 0 &&
	      tagged_type (cell) < zzz_type_descriptor_count);

      /* If the tagged type of the cell has a print function, use
	 it to print the value.  Otherwise, print some default
	 string, including the type and the address of the value.  */
      if (zzz_type_descriptors[tagged_type (cell)].print_func)
	zzz_type_descriptors[tagged_type (cell)].print_func
	  (port, to_tagged_ptr(cell), state);
      else
	{
	  len = sprintf (buffer, "#<tagged%lu:%p>", tagged_type (cell),
			 to_tagged_ptr (cell));
	  if ((err = zzz_port_puts (port, buffer, len, result)) !=
	      RESULT_SUCCESS)
	    return err;
	}
    }
  else
    abort ();			/* Sanity check. Should never reach
                                   this. */
  return RESULT_SUCCESS;
}

static port_desc_t
alloc_port (unsigned port_type, void * info)
{
  port_desc_t desc;

  desc = zzz_malloc (sizeof (struct port_descriptor));
  desc->port_type = port_type;
  desc->is_open = 1;
  desc->can_read = 0;
  desc->can_write = 0;
  desc->line_number = 0;
  desc->col_number = 0;
  desc->saved_col = -1;
  desc->filename = NULL;
  desc->tty_p = 0;
  desc->info = info;

  return desc;
}



static void *
port_constructor (zzz_tagged_t tagged, void * data0,
		    void * data1, void * data2)
{
  port_desc_t desc;

  desc = alloc_port ((unsigned) ((zzz_object_t) data0), data1);
  tagged->fill[1] = (zzz_object_t) desc;
  return tagged;
}

static void
port_print (zzz_scm_t port, zzz_tagged_t tagged, zzz_print_state_t state)
{
  zzz_scm_t result;
  port_desc_t desc = tagged_data0 (tagged);
  char buf[128];
  int len;

  len = sprintf (buf, "#<port:type=%u:open=%d:read=%d:write=%d>", 
		 desc->port_type, desc->is_open, desc->can_read,
		 desc->can_write);
  zzz_port_puts (port, buf, len, &result);
}

static void
port_free (zzz_tagged_t tagged)
{
  port_desc_t desc = tagged_data0 (tagged);
  zzz_scm_t tmp;

  /* If the port is open, we have to close it before it gets
     destroyed, so that any possibly buffered data may be flushed.  */
  if (desc->is_open)
    {
      zzz_port_close ((zzz_scm_t) ptr_to_tagged (tagged), &tmp);
    }
  /* If the port defines a free function, it gets called now so that
     it can free any resources it holds in the port object.  */
  if (zzz_port_types[desc->port_type].port_free)
    {
      zzz_port_types[desc->port_type].port_free 
	((zzz_scm_t) ptr_to_tagged (tagged));
    }
  /* Finally, we free the port object data itself.  */
  zzz_free (desc, sizeof (struct port_descriptor));
}

static void
port_mark (zzz_tagged_t tagged)
{
  port_desc_t desc = tagged_data0 (tagged);

  zzz_mark_cell (desc->filename);
  /* If the port defines a mark function, it gets called.  This makes
     it is possible to store Scheme values in a ports' info field. */
  if (zzz_port_types[desc->port_type].port_mark)
    {
      zzz_port_types[desc->port_type].port_mark 
	((zzz_scm_t) ptr_to_tagged (tagged));
    }
}

static int
port_equal (zzz_tagged_t tagged0, zzz_tagged_t tagged1)
{
  return 0;
}

zzz_scm_t
zzz_make_port (unsigned port_type, void * info)
{
  zzz_scm_t port;
  port = zzz_create_tagged_cell (zzz_tagged_type_port,
				 (void *) ((zzz_object_t) port_type), info,
				 NULL);
  return port;
}



result_t 
zzz_init_standard_ports (void)
{
  zzz_default_file_mode = 0644;

  /* Create and initialize standard ports. */
  zzz_stdin_port  = zzz_stdin_fdport;
  zzz_stdout_port = zzz_stdout_fdport;
  zzz_stderr_port = zzz_stderr_fdport;

  /* Give names to the standard input/output ports.  */
  port_filename (zzz_stdin_port) = zzz_make_ro_string ("stdin", -1);
  port_filename (zzz_stdout_port) = zzz_make_ro_string ("stdout", -1);
  port_filename (zzz_stderr_port) = zzz_make_ro_string ("stderr", -1);

  /* Make the standard input/output ports the current input/output
     ports.  */
  zzz_current_in_port = zzz_stdin_port;
  zzz_current_out_port = zzz_stdout_port;
  zzz_current_err_port = zzz_stderr_port;

  /* Define Scheme-accessible variables for the standard input/output
     ports.  */

  /*:doc
    standard-input-port
    Variable
    This variable holds the port bound to the standard input file.
    doc:*/
  zzz_define_variable ("standard-input-port", zzz_stdin_port);
  /*:doc
    standard-input-port
    Variable
    This variable holds the port bound to the standard output file.
    doc:*/
  zzz_define_variable ("standard-output-port", zzz_stdout_port);
  /*:doc
    standard-input-port
    Variable
    This variable holds the port bound to the standard error file.
    doc:*/
  zzz_define_variable ("standard-error-port", zzz_stderr_port);

  return RESULT_SUCCESS;
}

result_t
zzz_init_port (void)
{
  zzz_protect_global (&zzz_current_in_port);
  zzz_protect_global (&zzz_current_out_port);
  zzz_protect_global (&zzz_current_err_port);

  zzz_protect_global (&zzz_stdin_port);
  zzz_protect_global (&zzz_stdout_port);
  zzz_protect_global (&zzz_stderr_port);

  zzz_tagged_type_port = zzz_define_tagged_type (port_constructor,
						 port_print,
						 port_free, 
						 port_mark, 
						 port_equal);
  return RESULT_SUCCESS;
}
