/*
 * Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *    		 Crown Copyright (c) 1997
 *
 *    This TenDRA(r) Computer Program is subject to Copyright
 *    owned by the United Kingdom Secretary of State for Defence
 *    acting through the Defence Evaluation and Research Agency
 *    (DERA).  It is made available to Recipients with a
 *    royalty-free licence for its use, reproduction, transfer
 *    to other parties and amendment for any purpose not excluding
 *    product development provided that any such use et cetera
 *    shall be deemed to be acceptance of the following conditions:-
 *
 *        (1) Its Recipients shall ensure that this Notice is
 *        reproduced upon any copies or amended versions of it;
 *
 *        (2) Any amended version of it shall be clearly marked to
 *        show both the nature of and the organisation responsible
 *        for the relevant amendment or amendments;
 *
 *        (3) Its onward transfer from a recipient to another
 *        party shall be deemed to be that party's acceptance of
 *        these conditions;
 *
 *        (4) DERA gives no warranty or assurance as to its
 *        quality or suitability for any purpose and DERA accepts
 *        no liability whatsoever in relation to any use to which
 *        it may be put.
 *
 * $TenDRA: tendra/src/tools/tld/istream.h,v 1.11 2005/10/18 15:31:06 stefanf Exp $
 */


/**** istream.h --- Input stream handling.
 *
 ** Author: Steve Folkes <smf@hermes.mod.uk>
 *
 **** Commentary:
 *
 ***=== INTRODUCTION =========================================================
 *
 * This file specifies the interface to the input stream facility.
 *
 ***=== TYPES ================================================================
 *
 ** Type:	IStreamT
 ** Type:	IStreamP
 ** Repr:	<private>
 *
 * This is the input stream type.
 *
 ** Type:	IStreamStatusT
 ** Type:	IStreamStatusP
 ** Repr:	enum {ISTREAM_STAT_READ_CHAR, ISTREAM_STAT_NO_CHAR,
 *		      ISTREAM_STAT_SYNTAX_ERROR}
 *
 * This is the return type of the ``istream_read_escaped_char'' function.  The
 * constants represent the results: a character was read, no character was
 * read, and a syntax error occurred respectively.
 *
 ***=== CONSTANTS ============================================================
 *
 ** Constant:	istream_input
 *
 * This value is a pointer to an input stream object that will read from the
 * standard input.  The ``istream_setup'' function must be called before this
 * constant is used.
 *
 ***=== FUNCTIONS ============================================================
 *
 ** Function:	void		istream_setup(void)
 ** Exceptions:
 *
 * This function initialises the input stream facility.  It should be called
 * before any other istream manipulation function.
 *
 ** Function:	void		istream_init(IStreamP istream)
 ** Exceptions:
 *
 * This function initialises the specified istream not to read from any file.
 *
 ** Function:	BoolT		istream_open(IStreamP istream, char *name)
 ** Exceptions: XX_dalloc_no_memory, XX_istream_read_error
 *
 * This function initialises the specified istream to read from the file with
 * the specified name.  The name should not be modified or deallocated until
 * the istream has been closed.  If the file cannot be opened, the function
 * returns false. If the file is opened successfully, the function returns
 * true.
 *
 ** Function:	void		istream_assign(IStreamP to, IStreamP from)
 ** Exceptions:
 *
 * This function assigns the from istream to the to istream.  The from istream
 * should not be used again.
 *
 ** Function:	BoolT		istream_is_open(IStreamP istream)
 *
 * This function returns true if the specified istream is reading from a file,
 * and false otherwise.
 *
 ** Function:	BoolT		istream_read_char(IStreamP istream, char *c_ref)
 ** Exceptions:	XX_dalloc_no_memory, XX_istream_read_error
 *
 * This function reads the next character from the specified istream (and
 * advances the character pointer).  If a character is read then the character
 * is assigned to the reference argument, and the function returns true.  If
 * the end of file is reached, the function returns false.  If the character
 * read is a newline, then the istream's line count is incremented.
 *
 ** Function:	BoolT		istream_peek_char(IStreamP istream, char *c_ref)
 ** Exceptions:	XX_dalloc_no_memory, XX_istream_read_error
 *
 * This function reads the next character from the specified istream (but does
 * not advance the character pointer).  If a character is read then the
 * character is assigned to the reference argument, and the function returns
 * true.  If the end of file is reached, the function returns false.
 *
 ** Function:	IStreamStatusT	istream_read_escaped_char(IStreamP istream,
 *				    char *c_ref)
 ** Exceptions:	XX_dalloc_no_memory, XX_istream_read_error
 *
 * This function reads a character sequence from the specified istream, and
 * parses it as an escaped character sequence.  Normally, the character to
 * which the sequence evaluates is assigned to the reference argument and
 * ``ISTREAM_STAT_READ_CHAR'' is returned.  If if it evaluates to no
 * character, then ``ISTREAM_STAT_NO_CHAR'' is returned (this is not an
 * error).  If there is an error in the syntax of the character sequence, then
 * ``ISTREAM_STAT_SYNTAX_ERROR'' is returned.  If any of the characters read
 * are newline characters, then the istream's line counter will be incremented
 * for each newline.
 *
 ** Function:	void		istream_inc_line(IStreamP istream)
 ** Exceptions:
 *
 * This function increments the specified istream's line counter.  It should
 * only really be called as specified in the documentation for the
 * ``ISTREAM_READ_CHAR'' macro.
 *
 ** Function:	unsigned	istream_line(IStreamP istream)
 ** Exceptions:
 *
 * This function returns the line number of the specified istream (one more
 * than the number of newlines that have been read).
 *
 ** Function:	char *		istream_name(IStreamP istream)
 ** Exceptions:
 *
 * This function returns the name of the file from which the specified istream
 * is reading. The return value should not be modified or deallocated.
 *
 ** Function:	void		istream_close(IStreamP istream)
 ** Exceptions:
 *
 * This function closes the specified istream.
 *
 ***=== MACROS ===============================================================
 *
 ** Macro:	ISTREAM_READ_CHAR (istream)
 ** Exceptions:
 *
 * This macro returns the next character from the specified istream.  It is a
 * slightly faster alternative to the ``istream_read_char'' function.  In
 * order to get the speed improvement, the program needs to do some extra
 * work: if the character returned is a newline, then the program must call
 * the ``istream_inc_line'' function to increment the line count; if the
 * character returned is a null character, then the program must call the
 * ``ISTREAM_HANDLE_NULL'' macro on the istream that was read.  It is not
 * obvious that the speed increase is worth the extra effort in coding.
 *
 ** Macro:	ISTREAM_PEEK_CHAR (istream)
 ** Exceptions:
 *
 * This macro returns the next character from the specified istream, without
 * reading it.  It is a slightly faster alternative to the
 * ``istream_peek_char'' function.  In order to get the speed improvement, the
 * program needs to do some extra work: if the character returned is the null
 * character, then the program must call the ``ISTREAM_HANDLE_NULL'' macro on
 * the istream that was read.  Unlike the ``ISTREAM_READ_CHAR'' macro, it is
 * not necessary to increment the istream's line count.  It is not obvious
 * that the speed increase is worth the extra effort in coding.
 *
 ** Macro:	ISTREAM_HANDLE_NULL (istream, redo, eof)
 ** Exceptions:	XX_dalloc_no_memory, XX_istream_read_error
 *
 * This macro should be called when either of the previous two macros returns
 * the null character.  It checks to see if the null character is really a
 * null character, a refill buffer instruction, or an end of file.  If it is a
 * real null character, then the program continues normally.  If it is a
 * refill buffer instruction, the istream's buffer is refilled, and the
 * program goes to the label ``redo''.  If it is an end of file, then the
 * program goes to the label ``eof''.
 *
 ***=== EXCEPTIONS ===========================================================
 *
 ** Exception:	XX_istream_read_error (char *name)
 *
 * This exception is raised if a read attempt fails.  The data thrown is a
 * copy of the name of the file that the read error occurred on.  The copy
 * should be deallocated when finished with.
 *
 **** Change Log:*/

/****************************************************************************/

#ifndef H_ISTREAM
#define H_ISTREAM

#include "config.h"
#include "dalloc.h"
#include "exception.h"

/*--------------------------------------------------------------------------*/

typedef struct IStreamT {
	FILE		       *file;
	char *				buffer;
	char *				current;
	char *				end;
	char *				limit;
	unsigned			line;
	char *				name;
	BoolT			read_last;
} IStreamT, *IStreamP;

#ifdef FS_NO_ENUM
typedef int IStreamStatusT, *IStreamStatusP;
#define ISTREAM_STAT_READ_CHAR		(0)
#define ISTREAM_STAT_NO_CHAR		(1)
#define ISTREAM_STAT_SYNTAX_ERROR	(2)
#else
typedef enum {
	ISTREAM_STAT_READ_CHAR,
	ISTREAM_STAT_NO_CHAR,
	ISTREAM_STAT_SYNTAX_ERROR
} IStreamStatusT, *IStreamStatusP;
#endif /* defined (FS_NO_ENUM) */

/*--------------------------------------------------------------------------*/

extern ExceptionP		XX_istream_read_error;
extern IStreamT		 *const istream_input;

/*--------------------------------------------------------------------------*/

extern void			istream_setup(void);
extern void			istream_init(IStreamP);
extern BoolT			istream_open(IStreamP, char *);
extern void			istream_assign(IStreamP, IStreamP);
extern BoolT			istream_is_open(IStreamP);
extern BoolT			istream_read_char(IStreamP, char *);
extern BoolT			istream_peek_char(IStreamP, char *);
extern IStreamStatusT		istream_read_escaped_char(IStreamP, char *);
extern void			istream_inc_line(IStreamP);
extern unsigned			istream_line(IStreamP);
extern char *			istream_name(IStreamP);
extern void			istream_close(IStreamP);

/*--------------------------------------------------------------------------*/

extern void			X__istream_fill_buffer(IStreamP);

/*--------------------------------------------------------------------------*/

#define ISTREAM_READ_CHAR(istream) \
(((istream)->read_last = TRUE), (*((istream)->current) ++))

#define ISTREAM_PEEK_CHAR(istream) \
(((istream)->read_last = FALSE), (*((istream)->current)))

#define ISTREAM_HANDLE_NULL(istream,redo,eof) \
{ \
	IStreamP X___is = (istream); \
	if (X___is->read_last) { \
	if (X___is->current == X___is->end) { \
		if (X___is->end == X___is->limit) { \
		X__istream_fill_buffer (X___is); \
		goto redo; \
		} else { \
		X___is->current --; \
		goto eof; \
		} \
	} \
	} else { \
	if (X___is->current == (X___is->end - 1)) { \
		if (X___is->end == X___is->limit) { \
		X__istream_fill_buffer (X___is); \
		goto redo; \
		} else { \
		goto eof; \
		} \
	} \
	} \
}

/*--------------------------------------------------------------------------*/

#ifdef FS_FAST
#define istream_init(is) ((is)->name = NULL)
#define istream_is_open(is) ((is)->name != NULL)
#define istream_inc_line(is) ((is)->line ++)
#define istream_line(is) ((is)->line)
#define istream_name(is) ((is)->name)
#endif /* defined (FS_FAST) */

#endif /* !defined (H_ISTREAM) */
