//
//  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 <memory>
#include <vector>
#include <gensio/gensio_dllvisibility>
#include <gensio/gensioosh>

//#include <iostream>

namespace gensios {
    // Incuding this in the gensio namespace to keep things neat.
#include <gensio/gensio.h>

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

	// Errors from parameter parsing come in here.
	virtual void parmlog(const std::string log) { }

	// Window size events come in here.
	virtual void win_size(unsigned int height, unsigned int width) { }

	// General logs on running gensios come in here.
	virtual int log(enum gensio_log_levels level,
			const std::string log) { return GE_NOTSUP; }

	// See gensio_event.3 and gensio_acontrol.3 for details
	virtual void modemstate(unsigned int state) { }
	// See gensio_event.3 and gensio_acontrol.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 GENSIO_ACONTROL_SER_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 void modemstate_mask(unsigned int state) { }
	virtual void linestate_mask(unsigned int state) { }
    };

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

    // Used for done handlers for acontrol callbacks
    class GENSIOCPP_DLL_PUBLIC Gensio_Control_Done {
    public:
	virtual void control_done(int err,
				  const std::vector<unsigned char> data) = 0;
	virtual ~Gensio_Control_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);

    // Allocate a terminal gensio
    GENSIOCPP_DLL_PUBLIC
    Gensio *gensio_alloc(const char *gensiotype, const void *gdata,
			 const char * const args[], Os_Funcs &o,
			 Event *cb = NULL);

    // For internal use only.
    Gensio *gensio_alloc(struct gensio *io, Os_Funcs &o);

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

	int acontrol(int depth, bool get, unsigned int option,
		     const char *data, gensiods datalen,
		     Gensio_Control_Done *done = NULL,
		     gensio_time *timeout = NULL);

	int acontrol_s(int depth, bool get, unsigned int option,
		       char *data, gensiods *datalen,
		       gensio_time *timeout = NULL, bool intr = false);

	// 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); }
	inline bool is_mux() const { return gensio_is_mux(io); }
	inline bool is_serial() { return gensio_is_serial(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 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:
	Os_Funcs go;
	struct gensio *io = NULL;
	Event *gcb = NULL;

	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(std::move(str), o, cb);
	}
	GensioW(Gensio *child, std::string str, Os_Funcs &o, Event *cb = NULL) {
	    io = gensio_alloc(child, std::move(str), o, cb);
	}
	// Take over an existing gensio.
	GensioW(Gensio *iio): io(iio) { }
	~GensioW() {
	    try {
		io->close_s();
	    } catch (gensio_error &) {
		// 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;
    };

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

    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;

	// Errors from parameter parsing come in here.
	virtual void parmlog(const std::string log) { }
    };

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

    // Allocate a terminal gensio accepter by gensio type string.
    GENSIOCPP_DLL_PUBLIC
    Accepter *gensio_acc_alloc(const char *gensiotype, const void *gdata,
			       const char * const args[], Os_Funcs &o,
			       Accepter_Event *cb = NULL);

    // Allocate a filter gensio accepter by gensio type string.
    GENSIOCPP_DLL_PUBLIC
    Accepter *gensio_acc_alloc(const char *gensiotype, Accepter *child,
			       const char * const args[], 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); }
	inline bool is_mux() const { return gensio_acc_is_mux(acc); }
	inline bool is_serial() const { return gensio_acc_is_serial(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(std::move(str), o, cb);
	}
	AccepterW(Accepter *child, std::string str, Os_Funcs &o,
		  Accepter_Event *cb = NULL) {
	    acc = gensio_acc_alloc(child, std::move(str), o, cb);
	}
	// Take over an existing accepter.
	AccepterW(Accepter *iacc): acc(iacc) { }
	~AccepterW() {
	    try {
		acc->shutdown_s();
	    } catch (gensio_error &) {
		// 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; }
	Accepter * operator&() { return acc; }
    private:
	Accepter *acc;
    };
}
#endif /* GENSIO_CPP_INCLUDE */
