//
//  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 <vector>
#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>
#include <gensio/gensio_mdns.h>
#include <gensio/gensio_osops.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 GENSIOCPP_DLL_PUBLIC 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;
    };

    GENSIOCPP_DLL_PUBLIC
    std::string err_to_string(int err);

    class GENSIOCPP_DLL_PUBLIC Os_Funcs_Log_Handler {
    public:
	virtual void log(enum gensio_log_levels level, const std::string log)
		= 0;
	virtual ~Os_Funcs_Log_Handler() = default;
    };
    GENSIOCPP_DLL_PUBLIC
    std::string log_level_to_str(enum gensio_log_levels level);

    GENSIOCPP_DLL_PUBLIC
    void set_log_mask(int mask);

    GENSIOCPP_DLL_PUBLIC
    int get_log_mask();

    // This is a wrapper for gensio_os_funcs that makes using it a lot
    // cleaner.  The default constructor automatically allocates the
    // default os handler, 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, Os_Funcs_Log_Handler *logger = NULL);

	// This is for use by subclasses.  Don't use one with an empty
	// parameter list, that can be used accidentally.
	Os_Funcs(bool dummy) { };

	void set_log_handler(Os_Funcs_Log_Handler *ilogger) {
	    logger = ilogger;
	};

	Os_Funcs_Log_Handler *get_log_handler() { return logger; };

	// 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();

	// If you allocate your own subclass of this, you must call
	// this to set it up.  Note that this takes over freeing the
	// OS handler and the logger.
	void init(struct gensio_os_funcs *o,
		  Os_Funcs_Log_Handler *logger = NULL);

	Os_Funcs& operator=(const Os_Funcs &o);

	Os_Funcs(const Os_Funcs &o);

	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; }

	virtual ~Os_Funcs();

	void log(enum gensio_log_levels level, const std::string log) {
	    gensio_log(osf, level, "%s", log.c_str());
	}

	unsigned int get_refcount() { return *refcnt; }

    private:
	void refcount_from(const Os_Funcs *o);
	Os_Funcs_Log_Handler *logger = NULL;
	struct gensio_os_funcs *osf = NULL;
	struct gensio_os_proc_data *proc_data = NULL;
	std::atomic<unsigned int> *refcnt = NULL;
    };

    // 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);

	// Allocate an address based on the low-level address.
	// We assume ethe port is set in this case.
	Addr(struct gensio_addr *iaddr) {
	    gaddr = iaddr;
	    is_port_set = true;
	}

	// 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; }

	virtual ~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) const
	    { gensio_addr_getaddr(gaddr, oaddr, len); }
	int get_nettype() const { return gensio_addr_get_nettype(gaddr); }
	bool family_supports(int family, int flags) const
	    { return gensio_addr_family_supports(gaddr, family, flags); }
	std::string to_string() const;
	std::string to_string_all() const;

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

	bool port_set() const { return is_port_set; }

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

    // This is a simple vector of unsigned chars, we use it to pass
    // data to the read() call to avoid copying the data.
    class GENSIOCPP_DLL_PUBLIC SimpleUCharVector {
    public:
	SimpleUCharVector() { }
	SimpleUCharVector(unsigned char *idata, gensiods ilen) {
	    buffer = idata;
	    len = ilen;
	    datalen = ilen;
	}

	virtual ~SimpleUCharVector() { }

	gensiods size() const { return len; }
	void resize(gensiods ilen) { len = ilen; }
	gensiods capacity() const { return datalen; }
	unsigned char *data() const { return buffer; }
	unsigned char operator[](gensiods pos) const { return buffer[pos]; }
	void setbuf(unsigned char *ibuffer, gensiods ilen) {
	    buffer = ibuffer;
	    len = ilen;
	    datalen = ilen;
	};
    private:
	unsigned char *buffer = NULL;
	gensiods len = 0;
	gensiods datalen = 0;
    };

    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 gensiods read(int err,
			      const SimpleUCharVector data,
			      const char *const *auxdata) = 0;

	// Data can be written to the gensio.  You must implement this.
	virtual void write_ready() = 0;

	// A new channel is available on the gensio.  By default this
	// deletes the new channel.
	virtual int new_channel(Gensio *newg, 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 this does nothing.
	virtual void send_break() { };

	// Various authentication operations.  See gensio_event.3 for
	// details on what these do.
	virtual int auth_begin() { return GE_NOTSUP; }
	virtual int precert_verify() { return GE_NOTSUP; }
	virtual int postcert_verify(int err, const char *errstr)
	    { return GE_NOTSUP; }
	virtual int password_verify(const std::string val)
	    { return GE_NOTSUP; }
	virtual int request_password(gensiods maxsize, std::string &retval)
	    { return GE_NOTSUP; }
	virtual int verify_2fa(const std::vector<unsigned char> data)
	    { return GE_NOTSUP; }
	virtual int request_2fa(std::vector<unsigned char> &retval)
	    { 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(int event, int err,
			       std::vector<unsigned char> &userdata,
			       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.  This
	// is generally where you would free the event handler for a
	// gensio.  Obviously, the Event object here must stay around
	// until the Gensio is 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_Open_Done {
    public:
	virtual void open_done(int err) = 0;
	virtual ~Gensio_Open_Done() = default;
    };

    // Used for done handlers for gension operations that cannot fail.
    class GENSIOCPP_DLL_PUBLIC Gensio_Close_Done {
    public:
	virtual void close_done() = 0;
	virtual ~Gensio_Close_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 = NULL);

    // 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 = NULL);

    class GENSIOCPP_DLL_PUBLIC Raw_Event_Handler {
    public:
	virtual ~Raw_Event_Handler() = default;

	virtual int handle(Gensio *g, struct gensio *io,
			   int event, int err,
			   unsigned char *buf, gensiods *buflen,
			   const char *const *auxdata) = 0;

	// New channels are routed through here so they can be set up properly.
	virtual int new_channel(Event *e, Gensio *new_chan,
				const char *const *auxdata) = 0;

	// Final free calls come through here.
	virtual void freed(Event *e) = 0;
    };

    // 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 = NULL) { gcb = cb; }

	// Open a gensio.  When the done handler is called it is ready
	// (unless it reports an error).
	void open(Gensio_Open_Done *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_Open_Done *done);

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

	// Write datalen bytes of data to the given gensio.  The
	// actual number of bytes written is returned.  The
	// meaning of auxdata depends on the gensio, see gensio.5 for
	// detais.
	gensiods write(const void *data, gensiods datalen,
		       const char *const *auxdata);
	gensiods write(const std::vector<unsigned char> data,
		       const char *const *auxdata);
	gensiods write(const SimpleUCharVector data,
		       const char *const *auxdata);

	// Like the above, but use a scatter-gather structure to write
	// the data.
	gensiods write(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 = NULL);

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

	// 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.
	int 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 various characterstics about a gensio.  See the
	// gensio_is_xxx.3 man pages for details.
	inline bool is_client() const { return gensio_is_client(io); }
	inline bool is_reliable() const { return gensio_is_reliable(io); }
	inline bool is_packet() const { return gensio_is_packet(io); }
	inline bool is_authenticated() const
		{ return gensio_is_authenticated(io); }
	inline bool is_encrypted() const { return gensio_is_encrypted(io); }
	inline bool is_message() const { return gensio_is_message(io); }

	virtual bool is_serial() { return false; }

	// 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 vector's capacity should
	// be set to the max data to read, the length will be set to
	// the actual read length. 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.  If intr is true, it will return
	// GE_INTERRUPTED on a signal.  All other errors throw
	// gensio_error.
	int read_s(std::vector<unsigned char> &rvec,
		   gensio_time *timeout = NULL, bool intr = false);
	int read_s(SimpleUCharVector &data,
		   gensio_time *timeout = NULL, bool intr = false);

	// 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.  If intr is true, a signal
	// will cause this to return GE_INTERRUPTED.
	int write_s(gensiods *count, const void *data, gensiods datalen,
		    gensio_time *timeout = NULL, bool intr = false);
	int write_s(gensiods *count, const std::vector<unsigned char> data,
		    gensio_time *timeout = NULL, bool intr = false);
	int write_s(gensiods *count, const SimpleUCharVector data,
		    gensio_time *timeout = NULL, bool intr = false);

	// 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; }

	// This allows the user to intercept raw events, it is primarily
	// used to help other language bindings tie in things they need.
	Raw_Event_Handler *raw_event_handler = NULL;

    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) { }
	Gensio(struct gensio *iio, Os_Funcs &o): go(o), io(iio) { }
	virtual ~Gensio() {
	    if (raw_event_handler)
		delete raw_event_handler;
	}

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

	GENSIOCPP_DLL_PUBLIC
	friend Gensio *gensio_alloc(struct gensio *io,
				    Os_Funcs &o);
	friend class Main_Raw_Accepter_Event_Handler;
	friend void gensio_cpp_freed(struct gensio *io,
				     struct gensio_frdata *frdata);
	friend class Serial_Gensio;
    };

    // A wrapper to allow RAII to be used on a Gensio.  If you use
    // this, you should either make sure the gensio is closed before
    // the end of this object's lifetime, or you should not free it
    // and let this destructor close it.  What you should *not* do is
    // an asynchronous free then call this, because the close here
    // will fail and then the free won't be done after the destructor
    // returns.
    class GENSIOCPP_DLL_PUBLIC GensioW {
    public:
	GensioW(std::string str, Os_Funcs &o, Event *cb = NULL) {
	    io = gensio_alloc(str, o, cb);
	}
	GensioW(Gensio *child, std::string str, Os_Funcs &o, Event *cb = NULL) {
	    io = gensio_alloc(child, str, o, cb);
	}
	// Take over an existing gensio.
	GensioW(Gensio *iio): io(iio) { }
	~GensioW() {
	    try {
		io->close_s();
	    } catch (gensio_error e) {
		// Ignore the error, already closed.
	    }
	    // Make sure the freed handler doesn't get called.
	    io->set_event_handler(NULL);
	    io->free();
	}
	Gensio * operator->() { return io; }
	Gensio * operator&() { return io; }
    private:
	Gensio *io;
    };

    // 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));

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

    // 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) { }
	virtual void flow_state(bool state) { }
	virtual void sync() { }

	// Server side calls, used when the client requests changes.  See sergensio_xxx
	virtual void signature(const std::vector<unsigned char> data) { }
	virtual void flush(unsigned int val) { }
	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 rts) { }

	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 serial_op_done(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 serial_op_sig_done(int err,
				const std::vector<unsigned char> data) = 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.  You cannot
    // directly allocate one of these.  If you allocate a gensio that
    // is serial-capable, you can cast it to this.
    //
    // 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 are 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;

	// Non-blocking variants of these calls
	void flush(unsigned int flush);
	void baud(unsigned int baud, Serial_Op_Done *done = NULL);
	void datasize(unsigned int size, Serial_Op_Done *done = NULL);
	void parity(unsigned int par, Serial_Op_Done *done = NULL);
	void stopbits(unsigned int bits, Serial_Op_Done *done = NULL);
	void flowcontrol(unsigned int flow, Serial_Op_Done *done = NULL);
	void iflowcontrol(unsigned int flow, Serial_Op_Done *done = NULL);
	void sbreak(unsigned int sbreak, Serial_Op_Done *done = NULL);
	void dtr(unsigned int dtr, Serial_Op_Done *done = NULL);
	void rts(unsigned int rts, Serial_Op_Done *done = NULL);
	void cts(unsigned int cts, Serial_Op_Done *done = NULL);
	void dcd_dsr(unsigned int dcd_dsr, Serial_Op_Done *done = NULL);
	void ri(unsigned int ri, Serial_Op_Done *done = NULL);
	void signature(const std::vector<unsigned char> sig,
		       Serial_Op_Sig_Done *done = NULL);

	// If you pass in a valid timeout, this will return GE_TIMEDOUT
	// on a timeout.  If you set intr to true, it will return
	// GE_INTERRUPTED if a signal comes in.  On all other errors
	// it raises an exception.
	int baud_s(unsigned int *baud, gensio_time *timeout = NULL,
		   bool intr = false);
	int datasize_s(unsigned int *size, gensio_time *timeout = NULL,
		       bool intr = false);
	int parity_s(unsigned int *par, gensio_time *timeout = NULL,
		     bool intr = false);
	int stopbits_s(unsigned int *bits, gensio_time *timeout = NULL,
		       bool intr = false);
	int flowcontrol_s(unsigned int *flow, gensio_time *timeout = NULL,
			  bool intr = false);
	int iflowcontrol_s(unsigned int *flow, gensio_time *timeout = NULL,
			   bool intr = false);
	int sbreak_s(unsigned int *sbreak, gensio_time *timeout = NULL,
		     bool intr = false);
	int dtr_s(unsigned int *dtr, gensio_time *timeout = NULL,
		  bool intr = false);
	int rts_s(unsigned int *rts, gensio_time *timeout = NULL,
		  bool intr = false);
	int cts_s(unsigned int *cts, gensio_time *timeout = NULL,
		  bool intr = false);
	int dcd_dsr_s(unsigned int *dcd_dsr, gensio_time *timeout = NULL,
		      bool intr = false);
	int ri_s(unsigned int *ri, gensio_time *timeout = NULL,
		 bool intr = false);

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

	virtual bool is_serial() { return true; }

    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);
    };

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

    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(Gensio *newg) = 0;

	// An error has occurred in the accepter that cannot be
	// reported as a return value.
	virtual void log(enum gensio_log_levels level, const std::string log)
	{
	}

	// 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(Gensio *tmpg)
	{ return GE_NOTSUP; }
	virtual int precert_verify(Gensio *tmpg)
	{ return GE_NOTSUP; }
	virtual int postcert_verify(Gensio *tmpg,
				    int err, const char *errstr)
	{ return GE_NOTSUP; }
	virtual int password_verify(Gensio *tmpg,
				    const std::string val)
	{ return GE_NOTSUP; }
	virtual int request_password(Gensio *tmpg, gensiods maxsize,
				     std::string &retval)
	{ return GE_NOTSUP; }
	virtual int verify_2fa(Gensio *tmpg,
			       const std::vector<unsigned char> data)
	    { return GE_NOTSUP; }
	virtual int request_2fa(Gensio *tmpg,
				std::vector<unsigned char> &retval)
	    { return GE_NOTSUP; }

	// The free() operation for accepter this object is assigned
	// to has finished and the data will immediately be freed.
	// Usually used to free the Accepter Event object.  Like the
	// one for the Event class, see that for details.
	virtual void freed() { };

	virtual ~Accepter_Event() = default;
    };

    class GENSIOCPP_DLL_PUBLIC Accepter_Shutdown_Done {
    public:
	virtual void shutdown_done() = 0;
	virtual ~Accepter_Shutdown_Done() = default;
    };

    class GENSIOCPP_DLL_PUBLIC Accepter_Enable_Done {
    public:
	virtual void enable_done() = 0;
	virtual ~Accepter_Enable_Done() = default;
    };

    class GENSIOCPP_DLL_PUBLIC Raw_Accepter_Event_Handler {
    public:
	virtual ~Raw_Accepter_Event_Handler() = default;

	virtual int handle(Accepter *a, int event, void *data) = 0;

	// New connections are routed through here so they can be set
	// up properly.
	virtual void new_connection(Accepter_Event *e, Gensio *newg) = 0;

	// Freed accepters are routed through here so they can be shut
	// down properly.
	virtual void freed(Accepter_Event *e) = 0;
    };

    // 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 = NULL);

    // 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 = NULL);

    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_event_handler(Accepter_Event *cb = NULL) { 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_Shutdown_Done *done = NULL);

	// 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/disable, but call the done function when the
	// enable/disable completes, unless done is NULL.  Using done
	// is not really useful for enable, but it can let you know
	// that no callbacks are pending on a disable.  If you don't
	// have a done handler on a disable, you won't know when all
	// the callbacks are done.
	void set_callback_enable(bool enabled,
				 Accepter_Enable_Done *done = NULL);

	// 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.
	int 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 **gret, gensio_time *timeout = NULL,
		     bool intr = false);

	// 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 = NULL);

	// 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() const { return gensio_acc_is_reliable(acc); }
	inline bool is_packet() const { return gensio_acc_is_packet(acc); }
	inline bool is_message() const { return gensio_acc_is_message(acc); }

	// 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() const;

	// 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() const { return gcb; }

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

	// This allows the user to intercept raw events, it is primarily
	// used to help other language bindings tie in things they need.
	Raw_Accepter_Event_Handler *raw_event_handler = NULL;

    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() {
	    if (raw_event_handler)
		delete raw_event_handler;
	}

    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 void gensio_acc_cpp_freed(struct gensio_accepter *acc,
					 struct gensio_acc_frdata *frdata);
    };

    // A wrapper to allow RAII to be used on a Gensio.  If you use
    // this, you should either make sure the gensio is closed before
    // the end of this object's lifetime, or you should not free it
    // and let this destructor close it.  What you should *not* do is
    // an asynchronous free then call this, because the close here
    // will fail and then the free won't be done after the destructor
    // returns.
    class GENSIOCPP_DLL_PUBLIC AccepterW {
    public:
	AccepterW(std::string str, Os_Funcs &o, Accepter_Event *cb = NULL) {
	    acc = gensio_acc_alloc(str, o, cb);
	}
	AccepterW(Accepter *child, std::string str, Os_Funcs &o,
		  Accepter_Event *cb = NULL) {
	    acc = gensio_acc_alloc(child, str, o, cb);
	}
	// Take over an existing accepter.
	AccepterW(Accepter *iacc): acc(iacc) { }
	~AccepterW() {
	    try {
		acc->shutdown_s();
	    } catch (gensio_error e) {
		// Ignore the error, already closed.
	    }
	    // Make sure the freed handler doesn't get called.
	    acc->set_event_handler(NULL);
	    acc->free();
	}
	Accepter * operator->() { return acc; }
    private:
	Accepter *acc;
    };

    // 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));

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

    // 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);
	~Waiter();

	// Add one wakeup to the waiter.
	void wake();

	// 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.  If intr is
	// true it will return GE_INTERRUPTED if a signal comes in.
	// Any other error will result in a gensio_error being thrown.
	int wait(unsigned int count, gensio_time *timeout = NULL,
		 bool intr = false);

	Os_Funcs get_os_funcs() { return o; }

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

    // Access to the MDNS code

    class MDNS;
    class MDNS_Service;
    class MDNS_Watch;
    class MDNS_Watch_Event;

    // This class is used to know when an MDNS object has finished the
    // shutdown operation and will not call any more callbacks.
    class GENSIOCPP_DLL_PUBLIC MDNS_Free_Done {
    public:
	// Called when the shutdown is complete.  The mdns object may be
	// freed when this returns.
	virtual void mdns_free_done() = 0;

	virtual ~MDNS_Free_Done() = default;
    protected:
	MDNS *m;
    private:
	friend void mdns_free_done(struct gensio_mdns *m, void *userdata);
	friend class MDNS;
    };

    class GENSIOCPP_DLL_PUBLIC Raw_MDNS_Event_Handler {
    public:
	virtual ~Raw_MDNS_Event_Handler() = default;

	virtual void handle(MDNS_Watch_Event *e,
			    enum gensio_mdns_data_state state,
			    int interfacenum, int ipdomain,
			    const char *name, const char *mtype,
			    const char *domain, const char *host,
			    const struct gensio_addr *addr,
			    const char * const *txt) = 0;
	virtual void set_parent(Raw_MDNS_Event_Handler *parent) { }
    };

    class GENSIOCPP_DLL_PUBLIC MDNS {
    public:
	MDNS(Os_Funcs &o);

	inline Os_Funcs &get_os_funcs() { return go; }

	// Convenience functions to allocate a service and a watch.
	MDNS_Service *add_service(int interfacenum, int ipdomain,
				  const char *name, const char *mtype,
				  const char *domain, const char *host,
				  int port, const char * const *txt);
	MDNS_Watch *add_watch(int interfacenum, int ipdomain,
			      const char *name, const char *mtype,
			      const char *domain, const char *host,
			      MDNS_Watch_Event *event,
			      Raw_MDNS_Event_Handler *evh = NULL);

	// Like a gensio, you cannot directly delete an MDNS object.
	// It may be in callbacks.  You have to go through a special
	// free operation.  See the Gensio free() method for details.
	void free(MDNS_Free_Done *done = NULL);
    protected:
	virtual ~MDNS() { }
	Os_Funcs go;
    private:
	struct gensio_mdns *m;
	friend class MDNS_Service;
	friend class MDNS_Watch;
	friend void mdns_free_done(struct gensio_mdns *m, void *userdata);
    };

    // A class representing an MDNS service.  A wrapper around
    // gensio_mdns_add/remove_service(), see man pages on those
    // functions for details.
    class GENSIOCPP_DLL_PUBLIC MDNS_Service {
    public:
	MDNS_Service(MDNS *m, int interfacenum, int ipdomain,
		     const char *name, const char *mtype,
		     const char *domain, const char *host,
		     int port, const char * const *txt);
	~MDNS_Service();
    private:
	struct gensio_mdns_service *s;
    };

    // This class is used to know when an MDNS_Watch object has
    // finished the shutdown operation and will not call any more
    // callbacks.
    class GENSIOCPP_DLL_PUBLIC MDNS_Watch_Free_Done {
    public:
	// Called when the shutdown is complete.  The mdns object may be
	// freed when this returns.
	virtual void mdns_watch_free_done() = 0;

	virtual ~MDNS_Watch_Free_Done() = default;
    private:
	MDNS_Watch *w = NULL;
        friend void mdns_watch_free_done(struct gensio_mdns_watch *w,
					 void *userdata);
	friend class MDNS_Watch;
    };

    // This handles events from an mdns watch informing you that it
    // has a new MDNS entry.  Analogous to gensio_mdns_watch_cb, see
    // the gensio_mdns_add_watch() man page for details.
    class GENSIOCPP_DLL_PUBLIC MDNS_Watch_Event {
    public:
	virtual void event(enum gensio_mdns_data_state state,
			   int interfacenum, int ipdomain,
			   const char *name, const char *mtype,
			   const char *domain, const char *host,
			   const Addr *addr, const char * const *txt) = 0;
	virtual ~MDNS_Watch_Event() = default;
    private:
	MDNS_Watch *w;
	friend class MDNS_Watch;
	friend void mdns_watch_event(struct gensio_mdns_watch *w,
				     enum gensio_mdns_data_state state,
				     int interfacenum, int ipdomain,
				     const char *name, const char *mtype,
				     const char *domain, const char *host,
				     const struct gensio_addr *addr,
				     const char * const *txt, void *userdata);
    };

    // A class representing an MDNS service.  A wrapper around
    // gensio_mdns_add/remove_watch(), see man pages on those
    // functions for details.
    class GENSIOCPP_DLL_PUBLIC MDNS_Watch {
    public:
	// If you have a raw event handler, you must supply it here.
	// This avoids race conditions between creating the watch and
	// installing the new raw event handler.  This is not a
	// problem for gensios because events aren't enabled at
	// startup, but they are for MDNS watches.
	MDNS_Watch(MDNS *m, int interfacenum, int ipdomain,
		   const char *name, const char *mtype,
		   const char *domain, const char *host,
		   MDNS_Watch_Event *event,
		   Raw_MDNS_Event_Handler *raw_event_handler = NULL);

	inline Os_Funcs &get_os_funcs() { return m->get_os_funcs(); }

	// Like a gensio, you cannot directly delete an MDNS_Watch object.
	// It may be in callbacks.  You have to go through a special
	// free operation.  See the Gensio free() method for details.
	void free(MDNS_Watch_Free_Done *done = NULL);

	// This allows the user to intercept raw events, it is primarily
	// used to help other language bindings tie in things they need.
	Raw_MDNS_Event_Handler *raw_event_handler = NULL;

    protected:
	virtual ~MDNS_Watch() {
	    if (raw_event_handler)
		delete raw_event_handler;
	}
    private:
	MDNS *m;
	MDNS_Watch_Event *event;
	struct gensio_mdns_watch *w;
        friend void mdns_watch_free_done(struct gensio_mdns_watch *w,
					 void *userdata);
    };

    // Retrieve and view network interfaces on the system.
    class GENSIOCPP_DLL_PUBLIC Net_Ifs {
    public:
	Net_Ifs(Os_Funcs *ino): o(ino)
	{
	    int rv = gensio_os_get_net_ifs(*o, &ifs, &nifs);
	    if (rv)
		throw std::bad_alloc();
	}

	~Net_Ifs()
	{
	    gensio_os_free_net_ifs(*o, ifs, nifs);
	}

	unsigned int get_num_ifs()
	{
	    return this->nifs;
	}

	std::string get_name(unsigned int index)
	{
	    if (index >= this->nifs)
		throw gensio_error(GE_OUTOFRANGE);
	    return std::string(this->ifs[index]->name);
	}

	bool is_up(unsigned int index)
	{
	    if (index >= this->nifs)
		throw gensio_error(GE_OUTOFRANGE);
	    return !!(this->ifs[index]->flags & GENSIO_NET_IF_UP);
	}

	bool is_loopback(unsigned int index)
	{
	    if (index >= this->nifs)
		throw gensio_error(GE_OUTOFRANGE);
	    return !!(this->ifs[index]->flags & GENSIO_NET_IF_LOOPBACK);
	}

	bool is_multicast(unsigned int index)
	{
	    if (index >= this->nifs)
		throw gensio_error(GE_OUTOFRANGE);
	    return !!(this->ifs[index]->flags & GENSIO_NET_IF_MULTICAST);
	}

	unsigned int get_ifindex(unsigned int index)
	{
	    if (index >= this->nifs)
		throw gensio_error(GE_OUTOFRANGE);
	    return this->ifs[index]->ifindex;
	}

	unsigned int get_num_addrs(unsigned int index)
	{
	    if (index >= this->nifs)
		throw gensio_error(GE_OUTOFRANGE);
	    return this->ifs[index]->naddrs;
	}

	unsigned int get_addr_netbits(unsigned int index, unsigned int addridx)
	{
	    if (index >= this->nifs || addridx > this->ifs[index]->naddrs)
		throw gensio_error(GE_OUTOFRANGE);
	    return this->ifs[index]->addrs[addridx].netbits;
	}

	unsigned int get_addr_family(unsigned int index, unsigned int addridx)
	{
	    if (index >= this->nifs || addridx > this->ifs[index]->naddrs)
		throw gensio_error(GE_OUTOFRANGE);
	    return this->ifs[index]->addrs[addridx].family;
	}

	std::vector<unsigned char> get_addr(unsigned int index,
					    unsigned int addridx)
	{
	    if (index >= this->nifs || addridx > this->ifs[index]->naddrs)
		throw gensio_error(GE_OUTOFRANGE);
	    struct gensio_net_addr *a = &(this->ifs[index]->addrs[addridx]);
	    return std::vector<unsigned char>(a->addr,
					      a->addr + sizeof(a->addr));
	}

        std::string get_addrstr(unsigned int index, unsigned int addridx)
	{
	    if (index >= this->nifs || addridx > this->ifs[index]->naddrs)
		throw gensio_error(GE_OUTOFRANGE);
	    return std::string(this->ifs[index]->addrs[addridx].addrstr);
	}

    private:
	Os_Funcs *o;
	struct gensio_net_if **ifs;
	unsigned int nifs;
    };
}

#endif /* GENSIO_CPP_INCLUDE */
