/*  -*- Mode: C -*-  */

/* register.h --- printf clone for argv arrays */

/* Author:	       Gary V. Vaughan <gvv@techie.com>
 * Maintainer:	       Gary V. Vaughan <gvv@techie.com>
 * Created:	       Fri Nov 13 16:51:38 1998
 * Last Modified:      Wed Aug 04 13:09:20 1999
 *            by:      Gary V. Vaughan <gvv@techie.com>
 * ---------------------------------------------------------------------
 * @(#) $Id$
 * ---------------------------------------------------------------------
 */

/* Copyright (C) 1998, 1999 Gary V. Vaughan */

/* This program 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 of the
 * License, or (at your option) any later version.
 *
 * This program 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 this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * As a special exception to the GNU General Public License, if you
 * distribute this file as part of a program that also links with and
 * uses the libopts library from AutoGen, you may include it under
 * the same distribution terms used by the libopts library.
 */

/* Code: */

#ifndef SNPRINTFV_REGISTER_H
#define SNPRINTFV_REGISTER_H 1

#include <snprintfv.h>
#include <snprintfv/compat.h>
#include <snprintfv/filament.h>
#include <snprintfv/list.h>
#include <snprintfv/stream.h>

#ifdef WITH_DMALLOC
#  include <dmalloc.h>
#endif

typedef enum {
    SNV_ERROR = -1,
    SNV_OK
} snv_status;

/* Basic states required by the parser.  On initialisation the parser
   will be in SNV_STATE_BEGIN, and tokens will be parsed by the registered
   functions until the parser reached SNV_STATE_END. */
#define SNV_STATE_BEGIN		1
#define SNV_STATE_END		0

typedef struct {
    int state;			/* one of the defines above */
    snv_status status;		/* SNV_OK, or SNV_ERROR */
    filament *error;		/* accumulated error details */

    slist *data;		/* miscellaneous parser state */
} printfv_parser;

#ifdef __STDC__
#  define PARSER_ERROR(pp, str) 					\
	    parser_error(pp, "file " __FILE__ ": line "			\
		   	 STR(__LINE__) ERROR_FUNCTION ": " str "\n")
#else
   /* Yuck!  K&R doesn't handle string concatenation in macros like
      we use above, so we have to pass the responsibility through
      to runtime in this case. */
#  ifndef KNR_PARSE_ERROR_BUFSIZ
#  define KNR_PARSE_ERROR_BUFSIZ 4096
#  endif /* KNR_PARSE_ERROR_BUFSIZ */
#  define PARSER_ERROR(pp, str)		SNV_STMT_START{			\
	    char m_tmpstr1[KNR_PARSE_ERROR_BUFSIZ];			\
	    size_t m_tmplen2 = KNR_PARSE_ERROR_BUFSIZ -2; /* 2->\n\0 */	\
	    strcpy(m_tmpstr1, "file ");					\
	    strcat(m_tmpstr1, __FILE__);				\
	    strcat(m_tmpstr1, ": line ");				\
	    strcat(m_tmpstr1, STR(__LINE__));				\
	    strcat(m_tmpstr1, ERROR_FUNCTION);				\
	    m_tmplen2 -= strlen(m_tmpstr1);				\
	    strncat(m_tmpstr1, m_tmplen2, str);				\
	    strcat(m_tmpstr1, "\n");					\
	    parser_error(pp, m_tmpstr1);				\
	    				}SNV_STMT_END
#endif					

/* Type of a printf specifier-handler function.

   PPARSER holds the current state information for the format
   string parser.  The fields in the PPARSER type can (and should)
   be modified by functions of this type to implement the fsm which
   parses format strings.
   STREAM is the stream (possibly a struct printfv_stream appropriately
   cast) on which to write output.
   PPFORMAT is a pointer to printf style format string which describes
   how to format the values in ARGV.
   ARGV is a vector of pointers to the argument data; PARGTYPES is a
   parallel vector of bitflags, each entry of which indicates the type
   of the corresponding ARGV entry according to the macros defined
   above.

   PARGC holds the number of slots in ARGV and PARGTYPES, and PARGINDEX
   is the default slot to use for the current specifier.  For example,
   if PPFORMAT is "%3$*s", PARGC is 3 and PARGINDEX is 1, then functions
   of this type can tell that the width is in ARGV[1] (PARGINDEX is 1),
   the string is the third (from "3$") and last (PARGC is 3 too) element
   of ARGV.

   The core library may call a registered printfv_function twice for
   each place in PPFORMAT that warrants it.  The first time is when
   the number and types of arguments required by PPFORMAT is being
   determined so that ARGS can be built.  For this first call, STREAM,
   ARGS, and *PARGTYPES will be NULL, and the function should allocate
   and fill a suitable array to pass back as *PARGTYPES.  This first
   call is only performed if the non-vector API calls are used, because
   a vector has to be built for the parser engine.

   PPARSER will be reset for the next call (it is good practice to
   minimise the use of PPARSER to tracking the fsm state if a first
   pass call is made) where STREAM and ARGV will be set, and
   PARGTYPES (note: not *PARGTYPES) will be NULL.  It is this pass which
   should put characters in STREAM, always using the printfv_emit function
   so that buffer overflows can be detected and handled.

   The function should return the number of characters written,
   or -1 for errors.  In the case of an error, arbitrary strings can
   be stored in the parser for output by the engine using the parser_error
   function. */

typedef int	printfv_function PARAMS((printfv_parser * const pparser,
				STREAM *stream,
				const char **ppformat,
				snv_constpointer const *argv,
				int *pargc,
				int **pargtypes, int *pargindex));

/* Macros to ease the pain of typing in all these arguments for fsm
   implementations. */

#define DECLARE_PRINTFV_HANDLER(name)					\
	int	name PARAMS((printfv_parser * const pparser,		\
			STREAM *stream,					\
	    		const char **ppformat,				\
			snv_constpointer const *argv,			\
			int *pargc, int **pargtypes, int *pargindex))

#define DEFINE_PRINTFV_HANDLER(name)					\
    int									\
    name (pparser, stream, ppformat, argv, pargc, pargtypes, pargindex)	\
	    printfv_parser * const pparser;				\
	    STREAM *stream;						\
	    const char **ppformat;					\
	    snv_constpointer const *argv;				\
	    int *pargc, **pargtypes, *pargindex;

/* The type of each element in the spec_table exported by the fsm. */

typedef struct {
    int spec;
    int type; 
    printfv_function *func;
} spec_entry;

/* Codes to determine basic types.

   These values cover all the standard format specifications.
   Users can add new values after SNV_LAST for their own types.  */

enum
{
  SNV_INT,		/* int */
  SNV_CHAR,		/* int, cast to char */
  SNV_WCHAR,		/* wide char */
  SNV_STRING,		/* const char *, a '\0'-terminated string */
  SNV_WSTRING,		/* const wchar_t *, wide character string */
  SNV_POINTER,		/* void * */
  SNV_FLOAT,		/* float */
  SNV_DOUBLE,		/* double */
  SNV_LAST
};

/* Flag bits that can be set in a type. */
#define SNV_TYPE_MASK		0x00ff
#define	SNV_FLAG_MASK		~SNV_TYPE_MASK

#define	SNV_FLAG_LONG_LONG	(1 << 8)
#define	SNV_FLAG_LONG_DOUBLE	SNV_FLAG_LONG_LONG
#define	SNV_FLAG_LONG		(1 << 9)
#define	SNV_FLAG_SHORT		(1 << 10)
#define SNV_FLAG_UNSIGNED	(1 << 11)
#define	SNV_FLAG_PTR		(1 << 12)

/* The type of a parser element data deletion function. */
typedef void parser_delete_func PARAMS((snv_pointer address));

/**
 * printfv_function_register:  
 * @spec: the character which will trigger @func, cast to an unsigned int.
 * @type: the type tag which will be returned by the format parser.
 * @func: the handler function.
 * 
 * Register @func to be called when @spec is encountered in a format
 * string.
 * 
 * Return value:
 * Returns SNV_OK if @func was successfuly registered, SNV_ERROR
 * otherwise.
 **/
extern int printfv_function_register PARAMS((unsigned spec, int type, printfv_function *func));

/**
 * printfv_argtype_renew:  
 * @type: a specifier type tag.
 * @pargtypes: the address of a vector of type tags.
 * @pargc: the number of elements allocated to @pargtypes.
 * @argindex: the zero based element number of @pargtypes to set to @type.
 * 
 * Helper function which will ensure that @pargtypes has been allocated
 * enough elements to accomodate @argindex by reallocating if necessary.
 * @pargtypes can be a pointer to a NULL pointer when calling this
 * function, and the @argindex numbered element will be set to @type,
 * and @pargc adjusted to reflect the new size of @pargtypes on return.
 **/
extern void printfv_argtype_renew PARAMS((int type, int **pargtypes, int *pargc, int argindex));

/**
 * parser_data_get:  
 * @pparser: pointer to the current parser state.
 * @key: the storage key for the parser state data in question.
 * 
 * This function will attempt to retrieve data previously stored in
 * @pparser under @key.
 * 
 * Return value: 
 * If there is no data associated with @key, NULL is returned, otherwise
 * the address of the stored data is returned (so that 0 valued data can
 * be distinguished from a NULL return).
 **/ 
extern snv_pointer* parser_data_get PARAMS((printfv_parser *pparser, const char *key));

/**
 * parser_data_set:  
 * @pparser: pointer to the current parser state.
 * @key: the storage key for the parser state data in question.
 * @data: the data to be associated with @key in @pparser.
 * @delete_func: a function which will correctly free @data in due course.
 * 
 * This function will store the arbitrary stae information, @data, in
 * @pparser so that it can be shared with the other handler functions
 * which implement the format string parsing fsm.  @data is stored with
 * an associated @key for later retrieval.  @key is copied into @pparser.
 * so it is safe to pass static strings or recycle the original.  Any
 * stale @data already stored against @key in @pparser is passed to the
 * stale @delete_func for recycling, before the new @data and @delete_func
 * are used to overwrite the stale values.
 **/
extern void parser_data_set PARAMS((printfv_parser *pparser, const char *key, snv_pointer data, parser_delete_func *delete_func));

/**
 * parser_data_delete:  destructor 
 * @address: the address of the data to be recycled.
 * 
 * The memory associated with @address is released.
 *
 * This is a sample delete function which can be used with
 * parser_data_set() to delete data which was created with a single
 * memory allocation;  that is, the memory at @address contains no
 * pointers to other allocated memory which should be recycled and
 * is not itself statically allocated.
 **/
extern void parser_data_delete PARAMS((snv_pointer address));

/**
 * parser_error:  
 * @pparser: pointer to the current parser state.
 * @error_message: new error message to append to @pparser.
 * 
 * The contents of @error_message are appended to the @pparser internal
 * error string, so it is safe to pass static strings or recycle the
 * original when this function returns.
 * 
 * Return value:
 * The address of the full accumulated error message in @pparser is
 * returned.
 **/
extern char* parser_error PARAMS((printfv_parser *pparser, const char *error_message));


#ifdef __cplusplus
}
#endif

#endif /* COMPILING_SNPRINTFV_C */

/* register.h ends here */
