#ifndef MAWK_VIO_H
#define MAWK_VIO_H
/* virtual I/O */
typedef enum mawk_vio_open_mode_e {
	MAWK_VIO_O_TRUNC,
	MAWK_VIO_O_APPEND,
	MAWK_VIO_I
} mawk_vio_open_mode_t;

/**** init-type entry points: used to create a new vio or do a one-time-thing
      without an existing vio; MAWK->vio_init.* point to the defaults
      but the application is free to create a vf using a different
      implementation directly ****/
/* open a normal file */
typedef mawk_vio_t *mawk_vio_open_t(mawk_state_t *MAWK, const char *name, mawk_vio_open_mode_t mode);

/* open and close pipe (background process) */
typedef mawk_vio_t *mawk_vio_open_pipe_t(mawk_state_t *MAWK, const char *name, int type);

/* execute a shell command (system()-like wrapper around execl()) */
typedef void mawk_vio_exec_shell_t(mawk_state_t *MAWK, const char *cmd);


/**** regular vio calls on existing vios - vf->imp points to these ****/

/* write data to a vio * */
typedef int mawk_vio_putc_t(mawk_state_t *MAWK, mawk_vio_t *vf, char c);
typedef int mawk_vio_write_str_t(mawk_state_t *MAWK, mawk_vio_t *vf, const char *str);
typedef int mawk_vio_write_t(mawk_state_t *MAWK, mawk_vio_t *vf, const char *data, int len);
typedef int mawk_vio_printf_t(mawk_state_t *MAWK, mawk_vio_t *vf, const char *fmt, ...);

/* return -2 if there's no more input at the moment */
typedef int mawk_vio_read_t(mawk_state_t *MAWK, mawk_vio_t *f, char *dst, long int size);


typedef int mawk_vio_close_t(mawk_state_t *MAWK, mawk_vio_t *vf);
typedef int mawk_vio_flush_t(mawk_state_t *MAWK, mawk_vio_t *vf);

/* check the error state of a file node (ferror()-style) */
typedef int mawk_vio_error_t(mawk_state_t *MAWK, mawk_vio_t *vf);

/* mark the file no-close; this is useful for working on files that are
   not really open by the vio implementation, for example stdout/stderr
   in vio_orig. */
typedef void mawk_vio_mark_no_close_t(mawk_state_t *MAWK, mawk_vio_t *vf);

/* stdio hacks */

/* per file hooks */
typedef struct mawk_vio_imp_s {
	mawk_vio_putc_t           *vputc;
	mawk_vio_write_str_t      *vwrite_str;
	mawk_vio_write_t          *vwrite;
	mawk_vio_printf_t         *vprintf;
	mawk_vio_read_t           *vread;
	mawk_vio_close_t          *vclose;
	mawk_vio_flush_t          *vflush;
	mawk_vio_error_t          *error;
	mawk_vio_mark_no_close_t  *mark_no_close;
} mawk_vio_imp_t;

/* per file struct */
struct mawk_vio_s {
	/* common header */
	const mawk_vio_imp_t *imp;
	int refco; /* how many times this vf is open (and referenced from the FILE_NODEs) */
	/* implementations may have further fields here, never depend on sizeof(mawk_vio_t)! */
};

/* per script instance hooks */
typedef struct mawk_vio_init_s {
	mawk_vio_open_t          *vopen;
	mawk_vio_open_pipe_t     *vopen_pipe;
	mawk_vio_exec_shell_t    *exec_shell;
	int inited;
} mawk_vio_init_t;

#define mawk_vio_putc(MAWK, vf, c)              (vf)->imp->vputc(MAWK, (vf), (c))
#define mawk_vio_write_str(MAWK, vf, str)       (vf)->imp->vwrite_str(MAWK, (vf), (str))
#define mawk_vio_write(MAWK, vf, data, len)     (vf)->imp->vwrite(MAWK, (vf), (data), (len))
/* can not be implemented without C99 vararg macros:
 #define mawk_vio_printf_t(MAWK, vf, const char *fmt, ...); */
#define mawk_vio_read(MAWK, vf, dst, size)      (vf)->imp->vread(MAWK, (vf), (dst), (size))
#define mawk_vio_close(MAWK, vf)                ((((vf) == NULL) || ((vf)->imp->vclose == NULL)) ? (-1) : ((vf)->imp->vclose(MAWK, (vf))))
#define mawk_vio_flush(MAWK, vf)                (vf)->imp->vflush(MAWK, (vf))
#define mawk_vio_error(MAWK, vf)                (vf)->imp->error(MAWK, (vf))

/* default init (entry) hooks */
#define mawk_vio_open(MAWK, name, mode)            MAWK->vio_init.vopen(MAWK, (name), (mode))
#define mawk_vio_open_pipe(MAWK, name, type)       MAWK->vio_init.vopen_pipe(MAWK, (name), (type))
#define mawk_vio_exec_shell(MAWK, cmd)             MAWK->vio_init.exec_shell(MAWK, (cmd))
#define mawk_vio_setup_stdouts(MAWK)               MAWK->vio_init.mawk_vio_setup_stdouts(MAWK)

#endif
