//
//  gensio - A library for abstracting stream I/O
//  Copyright (C) 2021  Corey Minyard <minyard@acm.org>
//
//  SPDX-License-Identifier: LGPL-2.1-only

// This is a C++ wrapper for the gensio library.

#ifndef GENSIO_CPP_INCLUDE
#define GENSIO_CPP_INCLUDE

#include <stdexcept>
#include <string>
#include <memory>
#include <atomic>
#include <gensio/gensio_dllvisibility>

#include <iostream>

namespace gensio {
    // Incuding this in the gensio namespace to keep things neat, and
    // so we can have a gensio namespace.  You can't have a "struct
    // gensio" and "namespace gensio" in the same namespace.
#include <gensio/gensio.h>
#include <gensio/sergensio.h>

    // This is an exception that is raised by gensio operations that
    // get errors.  Most operations raise exceptions, unless otherwise
    // noted.  Note that the string is set, so what() will return the
    // string.  You can get the number with get_error().
    class gensio_error: public std::runtime_error {
    public:
	gensio_error(int ierr): std::runtime_error(gensio_err_to_str(ierr))
	{
	    err = ierr;
	};

	int get_error() { return err; };
    private:
	int err;
    };

    // This is a wrapper for gensio_os_funcs that makes using it a lot
    // cleaner.  The default constructor automatically allocates the
    // default, destruction takes place automatically.
    // This is a smart pointer like object.
    class GENSIOCPP_DLL_PUBLIC Os_Funcs {
    public:
	// Allocate the default os function handlers for the platform.
	// See gensio_default_os_hnd.3 for details.
	Os_Funcs(int wait_sig)
	{
	    int err;

	    refcnt = new std::atomic<unsigned int>(1);
	    err = gensio_default_os_hnd(wait_sig, &osf);
	    if (err)
		throw gensio_error(err);
	}

	// Do the setup for the process.  If you do this, this should be
	// the last os funcs to destruct, if you have more than one.  See
	// gensio_os_funcs.3 for details on process setup.
	void proc_setup() {
	    int err;

	    err = gensio_os_proc_setup(osf, &proc_data);
	    if (err) {
		osf->free_funcs(osf);
		throw gensio_error(err);
	    }
	}

	// Allocate an os function handler object from an existing os
	// handler.  Note that this takes over freeing the OS handler.
	Os_Funcs(struct gensio_os_funcs *o)
	{
	    refcnt = new std::atomic<unsigned int>(1);
	    osf = o;
	}

	// Assignment contructor
	Os_Funcs& operator=(const Os_Funcs &o)
	{
	    refcnt = o.refcnt;
	    osf = o.osf;
	    ++*refcnt;
	    return *this;
	}

	// Copy constructor
	Os_Funcs(const Os_Funcs &o) {
	    refcnt = o.refcnt;
	    osf = o.osf;
	    ++*refcnt;
	}

	struct gensio_os_proc_data *get_proc_data() { return proc_data; }

	// Instead of defining all our own functions, which would just
	// be direct wrapper, we just do this so we return the base os
	// funcs structure for a ->.
	struct gensio_os_funcs * operator->() { return osf; }

	// Automatically convert this object when a struct
	// gensio_os_funcs is asked for.
	operator struct gensio_os_funcs * () const { return osf; }

	~Os_Funcs()
	{
	    if (refcnt->fetch_sub(1) == 1) {
		if (proc_data)
		    gensio_os_proc_cleanup(proc_data);
		osf->free_funcs(osf);
		delete refcnt;
	    }
	}

    private:
	struct gensio_os_funcs *osf;
	struct gensio_os_proc_data *proc_data = NULL;
	std::atomic<unsigned int> *refcnt;
    };

    // A wrapper for the gensio_addr structure.
    class GENSIOCPP_DLL_PUBLIC Addr {
    public:
	// Create a new address structure using the passed in string.
	// This is basically the same as gensio_scan_network_port()
	// in gensio.h, see that for details.
	Addr(Os_Funcs &o, std::string str, bool listen, int *protocol,
	     int *argc, const char ***args);

	// Scan a string and create an address from it.  If the address
	// is being allocated for an accepter, set listen to true, othersize
	// set it to false.  Protocol must be one of GENSIO_NET_PROTOCOL_XXX.
	Addr(Os_Funcs &o, std::string str, bool listen, int protocol);

	// Allocate an address based upon the given passed in address and
	// port.  See gensio_addr_create() in gensio.h for details.
	Addr(Os_Funcs &o, int nettype, const void *iaddr, gensiods len,
	     unsigned int port);

	// For copying and assignment, we duplicate the low-level
	// address.  Addresses are immutable and refcounted.
	Addr(Addr &a)
	{
	    this->gaddr = gensio_addr_dup(a);
	    if (!this->gaddr)
		throw std::bad_alloc();
	    this->is_port_set = a.is_port_set;
	}
	Addr & operator=(const Addr &a)
	{
	    this->gaddr = gensio_addr_dup(a);
	    if (!this->gaddr)
		throw std::bad_alloc();
	    this->is_port_set = a.is_port_set;
	    return *this;
	}

	// Automatically convert to a low-level address
	operator struct gensio_addr * () const { return gaddr; }

	~Addr();

	// See gensio_addr_xxx functions in gensio.h for details on these.
	void rewind() { gensio_addr_rewind(gaddr); }
	bool next() { return gensio_addr_next(gaddr); }
	void getaddr(void *oaddr, gensiods *len)
	    { gensio_addr_getaddr(gaddr, oaddr, len); }
	int get_nettype() { return gensio_addr_get_nettype(gaddr); }
	bool family_supports(int family, int flags)
	    { return gensio_addr_family_supports(gaddr, family, flags); }
	std::string to_string();
	std::string to_string_all();

	inline bool operator==(const Addr &a2)
	    { return gensio_addr_equal(*this, a2, true, false); }
	bool equal(const Addr &a2, bool compare_ports, bool compare_all)
	    { return gensio_addr_equal(*this, a2, compare_ports, compare_all); }
	bool addr_present(const void *addr, gensiods addrlen,
			  bool compare_ports)
	{
	    return gensio_addr_addr_present(*this, addr, addrlen,
					    compare_ports);
	}

	bool port_set() { return is_port_set; }

    private:
	struct gensio_addr *gaddr;
	bool is_port_set;
    };

    class Gensio;
    class Serial_Gensio;

    // This is an abstract class to be passed into a gensio class for
    // delivery of events from the gensio.
    class GENSIOCPP_DLL_PUBLIC Event {
    public:
	// Data from the gensio is delivered in this callback.  You
	// must implement this.
	virtual int read(Gensio *io, int err, unsigned char *buf,
			 gensiods *buflen, const char *const *auxdata) = 0;

	// Data can be written to the gensio.  By default this
	// disables write callbacks.
	virtual void write_ready(Gensio *io);

	// A new channel is available on the gensio.  By default this
	// deletes the new channel.
	virtual void new_channel(Gensio *io, Gensio *new_channel,
				 const char *const *auxdata);

	// The remote end has requested that the receiver of this do a
	// break.  This is primarily for a telnet server that is
	// hooked to a serial port, if it receives this it should send
	// a break on the serial port.  By default htis does nothing.
	virtual void send_break(Gensio *io) { };

	// Various authentication operations.  See gensio_event.3 for
	// details on what these do.
	virtual int auth_begin(Gensio *io) { return GE_NOTSUP; }
	virtual int precert_verify(Gensio *io) { return GE_NOTSUP; }
	virtual int postcert_verify(Gensio *io) { return GE_NOTSUP; }
	virtual int password_verify(Gensio *io, const unsigned char *password)
	     { return GE_NOTSUP; }
	virtual int request_password(Gensio *io, unsigned char *password,
				     gensiods *pwlen) { return GE_NOTSUP; }
	virtual int verify_2fa(Gensio *io, const unsigned char *val_2fa,
			       gensiods len_2fa) { return GE_NOTSUP; }
	virtual int request_2fa(Gensio *io, unsigned char **val_2fa,
				gensiods *len_2fa) { return GE_NOTSUP; }

	// A gensio that is non-standard may generate events for its
	// own purposes; these events have event numbers that fall
	// into a range defined in gensio.h.  These events will be
	// delivered here.
	virtual int user_event(Gensio *io, int event, int err,
			       unsigned char *buf, gensiods *buflen,
			       const char *const *auxdata) { return GE_NOTSUP; }

	// The free() operation for gensio this object is assigned to
	// has finished and the data will immediately be freed.
	virtual void freed() { };

	virtual ~Event() = default;
    };

    // Used for done handlers for gensio operations that can fail,
    // failure is returned in the err field.
    class GENSIOCPP_DLL_PUBLIC Gensio_Done_Err {
    public:
	virtual void done(Gensio *io, int err) = 0;
	virtual ~Gensio_Done_Err() = default;
    };

    // Used for done handlers for gension operations that cannot fail.
    class GENSIOCPP_DLL_PUBLIC Gensio_Done {
    public:
	virtual void done(Gensio *io) = 0;
	virtual ~Gensio_Done() = default;
    };

    // Allocate a gensio based upon the given string.  The string
    // format is defiend in gensio.5.  You must provided an os
    // function handler as described in gensio_os_funcs.3 and an event
    // handler defined above.
    //
    // Note that will return a subclass of Gensio depending on the
    // particular string provided.
    GENSIOCPP_DLL_PUBLIC
    Gensio *gensio_alloc(std::string str, Os_Funcs &o,
			 Event *cb);

    // Like the above, but stacks the newly created gensio as defined
    // by str on top of the given gensio.  This can be used to
    // dynamically add gensios to a gensio stack.
    GENSIOCPP_DLL_PUBLIC
    Gensio *gensio_alloc(Gensio *child, std::string str,
			 Os_Funcs &o, Event *cb);

    // This is a gensio, the central class in the gensio framework.
    // This is the thing that you use to send/receive data and control
    // various operations of the gensio.
    class GENSIOCPP_DLL_PUBLIC Gensio {
    public:
	Gensio(const Gensio&) = delete;
	Gensio &operator=(const Gensio&) = delete;

	// Unfortunately, you can't use the destructor with this class
	// because of race conditions.  When you call this, there may
	// be other things pending in callbacks, and there's no way to
	// delay the free of the object in a destructor without
	// blocking.  So you call free here, and when the freed
	// function in the event handler gets called, the free is
	// complete.
	void free();

	// Change the event handler for a gensio.  This is provided so
	// gensios delivered via new_channel() or in an accepter can
	// get their event handlers set.  It's a bad idea to change
	// the event handler on a running gensio.
	inline void set_event_handler(Event *cb) { gcb = cb; }

	// Open a gensio.  When the done handler is called it is ready
	// (unless it reports an error).
	void open(Gensio_Done_Err *done);

	// Open a gensio and wait for it's open peration to complete.
	void open_s();

	// Open a gensio but assume that it's children are already
	// open.  This is used if you stacked a new gensio on top of a
	// running stack.
	void open_nochild(Gensio_Done_Err *done);

	// Like the above, but synchronous.
	void open_nochild_s();

	// Write buflen bytes of data in buf to the given gensio.  The
	// actual number of bytes written is returned in count.  The
	// meaning of auxdata depends on the gensio, see gensio.5 for
	// detais.
	void write(gensiods *count, const void *buf, gensiods buflen,
		   const char *const *auxdata);

	// Like the above, but use a scatter-gather structure to write
	// the data.
	void write_sg(gensiods *count,
		      const struct gensio_sg *sg, gensiods sglen,
		      const char *const *auxdata);

	// Allocate a new channel for the gensio based upon the given
	// arguments, and use the given event handler for it.  How
	// this works depends on the particular gensio, see gensio.5
	// for details.
	Gensio *alloc_channel(const char *const args[], Event *cb);

	// Close the given gensio.  When the close completely call the
	// done handler.
	void close(Gensio_Done *done);

	// Like the above, but do it synchronosly.
	void close_s();

	// This is used in specific circumstances to disable a gensio
	// that cannot function any more.  See gensio_disable.3 for
	// details.
	inline void disable() { gensio_disable(io); }

	// A gensio won't deliver any data events until you enable it.
	// In general, you should run with read enabled unless you
	// can't handle any more data, and you should run with write
	// disabled until you write and get an incomplete write.  When
	// you get an incomplete write, you can enable write callback
	// to know when you can transmit again.  Note that if you
	// disable one of these, there may still be callbacks pending
	// on the gensio.  Don't assume that when this returns there
	// are no callbacks pending.
	inline void set_read_callback_enable(bool enabled)
	{ gensio_set_read_callback_enable(io, enabled); }
	inline void set_write_callback_enable(bool enabled)
	{ gensio_set_write_callback_enable(io, enabled); }

	// Various control operations on a gensio, see
	// gensio_control.3 for details.
	void control(int depth, bool get, unsigned int option,
		     char *data, gensiods *datalen);

	// Return the type of the gensio.  If depth is larger than the
	// stack, returns NULL.
	inline const char *get_type(unsigned int depth)
	{
	    return gensio_get_type(io, depth);
	}

	// Return a gensio in the stack.  0 return this gensio, 1
	// returns its child, 2 returns it grandchild, etc.  If the
	// depth is larger than the stack, returns NULL.
	Gensio *get_child(unsigned int depth);

	// Return various characterstics about a gensio.  See the
	// gensio_is_xxx.3 man pages for details.
	inline bool is_client() { return gensio_is_client(io); }
	inline bool is_reliable() { return gensio_is_reliable(io); }
	inline bool is_packet() { return gensio_is_packet(io); }
	inline bool is_authenticated() { return gensio_is_authenticated(io); }
	inline bool is_encrypted() { return gensio_is_encrypted(io); }
	inline bool is_message() { return gensio_is_message(io); }

	// Turn on/off synchronous mode for a gensio.  In synchrohous
	// mode, the gensio will not deliver data via the read call.
	// You must call the read_s() functions below to read the data.
	// See the gensio_set_sync() man page for details.
	inline void set_sync() { gensio_set_sync(io); }
	inline void clear_sync() { gensio_clear_sync(io); }

	// Read data from the gensio in synchronous mode and wait up
	// to timeout time for the data.  The amount of data read is
	// returned in count.  If a timeout occurs, data may still
	// have been read.  If timeout is NULL, wait forever.  Note
	// that this returns if any data is available, even if it is
	// less than datalen.  This will return GE_TIMEDOUT on a
	// timeout or 0 on success.  All other errors throw
	// gensio_error.
	int read_s(gensiods *count, void *data, gensiods datalen,
		   gensio_time *timeout);

	// Like the above, but will also return on a signal.  It
	// returns GE_INTERRUPTED in this case.
	int read_s_intr(gensiods *count, void *data, gensiods datalen,
			gensio_time *timeout);

	// Write data and wait for the write to complete.  If the
	// write does not complete in the time specified by timeout,
	// returns GE_TIMEDOUT.  Note that some data may still have
	// been written, the amount written is returned in count.
	// If timeout is NULL, wait forever.
	int write_s(gensiods *count, const void *data, gensiods datalen,
		     gensio_time *timeout);

	// Like the above, but the write may be interrupted by a
	// signal.  If that happens, this returns GE_INTERRUPTED.
	int write_s_intr(gensiods *count, const void *data, gensiods datalen,
			 gensio_time *timeout);

	// Return the os funcs assigned to a gensio.
	inline Os_Funcs &get_os_funcs() { return go; }

	// Return the event handler assigned to a gensio.
	inline Event *get_cb() { return gcb; }

	// Return the raw gensio.  Don't use this, it's for subclasses
	// to use.
	struct gensio *get_gensio() { return io; }

    protected:
	// Subclasses can use this to initialize the gensio object.
	virtual void set_gensio(struct gensio *io, bool set_cb);
	Gensio(Os_Funcs &o, Event *cb): go(o), gcb(cb) { }
	virtual ~Gensio() { }

    private:
	struct gensio *io = NULL;
	Event *gcb;
	Os_Funcs go;

	GENSIOCPP_DLL_PUBLIC
	friend Gensio *gensio_alloc(struct gensio *io,
				    Os_Funcs &o);
	GENSIOCPP_DLL_PUBLIC
	friend Gensio *gensio_alloc(Gensio *child, std::string str,
				    Os_Funcs &o,
				    Event *cb);
	friend void gensio_cpp_freed(struct gensio *io,
				     struct gensio_frdata *frdata);
	friend class Serial_Gensio;
    };

    // Add a new class type.  This is for user-added gensios, so they
    // can tie in to the C++ frameworks.
    GENSIOCPP_DLL_PUBLIC
    void gensio_add_class(const char *name,
			  Gensio *(*allocator)(Os_Funcs &o,
					       struct gensio *io));

    // Subclasses of Gensio.  This allows direct allocation of a
    // gensio.

    class GENSIOCPP_DLL_PUBLIC Tcp: public Gensio {
    public:
	Tcp(const Addr &addr, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Tcp &operator=(const Gensio&) = delete;
    private:
	virtual ~Tcp() { }
	Tcp(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_tcp_class(Os_Funcs &o,
				       struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Udp: public Gensio {
    public:
	Udp(const Addr &addr, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Udp &operator=(const Gensio&) = delete;
    private:
	virtual ~Udp() { }
	Udp(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_udp_class(Os_Funcs &o,
				       struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Unix: public Gensio {
    public:
	Unix(const Addr &addr, const char * const args[],
	     Os_Funcs &o, Event *cb);

	Unix &operator=(const Gensio&) = delete;
    private:
	virtual ~Unix() { }
	Unix(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_unix_class(Os_Funcs &o,
					struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Sctp: public Gensio {
    public:
	Sctp(const Addr &addr, const char * const args[],
	     Os_Funcs &o, Event *cb);

	Sctp &operator=(const Gensio&) = delete;
    private:
	virtual ~Sctp() { }
	Sctp(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_sctp_class(Os_Funcs &o,
					struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Stdio: public Gensio {
    public:
	Stdio(const char *const argv[], const char * const args[],
	      Os_Funcs &o, Event *cb);

	Stdio &operator=(const Gensio&) = delete;
    private:
	virtual ~Stdio() { }
	Stdio(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_stdio_class(Os_Funcs &o,
					 struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Pty: public Gensio {
    public:
	Pty(const char *const argv[], const char * const args[],
	    Os_Funcs &o, Event *cb);

	Pty &operator=(const Gensio&) = delete;
    private:
	virtual ~Pty() { }
	Pty(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_pty_class(Os_Funcs &o,
				       struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Echo: public Gensio {
    public:
	Echo(const char * const args[],
	     Os_Funcs &o, Event *cb);

	Echo &operator=(const Gensio&) = delete;
    private:
	virtual ~Echo() { }
	Echo(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_echo_class(Os_Funcs &o,
					struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC File: public Gensio {
    public:
	File(const char * const args[],
	     Os_Funcs &o, Event *cb);

	File &operator=(const Gensio&) = delete;
    private:
	virtual ~File() { }
	File(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_file_class(Os_Funcs &o,
					struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Mdns: public Gensio {
    public:
	Mdns(const char *str, const char * const args[],
	     Os_Funcs &o, Event *cb);

	Mdns &operator=(const Gensio&) = delete;
    private:
	virtual ~Mdns() { }
	Mdns(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_mdns_class(Os_Funcs &o,
					struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Ssl: public Gensio {
    public:
	Ssl(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Ssl &operator=(const Gensio&) = delete;
    private:
	virtual ~Ssl() { }
	Ssl(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_ssl_class(Os_Funcs &o,
				       struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Mux: public Gensio {
    public:
	Mux(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Mux &operator=(const Gensio&) = delete;
    private:
	virtual ~Mux() { }
	Mux(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_mux_class(Os_Funcs &o,
				       struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Certauth: public Gensio {
    public:
	Certauth(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Certauth &operator=(const Gensio&) = delete;
    private:
	virtual ~Certauth() { }
	Certauth(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_certauth_class(Os_Funcs &o,
					    struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Telnet: public Gensio {
    public:
	Telnet(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Telnet &operator=(const Gensio&) = delete;
    private:
	virtual ~Telnet() { }
	Telnet(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_telnet_class(Os_Funcs &o,
					  struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Msgdelim: public Gensio {
    public:
	Msgdelim(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Msgdelim &operator=(const Gensio&) = delete;
    private:
	virtual ~Msgdelim() { }
	Msgdelim(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_msgdelim_class(Os_Funcs &o,
					    struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Relpkt: public Gensio {
    public:
	Relpkt(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Relpkt &operator=(const Gensio&) = delete;
    private:
	virtual ~Relpkt() { }
	Relpkt(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_relpkt_class(Os_Funcs &o,
					  struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Trace: public Gensio {
    public:
	Trace(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Trace &operator=(const Gensio&) = delete;
    private:
	virtual ~Trace() { }
	Trace(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_trace_class(Os_Funcs &o,
					 struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Perf: public Gensio {
    public:
	Perf(Gensio *child, const char * const args[],
	    Os_Funcs &o, Event *cb);

	Perf &operator=(const Gensio&) = delete;
    private:
	virtual ~Perf() { }
	Perf(Os_Funcs &o) : Gensio(o, NULL) { }
	friend Gensio *alloc_perf_class(Os_Funcs &o,
					struct gensio *io);
    };

    //*****************************************************************

    // These are events from a serial port device, client and server.
    // These are documented in sergensio_event.3
    class GENSIOCPP_DLL_PUBLIC Serial_Event: public Event {
    public:
	// Client-side calls, used to report serial line changes.

	// See sergensio_modemstate.3 for details
	virtual void modemstate(unsigned int state) { }
	// See sergensio_linestate.3 for details
	virtual void linestate(unsigned int state) { }

	// Server side calls, used when the client requests changes.  See sergensio_xxx
	virtual void signature(const char *sig, gensiods len) { }
	virtual void flow_state(bool state) { }
	virtual void flush(unsigned int val) { }
	virtual void sync() { }
	virtual void baud(unsigned int speed) { }
	virtual void datasize(unsigned int size) { }
	virtual void parity(unsigned int par) { }
	virtual void stopbits(unsigned int bits) { }
	virtual void flowcontrol(unsigned int flow) { }
	virtual void iflowcontrol(unsigned int flow) { }
	virtual void sbreak(unsigned int sbreak) { }
	virtual void dtr(unsigned int dtr) { }
	virtual void rts(unsigned int dsr) { }

	virtual ~Serial_Event() = default;
    };

    // Done handler for normal serial port operations.
    class GENSIOCPP_DLL_PUBLIC Serial_Op_Done {
    public:
	// Serial operation is finished, err non-zero if an error
	// occurred.  If no error, val is the result.
	virtual void done(Serial_Gensio *sf, int err, unsigned int val) = 0;
	virtual ~Serial_Op_Done() = default;
    };

    // Done handler for signature operations, passes a C string, not a
    // number.
    class GENSIOCPP_DLL_PUBLIC Serial_Op_Sig_Done {
    public:
	virtual void done(Serial_Gensio *sg, int err, const char *sig,
			  unsigned int siglen) = 0;
	virtual ~Serial_Op_Sig_Done() = default;
    };

    // This is a set of operations you can do on a serial port.
    // sergensio_xxx.3 man pages describe each of these.
    //
    // For the methods not ending in _s, the interface is
    // non-blocking, calling the function will start the process of
    // setting the value.  The value will be set and the done object
    // will be called upon completion.  Note that the value may not
    // match what you set if the remote serial port is not capable,
    // the current value is always returned.
    //
    // You can pass a 0 (or NULL in the signature case) into any of
    // these and it will not set the value, but wull return the
    // current value.
    //
    // The valid values for the ones that are not a direct integer
    // (baud, databits, stopbits) are given in the sergensio.h include
    // file above the C versions of these functions.
    //
    // The methods ending in _s a synchronous, they block until the
    // operation completes.
    //
    // For the server side, requests are received in the Serial_Event
    // class, the reponses are sent here.  The done value should
    // always be NULL and is ignore on the server side.
    class GENSIOCPP_DLL_PUBLIC Serial_Gensio: public Gensio {
    public:
	Serial_Gensio(const Serial_Gensio&) = delete;
	Serial_Gensio &operator=(const Serial_Gensio&) = delete;

	void baud(unsigned int baud, Serial_Op_Done *done);
	void datasize(unsigned int size, Serial_Op_Done *done);
	void parity(unsigned int par, Serial_Op_Done *done);
	void stopbits(unsigned int bits, Serial_Op_Done *done);
	void flowcontrol(unsigned int flow, Serial_Op_Done *done);
	void iflowcontrol(unsigned int flow, Serial_Op_Done *done);
	void sbreak(unsigned int sbreak, Serial_Op_Done *done);
	void dtr(unsigned int dtr, Serial_Op_Done *done);
	void rts(unsigned int rts, Serial_Op_Done *done);
	void cts(unsigned int cts, Serial_Op_Done *done);
	void dcd_dsr(unsigned int dcd_dsr, Serial_Op_Done *done);
	void ri(unsigned int ri, Serial_Op_Done *done);
	void signature(const char *sig, unsigned int len,
		       Serial_Op_Sig_Done *done);

	void baud_s(unsigned int *baud);
	void datasize_s(unsigned int *size);
	void parity_s(unsigned int *par);
	void stopbits_s(unsigned int *bits);
	void flowcontrol_s(unsigned int *flow);
	void iflowcontrol_s(unsigned int *flow);
	void sbreak_s(unsigned int *sbreak);
	void dtr_s(unsigned int *dtr);
	void rts_s(unsigned int *rts);
	void cts_s(unsigned int *cts);
	void dcd_dsr_s(unsigned int *dcd_dsr);
	void ri_s(unsigned int *ri);

	// Server side only, for reporting changes
	void flow_state(bool state);
	void modemstate(unsigned int state);
	void linestate(unsigned int state);

    protected:
	virtual void set_gensio(struct gensio *io, bool set_cb);
	Serial_Gensio(Os_Funcs &o, Event *cb) : Gensio(o, cb) { }
	virtual ~Serial_Gensio() { }

    private:
	struct sergensio *sio = NULL;

	GENSIOCPP_DLL_PUBLIC
	friend Gensio *gensio_alloc(struct gensio *io,
				    Os_Funcs &o);
    };

    // Subclasses of Serial_Gensio.  This allows direct allocation of
    // a serial gensio.

    class GENSIOCPP_DLL_PUBLIC Serialdev: public Serial_Gensio {
    public:
	Serialdev(const char *devname, const char * const args[],
		  Os_Funcs &o, Event *cb);

	Serialdev &operator=(const Gensio&) = delete;
    private:
	virtual ~Serialdev() { };
	Serialdev(Os_Funcs &o) : Serial_Gensio(o, NULL) { }
	friend Gensio *alloc_serialdev_class(Os_Funcs &o,
					     struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Serial_Telnet: public Serial_Gensio {
    public:
	Serial_Telnet(Gensio *child, const char * const args[],
		Os_Funcs &o, Event *cb);

	Serial_Telnet &operator=(const Gensio&) = delete;
    private:
	virtual ~Serial_Telnet() { }
	Serial_Telnet(Os_Funcs &o) : Serial_Gensio(o, NULL) { }
	friend Gensio *alloc_telnet_class(Os_Funcs &o,
					  struct gensio *io);
    };

    class GENSIOCPP_DLL_PUBLIC Ipmisol: public Serial_Gensio {
    public:
	Ipmisol(const char *devname, const char * const args[],
		Os_Funcs &o, Event *cb);

	Ipmisol &operator=(const Gensio&) = delete;
    private:
	virtual ~Ipmisol() { }
	Ipmisol(Os_Funcs &o) : Serial_Gensio(o, NULL) { }
	friend Gensio *alloc_ipmisol_class(Os_Funcs &o,
					   struct gensio *io);
    };

    //*****************************************************************

    class Accepter;

    // An object of this class is given to an Accepter to handle
    // events from that accepter.
    class GENSIOCPP_DLL_PUBLIC Accepter_Event {
    public:

	// A new connection has come in, the new gensio is in g.  You
	// must provide this.
	virtual void new_connection(Accepter *acc, Gensio *g) = 0;

	// An error has occurred in the accepter that cannot be
	// reported as a return value.
	virtual void log(enum gensio_log_levels level, char *str, va_list args)
	{
	}

	// When authenticating a new incoming gensio, these are used
	// to deliver the certification events for the gensio.  Note
	// that the delivered gensio is not operational, it can only
	// be used to fetch username, certificate info, etc.  See
	// gensio_event.3 for details on these.
	virtual int auth_begin(Accepter *acc, Gensio *g)
	{ return GE_NOTSUP; }
	virtual int precert_verify(Accepter *acc, Gensio *g)
	{ return GE_NOTSUP; }
	virtual int postcert_verify(Accepter *acc, Gensio *g,
				    int err, const char *errstr)
	{ return GE_NOTSUP; }
	virtual int password_verify(Accepter *acc, Gensio *g,
				    const char *password,
				    gensiods password_len)
	{ return GE_NOTSUP; }
	virtual int request_password(Accepter *acc, Gensio *g,
				     char *password, gensiods *password_len)
	{ return GE_NOTSUP; }

	// The free() operation for accepter this object is assigned to
	// has finished and the data will immediately be freed.
	virtual void freed() { };

	virtual ~Accepter_Event() = default;
    };

    class GENSIOCPP_DLL_PUBLIC Accepter_Done {
    public:
	virtual void done(Accepter *acc) = 0;
	virtual ~Accepter_Done() = default;
    };

    // Allocate a new accepter object based on the given string.  See
    // gensio.5 for details on the format of this string.  Note that
    // the returned object will be a subclass of Accepter.
    GENSIOCPP_DLL_PUBLIC
    Accepter *gensio_acc_alloc(std::string str,
			       Os_Funcs &o,
			       Accepter_Event *cb);

    // Like above, but stack the accepter on top of an existing
    // accepter stack given in child.
    GENSIOCPP_DLL_PUBLIC
    Accepter *gensio_acc_alloc(Accepter *child, std::string str,
			       Os_Funcs &o,
			       Accepter_Event *cb);

    class GENSIOCPP_DLL_PUBLIC Accepter {
    public:
	Accepter(const Accepter&) = delete;
	Accepter &operator=(const Accepter&) = delete;

	// Unfortunately, you can't use the destructor with this class
	// because of race conditions.  When you call this, there may
	// be other things pending in callbacks, and there's no way to
	// delay the free of the object in a destructor without
	// blocking.  So you call free here, and when the freed
	// function in the event handler gets called, the free is
	// complete.
	void free();

	// Set the callback object.  Not really very useful, and you
	// shouldn't do this while the accepter is started.
	inline void set_callback(Accepter_Event *cb) { gcb = cb; }

	// Start accepting connections.  You still need to set the
	// enable to actual receive connections, this opens the
	// accepting sockets or whatever and gets things ready.
	void startup();

	// Shutdown the accepter, closing the accept socket or
	// whatever is required for the gensio.  The done will be
	// called when the shutdown is complete.
	void shutdown(Accepter_Done *done);

	// Shutdown and block until it completes.
	void shutdown_s();

	// Disable the accepter, see gensio_acc_disable.3 for details.
	// This is not for normal use.
	void disable() { gensio_acc_disable(acc); }

	// Set the enable for receiving accepts.  Note that if you
	// disable this, there may still be callbacks pending on the
	// gensio.  Don't assume that when this returns there are no
	// callbacks pending.
	inline void set_callback_enable(bool enabled)
	{ gensio_acc_set_accept_callback_enable(acc, enabled); }

	// Set the enable/disable, but call the done function when the
	// enable/disable completes.  Not really useful for enable,
	// but it can let you know that no callbacks are pending on a
	// disable.
	void set_callback_enable_cb(bool enabled, Accepter_Done *done);

	// Synchronous enable/disable, won't return until the
	// enable/disable completes.
	void set_callback_enable_s(bool enabled);

	// Special control operations on the accepter, see
	// gensio_acc_control.3 for details.
	void control(int depth, bool get, unsigned int option,
		     char *data, gensiods *datalen);

	// Put an accepter in synchronous mode.  With this, all
	// accepts must be received with accept_s()
	inline void set_sync() { gensio_acc_set_sync(acc); }

	// Wait for an accept to come in.  You must have called
	// set_sync() first.  Wait for up to timeout time.  If this
	// times out, it returns GE_TIMEDOUT, otherwise it returns
	// zero.  Any other errors get thrown as a gensio_error.
	// The new gensio is returned in g.
	int accept_s(gensio_time *timeout, Gensio **g);

	// Like accept_s, but also returns if a signal is recevied.
	// In that cast it returns GE_INTERRUPTED.
	int accept_s_intr(gensio_time *timeout, Gensio **g);

	// Create a new gensio as if it came from this accepter.  This
	// doesn't have much meaning except for UDP.  For UDP, it uses
	// the socket of the accepter to create the connection, so
	// packets will come from this accepter's socket and packets
	// received on this accepters's socket from the given remote
	// end will be sent to this gensio.
	Gensio *str_to_gensio(std::string str, Event *cb);

	// Return the type string for the accepter.
	inline const char *get_type(unsigned int depth)
	{ return gensio_acc_get_type(acc, depth); }

	// Report capabilities of gensios from this accepter, see
	// gensio_acc_is_xxx.3 for details.
	inline bool is_reliable() { return gensio_acc_is_reliable(acc); }
	inline bool is_packet() { return gensio_acc_is_packet(acc); }
	inline bool is_message() { return gensio_acc_is_message(acc); }

	// Return a child of this accpeter.  A depth of zero returns
	// this accepter, 1 returns its child, 2 its grandchild, etc.
	// Returns NULL if the depth is deeper than the accepter stack.
	Accepter *get_child(unsigned int depth);

	// Return the local side port for the accepter.  This is
	// useful if you create a gensio with the port set to zero,
	// letting the code choose a port.  Then you can fetch the
	// actual port with this.  Note that some accepter types will
	// return something besides a number here (ie unix).
	std::string get_port();

	// Get the os funcs for this accepter.
	inline Os_Funcs &get_os_funcs() { return go; }

	// Get the event handler for this accepter.
	inline class Accepter_Event *get_cb() { return gcb; }

	// Return the raw accepter.  Don't use this, it's for subclasses
	// to use.
	struct gensio_accepter *get_accepter() { return acc; }

    protected:
	virtual void set_accepter(struct gensio_accepter *acc, bool set_cb);
    Accepter(Os_Funcs &o, Accepter_Event *cb) : go(o), gcb(cb) { }
	virtual ~Accepter() { }

    private:
	struct gensio_accepter *acc = NULL;
	Os_Funcs go;
	Accepter_Event *gcb;

	GENSIOCPP_DLL_PUBLIC
	friend Accepter *gensio_acc_alloc(struct gensio_accepter *acc,
					  Os_Funcs &o);
	GENSIOCPP_DLL_PUBLIC
	friend Accepter *gensio_acc_alloc(Accepter *child,
					  std::string str,
					  Os_Funcs &o,
					  Accepter_Event *cb);
	friend void gensio_acc_cpp_freed(struct gensio_accepter *acc,
					 struct gensio_acc_frdata *frdata);
    };

    // Add a new accepter class type.  This is for user-added gensios,
    // so they can tie in to the C++ frameworks.
    GENSIOCPP_DLL_PUBLIC
    void gensio_add_accepter_class(
			  const char *name,
			  Accepter *(*allocator)(Os_Funcs &o,
						 struct gensio_accepter *a));

    // Subclasses of Accepter.  These allow you to directly allocate
    // an Accepter, stack new accepters on top of old ones, etc.

    class GENSIOCPP_DLL_PUBLIC Tcp_Accepter : public Accepter {
    public:
	Tcp_Accepter(const Addr &addr, const char * const args[],
		     Os_Funcs &o, Accepter_Event *cb);
	Tcp_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Tcp_Accepter() { }
	Tcp_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_tcp_accepter_class(Os_Funcs &o,
						  struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Udp_Accepter : public Accepter {
    public:
	Udp_Accepter(const Addr &addr, const char * const args[],
		     Os_Funcs &o, Accepter_Event *cb);
	Udp_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Udp_Accepter() { }
	Udp_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_udp_accepter_class(Os_Funcs &o,
						  struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Unix_Accepter : public Accepter {
    public:
	Unix_Accepter(const Addr &addr, const char * const args[],
		      Os_Funcs &o, Accepter_Event *cb);
	Unix_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Unix_Accepter() { }
	Unix_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_unix_accepter_class(Os_Funcs &o,
						   struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Sctp_Accepter : public Accepter {
    public:
	Sctp_Accepter(const Addr &addr, const char * const args[],
		     Os_Funcs &o, Accepter_Event *cb);
	Sctp_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Sctp_Accepter() { }
	Sctp_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_sctp_accepter_class(Os_Funcs &o,
						   struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Stdio_Accepter : public Accepter {
    public:
	Stdio_Accepter(const char * const args[],
		       Os_Funcs &o, Accepter_Event *cb);
	Stdio_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Stdio_Accepter() { }
	Stdio_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_stdio_accepter_class(Os_Funcs &o,
						    struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Dummy_Accepter : public Accepter {
    public:
	Dummy_Accepter(const char * const args[],
		       Os_Funcs &o, Accepter_Event *cb);
	Dummy_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Dummy_Accepter() { }
	Dummy_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_dummy_accepter_class(Os_Funcs &o,
						    struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Conacc_Accepter : public Accepter {
    public:
	Conacc_Accepter(const char *str, const char * const args[],
		       Os_Funcs &o, Accepter_Event *cb);
	Conacc_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Conacc_Accepter() { }
	Conacc_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_conacc_accepter_class(Os_Funcs &o,
						     struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Ssl_Accepter : public Accepter {
    public:
	Ssl_Accepter(Accepter *child, const char * const args[],
		     Os_Funcs &o, Accepter_Event *cb);
	Ssl_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Ssl_Accepter() { }
	Ssl_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_ssl_accepter_class(Os_Funcs &o,
						  struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Mux_Accepter : public Accepter {
    public:
	Mux_Accepter(Accepter *child, const char * const args[],
		     Os_Funcs &o, Accepter_Event *cb);
	Mux_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Mux_Accepter() { }
	Mux_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_mux_accepter_class(Os_Funcs &o,
						  struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Certauth_Accepter : public Accepter {
    public:
	Certauth_Accepter(Accepter *child, const char * const args[],
			  Os_Funcs &o, Accepter_Event *cb);
	Certauth_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Certauth_Accepter() { }
	Certauth_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_certauth_accepter_class(
				Os_Funcs &o,
				struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Telnet_Accepter : public Accepter {
    public:
	Telnet_Accepter(Accepter *child, const char * const args[],
			Os_Funcs &o, Accepter_Event *cb);
	Telnet_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Telnet_Accepter() { }
	Telnet_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_telnet_accepter_class(
				Os_Funcs &o,
				struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Msgdelim_Accepter : public Accepter {
    public:
	Msgdelim_Accepter(Accepter *child, const char * const args[],
			  Os_Funcs &o, Accepter_Event *cb);
	Msgdelim_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Msgdelim_Accepter() { }
	Msgdelim_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_msgdelim_accepter_class(
				Os_Funcs &o,
				struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Relpkt_Accepter : public Accepter {
    public:
	Relpkt_Accepter(Accepter *child, const char * const args[],
			Os_Funcs &o, Accepter_Event *cb);
	Relpkt_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Relpkt_Accepter() { }
	Relpkt_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_relpkt_accepter_class(
				Os_Funcs &o,
				struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Trace_Accepter : public Accepter {
    public:
	Trace_Accepter(Accepter *child, const char * const args[],
		       Os_Funcs &o, Accepter_Event *cb);
	Trace_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Trace_Accepter() { }
	Trace_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_trace_accepter_class(
				Os_Funcs &o,
				struct gensio_accepter *a);
    };

    class GENSIOCPP_DLL_PUBLIC Perf_Accepter : public Accepter {
    public:
	Perf_Accepter(Accepter *child, const char * const args[],
		      Os_Funcs &o, Accepter_Event *cb);
	Perf_Accepter &operator=(const Gensio&) = delete;
    private:
	virtual ~Perf_Accepter() { }
	Perf_Accepter(Os_Funcs &o) : Accepter(o, NULL) { }
	friend Accepter *alloc_perf_accepter_class(
				Os_Funcs &o,
				struct gensio_accepter *a);
    };

    //*****************************************************************

    // This is a waiter class.  You use one of these to wait for
    // things while running the event-driven code.
    class GENSIOCPP_DLL_PUBLIC Waiter {
    public:
	Waiter(Os_Funcs &io) : o(io)
	{
	    waiter = o->alloc_waiter(o);
	    if (!waiter)
		throw std::bad_alloc();
	}
	~Waiter() { o->free_waiter(waiter); }

	// Add one wakeup to the waiter.
	void wake() { o->wake(waiter); }

	// Wait for count wakeups to be delivered to the waiter, up to
	// timeout time.  If the timeout occurs before count events
	// are delivered, none of the wakeups are used.  If timeout is
	// NULL, wait forever.  This will return either 0 if woken or
	// GE_TIMEDOUT if not woken before the timeout.  Any other
	// error will result in a gensio_error being thrown.
	int wait(unsigned int count, gensio_time *timeout);

	// Like the above but installs the given sigmask (if not NULL)
	// and will return if a interrupt (signal) occurs while
	// waiting.  This will also return GE_INTERRUPTED in addition
	// to 0 and GE_TIMEDOUT, an interrupt will not throw an
	// exception.
	int wait_intr(unsigned int count, gensio_time *timeout);

    private:
	Os_Funcs o;
	struct gensio_waiter *waiter;
    };

}

#endif /* GENSIO_CPP_INCLUDE */
