/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/

/**	@file dkstream.h
	Generic I/O API.
	This module provides a generic I/O API allowing applications
	to use the same code regardless whether to write plain
	or compressed files (and may be network connections in the
	future...).

	Functions in this module can be grouped into three categories:
	- Functions to create and destroy dk_stream_t data structures,
	- functions for binary I/O,
	- functions for text I/O and
	- other functions.

	To open a stream you should use dkstream_openfile(),
	dkstream_opengz(), dkstream_openbz2() and dkstream_close()
	instead of dkstream_new() and dkstream_delete(). If you already
	opened the file use dkstream_for_file(), dkstream_for_gz()
	or dkstream_for_bz2() to create a dk_stream_t.

	Unless otherwise stated, int functions in this module return a positive
	number to indicate success or a true condition, 0 to indicate an error
	or an unfullfilled condition.
	Pointer functions return valid pointers on success, NULL on error.

*/

#ifndef DK_STREAM_INC
#define DK_STREAM_INC 1

#include <dk.h>
#include <dktypes.h>

#if DK_HAVE_ZLIB_H
#include <zlib.h>
#endif
#if DK_HAVE_BZLIB_H
#include <bzlib.h>
#endif

#if defined(EXTERN)
#undef EXTERN
#endif
#ifndef DK_STREAM_C
#if !DK_HAVE_PROTOTYPES
#define EXTERN extern
#else
#define EXTERN /* nix */
#endif
#else
#define EXTERN /* nix */
#endif

#if defined(__cplusplus)
extern "C" {
#endif



/**	Create a new dk_stream_t dynamically.
	The stream must be released using dkstream_delete().
	@param	data	Data for the stream.
	@param	fct	Low-level function for the stream.
	@return	Pointer to the new dk_stream_t on success, NULL on error.
*/
EXTERN
dk_stream_t *dkstream_new DK_PR((void *data, dk_stream_fct_t *fct));



/**	Release a dk_stream_t structure obtained from dkstream_new().
	@param	st	Stream data to release.
*/
EXTERN
void  dkstream_delete DK_PR((dk_stream_t *st));



/**	Open stream for plain file.
	The stream must be relased and the file must be closed using
	dkstream_close().
	@param	n	Path name of the file.
	@param	m	Open mode (i.e. "r", "w"...).
	@param	i	Security checks to ignore (DK_SF_SEC_xxx).
	@param	r	Pointer to a variable receiving the error code
			for failed security checks (if any).
	@return	Stream to use for I/O.
*/
EXTERN
dk_stream_t *dkstream_openfile DK_PR((char *n, char *m, int i, int *r));



/**	Open stream for gzip compressed file.
	The stream must be relased and the file must be closed using
	dkstream_close().
	@param	n	Path name of the file.
	@param	m	Open mode (i.e. "r", "w"...).
	@param	i	Security checks to ignore (DK_SF_SEC_xxx).
	@param	r	Pointer to a variable receiving the error code
			for failed security checks (if any).
	@return	Stream to use for I/O.
*/
EXTERN
dk_stream_t *dkstream_opengz DK_PR((char *n, char *m, int i, int *r));



/**	Open stream for bzip2 compressed file.
	The stream must be relased and the file must be closed using
	dkstream_close().
	@param	n	Path name of the file.
	@param	m	Open mode (i.e. "r", "w"...).
	@param	i	Security checks to ignore (DK_SF_SEC_xxx).
	@param	r	Pointer to a variable receiving the error code
			for failed security checks (if any).
	@return	Stream to use for I/O.
*/
EXTERN
dk_stream_t *dkstream_openbz2 DK_PR((char *n, char *m, int i, int *r));



/**	Open stream for already opened file.
	The stream must be released using dkstream_close(). The
	file \a f is not closed by dkstream_close().
	@param	f	The file.
	@return	Stream to use for I/O.
*/
EXTERN
dk_stream_t *dkstream_for_file DK_PR((FILE *f));



#if DK_HAVE_ZLIB_H
/**	Open stream for already gzFile.
	The stream must be released using dkstream_close(). The
	gzFile \a gzf is not closed by dkstream_close().
	@param	gzf	The gzFile.
	@return	Stream to use for I/O.
*/
EXTERN
dk_stream_t *dkstream_for_gz DK_PR((gzFile gzf));
#endif



#if DK_HAVE_BZLIB_H
/**	Open stream for already opened BZFILE.
	The stream must be released using dkstream_close(). The
	BZFILE \a bzf is not closed by dkstream_close().
	@param	bzf	The BZFILE.
	@return	Stream to use for I/O.
*/
EXTERN
dk_stream_t *dkstream_for_bz2 DK_PR((BZFILE *bzf));
#endif



/**	Releases dk_stream_t structures obtained from
	dkstream_openfile(), dkstream_opengz(), dkstream_openbz2(),
	dkstream_for_file(), dkstream_for_gz() and dkstream_for_bz2().
	@param	st	The stream to close.
*/
EXTERN
void dkstream_close DK_PR((dk_stream_t *st));



/**	Write binary data to stream.
	@param	s	The stream to write to.
	@param	b	Pointer to buffer containing data to write.
	@param	l	Size of \a b in bytes.
	@return	The number of bytes written.
*/
EXTERN
size_t dkstream_write DK_PR((dk_stream_t *s, char *b, size_t l));



/**	Read binary data from stream.
	@param	s	The stream to read from.
	@param	b	Pointer to buffer.
	@param	l	Length of buffer in bytes.
	@return	The number of bytes read.
*/
EXTERN
size_t dkstream_read  DK_PR((dk_stream_t *s, char *b, size_t l));



/**	Write a word (2 bytes) to stream binary.
	The word is converted to network byte order before
	it is written to the stream.
	@param	s	The stream to write to.
	@param	w	The word to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_wb_word DK_PR((dk_stream_t *s, dk_word w));



/**	Write an unsigned word (2 bytes) to stream binary.
	The word is converted to network byte order before
	it is written to the stream.
	@param	s	The stream to write to.
	@param	w	The word to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_wb_uword DK_PR((dk_stream_t *s, dk_uword w));



/**	Write a double-word (4 bytes) to stream binary.
	The double-word is converted to network byte order before
	it is written to the stream.
	@param	s	The stream to write to.
	@param	w	The double-word to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_wb_dword DK_PR((dk_stream_t *s, dk_dword w));



/**	Write an unsigned double-word (4 bytes) to stream binary.
	The double-word is converted to network byte order before
	it is written to the stream.
	@param	s	The stream to write to.
	@param	w	The double-word to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_wb_udword DK_PR((dk_stream_t *s, dk_udword w));



/**	Write a string to stream in binary form.
	A string in binary form is written to the stream in 2 parts:
	- The string length (including the finalizing 0x00 byte) as
	  unsigned word.
	- The string text including the finalizing 0x00 byte.
	@param	s	The stream to write to.
	@param	str	The string to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_wb_string DK_PR((dk_stream_t *s, char *str));



/**	Read word (2 bytes) binary from stream.
	Two bytes are read from stream and converted to
	host byte order.
	@param	s	The stream to read from.
	@param	w	Pointer to variable to store the result.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_rb_word DK_PR((dk_stream_t *s, dk_word *w));



/**	Read unsigned word (2 bytes) binary from stream.
	Two bytes are read from stream and converted to
	host byte order.
	@param	s	The stream to read from.
	@param	w	Pointer to variable to store the result.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_rb_uword DK_PR((dk_stream_t *s, dk_uword *w));



/**	Read double-word (4 bytes) binary from stream.
	Four bytes are read from stream and converted to
	host byte order.
	@param	s	The stream to read from.
	@param	d	Pointer to variable to store the result.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_rb_dword DK_PR((dk_stream_t *s, dk_dword *d));



/**	Read unsigned double-word (4 bytes) binary from stream.
	Four bytes are read from stream and converted to
	host byte order.
	@param	s	The stream to read from.
	@param	d	Pointer to variable to store the result.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_rb_udword DK_PR((dk_stream_t *s, dk_udword *d));



/**	Read string in binary form from stream.
	Reading consists of the following steps:
	- The string length (2-byte word) is read from stream,
	- buffer memory is allocated dynamically to store string and
	- the string is read from stream into the buffer.
	You must release the strings memory using dk_delete()
	if the string is not longer needed.
	@param	s	The stream to read from.
	@return	Pointer to the buffer on success, NULL on error.
*/
EXTERN
char *dkstream_rb_string DK_PR((dk_stream_t *s));



/**	Write string in text form to stream.
	The string is written "as is" witout adding a trailing
	newline.
	@param	s	The stream to write to.
	@param	b	The string to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_puts DK_PR((dk_stream_t *s, char *b));

/**	Write double in text form to stream.
	Check opt for DK_STREAM_OPT_DOUBLE_NO_EXPONENT to see
	whether or not exponent notation is allowed.
	@param	s	The stream to write to.
	@param	d	The number to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_puts_double_use_exp DK_PR((dk_stream_t *s, double d));


/**	Write double in text form to stream, exponent notation
	allowed.
	@param	s	The stream to write to.
	@param	d	The number to write.
	@return	Flag to indicate success.
*/
EXTERN
int dkstream_puts_double DK_PR((dk_stream_t *s, double d));



/**	Write unsigned long in text form to stream.
	@param	s	The stream to write to.
	@param	u	The value to write.
	@return	Flag to indicate success.
*/
EXTERN int
dkstream_puts_ul DK_PR((dk_stream_t *s, unsigned long u));



/**	Write long in text form to stream.
	@param	s	The stream to write to.
	@param	l	The value to write.
	@return	Flag to indicate success.
*/
EXTERN int
dkstream_puts_long DK_PR((dk_stream_t *s, long l));



/**	Write a text (array of strings) in text form to stream.
	A newline is appended after each array element.
	The array must be finished by a NULL pointer.
	@param	s	The stream to write to.
	@param	a	An array containing pointers to the strings
	to write.
	@return	Flag to indicate success.
*/
EXTERN int
dkstream_puts_array DK_PR((dk_stream_t *s, char **a));



/**	Retrieve text line from stream.
	Text is read from stream to the buffer until either
	the buffer is full or a newline is found.
	@param	s	The stream to read from.
	@param	b	The destination buffer.
	@param	l	Length of buffer \a b in bytes.
	@return	Pointer to buffer on success, NULL on error.

*/
EXTERN
char *dkstream_gets DK_PR((dk_stream_t *s, char *b, size_t l));



/**	Get number of bytes written to the stream.
	@param	s	The stream to check.
	@return	Number of bytes written to the stream.
*/
EXTERN
unsigned long dkstream_get_bytes_written DK_PR((dk_stream_t *s));



/**	Find file type suffixes to handle (read operations).
	@return	Array of elements connecting a file type suffix
	to a stream-open function.
*/
EXTERN
dk_stream_suffix_t *dkstream_get_read_suffixes DK_PR((void));



/**	Find file type suffixes to handle (write operations).
	@return	Array of elements connecting a file type suffix
	to a stream open function.
*/
EXTERN
dk_stream_suffix_t *dkstream_get_write_suffixes DK_PR((void));

/**	Print double value to stream, avoid exponential notation.
	@param	s	Stream to print to.
	@param	d	Value to print.
	@return	Flag to indicate success.
*/
EXTERN int
dkstream_puts_double_no_exp DK_PR((dk_stream_t *s, double d));

/**	Print double value in string to stream, avoid exponential
	notation.
	This function is not intended for use by the application
	programmer, it is used internally by
	dkstream_puts_double_no_exp().
	@param	s	Stream to print to.
	@param	t	Text buffer containing exponential notation.
	@return	Flag to indicate success.
*/
EXTERN int
dkstream_puts_double_str_no_exp DK_PR((dk_stream_t *s, char *t));

/**	Set up to deny (fl=1) or allow (fl=0) the use of
	exponent notation when printing double values.
	@param	s	Stream to configure.
	@param	fl	New flag value.
	@return	Flag to indicate success.
*/
EXTERN void
dkstream_set_double_no_exponent DK_PR((dk_stream_t *s, int fl));

/**	Get flag for: no exponent notation for double values.
	@param	s	Stream to retrieve information from.
	@return	Flag value.
*/
EXTERN int
dkstream_get_double_no_exponent DK_PR((dk_stream_t *s));

#if defined(__cplusplus)
}
#endif


/** Low-level API commands: Test support for command. */
#define DK_STREAM_CMD_TEST	1

/** Low-level API commands: Read bytes into buffer. */
#define DK_STREAM_CMD_RDBUF	2

/** Low-level API commands: Write bytes from buffer. */
#define DK_STREAM_CMD_WRBUF	3

/** Low-level API commands: Flush after final write operation. */
#define DK_STREAM_CMD_FINAL	4

/** Low-level API commands: Stream is closed now. */
#define DK_STREAM_CMD_FINISH	5

/** Low-level API commands: Rewind to start of stream data. */
#define DK_STREAM_CMD_REWIND	6

/** Low-level API commands: Flush. */
#define DK_STREAM_CMD_FLUSH	7

/** Low-level API commands: Check whether end of input is reached. */
#define DK_STREAM_CMD_AT_END	8

/** Low-level API commands: Get string. */
#define DK_STREAM_CMD_GETS	9

/** Low-level API commands: Put string. */
#define DK_STREAM_CMD_PUTS	10

/* The opt field */

/** Do not use exponent notation for double values.*/
#define DK_STREAM_OPT_DOUBLE_NO_EXPONENT	1

/* Internally used by Perl XS */
#define DK_STREAM_FLAGS_STATE_USABLE	1
#define DK_STREAM_FLAGS_STATE_CLOSED	0
#define DK_STREAM_FLAGS_STATE_MASK	1
#define DK_STREAM_FLAGS_FILTER		4

#endif

