/*
 * s p o r t . c			-- String ports management
 *
 * Copyright (C) 1993,1994,1995 Erick Gallesio - I3S-CNRS/ESSI <eg@unice.fr>
 * 
 *
 * Permission to use, copy, and/or distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that both the above copyright notice and this permission notice appear in
 * all copies and derived works.  Fees for distribution or use of this
 * software or derived works may only be charged with express written
 * permission of the copyright holder.  
 * This software is provided ``as is'' without express or implied warranty.
 *
 * This software is a derivative work of other copyrighted softwares; the
 * copyright notices of these softwares are placed in the file COPYRIGHTS
 *
 *
 *            Author: Erick Gallesio [eg@unice.fr]
 *    Creation date: 17-Feb-1993 12:27
 * Last file update:  4-Jul-1995 14:02
 *
 *
 * This is achieved in a (surely very) dependant way. A string port is implemented
 * via a pseudo FILE descriptor malloc'd when open-input-string is called. This 
 * descriptor is released when free-string-port is called.
 */

#include "stk.h"
#include "sport.h"

SCM STk_internal_open_input_string(char *str)
{
  struct str_iob *p;
  SCM z;

  NEWCELL(z, tc_isport);
  p = (struct str_iob *) must_malloc(sizeof (struct str_iob));
  
  p->signature = SPORT_SIGNATURE;
  p->flag      = READING;
  p->cnt       = p->bufsiz = strlen(str);
  p->base      = p->ptr    = must_malloc(p->cnt + 1);
  strcpy(p->base, str);

  z->storage_as.port.f    = (FILE *) p;
  z->storage_as.port.name = NULL;
  return z;
}

void STk_free_string_port(SCM port)
{
  struct str_iob * p;

  p = (struct str_iob *) port->storage_as.port.f;
  if (p) {
    /* p is  NULL if the string-port has been manually closed */
    free(p->base);
    free(p);
  }
}

SCM STk_internal_read_from_string(SCM port, int *eof, int case_significant)
{
  jmp_buf jb, *prev_jb = Top_jmp_buf;
  long prev_context     = Error_context;
  SCM result;
  int k;

  /* save normal error jmpbuf  so that read error don't lead to toplevel */
  /* If in a "catch", keep the ERR_IGNORED bit set */
  if ((k = setjmp(jb)) == 0) {
    Top_jmp_buf   = &jb;
    Error_context = (Error_context & ERR_IGNORED) | ERR_READ_FROM_STRING;
    result 	  = STk_readf(port->storage_as.port.f, case_significant);
    *eof   	  = Eof(port->storage_as.port.f);
  }
  Top_jmp_buf   = prev_jb;;
  Error_context = prev_context;
  
  if (k == 0) return result;
  
  /* if we are here, an error has occured during the string reading 
   * Two cases:
   *    - we are in a catch. Do a longjump to the catch to signal it a fail
   *    - otherwise error has already signaled, just return EVAL_ERROR
   */
  if (Error_context & ERR_IGNORED) longjmp(*Top_jmp_buf, k);
  return EVAL_ERROR;
}

PRIMITIVE STk_open_input_string(SCM s)
{
  if (NSTRINGP(s)) Err("open-input-string: not a string", s);
  return STk_internal_open_input_string(CHARS(s));
}


PRIMITIVE STk_open_output_string()
{
  struct str_iob *p;
  SCM z;

  NEWCELL(z, tc_osport);
  p = (struct str_iob *) must_malloc(sizeof (struct str_iob));

  p->signature = SPORT_SIGNATURE;
  p->flag      = WRITING;
  p->cnt       = 0;
  p->bufsiz    = START_ALLOC;
  p->base      = p->ptr = (char *) must_malloc(START_ALLOC);

  z->storage_as.port.f    = (FILE *) p;
  z->storage_as.port.name = NULL;
  return z;
}

PRIMITIVE STk_get_output_string(SCM port)
{
  if (NOSPORTP(port)) Err("get-output-string: Bad string-port", port);
  if (port->storage_as.port.f == NULL) 
    Err("get-output-string: string port is closed", port);

  return STk_makestrg(((struct str_iob *)port->storage_as.port.f)->cnt, 
		      ((struct str_iob *)port->storage_as.port.f)->base);
}

PRIMITIVE STk_input_string_portp(SCM port)
{
  return (ISPORTP(port)) ? Truth: Ntruth;
}

PRIMITIVE STk_output_string_portp(SCM port)
{
  return (OSPORTP(port)) ? Truth: Ntruth;
}

PRIMITIVE STk_with_input_from_string(SCM string, SCM thunk)
{
  jmp_buf env, *prev_env = Top_jmp_buf;
  SCM result, prev_iport = STk_curr_iport;
  int prev_context	 = Error_context;
  int k;

  if (NSTRINGP(string))     Err("with-input-from-string: bad string", string);
  if (!STk_is_thunk(thunk)) Err("with-input-from-string: bad thunk", thunk);

  if ((k = setjmp(env)) == 0) {
    Top_jmp_buf    = &env;
    STk_curr_iport = STk_internal_open_input_string(CHARS(string));
    result         = Apply(thunk, NIL);
  }
  /* restore normal error jmpbuf  and current input port*/
  STk_curr_iport = prev_iport;
  Top_jmp_buf    = prev_env;
  Error_context  = prev_context;

  if (k) /*propagate error */ longjmp(*Top_jmp_buf, k);
  return result;
}

PRIMITIVE STk_with_output_to_string(SCM thunk)
{
  jmp_buf env, *prev_env = Top_jmp_buf;
  SCM result, prev_oport = STk_curr_oport;
  int prev_context       = Error_context;
  int k;

  if (!STk_is_thunk(thunk)) Err("with-output-to-string: bad thunk", thunk);

  if ((k = setjmp(env)) == 0) {
    Top_jmp_buf    = &env;
    STk_curr_oport = STk_open_output_string();
    Apply(thunk, NIL);
    result         = STk_get_output_string(STk_curr_oport);
  }
  /* restore normal error jmpbuf  and current input port*/
  STk_curr_oport = prev_oport;
  Top_jmp_buf    = prev_env;
  Error_context  = prev_context;

  if (k) /*propagate error */ longjmp(*Top_jmp_buf, k);
  return result;
}



PRIMITIVE STk_read_from_string(SCM str)
{
  SCM result, port;
  int eof;	/* not used here */

  if (NSTRINGP(str)) Err("read-from-string: Bad string", str);

  /* Create a string port to read in the expression */
  port   = STk_internal_open_input_string(CHARS(str));
  result = STk_internal_read_from_string(port, &eof, FALSE);

  return result == EVAL_ERROR? UNDEFINED: result;
}


