/* emio.h */

/* Copyright 1997-2000 by Eberhard Mattes <em-gw@windhager.de>
   Donated to the public domain.  No warranty.

   1997-07-19 Initial version
   1999-03-17 struct emi_file (for copy_bidir_emi() in libemfw.h)
   2000-05-01 Add emi_fill()
   2000-05-28 Add emo_set_read(), emo_read(), and emo_shutdown() */

/* Fast stream I/O with some additional bells and whistles.

   Features:

   - Fast.  As we use different implementations for read-only and
     write-only streams, emi_getc() and emo_putc() are very simple and
     fast.

   - Simple to use.  Simpler to use than Unix file descriptors and C
     streams.  For instance, emo_write() tries to write all the data.

   - Safe.  The code contains lots of assertions (except for the
     functions implemented as macros) and does not use function
     pointers.  Source included :-)

   - Built-in support for time out.  A time out can be defined which
     will be applied to the underlying functions read() and write(),
     but not close().

   - Built-in support for artificially restricting the length of a
     read-only file.  This is quite useful for implementing
     "Content-Length".

   - If an operation fails due to an error reported by an underlying
     function such as write(), errno is set and all future I/O
     requests will fail, reporting the original errno.  That is, we
     can delay error checking in inner loops to the end of the loop
     without risking to loose errno.  As we cannot look into the
     future, write operations which go to the buffer cannot report an
     error immediately and we cannot tell how much data has been
     actually written.

     Drawbacks:

     - There are no read/write streams.

     - Opening a write-only file always truncates an existing file or
       creates a new file.

     - emo_printf() uses a fixed-length buffer.  Output might be
       truncated.  If HAVE_VSNPRINTF is not defined, you have to be
       very careful to avoid buffer overflow.  (emo_printf()
       terminates the process on buffer overflow.)

     Notes:

     - If NDEBUG is not defined, emi_getc() and emo_putc() will be
       slow. */

#ifndef EMIO_INCLUDED
#define EMIO_INCLUDED

#include <stdarg.h>		/* va_list */
#include "libemcommon.h"

/* ------------------------------ EMI_FILE ------------------------------ */

typedef struct emi_file
{
  int fd;			/* File descriptor */
  unsigned flags;		/* Flag bits (EMI_FLAG_xxx) */
  size_t bufsize;		/* Buffer size */
  int timeout;			/* Timeout or 0 */
  int error;			/* Saved errno from last error */
  long limit;			/* Artificial file length limit or -1 */
  const char *ptr;		/* Pointer to next character in buffer */
  const char *end;		/* Pointer to end of buffer */
  char *buf;			/* Buffer */
  unsigned long amount;		/* Total number of bytes read */
} EMI_FILE;

#define EMI_EOF		(-1)	/* Returned by emi_getc() on EOF or error */
#define EMI_FLAG_ERROR	0x0001	/* read() failed, no input possible */
#define EMI_FLAG_EOF	0x0002	/* EOF reached */

EMI_FILE *emi_open (const char *, size_t);
EMI_FILE *emi_fdopen (int, size_t);
int emi_close (EMI_FILE *);
int emi_read (EMI_FILE *, void *, int);
int emi_fill (EMI_FILE *, int);
int emi_limit (EMI_FILE *, long);
int emi_timeout (EMI_FILE *, int);
unsigned long emi_amount (EMI_FILE *, int);
int emi_peek (EMI_FILE *, void *, int);
int emi_getc1 (EMI_FILE *);
void emi_getc_assert (EMI_FILE *);

#define emi_error(f) ((f)->flags & EMI_FLAG_ERROR)
#define emi_getc(f) ( EMI_GETC_ASSERT (f), \
  ((f)->ptr != (f)->end ? (unsigned char)*(f)->ptr++ : emi_getc1 (f)))

#ifdef NDEBUG
#define EMI_GETC_ASSERT(f) ((void)0)
#else
#define EMI_GETC_ASSERT(f) emi_getc_assert(f)
#endif

/* ------------------------------ EMO_FILE ------------------------------ */

typedef struct
{
  int fd;			/* File descriptor */
  unsigned flags;		/* Flag bits (EMO_FLAG_xxx) */
  size_t bufsize;		/* Buffer size */
  int timeout;			/* Timeout or 0 */
  int error;			/* Saved errno from last error */
  char *ptr;			/* Pointer to next character in buffer */
  char *end;			/* Pointer to end of buffer */
  char *buf;			/* Buffer */
  unsigned long amount;		/* Total number of bytes written */
} EMO_FILE;

#define EMO_EOF		(-1)	/* Return by emo_putc() on error */

#define EMO_FLAG_ERROR	0x0001	/* An error has occurred */
#define EMO_FLAG_READ	0x0002	/* Read while writing to avoid dead lock */

EMO_FILE *emo_open (const char *, size_t);
EMO_FILE *emo_fdopen (int, size_t);
int emo_close (EMO_FILE *);
int emo_flush (EMO_FILE *);
int emo_write (EMO_FILE *, const void *, int);
int emo_puts (EMO_FILE *, const char *);
int emo_printf (EMO_FILE *, const char *, ...) ATTR_PRINTF (2, 3);
int emo_vprintf (EMO_FILE *, const char *, va_list);
int emo_timeout (EMO_FILE *, int);
unsigned long emo_amount (EMO_FILE *, int);
int emo_set_read (EMO_FILE *);
int emo_read (EMO_FILE *, int);
int emo_shutdown (EMO_FILE *);
int emo_putc1 (EMO_FILE *, int);
void emo_putc_assert (EMO_FILE *);

#define emo_error(f) ((f)->flags & EMO_FLAG_ERROR)
#define emo_putc(f,c) ( EMO_PUTC_ASSERT (f), \
  ((f)->ptr != (f)->end ? (unsigned char)(*(f)->ptr++ = (c)) \
   : emo_putc1 (f, c)))

#ifdef NDEBUG
#define EMO_PUTC_ASSERT(f) ((void)0)
#else
#define EMO_PUTC_ASSERT(f) emo_putc_assert(f)
#endif

#endif
