/****************************************************************************/
/*	Copyright (c) 2009, Bendor Research Pty. Ltd. All rights reserved.		*/
/*	This software is released under the GNU General Public License, version	*/
/*	3, with the exemptions that 1) the header files installed by this 	 	*/
/*	package can be included in closed-source code and 2) the libraries		*/
/*	built by this package can be statically linked into a closed-source 	*/
/*	binary. See the file LICENSE for the details.							*/
/*	This software is provided "AS IS", without any warranty or guarantee.	*/
/*	By using this software you acknowledge that neither Bendor Research nor	*/
/*	any of its personnel or affiliates can be held liable for any damages,	*/
/*	including, but not limited to, loss of business, loss of money, loss of */
/*	life, loss of reputation, arising out of this software being faulty or	*/
/*	not performing as advertised, even if Bendor Research was aware of such	*/
/*	faults in the software. You use this software at your own risk, any and */
/*	all liabilities that arise out of your using this software are strictly	*/
/*	yours.																	*/
/****************************************************************************/

#include <string.h>
#include <malloc.h>
#include "lpc-internals.h"

/** @addtogroup lpc_prg_lib */
/** @{ */

/**
*	Register a new file type.
*
*	The function receives a file I/O function table and adds it to the
*	list of know file types. After this call, the new type identified by
*	the 'name' field of the LPC_IO_FUNC structure can be used just like
*	the built-in types.
*
*	@param	lpc		The library state pointer
*
*	@param	ftab	File handling function table
*
*	@return			0 on success, a negative error code on failure.
*/

LPC_ERROR	LPC_FileType( LPC lpc, const LPC_IO_FUNC *ftab )
{
LPC_IO_FUNC	*new;

	for ( new = lpc->hex_list ; new ; new = new->link ) {

		if ( ! strcmp( ftab->name, new->name ) ) {

			// The name already exists

			return( LPC_ERROR_FILETYPE );
		}
	}

	if ( ! ( new = malloc( sizeof( LPC_IO_FUNC ) ) ) )

		return( LPC_ERROR_SYSTEM );

	memcpy( new, ftab, sizeof( LPC_IO_FUNC ) );

	if ( ! ( new->name = malloc( strlen( ftab->name ) + 1 ) ) ) {

		free( new );
		return( LPC_ERROR_SYSTEM );
	}

	strcpy( (char *) new->name, ftab->name );
	new->link = lpc->hex_list;
	lpc->hex_list = new;
	return( 0 );
}

/**
*	Open a file for subsequent processing.
*
*	@param	lpc		The library state pointer
*
*	@param	io		Pointer to an LPC_IO descriptor. The interpretation
*					of the fields of the descriptor is up to the actual
*					file format dependent implementation. The \p srec
*					and \p ihex implementations do not use any fields
*					in the structure.
*
*	@param	name	The name of the file to be opened or created
*
*	@param	type	The name of the file type, like "srec" or "ihex"
*
*	@param	wr		A flag indicating write. If this argument is non-zero,
*					then a file with the given name will be created (or if
*					the file exists, its size will be truncated to 0) and
*					LPC_FileWrite() calls can write into the file.
*					If the argument is 0, then an existing file is opened
*					and can be read by calls to LPC_FileRead().
*
*	@return		0 on success, a negative error code on failure.
*/

LPC_ERROR	LPC_FileOpen( LPC lpc, LPC_IO *io,  const char *name,
					   	  const char *type, int wr )
{
LPC_IO_FUNC *ftab;

	for ( ftab = lpc->hex_list ; ftab ; ftab = ftab->link ) {

		if ( ! strcmp( ftab->name, type ) )

			break;
	}

	if ( ! ftab ) {

		_lpc_Error( lpc, "Unknown file type \"%s\"", type );
		return( LPC_ERROR_PARAM );
	}

	io->func = ftab;

	if ( wr )

		return( (*ftab->create)( io, name ) );
	else
		return( (*ftab->open)( io, name ) );
}

/**
*	Read a record from an already open file.
*
*	The file must have been opened for reading, using the LPC_FileOpen()
*	function. This call will read one record and place it in a buffer.
*	<br><br>
*	At entry:
*	- io->data<br>
*		Contains a pointer to a buffer. This buffer will contain the
*		data read from the file. If this pointer is NULL, then data
*		will be consumed from the file and then discarded.
*	- io->rlen<br>
*		The size of the buffer, in bytes.
*	- io->addr<br>
*		The value of this field is not used by the \p srec and \p ihex
*		readers, it might be used for any purpose in a user-implemented
*		reader.
*	- io->line<br>
*		The value of this field is not used by the \p srec and \p ihex
*		readers, it might be used for any purpose in a user-implemented
*		reader.
*
*	At exit:
*	- io->data<br>
*		The field is unchanged. If it is not NULL, then the data read from
*		the file is placed into the memory area pointed by it.
*	- io->rlen<br>
*		The number of bytes placed into the data buffer. It might be 0.
*	- io->addr<br>
*		The address associated with the record read from the file. If
*		data was placed into the buffer, then this is the target address
*		corresponding to the first data byte.
*	- io->line<br>
*		Implementation dependent. For the \p srec and \p ihex readers it
*		will contain the line number that supplied the record.
*
*	@param	io		Pointer to an LPC_IO descriptor which must have been
*					prepared by a call to LPC_FileOpen() specifying read
*					mode.
*
*	@retval	>0		A record has been read. The actual numerical value
*					is implementation dependent. The \p srec and \p ihex
*					readers always return 1 on success.
*
*	@retval	0		The last record in the file was processed. Any further
*					calls will immediately return 0 with data length of 0.
*
*	@retval <0		There was some error, the returned value is one of the
*					LPC_ERROR codes.
*/

LPC_ERROR	LPC_FileRead( LPC_IO *io )
{
	return( (*io->func->read)( io ) );
}

/**
*	Write data to an already open file.
*
*	The file must have been opened for writing, using the LPC_FileOpen()
*	function. This call will write some amount of data into the file.
*	<br><br>
*	At entry:
*	- io->data<br>
*		A pointer to a buffer that contains the data to be written
*		into the file. If it is NULL, then the behaviour depends on
*		the actual file format implementation. The \p srec and \p ihex
*		functions ignore calls with a NULL pointer.
*	- io->rlen<br>
*		The size of the buffer, in bytes. That many bytes will be written
*		to the file, at consecutive addresses.
*	  io->addr<br>
*		The target address that corresponds to the first byte in the buffer.
*	- io->line<br>
*		The value of this field can be interpreted by any way by the
*		implementation. The \p srec and \p ihex routines interpret it as the
*		maximum number of data bytes written per line; if it is less that 16,
*		then 16 will be used.
*
*	At exit:
*	- io->data<br>
*		The field is unchanged.
*	- io->rlen<br>
*		Implementation dependent, the \p srec and \p ihex routines do
*		not return anything in this field.
*	- io->addr<br>
*		Implementation dependent, the \p srec and \p ihex routines do
*		not return anything in this field.
*	- io->line<br>
*		Implementation dependent. For the \p srec and \p ihex readers it
*		will contain the number of lines written to the file so far.
*
*	@param	io		Pointer to an LPC_IO descriptor which must have been
*					prepared by a call to LPC_FileOpen() specifying write
*					mode.
*
*	@retval	>=0		The data was written out. The actual numerical value
*					if implementation dependent. The \p srec and \p ihex
*					readers always return 1 on success.
*
*	@retval <0		There was some error, the returned value is one of the
*					LPC_ERROR codes.
*/

LPC_ERROR	LPC_FileWrite( LPC_IO *io )
{
	return( (*io->func->write)( io ) );
}

/**
*	Close a file.
*
*	@param	io		Pointer to an LPC_IO descriptor which must have been
*					prepared by a call to LPC_FileOpen().

*	@return		0 on success and a negative error code in case of an error.
*				The file will be closed and all associated state will be
*				released in any case
*/

LPC_ERROR	LPC_FileClose( LPC_IO *io )
{
	return( (*io->func->close)( io ) );
}

/** @} */

