
/*
   Eunuchs compatibility code.
*/

#include "io-defs.h"
#include <stdio.h>
#include "gem-meth.h"
#include "buf-meth.h"
#include <file.h>
#include <errno.h>
#include <osbind.h>

/*
   util fun for fopen and fdopen.  return modified flags, suitable for
   shoving into the flags halfword of a streamoid, and set append_p if 
   open in append mode.
*/

local int parse_open_mode(mode, flags, create_p, append_p)
char * mode;
int flags;
int * create_p;			/* if should create the file */
int * append_p;			/* if should append to existing file */
{
  while (*mode)
	switch(*mode++)
		{
		case 'r': flags |= STR_INPUT_P; 
			  break;
		case 'w': flags |= STR_OUTPUT_P; 
			  *create_p = 1;
			  break;
		case 'a': flags |= STR_OUTPUT_P; 
			  *append_p = 1;
			  break;
		case 'b': flags &= ~STR_TEXT_P;
			  break;
		case '+': flags |= (STR_INPUT_P | STR_OUTPUT_P);
			  break;
		}
  return(flags);
}

/*
   another util, used by fopen and freopen
*/
local int open_internal(name, input_p, output_p, create_p, append_p)
char * name;
int input_p;
int output_p;
int create_p;
int append_p;
{
  int handle, Fopen_mode;

  /* if output-only and not append, do Fcreate rather than Fopen */
  if (create_p && !input_p && !append_p)
	handle = Fcreate(name, 0);	/* create with no funny mode bits */
    else
	{
	if (!output_p)
		Fopen_mode = 0;		/* read-only */
	    else
	if (input_p)			/* read/write */
		Fopen_mode = 2;
	    else
		Fopen_mode = 1;		/* write-only */
	handle = Fopen(name, Fopen_mode);
	}

  if ((handle == ENOENT) && create_p)	/* no such file */
	{
	handle = Fcreate(name, 0);	/* create it */
	if ((handle >= 0) && input_p)	/* need input too? */
		{
		Fclose(handle);
		handle = Fopen(name, Fopen_mode);
		}
	}
  return(handle);
}

FILE * fopen(name, mode)
char * name;
char * mode;
{
  int handle;
  int flags, input_p, output_p;
  int create_p = 0;
  int append_p = 0;
  struct streamoid * s;

  flags = parse_open_mode(mode, STR_BUFFERED_P | STR_TEXT_P, 
			  &create_p, &append_p);

  input_p = flags & STR_INPUT_P;
  output_p = flags & STR_OUTPUT_P;
  handle = open_internal(name, input_p, output_p, create_p, append_p);
  if (handle < -3)			/* really an error? */
	{
	errno = handle;
	return(NULL);
	}
  
  s = __make_stream(handle, flags, gem_method_selector, append_p);
  return((FILE * )s);
}

/*
   somewhat like above, but use existing handle
*/
FILE * fdopen(handle, mode)
int handle;
char * mode;
{
  int create_p = 0;		/* not really used */
  int append_p = 0;
  int flags;
  struct streamoid * s;

  if (isatty(handle))
	flags = STR_TEXT_P | STR_INTERACTIVE_P;
    else
	flags = STR_TEXT_P | STR_BUFFERED_P;
  if (handle <= GEM_MAX_SYSTEM_HANDLE)		/* special one? */
	flags |= STR_NOCLOSE_P;			/* don't close this handle
						   at exit time */
  flags = parse_open_mode(mode, flags, &create_p, &append_p);

  s = __make_stream(handle, flags, gem_method_selector, append_p);
  return((FILE * )s);
}

/*
   el disgusto.
*/
FILE * freopen(name, mode, s)
char * name;
char * mode;
struct streamoid * s;
{
  int new_handle;
  int create_p, append_p, flags;

  if (!(s->flags & STR_OPEN_P))		/* trying to fool us? */
	return(NULL);
#ifdef new_experimental_version
  flags = parse_open_mode(mode, STR_BUFFERED_P | STR_TEXT_P,
				&create_p, &append_p);
#else
  flags = parse_open_mode(mode, s->flags & (STR_BUFFERED_P | STR_TEXT_P),
				&create_p, &append_p);
#endif
  new_handle = open_internal(name, flags & STR_INPUT_P, flags & STR_OUTPUT_P,
				create_p, append_p);
  if (new_handle < -3)
	return(NULL);			/* I think this is right; lose
					   and leave stream unaltered if
					   can't open */
  
  s->gemdos_handle = new_handle;
  s->file_position = 0;
  if (s->flags & STR_BUFFERED_P)
	{
#ifdef new_experimental_version
	if (!s->buffer)
		{
		s->buffer = (unsigned char * )
			malloc(s->buffer_size = 8192);
		}
#endif
	s->buffer_nbytes = 0;
	s->buffer_pos = 0;
	}
  s->flags = flags & ~(STR_AT_EOF_P | STR_NOCLOSE_P);
#ifdef new_experimental_version
  gem_method_selector(s, flags);
  s->flags |= STR_OPEN_P;
#endif
  return((FILE * )s);
}

fclose(s)
struct streamoid * s;
{
/*
   temporary kludge til get unbuffered methods straightened out
   w/r/t freopen
*/
  if ((s->flags & STR_BUFFERED_P) && (s->flags & STR_OUTPUT_P))
	__force_output(s);

  method_call_0(s, METHOD_CLOSE);
}

fflush(s)
struct streamoid * s;
{
  if ((s->flags & STR_BUFFERED_P) && (s->flags & STR_OUTPUT_P))
	__force_output(s);
}

int fgetc(s)
struct streamoid * s;
{
  int c = method_call_0(s, METHOD_TYI);
  if (c == EOF_VALUE)
	return(EOF);
    else
	return(c);
}

char * fgets(buf, nbytes, s)
char * buf;
int nbytes;
struct streamoid * s;
{
  int nbytes_read;
  
  *buf = '\0';
  nbytes_read = method_call_2(s, METHOD_READL, buf, nbytes);
  if (nbytes_read > 0)
	return(buf);
    else
	return(NULL);
}

int fputc(c, s)
int c;
struct streamoid * s;
{
  int res = method_call_1(s, METHOD_TYO, c);
  return(res < 0 ? res : c & 0xFF);
}

int fputs(str, s)
char * str;
struct streamoid * s;
{
  method_call_1(s, METHOD_WRITEL, str);
/* zzz what should this return? */
  return(0);
}

long ftell(s)
struct streamoid * s;
{
  return(s->file_position);
}

long fseek(s, pos, mode)
struct streamoid * s;
long pos;
int mode;
{
  int result;

  result = method_call_2(s, METHOD_SEEK, pos, mode);
  if (result >= 0)
	return(0);
    else
	return(errno);
}

/*
   this is really not quite right, as it returns T for anything that's
   NOT seekable.  that's as close as we can come to the broken eunuchs
   idea of what's not an interactive device.
*/
int isatty(handle)
int handle;
{
  int test_pos;
  int current_pos = Fseek(0, handle, L_INCR);

  if (current_pos != 0)				/* not 0? */
	return(0);				/* we know it's not a file */ 
  test_pos = Fseek(1, handle, L_INCR);		/* try to seek ahead one */
  if (test_pos == 0)				/* couldn't seek forward? */
	return(1);				/* then it 'is a tty' */
  if (test_pos == EBADARG)			/* out of range? */
	{
	Fseek(current_pos, handle, L_SET);	/* put it back */
	return(0);				/* not a tty */
	}
  errno = EBADF;				/* can't figure it out */
  return(0);
}

