

#define WRAP_INVALID_FHINFO	(-1ull)
#define WRAP_MAX_PATH		(1024+512)
#define WRAP_MAX_NAME		256
#define WRAP_MAX_ENV		100
#define WRAP_MAX_FILE		100
#define WRAP_MAX_COMMAND	(20*1024)
#define WRAP_MAX_O_OPTION	100


/* forward */
struct wrap_ccb;

/*
 * MAIN helpers
 ****************************************************************
 */

extern int	wrap_main (int ac, char *av[], struct wrap_ccb *wccb);
extern int	wrap_main_start_index_file (struct wrap_ccb *wccb);
extern int	wrap_main_start_image_file (struct wrap_ccb *wccb);

extern void	wrap_log (struct wrap_ccb *wccb, char *fmt, ...);

extern int	wrap_set_error (struct wrap_ccb *wccb, int error);




/*
 * Command Execution
 ****************************************************************
 *
 * This wrapper will spawn (fork/exec) a subprocess to do
 * the real work. These help form the sh(1) command line,
 * create the pipe fittings, and spawn the subprocess.
 *
 * The fdmap[3] corresponds to the subprocess stdin, stdout,
 * and stderr. A value >= 0 is assumed to be an inherited
 * file descriptor. A value < 0 is one of the special codes
 * WRAP_FDMAP_xxx. On return, the _PIPE special codes are
 * replaced with the file descriptor for the parent process
 * end of the pipe.
 */

#define WRAP_FDMAP_INPUT_PIPE	-2	/* input to child, parent writes */
#define WRAP_FDMAP_OUTPUT_PIPE	-3	/* output from child, parent reads */
#define WRAP_FDMAP_DEV_NULL	-4	/* /dev/null */

extern int	wrap_cmd_add_with_escapes (char *cmd, char *word,
				char *special);
extern int	wrap_cmd_add_with_sh_escapes (char *cmd, char *word);
extern int	wrap_cmd_add_allow_file_wildcards (char *cmd, char *word);
extern int	wrap_pipe_fork_exec (char *cmd, int fdmap[3]);




/*
 * CCB -- Command Control Block
 ****************************************************************
 *
 * A digested form of command line arguments
 */

enum wrap_ccb_op {
	WRAP_CCB_OP_NONE = 0,
	WRAP_CCB_OP_BACKUP = 1,			/* -c */
	WRAP_CCB_OP_RECOVER = 2,		/* -x */
	WRAP_CCB_OP_RECOVER_FILEHIST = 3	/* -t */
};

struct wrap_env {
	char *		name;
	char *		value;
};

struct wrap_file {
	unsigned long long	fhinfo;
	char *			original_name;	/* relative to backup root */
	char *			save_to_name;	/* relative to file system */
};

struct wrap_ccb {
	int			error;
	int			log_seq_num;
	char			errmsg[WRAP_MAX_NAME];

	/* Raw arguments */
	char *			B_butype;		/* -B TYPE */
	int			d_debug;		/* -d N */
	struct wrap_env		env[WRAP_MAX_ENV];	/* -E NAME=VALUE */
	int			n_env;
	char *			f_file_name;		/* -f FILE */
	char *			I_index_file_name;	/* -I FILE */
	char *			o_option[WRAP_MAX_O_OPTION]; /* -o OPTION */
	int			n_o_option;

	struct wrap_file	file[WRAP_MAX_FILE];	/* recovery only */
	int			n_file;

	/* derived from arguments */
	char *			progname;
	enum wrap_ccb_op	op;
	FILE *			index_fp;
	int			data_conn_fd;

	/* Common interprettations of the env */
	int			hist_enable;
	int			direct_enable;
	char *			backup_root;

	/*
	 * Recovery variables.
	 *
	 * All offset/length pairs refer to a portion of the
	 * backup image.
	 *
	 * have		The portion currently in the buffer.
	 * want		The portion wanted by the formatter
	 *		(e.g. tar, dump).
	 * reading	The portion immediately after what we
	 *		"have" (in the buffer) still coming due
	 *		to the last NDMP_NOTIFY_DATA_READ.
	 * last_read	The portion requested by the last
	 *		NDMP_NOTIFY_DATA_READ.
	 * expect	Composite of have and reading.
	 */
	char *			iobuf;
	unsigned long		n_iobuf;

	char *			have;
	unsigned long long	have_offset;
	unsigned long		have_length;	/* never bigger than iobuf */
	unsigned long long	want_offset;
	unsigned long long	want_length;
	unsigned long long	reading_offset;
	unsigned long long	reading_length;
	unsigned long long	last_read_offset;
	unsigned long long	last_read_length;
	unsigned long long	expect_offset;
	unsigned long long	expect_length;
	int			data_conn_mode;
};

extern int	wrap_process_args (int argc, char *argv[],
			struct wrap_ccb *wccb);
extern char *	wrap_find_env (struct wrap_ccb *wccb, char *name);


extern int	wrap_reco_seek (struct wrap_ccb *wccb,
			unsigned long long want_offset,
			unsigned long long want_length,
			unsigned long must_have_length);
extern int	wrap_reco_must_have (struct wrap_ccb *wccb,
			unsigned long length);
extern int	wrap_reco_pass (struct wrap_ccb *wccb, int write_fd,
			unsigned long long length, unsigned write_bsize);
extern int	wrap_reco_align_to_wanted (struct wrap_ccb *wccb);
extern int	wrap_reco_receive (struct wrap_ccb *wccb);
extern int	wrap_reco_consume (struct wrap_ccb *wccb,
			unsigned long length);
extern int	wrap_reco_issue_read (struct wrap_ccb *wccb);





/*
 * WRAP Messages
 ****************************************************************
 *
 * A message is simply one text line following a format.
 * These structures are used to buffer incoming messages.
 */

struct wrap_log_message {				/* Lx */
	char			message[WRAP_MAX_PATH];
};

enum wrap_ftype {
	WRAP_FTYPE_INVALID = 0,
	WRAP_FTYPE_DIR = 1,		/* d */
	WRAP_FTYPE_FIFO = 2,		/* p */
	WRAP_FTYPE_CSPEC = 3,		/* c */
	WRAP_FTYPE_BSPEC = 4,		/* b */
	WRAP_FTYPE_REG = 5,		/* - */
	WRAP_FTYPE_SLINK = 6,		/* l */
	WRAP_FTYPE_SOCK = 7,		/* s */
	WRAP_FTYPE_REGISTRY = 8,	/* R */
	WRAP_FTYPE_OTHER = 9		/* o */
};

struct wrap_fstat {
	unsigned long		valid;
#define WRAP_FSTAT_VALID_FTYPE		(1ul<<0u)
#define WRAP_FSTAT_VALID_MODE		(1ul<<1u)
#define WRAP_FSTAT_VALID_LINKS		(1ul<<2u)
#define WRAP_FSTAT_VALID_SIZE		(1ul<<3u)
#define WRAP_FSTAT_VALID_UID		(1ul<<4u)
#define WRAP_FSTAT_VALID_GID		(1ul<<5u)
#define WRAP_FSTAT_VALID_ATIME		(1ul<<6u)
#define WRAP_FSTAT_VALID_MTIME		(1ul<<7u)
#define WRAP_FSTAT_VALID_CTIME		(1ul<<8u)
#define WRAP_FSTAT_VALID_FILENO		(1ul<<9u)

	enum wrap_ftype		ftype;		/* f%s */
	unsigned short		mode;		/* m%04o */
	unsigned long		links;		/* l%lu */
	unsigned long long	size;		/* s%llu */
	unsigned long		uid;		/* u%lu */
	unsigned long		gid;		/* g%lu */
	unsigned long		atime;		/* ta%lu */
	unsigned long		mtime;		/* tm%lu */
	unsigned long		ctime;		/* tc%lu */
	unsigned long long	fileno;		/* i%llu */
};

/*
 * HF path [@fhinfo] [stats]
 *
 * History File -- Corresponds to NDMPv?_FH_ADD_FILE
 */
struct wrap_add_file {
	unsigned long long	fhinfo;		/* @%llu */
	struct wrap_fstat	fstat;
	char			path[WRAP_MAX_PATH];
};

/*
 * HD dir_fileno name fileno [@fhinfo]
 *
 * History Directory entry -- Corresponds to NDMPv?_FH_ADD_DIR
 */
struct wrap_add_dirent {
	unsigned long long	fhinfo;		/* @%llu */
	unsigned long long	dir_fileno;	/* %llu */
	unsigned long long	fileno;		/* %llu */
	char			name[WRAP_MAX_NAME];
};

/*
 * HN [@fhinfo] [stats] -- iFILENO must be present
 *
 * History Node -- Corresponds to NDMPv?_FH_ADD_NODE
 */
struct wrap_add_node {					/* HN */
	unsigned long long	fhinfo;		/* @%llu */
	struct wrap_fstat	fstat;
};

/*
 * DE name value
 *
 * Data Env -- Corresponds to NDMPv?_DATA_GET_ENV
 * This is used for the post backup processing env[].
 */
struct wrap_add_env {
	char			name[WRAP_MAX_NAME];
	char			value[WRAP_MAX_PATH];
};

/*
 * DR offset length
 *
 * Data Read -- Corresponds to NDMPv?_NOTIFY_DATA_READ
 * This is used during recovery operations to retrieve
 * portions of the backup image.
 */
struct wrap_data_read {					/* DR */
	unsigned long long	offset;		/* %llu */
	unsigned long long	length;		/* %llu */
};

/*
 * DS s{r|d|f} [wN] [etN] [ebN]
 *
 * Data Stats -- Supplemental info for NDMPv?_DATA_GET_STATE
 * Sent periodically to update certain fields of the
 * DATA_GET_STATE reply.
 */

enum wrap_data_status {
	WRAP_DS_INVALID = 0,
	WRAP_DS_RUNNING = 1,			/* sr */
	WRAP_DS_DONE_OK = 2,			/* sd */
	WRAP_DS_DONE_FAILED = 3			/* sf */
};

struct wrap_data_stats {				/* DS */
	unsigned long		valid;
#define WRAP_DATASTATS_VALID_BYTES_WRITTEN	(1ul<<0u)
#define WRAP_DATASTATS_VALID_EST_TIME_REMAINING	(1ul<<1u)
#define WRAP_DATASTATS_VALID_EST_BYTES_REMAINING (1ul<<2u)

	enum wrap_data_status	status;			/* s{r|d|f} */
	unsigned long long	bytes_written;		/* w%llu */
	unsigned long long	est_time_remaining;	/* et%llu */
	unsigned long long	est_bytes_remaining;	/* eb%llu */
};

/*
 * RR errno path
 *
 * Recovery Result -- Corresponds to NDMPv?_LOG_FILE
 * Sent during recovery operations to report the
 * success or failure of recovery.
 */
struct wrap_recovery_result {				/* RR */
	int			rr_errno;	/* sys/errno.h */
	char			path[WRAP_MAX_PATH];
};

enum wrap_msg_type {
	WRAP_MSGTYPE_LOG_MESSAGE = 1,
	WRAP_MSGTYPE_ADD_FILE = 2,
	WRAP_MSGTYPE_ADD_DIRENT = 3,
	WRAP_MSGTYPE_ADD_NODE = 4,
	WRAP_MSGTYPE_ADD_ENV = 5,
	WRAP_MSGTYPE_DATA_READ = 6,
	WRAP_MSGTYPE_DATA_STATS = 7,
	WRAP_MSGTYPE_RECOVERY_RESULT = 8,
};

struct wrap_msg_buf {
	enum wrap_msg_type	msg_type;
	union {
	  struct wrap_log_message	log_message;
	  struct wrap_add_file		add_file;
	  struct wrap_add_dirent	add_dirent;
	  struct wrap_add_node		add_node;
	  struct wrap_add_env		add_env;
	  struct wrap_data_read		data_read;
	  struct wrap_data_stats	data_stats;
	  struct wrap_recovery_result	recovery_result;
	} body;
};

extern int wrap_parse_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_parse_log_message_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_send_log_message (FILE *fp, char *message);
extern int wrap_parse_add_file_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_send_add_file (FILE *fp, char *path, unsigned long long fhinfo,
				struct wrap_fstat *fstat);
extern int wrap_parse_add_dirent_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_send_add_dirent (FILE *fp, char *name,
				unsigned long long fhinfo,
				unsigned long long dir_fileno,
				unsigned long long fileno);
extern int wrap_parse_add_node_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_send_add_node (FILE *fp, unsigned long long fhinfo,
				struct wrap_fstat *fstat);
extern int wrap_parse_fstat_subr (char **scanp, struct wrap_fstat *fstat);
extern int wrap_send_fstat_subr (FILE *fp, struct wrap_fstat *fstat);
extern int wrap_parse_add_env_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_send_add_env (FILE *fp, char *name, char *value);
extern int wrap_parse_data_read_msg (char *buf, struct wrap_msg_buf *wmsg);
extern int wrap_send_data_read (FILE *fp, unsigned long long offset,
				unsigned long long length);




/*
 * Canonical strings
 ****************************************************************
 * Convert strings to/from HTTP-like canonical strings (%xx).
 * Example "a b%c" --> "a%20b%25c"
 */

#define NDMCSTR_WARN	'%'
extern int	wrap_cstr_from_str (char *src, char *dst, unsigned dst_max);
extern int	wrap_cstr_to_str (char *src, char *dst, unsigned dst_max);
extern int	wrap_cstr_from_hex (int c);
