/*
    Copyright (c) 2006-2020 Chung, Hyung-Hwan. 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, 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.
 */

#ifndef _HAWK_SED_H_
#define _HAWK_SED_H_

#include <hawk-cmn.h>
#include <hawk-gem.h>
#include <hawk-sio.h>

/** \file
 * This file defines data types and functions to use for creating a custom
 * stream editor commonly available on many platforms. A stream editor is
 * a non-interactive text editing tool that reads text from an input stream,
 * stores it to pattern space, manipulates the pattern space by applying a set
 * of editing commands, and writes the pattern space to an output stream.
 * Typically, the input and output streams are a console or a file.
 *
 * \code
 * sed = hawk_sed_open ();
 * hawk_sed_comp (sed);
 * hawk_sed_exec (sed);
 * hawk_sed_close (sed);
 * \endcode
 */

/** \struct hawk_sed_t
 * The hawk_sed_t type defines a stream editor. The structural details are
 * hidden as it is a relatively complex data type and fragile to external
 * changes. To use a stream editor, you typically can:
 *
 * - create a stream editor object with hawk_sed_open().
 * - compile stream editor commands with hawk_sed_comp().
 * - execute them over input and output streams with hawk_sed_exec().
 * - destroy it with hawk_sed_close() when done.
 *
 * The input and output streams needed by hawk_sed_exec() are implemented in
 * the form of callback functions. You should implement two functions
 * conforming to the ::hawk_sed_io_impl_t type.
 */
typedef struct hawk_sed_t hawk_sed_t;

#define HAWK_SED_HDR \
	hawk_oow_t _instsize; \
	hawk_gem_t _gem

typedef struct hawk_sed_alt_t hawk_sed_alt_t;
struct hawk_sed_alt_t
{
	/* ensure that hawk_sed_alt_t matches the beginning part of hawk_sed_t */
	HAWK_SED_HDR;
};

typedef struct hawk_sed_adr_t hawk_sed_adr_t;
typedef struct hawk_sed_cmd_t hawk_sed_cmd_t;

struct hawk_sed_adr_t
{
	enum
	{
		HAWK_SED_ADR_NONE,     /* no address */
		HAWK_SED_ADR_DOL,      /* $ - last line */
		HAWK_SED_ADR_LINE,     /* specified line */
		HAWK_SED_ADR_REX,      /* lines matching regular expression */
		HAWK_SED_ADR_STEP,     /* line steps - only in the second address */
		HAWK_SED_ADR_RELLINE,  /* relative line - only in second address */
		HAWK_SED_ADR_RELLINEM  /* relative line in the multiples - only in second address */
	} type;

	union
	{
		hawk_oow_t lno;
		void*      rex;
	} u;
};

typedef struct hawk_sed_cut_sel_t hawk_sed_cut_sel_t;

struct hawk_sed_cut_sel_t
{
	hawk_oow_t len;

	struct
	{
		enum
		{
			HAWK_SED_CUT_SEL_CHAR = HAWK_T('c'),
			HAWK_SED_CUT_SEL_FIELD = HAWK_T('f')
		} id;
		hawk_oow_t start;
		hawk_oow_t end;
	} range[128];

	hawk_sed_cut_sel_t* next;
};

#define HAWK_SED_CMD_NOOP            HAWK_T('\0')
#define HAWK_SED_CMD_QUIT            HAWK_T('q')
#define HAWK_SED_CMD_QUIT_QUIET      HAWK_T('Q')
#define HAWK_SED_CMD_APPEND          HAWK_T('a')
#define HAWK_SED_CMD_INSERT          HAWK_T('i')
#define HAWK_SED_CMD_CHANGE          HAWK_T('c')
#define HAWK_SED_CMD_DELETE          HAWK_T('d')
#define HAWK_SED_CMD_DELETE_FIRSTLN  HAWK_T('D')
#define HAWK_SED_CMD_PRINT_LNNUM     HAWK_T('=')
#define HAWK_SED_CMD_PRINT           HAWK_T('p')
#define HAWK_SED_CMD_PRINT_FIRSTLN   HAWK_T('P')
#define HAWK_SED_CMD_PRINT_CLEARLY   HAWK_T('l')
#define HAWK_SED_CMD_HOLD            HAWK_T('h')
#define HAWK_SED_CMD_HOLD_APPEND     HAWK_T('H')
#define HAWK_SED_CMD_RELEASE         HAWK_T('g')
#define HAWK_SED_CMD_RELEASE_APPEND  HAWK_T('G')
#define HAWK_SED_CMD_EXCHANGE        HAWK_T('x')
#define HAWK_SED_CMD_NEXT            HAWK_T('n')
#define HAWK_SED_CMD_NEXT_APPEND     HAWK_T('N')
#define HAWK_SED_CMD_READ_FILE       HAWK_T('r')
#define HAWK_SED_CMD_READ_FILELN     HAWK_T('R')
#define HAWK_SED_CMD_WRITE_FILE      HAWK_T('w')
#define HAWK_SED_CMD_WRITE_FILELN    HAWK_T('W')
#define HAWK_SED_CMD_BRANCH          HAWK_T('b')
#define HAWK_SED_CMD_BRANCH_COND     HAWK_T('t')
#define HAWK_SED_CMD_SUBSTITUTE      HAWK_T('s')
#define HAWK_SED_CMD_TRANSLATE       HAWK_T('y')
#define HAWK_SED_CMD_CLEAR_PATTERN   HAWK_T('z')
#define HAWK_SED_CMD_CUT             HAWK_T('C')

struct hawk_sed_cmd_t
{
	hawk_ooch_t type;

	const hawk_ooch_t* lid;
	hawk_loc_t     loc;

	int negated;

	hawk_sed_adr_t a1; /* optional start address */
	hawk_sed_adr_t a2; /* optional end address */

	union
	{
		/* text for the a, i, c commands */
		hawk_oocs_t text;

		/* file name for r, w, R, W */
		hawk_oocs_t file;

		/* data for the s command */
		struct
		{
			void* rex; /* regular expression */
			hawk_oocs_t rpl;  /* replacement */

			/* flags */
			hawk_oocs_t file; /* file name for w */
			unsigned short occ;
			unsigned short g: 1; /* global */
			unsigned short p: 1; /* print */
			unsigned short i: 1; /* case insensitive */
			unsigned short k: 1; /* kill unmatched portion */
		} subst;

		/* translation set for the y command */
		hawk_oocs_t transet;

		/* branch target for b and t */
		struct
		{
			hawk_oocs_t label;
			hawk_sed_cmd_t* target;
		} branch;

		/* cut command information */
		struct
		{
			hawk_sed_cut_sel_t* fb;/**< points to the first block */
			hawk_sed_cut_sel_t* lb; /**< points to the last block */

			hawk_ooch_t         delim[2]; /**< input/output field delimiters */
			unsigned short     w: 1; /* whitespace for input delimiters. ignore delim[0]. */
			unsigned short     f: 1; /* fold delimiters */
			unsigned short     d: 1; /* delete if not delimited */

			hawk_oow_t         count;
			hawk_oow_t         fcount;
			hawk_oow_t         ccount;
		} cut;
	} u;

	struct
	{
		int a1_matched;
		hawk_oow_t a1_match_line;

		int c_ready;

		/* points to the next command for fast traversal and
		 * fast random jumps */
		hawk_sed_cmd_t* next;
	} state;
};


/**
 * The hawk_sed_opt_t type defines various option types.
 */
enum hawk_sed_opt_t
{
	HAWK_SED_TRAIT,      /**< trait */
	HAWK_SED_TRACER,     /**< tracer hook */
	HAWK_SED_LFORMATTER, /**< formatter for the 'l' command */

	HAWK_SED_DEPTH_REX_BUILD,
	HAWK_SED_DEPTH_REX_MATCH
};
typedef enum hawk_sed_opt_t hawk_sed_opt_t;

/**
 * The hawk_sed_trait_t type defines various trait codes for a stream editor.
 * Options can be OR'ed with each other and be passed to a stream editor with
 * the hawk_sed_setopt() function.
 */
enum hawk_sed_trait_t
{
	HAWK_SED_STRIPLS      = (1 << 0), /**< strip leading spaces from text */
	HAWK_SED_KEEPTBS      = (1 << 1), /**< keep an trailing backslash */
	HAWK_SED_ENSURENL     = (1 << 2), /**< ensure NL at the text end */
	HAWK_SED_QUIET        = (1 << 3), /**< do not print pattern space */
	HAWK_SED_STRICT       = (1 << 4), /**< do strict address and label check */
	HAWK_SED_EXTENDEDADR  = (1 << 5), /**< allow start~step , addr1,+line, addr1,~line */
	HAWK_SED_SAMELINE     = (1 << 7), /**< allow text on the same line as c, a, i */
	HAWK_SED_EXTENDEDREX  = (1 << 8), /**< use extended regex */
	HAWK_SED_NONSTDEXTREX = (1 << 9)  /**< enable non-standard extensions to regex */
};
typedef enum hawk_sed_trait_t hawk_sed_trait_t;

/**
 * The hawk_sed_io_cmd_t type defines I/O command codes. The code indicates
 * the action to take in an I/O handler.
 */
enum hawk_sed_io_cmd_t
{
	HAWK_SED_IO_OPEN  = 0,
	HAWK_SED_IO_CLOSE = 1,
	HAWK_SED_IO_READ  = 2,
	HAWK_SED_IO_WRITE = 3
};
typedef enum hawk_sed_io_cmd_t hawk_sed_io_cmd_t;

/**
 * The hawk_sed_io_arg_t type defines a data structure required by
 * an I/O handler.
 */
struct hawk_sed_io_arg_t
{
	void*             handle; /**< I/O handle */
	const hawk_ooch_t* path;   /**< file path. HAWK_NULL for a console */
};
typedef struct hawk_sed_io_arg_t hawk_sed_io_arg_t;

/**
 * The hawk_sed_io_impl_t type defines an I/O handler. I/O handlers are called by
 * hawk_sed_exec().
 */
typedef hawk_ooi_t (*hawk_sed_io_impl_t) (
	hawk_sed_t*        sed,
	hawk_sed_io_cmd_t  cmd,
	hawk_sed_io_arg_t* arg,
	hawk_ooch_t*       data,
	hawk_oow_t         count
);

/**
 * The hawk_sed_lformatter_t type defines a text formatter for the 'l' command.
 */
typedef int (*hawk_sed_lformatter_t) (
	hawk_sed_t*        sed,
	const hawk_ooch_t* str,
	hawk_oow_t        len,
	int (*cwriter) (hawk_sed_t*, hawk_ooch_t)
);

/**
 * The hawk_sed_ecb_close_t type defines the callback function
 * called when an sed object is closed.
 */
typedef void (*hawk_sed_ecb_close_t) (
	hawk_sed_t* sed,  /**< sed */
	void*       ctx
);

/**
 * The hawk_sed_ecb_t type defines an event callback set.
 * You can register a callback function set with
 * hawk_sed_pushecb().  The callback functions in the registered
 * set are called in the reverse order of registration.
 */
typedef struct hawk_sed_ecb_t hawk_sed_ecb_t;
struct hawk_sed_ecb_t
{
	/**
	 * called by hawk_sed_close().
	 */
	hawk_sed_ecb_close_t close;
	void* ctx;

	/* internal use only. don't touch this field */
	hawk_sed_ecb_t* next;
};

enum hawk_sed_tracer_op_t
{
	HAWK_SED_TRACER_READ,
	HAWK_SED_TRACER_WRITE,
	HAWK_SED_TRACER_MATCH,
	HAWK_SED_TRACER_EXEC
};
typedef enum hawk_sed_tracer_op_t hawk_sed_tracer_op_t;

typedef void (*hawk_sed_tracer_t) (
	hawk_sed_t*           sed,
	hawk_sed_tracer_op_t  op,
	const hawk_sed_cmd_t* cmd
);

/**
 * The hawk_sed_space_t type defines the types of
 * sed bufferspaces.
 */
enum hawk_sed_space_t
{
	HAWK_SED_SPACE_HOLD,   /**< hold space */
	HAWK_SED_SPACE_PATTERN /**< pattern space */
};
typedef enum hawk_sed_space_t hawk_sed_space_t;


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

/**
 * This section defines easier-to-use helper interface for a stream editor.
 * If you don't care about the details of memory management and I/O handling,
 * you can choose to use the helper functions provided here. It is
 * a higher-level interface that is easier to use as it implements
 * default handlers for I/O and memory management.
 */

/**
 * The hawk_sed_iostd_type_t type defines types of standard
 * I/O resources.
 */
enum hawk_sed_iostd_type_t
{
	HAWK_SED_IOSTD_NULL,  /**< null resource type */
	HAWK_SED_IOSTD_FILE,  /**< file */
	HAWK_SED_IOSTD_FILEB, /**< file */
	HAWK_SED_IOSTD_FILEU, /**< file */
	HAWK_SED_IOSTD_OOCS,  /**< string  */
	HAWK_SED_IOSTD_BCS,
	HAWK_SED_IOSTD_UCS,
	HAWK_SED_IOSTD_SIO    /**< sio */
};
typedef enum hawk_sed_iostd_type_t hawk_sed_iostd_type_t;

/**
 * The hawk_sed_iostd_t type defines a standard I/O resource.
 */
struct hawk_sed_iostd_t
{
	/** resource type */
	hawk_sed_iostd_type_t type;

	/** union describing the resource of the specified type */
	union
	{
		/** file path with character encoding */
		struct
		{
			/** file path to open. #HAWK_NULL or '-' for stdin/stdout. */
			const hawk_ooch_t* path;
			/** a stream created with the file path is set with this
			 * cmgr if it is not #HAWK_NULL. */
			hawk_cmgr_t* cmgr;
		} file;

		struct
		{
			const hawk_bch_t* path;
			hawk_cmgr_t* cmgr;
		} fileb;

		struct
		{
			const hawk_uch_t* path;
			hawk_cmgr_t* cmgr;
		} fileu;

		/**
		 * input string or dynamically allocated output string
		 *
		 * For input, the ptr and the len field of str indicates the
		 * pointer and the length of a string to read. You must set
		 * these two fields before calling hawk_sed_execstd().
		 *
		 * For output, the ptr and the len field of str indicates the
		 * pointer and the length of produced output. The output
		 * string is dynamically allocated. You don't need to set these
		 * fields before calling hawk_sed_execstd() because they are
		 * set by hawk_sed_execstd() and valid while the relevant sed
		 * object is alive. You must free the memory chunk pointed to by
		 * the ptr field with hawk_sed_freemem() once you're done with it
		 * to avoid memory leaks.
		 */
		hawk_oocs_t        oocs;
		hawk_bcs_t         bcs;
		hawk_ucs_t         ucs;

		/** pre-opened sio stream */
		hawk_sio_t*        sio;
	} u;
};

typedef struct hawk_sed_iostd_t hawk_sed_iostd_t;

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

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

/**
 * The hawk_sed_open() function creates a stream editor object. A memory
 * manager provided is used to allocate and destory the object and any dynamic
 * data through out its lifetime. An extension area is allocated if an
 * extension size greater than 0 is specified. You can access it with the
 * hawk_sed_getxtn() function and use it to store arbitrary data associated
 * with the object.  When done, you should destroy the object with the
 * hawk_sed_close() function to avoid any resource leaks including memory.
 * \return pointer to a stream editor on success, HAWK_NULL on failure
 */
HAWK_EXPORT hawk_sed_t* hawk_sed_open (
	hawk_mmgr_t*   mmgr,    /**< memory manager */
	hawk_oow_t     xtnsize, /**< extension size in bytes */
	hawk_cmgr_t*   cmgr,
	hawk_errinf_t* errinf
);

/**
 * The hawk_sed_close() function destroys a stream editor.
 */
HAWK_EXPORT void hawk_sed_close (
	hawk_sed_t* sed /**< stream editor */
);

#if defined(HAWK_HAVE_INLINE)
/**
 * The hawk_sed_getxtn() function returns the pointer to the extension area
 * placed behind the actual sed object.
 */
static HAWK_INLINE void* hawk_sed_getxtn (hawk_sed_t* sed) { return (void*)((hawk_uint8_t*)sed + ((hawk_sed_alt_t*)sed)->_instsize); }

/**
 * The hawk_sed_getgem() function gets the pointer to the gem structure of the
 * sed object.
 */
static HAWK_INLINE hawk_gem_t* hawk_sed_getgem (hawk_sed_t* sed) { return &((hawk_sed_alt_t*)sed)->_gem; }

/**
 * The hawk_sed_getmmgr() function gets the memory manager used in
 * hawk_sed_open().
 */
static HAWK_INLINE hawk_mmgr_t* hawk_sed_getmmgr (hawk_sed_t* sed) { return ((hawk_sed_alt_t*)sed)->_gem.mmgr; }
static HAWK_INLINE hawk_cmgr_t* hawk_sed_getcmgr (hawk_sed_t* sed) { return ((hawk_sed_alt_t*)sed)->_gem.cmgr; }
static HAWK_INLINE void hawk_sed_setcmgr (hawk_sed_t* sed, hawk_cmgr_t* cmgr) { ((hawk_sed_alt_t*)sed)->_gem.cmgr = cmgr; }
#else
#define hawk_sed_getxtn(sed) ((void*)((hawk_uint8_t*)sed + ((hawk_sed_alt_t*)sed)->_instsize))
#define hawk_sed_getgem(sed) (&((hawk_sed_alt_t*)(sed))->_gem)
#define hawk_sed_getmmgr(sed) (((hawk_sed_alt_t*)(sed))->_gem.mmgr)
#define hawk_sed_getcmgr(sed) (((hawk_sed_alt_t*)(sed))->_gem.cmgr)
#define hawk_sed_setcmgr(sed,_cmgr) (((hawk_sed_alt_t*)(sed))->_gem.cmgr = (_cmgr))
#endif /* HAWK_HAVE_INLINE */


/**
 * The hawk_sed_getopt() function gets the value of an option
 * specified by \a id into the buffer pointed to by \a value.
 *
 * The \a value field is dependent on \a id:
 *  - #HAWK_SED_TRAIT - int*, 0 or bitwised-ORed of #hawk_sed_trait_t values
 *  - #HAWK_SED_TRACER - hawk_sed_tracer_t*
 *  - #HAWK_SED_LFORMATTER - hawk_sed_lformatter_t*
 *
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_getopt (
	hawk_sed_t*    sed,
	hawk_sed_opt_t id,
	void*         value
);

/**
 * The hawk_sed_setopt() function sets the value of an option
 * specified by \a id to the value pointed to by \a value.
 *
 * The \a value field is dependent on \a id:
 *  - #HAWK_SED_TRAIT - const int*, 0 or bitwised-ORed of #hawk_sed_trait_t values
 *  - #HAWK_SED_TRACER - hawk_sed_tracer_t
 *  - #HAWK_SED_LFORMATTER - hawk_sed_lformatter_t
 *
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_setopt (
	hawk_sed_t*    sed,
	hawk_sed_opt_t id,
	const void*   value
);

HAWK_EXPORT hawk_errstr_t hawk_sed_geterrstr (
	hawk_sed_t* sed
);

/**
 * The hawk_sed_geterrnum() function returns the number of the last error
 * occurred.
 * \return error number
 */

/**
 * The hawk_sed_geterror() function gets an error number, an error location,
 * and an error message. The information is set to the memory area pointed
 * to by each parameter.
 */
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE hawk_errnum_t hawk_sed_geterrnum (hawk_sed_t* sed) { return hawk_gem_geterrnum(hawk_sed_getgem(sed)); }
static HAWK_INLINE const hawk_loc_t* hawk_sed_geterrloc (hawk_sed_t* sed) { return hawk_gem_geterrloc(hawk_sed_getgem(sed)); }
static HAWK_INLINE const hawk_bch_t* hawk_sed_geterrbmsg (hawk_sed_t* sed) { return hawk_gem_geterrbmsg(hawk_sed_getgem(sed)); }
static HAWK_INLINE const hawk_uch_t* hawk_sed_geterrumsg (hawk_sed_t* sed) { return hawk_gem_geterrumsg(hawk_sed_getgem(sed)); }
static HAWK_INLINE void hawk_sed_geterrbinf (hawk_sed_t* sed, hawk_errbinf_t* errinf) { return hawk_gem_geterrbinf(hawk_sed_getgem(sed), errinf); }
static HAWK_INLINE void hawk_sed_geterruinf (hawk_sed_t* sed, hawk_erruinf_t* errinf) { return hawk_gem_geterruinf(hawk_sed_getgem(sed), errinf); }
static HAWK_INLINE void hawk_sed_geterror (hawk_sed_t* sed, hawk_errnum_t* errnum, const hawk_ooch_t** errmsg, hawk_loc_t* errloc) { return hawk_gem_geterror(hawk_sed_getgem(sed), errnum, errmsg, errloc); }
#else
#define hawk_sed_geterrnum(sed) hawk_gem_geterrnum(hawk_sed_getgem(sed))
#define hawk_sed_geterrloc(sed) hawk_gem_geterrloc(hawk_sed_getgem(sed))
#define hawk_sed_geterrbmsg(sed) hawk_gem_geterrbmsg(hawk_sed_getgem(sed))
#define hawk_sed_geterrumsg(sed) hawk_gem_geterrumsg(hawk_sed_getgem(sed))
#define hawk_sed_geterrbinf(sed, errinf) (hawk_gem_geterrbinf(hawk_sed_getgem(sed), errinf))
#define hawk_sed_geterruinf(sed, errinf) (hawk_gem_geterruinf(hawk_sed_getgem(sed), errinf))
#define hawk_sed_geterror(sed, errnum, errmsg, errloc) (hawk_gem_geterror(hawk_sed_getgem(sed), errnum, errmsg, errloc))
#endif

#if defined(HAWK_OOCH_IS_BCH)
#	define hawk_sed_geterrmsg hawk_sed_geterrbmsg
#	define hawk_sed_geterrinf hawk_sed_geterrbinf
#else
#	define hawk_sed_geterrmsg hawk_sed_geterrumsg
#	define hawk_sed_geterrinf hawk_sed_geterruinf
#endif


/**
 * The hawk_sed_seterrnum() function sets the error information omitting
 * error location. You must pass a non-NULL for \a errarg if the specified
 * error number \a errnum requires one or more arguments to format an
 * error message.
 */
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE void hawk_sed_seterrnum (hawk_sed_t* sed, const hawk_loc_t* errloc, hawk_errnum_t errnum) { hawk_gem_seterrnum (hawk_sed_getgem(sed), errloc, errnum); }
static HAWK_INLINE void hawk_sed_seterrinf (hawk_sed_t* sed, const hawk_errinf_t* errinf) { hawk_gem_seterrinf (hawk_sed_getgem(sed), errinf); }
static HAWK_INLINE void hawk_sed_seterror (hawk_sed_t* sed, const hawk_loc_t*  errloc, hawk_errnum_t errnum, const hawk_oocs_t* errarg) { hawk_gem_seterror(hawk_sed_getgem(sed), errloc, errnum, errarg); }
static HAWK_INLINE const hawk_ooch_t* hawk_sed_backuperrmsg (hawk_sed_t* sed) { return hawk_gem_backuperrmsg(hawk_sed_getgem(sed)); }
#else
#define hawk_sed_seterrnum(sed, errloc, errnum) hawk_gem_seterrnum(hawk_sed_getgem(sed), errloc, errnum)
#define hawk_sed_seterrinf(sed, errinf) hawk_gem_seterrinf(hawk_sed_getgem(sed), errinf)
#define hawk_sed_seterror(sed, errloc, errnum, errarg) hawk_gem_seterror(hawk_sed_getgem(sed), errloc, errnum, errarg)
#define hawk_sed_backuperrmsg(sed) hawk_gem_backuperrmsg(hawk_sed_getgem(sed))
#endif


HAWK_EXPORT void hawk_sed_seterrbfmt (
	hawk_sed_t*       sed,
	const hawk_loc_t* errloc,
	hawk_errnum_t     errnum,
	const hawk_bch_t* fmt,
	...
);

HAWK_EXPORT void hawk_sed_seterrufmt (
	hawk_sed_t*       sed,
	const hawk_loc_t* errloc,
	hawk_errnum_t     errnum,
	const hawk_uch_t* fmt,
	...
);


HAWK_EXPORT void hawk_sed_seterrbvfmt (
	hawk_sed_t*         sed,
	const hawk_loc_t*   errloc,
	hawk_errnum_t       errnum,
	const hawk_bch_t*   errfmt,
	va_list             ap
);

HAWK_EXPORT void hawk_sed_seterruvfmt (
	hawk_sed_t*         sed,
	const hawk_loc_t*   errloc,
	hawk_errnum_t       errnum,
	const hawk_uch_t*   errfmt,
	va_list             ap
);


HAWK_EXPORT void hawk_sed_killecb (
	hawk_sed_t*     sed,
	hawk_sed_ecb_t* ecb
);

/**
 * The hawk_sed_popecb() function pops an sed event callback set
 * and returns the pointer to it. If no callback set can be popped,
 * it returns #HAWK_NULL.
 */
HAWK_EXPORT hawk_sed_ecb_t* hawk_sed_popecb (
	hawk_sed_t* sed
);

/**
 * The hawk_sed_pushecb() function register a runtime callback set.
 */
HAWK_EXPORT void hawk_sed_pushecb (
	hawk_sed_t*     sed,
	hawk_sed_ecb_t* ecb
);

/**
 * The hawk_sed_comp() function compiles editing commands into an internal form.
 * \return 0 on success, -1 on error
 */
HAWK_EXPORT int hawk_sed_comp (
	hawk_sed_t*        sed, /**< stream editor */
	hawk_sed_io_impl_t  inf  /**< script stream reader */
);

/**
 * The hawk_sed_exec() function executes the compiled commands.
 * \return 0 on success, -1 on error
 */
HAWK_EXPORT int hawk_sed_exec (
	hawk_sed_t*        sed,  /**< stream editor */
	hawk_sed_io_impl_t  inf,  /**< stream reader */
	hawk_sed_io_impl_t  outf  /**< stream writer */
);

/**
 * The hawk_sed_halt() function breaks running loop in hawk_sed_exec().
 * It doesn't affect blocking calls in stream handlers.
 */
HAWK_EXPORT void hawk_sed_halt (
	hawk_sed_t* sed   /**< stream editor */
);

/**
 * The hawk_sed_ishalt() functions tests if hawk_sed_halt() is called.
 */
HAWK_EXPORT int hawk_sed_ishalt (
	hawk_sed_t* sed   /**< stream editor */
);

/**
 * The hawk_sed_getcompid() function returns the latest
 * identifier successfully set with hawk_sed_setcompid().
 */
HAWK_EXPORT const hawk_ooch_t* hawk_sed_getcompid (
	hawk_sed_t* sed
);

/**
 * The hawk_sed_setcompid() functions duplicates a string
 * pointed to by \a id and stores it internally to identify
 * the script currently being compiled. The lid field of the
 * current command being compiled in the script is set to the
 * lastest identifer successfully set with this function.
 * If this function fails, the location set in the command
 * may be wrong.
 */
#if defined(HAWK_OOCH_IS_BCH)
#define hawk_sed_setcompid(sed, id) hawk_sed_setcompidwithbcstr(sed, id)
#else
#define hawk_sed_setcompid(sed, id) hawk_sed_setcompidwithucstr(sed, id)
#endif

HAWK_EXPORT const hawk_ooch_t* hawk_sed_setcompidwithbcstr (
	hawk_sed_t*        sed,
	const hawk_bch_t*  id
);

HAWK_EXPORT const hawk_ooch_t* hawk_sed_setcompidwithucstr (
	hawk_sed_t*        sed,
	const hawk_uch_t*  id
);

/**
 * The hawk_sed_getlinnum() function gets the current input line number.
 * \return current input line number
 */
HAWK_EXPORT hawk_oow_t hawk_sed_getlinenum (
	hawk_sed_t* sed /**< stream editor */
);

/**
 * The hawk_sed_setlinenum() function changes the current input line number.
 */
HAWK_EXPORT void hawk_sed_setlinenum (
	hawk_sed_t* sed,   /**< stream editor */
	hawk_oow_t  num    /**< a line number */
);

/**
 * The hawk_sed_getspace() function gets the pointer and the length
 * to a buffer space specfied by \a space.
 */
HAWK_EXPORT void hawk_sed_getspace (
	hawk_sed_t*      sed,
	hawk_sed_space_t space,
	hawk_oocs_t*     str
);

/**
 * The hawk_sed_allocmem() function allocates a chunk of memory using
 * the memory manager of \a sed.
 */
HAWK_EXPORT void* hawk_sed_allocmem (
	hawk_sed_t* sed,
	hawk_oow_t  size
);

/**
 * The hawk_sed_callocmem() function allocates a chunk of memory using
 * the memory manager of \a sed and clears it to zeros.
 */
HAWK_EXPORT void* hawk_sed_callocmem (
	hawk_sed_t* sed,
	hawk_oow_t  size
);

/**
 * The hawk_sed_reallocmem() function reallocates a chunk of memory using
 * the memory manager of \a sed.
 */
HAWK_EXPORT void* hawk_sed_reallocmem (
	hawk_sed_t* sed,
	void*       ptr,
	hawk_oow_t  size
);

/**
 * The hawk_sed_freemem() function frees a chunk of memory using
 * the memory manager of \a sed.
 */
HAWK_EXPORT void hawk_sed_freemem (
	hawk_sed_t* sed,
	void*       ptr
);

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

/**
 * The hawk_sed_openstd() function creates a stream editor with the default
 * memory manager and initializes it.
 * \return pointer to a stream editor on success, #HAWK_NULL on failure.
 */
HAWK_EXPORT hawk_sed_t* hawk_sed_openstd (
	hawk_oow_t     xtnsize,  /**< extension size in bytes */
	hawk_errinf_t* errinf
);

/**
 * The hawk_sed_openstdwithmmgr() function creates a stream editor with a
 * user-defined memory manager. It is equivalent to hawk_sed_openstd(),
 * except that you can specify your own memory manager.
 * \return pointer to a stream editor on success, #HAWK_NULL on failure.
 */
HAWK_EXPORT hawk_sed_t* hawk_sed_openstdwithmmgr (
	hawk_mmgr_t*   mmgr,     /**< memory manager */
	hawk_oow_t     xtnsize,  /**< extension size in bytes */
	hawk_cmgr_t*   cmgr,
	hawk_errinf_t* errinf
);

/**
 * The hawk_sed_compstd() function compiles sed scripts specified in
 * an array of stream resources. The end of the array is indicated
 * by an element whose type is #HAWK_SED_IOSTD_NULL. However, the type
 * of the first element shall not be #HAWK_SED_IOSTD_NULL. The output
 * parameter \a count is set to the count of stream resources
 * opened on both success and failure. You can pass #HAWK_NULL to \a
 * count if the count is not needed.
 *
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_compstd (
	hawk_sed_t*        sed,  /**< stream editor */
	hawk_sed_iostd_t   in[], /**< input scripts */
	hawk_oow_t*        count /**< number of input scripts opened */
);

/**
 * The hawk_sed_compstdfile() function compiles a sed script from
 * a single file \a infile.  If \a infile is #HAWK_NULL, it reads
 * the script from the standard input.
 * When #HAWK_OOCH_IS_UCH is defined, it converts the multibyte
 * sequences in the file \a infile to wide characters via the
 * #hawk_cmgr_t interface \a cmgr. If \a cmgr is #HAWK_NULL, it uses
 * the default interface. It calls cmgr->mbtowc() for conversion.
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_compstdfile (
	hawk_sed_t*        sed,
	const hawk_ooch_t* infile,
	hawk_cmgr_t*       cmgr
);

HAWK_EXPORT int hawk_sed_compstdfileb (
	hawk_sed_t*        sed,
	const hawk_bch_t*  infile,
	hawk_cmgr_t*       cmgr
);

HAWK_EXPORT int hawk_sed_compstdfileu (
	hawk_sed_t*        sed,
	const hawk_uch_t*  infile,
	hawk_cmgr_t*       cmgr
);

/**
 * The hawk_sed_compstdoocstr() function compiles a sed script stored
 * in a null-terminated string pointed to by \a script.
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_compstdoocstr (
	hawk_sed_t*        sed,
	const hawk_ooch_t* script
);

/**
 * The hawk_sed_compstdoocs() function compiles a sed script of the
 * length \a script->len pointed to by \a script->ptr.
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_compstdoocs (
	hawk_sed_t*        sed,
	const hawk_oocs_t* script
);

/**
 * The hawk_sed_execstd() function executes a compiled script
 * over input streams \a in and an output stream \a out.
 *
 * If \a in is not #HAWK_NULL, it must point to an array of stream
 * resources whose end is indicated by an element with #HAWK_SED_IOSTD_NULL
 * type. However, the type of the first element \a in[0].type show not
 * be #HAWK_SED_IOSTD_NULL. It requires at least 1 valid resource to be
 * included in the array.
 *
 * If \a in is #HAWK_NULL, the standard console input is used.
 * If \a out is #HAWK_NULL, the standard console output is used.
 *
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_execstd (
	hawk_sed_t*       sed,
	hawk_sed_iostd_t  in[],
	hawk_sed_iostd_t* out
);

/**
 * The hawk_sed_execstdfile() function executes a compiled script
 * over a single input file \a infile and a single output file \a
 * outfile.
 *
 * If \a infile is #HAWK_NULL, the standard console input is used.
 * If \a outfile is #HAWK_NULL, the standard console output is used.
 *
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_execstdfile (
	hawk_sed_t*        sed,
	const hawk_ooch_t* infile,
	const hawk_ooch_t* outfile,
	hawk_cmgr_t*       cmgr
);


/**
 * The hawk_sed_execstdfile() function executes a compiled script
 * over a single input string \a instr and a dynamically allocated buffer.
 * It copies the buffer pointer and length to the location pointed to
 * by \a outstr on success. The buffer pointer copied to \a outstr
 * must be released with hawk_sed_freemem().
 *
 * \return 0 on success, -1 on failure
 */
HAWK_EXPORT int hawk_sed_execstdxstr (
	hawk_sed_t*        sed,
	const hawk_oocs_t* instr,
	hawk_oocs_t*       outstr,
	hawk_cmgr_t*       cmgr
);

#if defined(__cplusplus)
}
#endif

#endif
