/* Generic stream implementation -- header file.
   Copyright (C) 1995 Amdahl Corporation.

This file is part of XEmacs.

XEmacs 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.

XEmacs 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 XEmacs; see the file COPYING.  If not, write to the Free
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Synched up with: Not in FSF. */

/* Written by Ben Wing. */

DECLARE_LRECORD (lstream, struct lstream);
#define XLSTREAM(x) XRECORD (x, lstream, struct lstream)
#define XSETLSTREAM(x, p) XSETRECORD (x, p, lstream)
#define LSTREAMP(x) RECORDP (x, lstream)
#define CHECK_LSTREAM(x, i) CHECK_RECORD (x, lstream)

extern Lisp_Object Fstreamp (Lisp_Object obj);

#ifndef EOF
#define EOF (-1)
#endif

typedef enum lstream_buffering
{
  /* No buffering. */
  LSTREAM_UNBUFFERED,
  /* Buffer until a '\n' character is reached. */
  LSTREAM_LINE_BUFFERED,
  /* Buffer in standard-size (i.e. 512-byte) blocks. */
  LSTREAM_BLOCK_BUFFERED,
  /* Buffer in blocks of a specified size. */
  LSTREAM_BLOCKN_BUFFERED,
  /* Buffer until the stream is closed (only applies to write-only
     streams).  Only one call to the stream writer will be made,
     and that is when the stream is closed. */
  LSTREAM_UNLIMITED
} Lstream_buffering;

/* Methods defining how this stream works.  Some may be undefined. */
   
typedef struct lstream_implementation
{
  char *name;
  /* Read some data from the stream's end and store it into DATA, which
     can hold SIZE bytes.  Return the number of bytes read.  0 means
     EOF reached.  This function can be NULL if the stream is output-
     only. */
  int (*reader) (Lstream *stream, unsigned char *data, int size);
  /* Send some data to the stream's end.  Data to be sent is in
     DATA and is SIZE bytes.  Return the number of bytes sent.  0
     means cannot store any more data (e.g. fixed block of memory),
     and the data will be discarded.  If fewer bytes are sent than
     provided, the remaining bytes will be sent the next time around.
     This function can be NULL if the stream is input-only. */
  int (*writer) (Lstream *stream, CONST unsigned char *data, int size);
  /* Rewind the stream.  If this is NULL, the stream is not seekable. */
  int (*rewinder) (Lstream *stream);
  /* Perform any additional operations necessary to close this
     stream down.  May be NULL.  This function is called when
     Lstream_close() is called or when the stream is garbage-
     collected.  When this function is called, all pending data
     in the stream will already have been written out. */
  int (*closer) (Lstream *stream);
  /* Mark this object for garbage collection.  Same semantics as
     a standard Lisp_Object marker.  This function can be NULL. */
  Lisp_Object (*marker) (Lisp_Object lstream, void (*markfun) (Lisp_Object));
  int size; /* Number of additional bytes to be allocated with this
	       stream.  Access this data using Lstream_data(). */
} Lstream_implementation; 

#define DEFINE_LSTREAM_IMPLEMENTATION(name,c_name,richman,poorman,beggarman,thief,and_somebody_else,size)						\
 CONST_IF_NOT_DEBUG Lstream_implementation c_name[1] =			\
   { { (name), (richman), (poorman), (beggarman), (thief),		\
       (and_somebody_else), (size) } }

struct lstream
{
  struct lcrecord_header header;
  CONST Lstream_implementation *imp; /* methods for this stream */
  Lstream_buffering buffering; /* type of buffering in use */
  int buffering_size; /* number of bytes buffered */

  unsigned char *in_buffer; /* holds characters read from stream end */
  int in_buffer_size; /* allocated size of buffer */
  int in_buffer_current; /* number of characters in buffer */
  int in_buffer_ind; /* pointer to next character to take from buffer */

  unsigned char *out_buffer; /* holds characters to write to stream end */
  int out_buffer_size; /* allocated size of buffer */
  int out_buffer_ind; /* pointer to next buffer spot to write a character */

  unsigned char *unget_buffer; /* holds characters pushed back onto input */
  int unget_buffer_size; /* allocated size of buffer */
  int unget_buffer_start; /* read characters from here */
  int unget_buffer_end; /* write characters to here */

  int byte_count;
  long is_open;  /* Align pointer for 64 bit machines (kny) */
  char data[1];
};

extern Lstream *Lstream_new (CONST Lstream_implementation *imp);
extern void Lstream_reopen (Lstream *lstr);
extern void Lstream_set_buffering (Lstream *lstr, Lstream_buffering buffering,
				   int buffering_size);
extern int Lstream_flush (Lstream *lstr);
extern int Lstream_flush_out (Lstream *lstr);
extern int Lstream_fputc (Lstream *stream, int c);
extern int Lstream_fgetc (Lstream *stream);
extern void Lstream_fungetc (Lstream *stream, int c);
extern int Lstream_read (Lstream *stream, void *data, int size);
extern int Lstream_write (Lstream *stream, CONST void *data, int size);
extern void Lstream_unread (Lstream *stream, void *data, int size);
extern int Lstream_rewind (Lstream *stream);
extern int Lstream_close (Lstream *stream);

/* Call the function equivalent if the out buffer is full.  Otherwise,
   add to the end of the out buffer and, if line buffering is called for
   and the character marks the end of a line, write out the buffer. */
   
#define Lstream_putc(stream, c) 					\
  ((stream)->out_buffer_ind >= (stream)->out_buffer_size ?		\
   Lstream_fputc (stream, c) :						\
   ((stream)->out_buffer[(stream)->out_buffer_ind++] =			\
    (unsigned char) (c),						\
    (stream)->byte_count++,						\
    (stream)->buffering == LSTREAM_LINE_BUFFERED &&			\
    (stream)->out_buffer[(stream)->out_buffer_ind - 1] == '\n' ?	\
    Lstream_flush_out (stream) : 0))

/* Retrieve from unget buffer if there are any characters there;
   else retrieve from in buffer if there's anything there;
   else call the function equivalent */
#define Lstream_getc(stream) 						\
  ((stream)->unget_buffer_start < (stream)->unget_buffer_end ?		\
   ((stream)->byte_count++,						\
    (stream)->unget_buffer[(stream)->unget_buffer_start++]) :		\
   (stream)->in_buffer_ind < (stream)->in_buffer_current ?		\
    ((stream)->byte_count++,						\
     (stream)->in_buffer[(stream)->in_buffer_ind++]) :			\
    Lstream_fgetc (stream))

/* Add to the end if it won't overflow buffer; otherwise call the
   function equivalent */
#define Lstream_ungetc(stream, c)					\
  ((stream)->unget_buffer_end >= (stream)->unget_buffer_size ?		\
   Lstream_fungetc (stream, c) :					\
   (void) ((stream)->byte_count--,					\
   ((stream)->unget_buffer[(stream)->unget_buffer_end++] =		\
    (unsigned char) (c))))

#define Lstream_data(stream) ((void *) ((stream)->data))
#define Lstream_byte_count(stream) ((stream)->byte_count)



/**************** stream implementations ****************/

#define LSTR_CLOSING 1
#define LSTR_ALLOW_QUIT 2
extern Lisp_Object make_stdio_stream (FILE *stream, int flags);
extern Lisp_Object make_filedesc_stream (int filedesc, int flags);
extern void filedesc_stream_set_pty_flushing (Lstream *stream,
					      int pty_max_bytes,
					      Bufbyte eof_char);
extern Lisp_Object make_lisp_string_stream (Lisp_Object string,
					    Bytecount offset,
					    Bytecount len);
extern Lisp_Object make_fixed_buffer_stream (unsigned char *buf,
					     int size);
extern unsigned char *fixed_buffer_stream_ptr (Lstream *stream);
extern Lisp_Object make_resizing_buffer_stream (void);
extern unsigned char *resizing_buffer_stream_ptr (Lstream *stream);
#define LSTR_SELECTIVE 1
#define LSTR_IGNORE_ACCESSIBLE 2
extern Lisp_Object make_lisp_buffer_stream (struct buffer *buf, Bufpos pos,
					    Bufpos length, int flags);
