/*
 * Copyright (c) 1998,1999,2000,2002
 *	Traakan, Inc., Los Altos, CA
 *	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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 */

/*
 * Project:  NDMJOB
 * Ident:    $Id: $
 *
 * Description:
 *
 */


#ifndef _NDMLIB_H_
#define _NDMLIB_H_

#include "ndmos.h"

#include "ndmprotocol.h"
#include "ndmp_msg_buf.h"
#include "ndmp_translate.h"

/* Probably unnecessary, yet prudent. Compilers/debuggers sometimes goof. */
#ifndef NDM_FLAG_DECL
#define NDM_FLAG_DECL(XXX) unsigned XXX : 1;
#endif /* !NDM_FLAG_DECL */

/* boring forward reference stuff */
struct ndmagent;




/*
 * NDMLOG
 ****************************************************************
 *
 * ndmlog is a simple abstraction for log messages.
 * Each log entry has:
 *   - a tag, which is a short string indicating origin or purpose
 *   - a level between 0-9, the higher the value the greater the detail
 *   - a message
 * The application will typically direct log messages to a file.
 * Yet, logging directly to a FILE tends to be restrictive. Hence
 * this abstraction.
 *
 * The time stamp is relative to the start time, and has millisecond
 * granularity.
 */

struct ndmlog {
	void	(*deliver)(struct ndmlog *log, char *tag, int lev, char *msg);
	void *	cookie;
};
extern char *	ndmlog_time_stamp (void);
extern void	ndmlogf (struct ndmlog *log, char *tag,
					int level, char *fmt, ...);
extern void	ndmlogfv (struct ndmlog *log, char *tag,
					int level, char *fmt, va_list ap);




/*
 * NDMNMB -- NDMP Message Buffer
 ****************************************************************
 *
 * The ndmnmb routines are trivial aids for handling
 * NMB (NDMP Messsage Buffer). ndmp_msg_buf is defined in
 * ndmp_msg_buf.h, and pretty much amounts to a huge
 * union of all NDMP request and reply types.
 */

extern xdrproc_t	ndmnmb_find_xdrproc (struct ndmp_msg_buf *nmb);
extern void		ndmnmb_free (struct ndmp_msg_buf *nmb);
extern void		ndmnmb_snoop (struct ndmlog *log, char *tag, int level,
				struct ndmp_msg_buf *nmb, char *whence);
extern unsigned		ndmnmb_get_reply_error_raw (struct ndmp_msg_buf *nmb);
extern ndmp9_error	ndmnmb_get_reply_error (struct ndmp_msg_buf *nmb);
extern int		ndmnmb_set_reply_error_raw (struct ndmp_msg_buf *nmb,
				unsigned raw_error);
extern int		ndmnmb_set_reply_error (struct ndmp_msg_buf *nmb,
				ndmp9_error error);







/*
 * NDMCHAN -- Async I/O channel
 ****************************************************************
 *
 * ndmchan is a wrapper around I/O channels, and is used
 * to juggle (manage) multiple I/O activities at one time.
 * The data buffer is used linearly. beg_ix and end_ix
 * bracket the valid data. When the end of the buffer is reached,
 * the remaining valid data is moved to the begining.
 */
struct ndmchan {
	char *		name;		/* short name, helps debugging */

	char		mode;		/* NDMCHAN_MODE_... (see below) */

	NDM_FLAG_DECL(check)		/* Want select()/poll() to check */
	NDM_FLAG_DECL(ready)		/* select()/poll() indicates ready */
	NDM_FLAG_DECL(eof)		/* eof pending upon n_ready()==0 */
	NDM_FLAG_DECL(error)		/* error (channel shutdown) */

	int		fd;		/* der eff dee */
	int		saved_errno;	/* errno captured if ->error occurs */

	unsigned	beg_ix;		/* relative to ->data */
	unsigned	end_ix;		/* relative to ->data */
	char *		data;		/* data buffer (READ/WRITE/RESIDENT) */
	unsigned	data_size;	/* size of data buffer */
};
#define NDMCHAN_MODE_IDLE	0	/* not doing anything */
#define NDMCHAN_MODE_RESIDENT	1	/* resident, within this process */
#define NDMCHAN_MODE_READ	2	/* read from ->fd into ->data */
#define NDMCHAN_MODE_WRITE	3	/* write to ->fd from ->data */
#define NDMCHAN_MODE_READCHK	4	/* check ->fd readable, no ->data */
#define NDMCHAN_MODE_LISTEN	5	/* ->fd listen()ing */
#define NDMCHAN_MODE_PENDING	6	/* ->fd and ->data ready */
#define NDMCHAN_MODE_CLOSED	7	/* ->fd closed */

enum ndmchan_read_interpretation {
	NDMCHAN_RI_EMPTY = 10,	/* no data, might be more coming */
	NDMCHAN_RI_READY,	/* data ready */
	NDMCHAN_RI_READY_FULL,	/* data ready, no more until consumed */
	NDMCHAN_RI_DRAIN_EOF,	/* data ready, DONE_EOF after consumed */
	NDMCHAN_RI_DRAIN_ERROR,	/* data ready, DONE_ERROR after consumed */
	NDMCHAN_RI_DONE_EOF,	/* no data, no more coming, normal EOF */
	NDMCHAN_RI_DONE_ERROR,	/* no data, no more coming, something wrong */
	NDMCHAN_RI_FAULT,	/* crazy request */
};

enum ndmchan_write_interpretation {
	NDMCHAN_WI_FULL = 30,	/* no buffer, no more until some sent */
	NDMCHAN_WI_AVAIL,	/* buffer ready, sending in progress */
	NDMCHAN_WI_AVAIL_EMPTY,	/* buffer ready, done sending */
	NDMCHAN_WI_DRAIN_EOF,	/* no more buffer, DONE_EOF after sent */
	NDMCHAN_WI_DRAIN_ERROR,	/* no more buffer, DONE_ERROR after sent */
	NDMCHAN_WI_DONE_EOF,	/* no more buffer, done sending, normal EOF */
	NDMCHAN_WI_DONE_ERROR,	/* no more buffer, done sending, went wrong */
	NDMCHAN_WI_FAULT,	/* crazy request */
};

extern void	ndmchan_initialize (struct ndmchan *ch, char *name);
extern int	ndmchan_setbuf (struct ndmchan *ch, char *data,
			unsigned data_size);
extern int	ndmchan_start_mode (struct ndmchan *ch, int fd, int chan_mode);
extern int	ndmchan_start_read (struct ndmchan *ch, int fd);
extern int	ndmchan_start_write (struct ndmchan *ch, int fd);
extern int	ndmchan_start_readchk (struct ndmchan *ch, int fd);
extern int	ndmchan_start_listen (struct ndmchan *ch, int fd);
extern int	ndmchan_start_resident (struct ndmchan *ch);
extern int	ndmchan_start_pending (struct ndmchan *ch, int fd);
extern int	ndmchan_pending_to_mode (struct ndmchan *ch, int chan_mode);
extern int	ndmchan_pending_to_read (struct ndmchan *ch);
extern int	ndmchan_pending_to_write (struct ndmchan *ch);
extern void	ndmchan_set_eof (struct ndmchan *ch);
extern void	ndmchan_close_set_errno (struct ndmchan *ch, int err_no);
extern void	ndmchan_close (struct ndmchan *ch);
extern void	ndmchan_abort (struct ndmchan *ch);
extern void	ndmchan_close_as_is (struct ndmchan *ch);
extern void	ndmchan_cleanup (struct ndmchan *ch);
extern int	ndmchan_quantum (struct ndmchan *chtab[],
			unsigned n_chtab, int milli_timo);
extern int	ndmchan_pre_poll (struct ndmchan *chtab[], unsigned n_chtab);
extern int	ndmchan_post_poll (struct ndmchan *chtab[], unsigned n_chtab);
extern void	ndmchan_compress (struct ndmchan *ch);
extern int	ndmchan_n_avail (struct ndmchan *ch);
extern int	ndmchan_n_avail_record (struct ndmchan *ch, unsigned long size);
extern int	ndmchan_n_avail_total (struct ndmchan *ch);
extern int	ndmchan_n_ready (struct ndmchan *ch);
extern enum ndmchan_read_interpretation
		ndmchan_read_interpret (struct ndmchan *ch, char **data_p,
			unsigned *n_ready_p);
extern enum ndmchan_write_interpretation
		ndmchan_write_interpret (struct ndmchan *ch, char **data_p,
			unsigned *n_avail_p);

extern void	ndmchan_pp (struct ndmchan *ch, char *buf);

extern int	ndmos_chan_poll (struct ndmchan *chtab[],
			unsigned n_chtab, int milli_timo);




/*
 * NDMCONN -- Bidirectional control connections
 ****************************************************************
 */

#define NDMCONN_TYPE_NONE		0
#define NDMCONN_TYPE_RESIDENT		1
#define NDMCONN_TYPE_REMOTE		2


#define NDMCONN_CALL_STATUS_HDR_ERROR	(-2)
#define NDMCONN_CALL_STATUS_BOTCH	(-1)
#define NDMCONN_CALL_STATUS_OK		0
#define NDMCONN_CALL_STATUS_REPLY_ERROR	1
#define NDMCONN_CALL_STATUS_REPLY_LATE	2


struct ndmconn {
	struct sockaddr	sa;

	struct ndmchan	chan;

	char		conn_type;
	char		protocol_version;
	char		was_allocated;

	void *		context;

	XDR		xdrs;
	unsigned char	frag_hdr_buf[4];	/* see ndmconn_readit() */
	unsigned	fhb_off;
	unsigned long	frag_resid;

	unsigned long	next_sequence;

	void		(*unexpected)(struct ndmconn *conn,
					struct ndmp_msg_buf *nmb);

	int		snoop_level;
	struct ndmlog *	snoop_log;

	char *		last_err_msg;

	int		(*call) (struct ndmconn *conn, struct ndmp_xa_buf *xa);

	struct ndmp_xa_buf call_xa_buf;

	int		last_message;
	int		last_call_status;
	ndmp9_error	last_header_error;
	ndmp9_error	last_reply_error;

	long   		sent_time;
	long		received_time;
	long            time_limit;
};

extern struct ndmconn *	ndmconn_initialize (struct ndmconn *aconn, char *name);
extern void		ndmconn_destruct (struct ndmconn *conn);
extern int		ndmconn_connect_agent (struct ndmconn *conn,
				struct ndmagent *agent);
extern int		ndmconn_connect_host_port (struct ndmconn *conn,
				char * hostname, int port,
				unsigned want_protocol_version);
extern int		ndmconn_connect_sockaddr_in (struct ndmconn *conn,
				struct sockaddr_in *sin,
				unsigned want_protocol_version);
extern int		ndmconn_try_open (struct ndmconn *conn,
				unsigned protocol_version);
extern int		ndmconn_accept (struct ndmconn *conn, int sock);
extern int		ndmconn_abort (struct ndmconn *conn);
extern int		ndmconn_close (struct ndmconn *conn);
extern int		ndmconn_fileno (struct ndmconn *conn);
extern int		ndmconn_auth_agent (struct ndmconn *conn,
				struct ndmagent *agent);
extern int		ndmconn_auth_none (struct ndmconn *conn);
extern int		ndmconn_auth_text (struct ndmconn *conn,
				char *id, char *pw);
extern int		ndmconn_auth_md5 (struct ndmconn *conn,
				char *id, char *pw);
extern int		ndmconn_call (struct ndmconn *conn,
				struct ndmp_xa_buf *xa);
extern int		ndmconn_exchange_nmb (struct ndmconn *conn,
				struct ndmp_msg_buf *request_nmb,
				struct ndmp_msg_buf *reply_nmb);
extern int		ndmconn_send_nmb (struct ndmconn *conn,
				struct ndmp_msg_buf *nmb);
extern int		ndmconn_recv_nmb (struct ndmconn *conn,
				struct ndmp_msg_buf *nmb);
extern void		ndmconn_free_nmb (struct ndmconn *conn,
				struct ndmp_msg_buf *nmb);
extern int		ndmconn_xdr_nmb (struct ndmconn *conn,
				struct ndmp_msg_buf *nmb,
				enum xdr_op x_op);
extern int		ndmconn_readit (void *a_conn, char *buf, int len);
extern int		ndmconn_writeit (void *a_conn, char *buf, int len);
extern int		ndmconn_sys_read (struct ndmconn *conn,
				char *buf, unsigned len);
extern int		ndmconn_sys_write (struct ndmconn *conn,
				char *buf, unsigned len);
extern void		ndmconn_unexpected (struct ndmconn *conn,
				struct ndmp_msg_buf *nmb);
extern int		ndmconn_set_snoop (struct ndmconn *conn,
				struct ndmlog *log, int level);
extern void		ndmconn_clear_snoop (struct ndmconn *conn);
extern void		ndmconn_snoop_nmb (struct ndmconn *conn,
				struct ndmp_msg_buf *nmb,
				char *whence);
extern void		ndmconn_snoop (struct ndmconn *conn,
				int level, char *fmt, ...);
extern void		ndmconn_hex_dump (struct ndmconn *conn,
				char *buf, unsigned len);
extern int		ndmconn_set_err_msg (struct ndmconn *conn,
				char *err_msg);
extern char *		ndmconn_get_err_msg (struct ndmconn *conn);




/*
 * NDMC_WITH() AND FRIENDS
 ****************************************************************
 *
 * Macro NDMC_WITH() and friends. These are patterned after
 * the Pascal "with" construct. These macros take care of
 * the tedious, error prone, mind-numbing, and distracting
 * code required to perform NDMP RPCs. These greatly
 * facilitate the clarity of the main body of code.
 * Code sequences look something like:
 *
 *	NDMC_WITH(ndmp_config_get_butype_attr)
 *		request->xxx = yyy;
 *		...
 *		rc = NDMC_CALL(ndmconn);
 *		if (rc == 0) {
 *			reply->xxx ...
 *			....
 *		}
 *		NDMC_FREE_REPLY()
 *	NDMC_ENDWITH
 *
 * The NDMC macros are for client-side (caller) sequences.
 * The NDMS macros are for server-side (callee) sequences.
 *
 * These macros are very dependent on ndmp_msg_buf.h and ndmp_ammend.h
 *
 * Implementation note: initialization of *request and *reply
 * are separate from their declarations. They used to be
 * initialized declarators. The separation made gcc -Wall happy.
 */

#define NDMC_WITH(TYPE,VERS) \
  { \
	struct ndmp_xa_buf *	xa = &conn->call_xa_buf; \
	TYPE##_request * request; \
	TYPE##_reply   * reply; \
	request = &xa->request.body.TYPE##_request_body; \
	reply = &xa->reply.body.TYPE##_reply_body; \
	NDMOS_MACRO_ZEROFILL (xa); \
	xa->request.protocol_version = VERS; \
	xa->request.header.message = (ndmp0_message) MT_##TYPE; \
     {


#define NDMC_WITH_VOID_REQUEST(TYPE,VERS) \
  { \
	struct ndmp_xa_buf *	xa = &conn->call_xa_buf; \
	TYPE##_reply   * reply; \
	reply = &xa->reply.body.TYPE##_reply_body; \
	NDMOS_MACRO_ZEROFILL (xa); \
	xa->request.protocol_version = VERS; \
	xa->request.header.message = (ndmp0_message) MT_##TYPE; \
     {

#define NDMC_WITH_NO_REPLY(TYPE,VERS) \
  { \
	struct ndmp_xa_buf *	xa = &conn->call_xa_buf; \
	TYPE##_request * request; \
	request = &xa->request.body.TYPE##_request_body; \
	NDMOS_MACRO_ZEROFILL (xa); \
	xa->request.protocol_version = VERS; \
	xa->request.header.message = (ndmp0_message) MT_##TYPE; \
     {

#ifndef NDMOS_OPTION_NO_NDMP4
#define NDMC_WITH_POST(TYPE,VERS) \
  { \
	struct ndmp_xa_buf *	xa = &conn->call_xa_buf; \
	TYPE##_post * request; \
	request = &xa->request.body.TYPE##_post_body; \
	NDMOS_MACRO_ZEROFILL (xa); \
	xa->request.protocol_version = VERS; \
	xa->request.header.message = (ndmp0_message) MT_##TYPE; \
     {
#endif /* !NDMOS_OPTION_NO_NDMP4 */


#define NDMC_ENDWITH \
  } }

#define NDMC_CALL(CONN) (*(CONN)->call)(CONN, xa);
#define NDMC_SEND(CONN) (*(CONN)->call)(CONN, xa);
#define NDMC_FREE_REPLY() ndmconn_free_nmb ((void*)0, &xa->reply)



#define NDMS_WITH(TYPE) \
  { \
	TYPE##_request * request; \
	TYPE##_reply   * reply; \
	request = &xa->request.body.TYPE##_request_body; \
	reply = &xa->reply.body.TYPE##_reply_body; \
     {

#define NDMS_WITH_VOID_REQUEST(TYPE) \
  { \
	TYPE##_reply   * reply; \
	reply = &xa->reply.body.TYPE##_reply_body; \
     {

#define NDMS_WITH_NO_REPLY(TYPE) \
  { \
	TYPE##_request * request; \
	request = &xa->request.body.TYPE##_request_body; \
     {

#ifndef NDMOS_OPTION_NO_NDMP4
#define NDMS_WITH_POST(TYPE) \
  { \
	TYPE##_post * request; \
	request = &xa->request.body.TYPE##_post_body; \
     {
#endif /* !NDMOS_OPTION_NO_NDMP4 */

#define NDMS_ENDWITH \
  } }






/*
 * NDMAGENT -- "Address" of agent
 ****************************************************************
 *
 * A struct ndmagent contains the information necessary
 * to establish a connection with an NDMP agent (server).
 * An agent can be remote (NDMCONN_TYPE_REMOTE) or resident
 * (...._RESIDENT).
 * TODO: MD5
 */
#define NDMAGENT_HOST_MAX	63
#define NDMAGENT_ACCOUNT_MAX	15
#define NDMAGENT_PASSWORD_MAX	32

struct ndmagent {
	char		conn_type;	/* NDMCONN_TYPE_... (see above) */
	char		protocol_version; /* 0->best, 2->v2 3->v3 */
	char		host[NDMAGENT_HOST_MAX+1];	/* name */
	int		port;		/* 0->default (NDMPPORT) */
	char		account[NDMAGENT_ACCOUNT_MAX+1]; /* clear text */
	char		password[NDMAGENT_PASSWORD_MAX+1]; /* clear text */
#if 0
	ndmp_auth_type	auth_type;
#else
	int		auth_type;
#endif
};
extern int	ndmagent_from_str (struct ndmagent *agent, char *str);
extern int	ndmhost_lookup (char *hostname, struct sockaddr_in *sin);
extern int	ndmagent_to_sockaddr_in (struct ndmagent *agent,
			struct sockaddr_in *sin);




/*
 * NDMSCSI -- "Address" of SCSI device
 ****************************************************************
 */

struct ndmscsi_target {
	char		dev_name[PATH_MAX];
	int		controller;
	int		sid;
	int		lun;
};

#define NDMSCSI_MAX_SENSE_DATA	127
struct ndmscsi_request {
	unsigned char	completion_status;
	unsigned char	status_byte;
	unsigned char	data_dir;
	unsigned char	n_cmd;

	unsigned char	cmd[12];

	unsigned char *	data;
	unsigned	n_data_avail;
	unsigned	n_data_done;
	unsigned long	_pad;

	unsigned char	n_sense_data;
	unsigned char	sense_data[NDMSCSI_MAX_SENSE_DATA];
};

#define NDMSCSI_CS_GOOD	0
#define NDMSCSI_CS_FAIL	1
/* more? */

#define NDMSCSI_DD_NONE	0
#define NDMSCSI_DD_IN	1	/* adapter->app */
#define NDMSCSI_DD_OUT	2	/* app->adapter */


extern int		ndmscsi_target_from_str (struct ndmscsi_target *targ,
				char *str);
extern int		ndmscsi_open (struct ndmconn *conn, char *dev_name);
extern int		ndmscsi_close (struct ndmconn *conn);
extern int		ndmscsi_get_state (struct ndmconn *conn,
				struct ndmscsi_target *targ);
extern int		ndmscsi_set_target (struct ndmconn *conn,
				struct ndmscsi_target *targ);
extern int		ndmscsi_use (struct ndmconn *conn,
				struct ndmscsi_target *targ);
extern int		ndmscsi_execute (struct ndmconn *conn,
				struct ndmscsi_request *req,
				struct ndmscsi_target *targ);




/*
 * NDMMEDIA -- media (tape) labels, position, and status
 ****************************************************************
 */

#define NDMMEDIA_LABEL_MAX	31

struct ndmmedia {
	NDM_FLAG_DECL(valid_label)	/* ->label[] valid */
	NDM_FLAG_DECL(valid_filemark)	/* ->file_mark_skip valid */
	NDM_FLAG_DECL(valid_n_bytes)	/* ->n_bytes valid */
	NDM_FLAG_DECL(valid_slot)	/* ->slot_addr valid */

	/* results flags */
	NDM_FLAG_DECL(media_used)	/* was used (loaded) */
	NDM_FLAG_DECL(media_written)	/* media was written */
	NDM_FLAG_DECL(media_eof)	/* reached EOF of tape file */
	NDM_FLAG_DECL(media_eom)	/* reached EOM (tape full) */
	NDM_FLAG_DECL(media_open_error)	/* open-time error (write-protect?) */
	NDM_FLAG_DECL(media_io_error)	/* media error */

	NDM_FLAG_DECL(label_read)	/* ->label[] read fm media */
	NDM_FLAG_DECL(label_written)	/* ->label[] writn to media */
	NDM_FLAG_DECL(label_io_error)	/* error label read/write */
	NDM_FLAG_DECL(label_mismatch)	/* label wasn't as expected */

	NDM_FLAG_DECL(fmark_error)	/* error skipping file marks */

	NDM_FLAG_DECL(nb_determined)	/* true ->n_bytes determined */
	NDM_FLAG_DECL(nb_aligned)	/* ->n_bytes aligned per rec_size */

	NDM_FLAG_DECL(slot_empty)	/* slot empty per robot */
	NDM_FLAG_DECL(slot_bad)		/* ->slot_addr invalid */
	NDM_FLAG_DECL(slot_missing)	/* !->valid_slot */

	/* all fields are specified/actual depending on context */
	char		label[NDMMEDIA_LABEL_MAX+1];
	unsigned	file_mark_offset;
	unsigned long long n_bytes;
	unsigned	slot_addr;

	/* scratch pad */
	unsigned long long begin_offset, end_offset;
};

extern int	ndmmedia_from_str (struct ndmmedia *me, char *str);
extern int	ndmmedia_to_str (struct ndmmedia *me, char *str);
extern int	ndmmedia_pp (struct ndmmedia *me, int lineno, char *buf);
extern long long ndmmedia_strtoll (char *str, char **tailp, int defbase);




/*
 * NDMFHH -- file history (FH) heap
 ****************************************************************
 *
 * As DATA accumulates individual File History (FH) entries they
 * are saved into a heap buffer. When the heap is full it is flushed
 * from DATA to CONTROL using NDMP?_FH_ADD_.... requests/posts.
 */

struct ndmfhheap {
	int		fhtype;
	int		entry_size;

	void *		table;
	void *		allo_entry;

	void *		allo_item;

	void *		heap_base;
	void *		heap_end;
	unsigned	heap_size;

	void *		heap_top;
	void *		heap_bot;
};

struct ndmfhh_generic_table {
	u_int	table_len;
	void *	table_val;
};

#define NDMFHH_RET_OK		(0)
#define NDMFHH_RET_OVERFLOW	(-1)
#define NDMFHH_RET_TYPE_CHANGE	(-2)
#define NDMFHH_RET_NO_HEAP	(-3)
#define NDMFHH_RET_ENTRY_SIZE_MISMATCH (-4)


extern int	ndmfhh_initialize (struct ndmfhheap *fhh);
extern int	ndmfhh_commission (struct ndmfhheap *fhh,
					void *heap, unsigned size);
extern int	ndmfhh_prepare (struct ndmfhheap *fhh,
					int fhtype, int entry_size,
					unsigned n_item,
					unsigned total_size_of_items);
extern void *	ndmfhh_add_entry (struct ndmfhheap *fhh);
extern void *	ndmfhh_add_item (struct ndmfhheap *fhh, unsigned size);
extern void *	ndmfhh_save_item (struct ndmfhheap *fhh,
					void *item, unsigned size);
extern int	ndmfhh_reset (struct ndmfhheap *fhh);
extern int	ndmfhh_get_table (struct ndmfhheap *fhh,
					int *fhtype_p, void **table_p,
					unsigned *n_entry_p);




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

#define NDMCSTR_WARN	'%'
extern int	ndmcstr_from_str (char *src, char *dst, unsigned dst_max);
extern int	ndmcstr_to_str (char *src, char *dst, unsigned dst_max);
extern int	ndmcstr_from_hex (int c);




/*
 * NDMMD5 -- MD5 helpers
 ****************************************************************
 * This is a wrapper around the MD5 functions. ndml_md5.c
 * is the only thing that needs to #include md5.h.
 * The NDMP rules for converting a clear-text password
 * into an MD5 digest are implemented here.
 */

#define NDMP_MD5_CHALLENGE_LENGTH	64
#define NDMP_MD5_DIGEST_LENGTH		16
#define NDMP_MD5_MESSAGE_LENGTH		128
#define NDMP_MD5_MAX_PASSWORD_LENGTH	32


extern int	ndmmd5_digest (char challenge[NDMP_MD5_CHALLENGE_LENGTH],
				char *clear_text_password,
				char digest[NDMP_MD5_DIGEST_LENGTH]);
extern int	ndmmd5_generate_challenge (
				char challenge[NDMP_MD5_CHALLENGE_LENGTH]);

extern int	ndmmd5_ok_digest (char challenge[NDMP_MD5_CHALLENGE_LENGTH],
				char *clear_text_password,
				char digest[NDMP_MD5_DIGEST_LENGTH]);




/*
 * NDMBSTF -- Binary Search Text File
 ****************************************************************
 * Use conventional binary search method on a sorted text
 * file. The file MUST be sorted in ascending, lexicographic
 * order. This is the default order of sort(1).
 */

extern int ndmbstf_first (FILE *fp, char *key, char *buf, unsigned max_buf);
extern int ndmbstf_next (FILE *fp, char *key, char *buf, unsigned max_buf);
extern int ndmbstf_first_with_bounds (FILE *fp, char *key,
	    char *buf, unsigned max_buf, off_t lower_bound, off_t upper_bound);
extern int ndmbstf_getline (FILE *fp, char *buf, unsigned max_buf);
extern int ndmbstf_seek_and_align (FILE *fp, off_t *off);
extern int ndmbstf_match (char *key, char *buf);
extern int ndmbstf_compare (char *key, char *buf);




/*
 * NDMSTZF -- Stanza File
 ****************************************************************
 *
 * Stanza files look about like this:
 *	[stanza name line]
 *	stanza body lines
 *
 * These are used for config files.
 */
extern int ndmstz_getline (FILE *fp, char *buf, int n_buf);
extern int ndmstz_getstanza (FILE *fp, char *buf, int n_buf);
extern int ndmstz_parse (char *buf, char *argv[], int max_argv);




/*
 * NDMCFG -- Config File
 ****************************************************************
 *
 * Config files are stanza files (see above) which describe
 * backup types, tape and scsi devices, etc.
 * See ndml_config.c for details and stanza formats.
 */
extern int	ndmcfg_load (char *filename, ndmp9_config_info *config_info);
extern int	ndmcfg_loadfp (FILE *fp, ndmp9_config_info *config_info);




/*
 * NDMFHDB -- File History Database
 *
 * The File History is generated by the DATA and sent to the CONTROL
 * using NDMP?_FH_ADD_... requests/posts. During backup the CONTROL
 * writes the File History info to a text file as it arrives. Upon
 * completion of the backup the text file should be sorted (UNIX
 * sort(1) command). For recovery the file history index is searched
 * using binary search (see NDMBSTF above). The fh_info, a 64-bit
 * cookie used by DATA to identify the region of the backup image
 * containing the corresponding object, is retreived from the index.
 */

struct ndmfhdb {
	FILE *			fp;
	int			use_dir_node;
	unsigned long long	root_node;
};

extern int	ndmfhdb_add_file (struct ndmlog *ixlog, int tagc,
			char *raw_name, ndmp9_file_stat *fstat);
extern int	ndmfhdb_add_dir (struct ndmlog *ixlog, int tagc,
			char *raw_name, ndmp9_u_quad dir_node,
			ndmp9_u_quad node);
extern int	ndmfhdb_add_node (struct ndmlog *ixlog, int tagc,
			ndmp9_u_quad node, ndmp9_file_stat *fstat);
extern int	ndmfhdb_add_dirnode_root (struct ndmlog *ixlog, int tagc,
			ndmp9_u_quad root_node);

extern int	ndmfhdb_add_fh_info_to_nlist (FILE *fp,
			ndmp9_name *nlist, int n_nlist);
extern int	ndmfhdb_open (FILE *fp, struct ndmfhdb *fhcb);
extern int	ndmfhdb_lookup (struct ndmfhdb *fhcb, char *path,
			ndmp9_file_stat *fstat);
extern int	ndmfhdb_dirnode_root (struct ndmfhdb *fhcb);
extern int	ndmfhdb_dirnode_lookup (struct ndmfhdb *fhcb, char *path,
			ndmp9_file_stat *fstat);
extern int	ndmfhdb_dir_lookup (struct ndmfhdb *fhcb,
			unsigned long long dir_node,
			char *name, unsigned long long *node_p);
extern int	ndmfhdb_node_lookup (struct ndmfhdb *fhcb,
			unsigned long long node,
			ndmp9_file_stat *fstat);
extern int	ndmfhdb_file_root (struct ndmfhdb *fhcb);
extern int	ndmfhdb_file_lookup (struct ndmfhdb *fhcb, char *path,
			ndmp9_file_stat *fstat);
extern char *	ndm_fstat_to_str (ndmp9_file_stat *fstat, char *buf);
extern int	ndm_fstat_from_str (ndmp9_file_stat *fstat, char *buf);


#endif /* _NDMLIB_H_ */
