hyung-hwan
792e4da4c2
removed mio->renew_watch and moved it to each device by using the MIO_DEV_RENEW_REQUIRED bit
1019 lines
29 KiB
C
1019 lines
29 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2015-2016 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef _MIO_H_
|
|
#define _MIO_H_
|
|
|
|
#include <mio-cmn.h>
|
|
|
|
#if defined(_WIN32)
|
|
typedef mio_uintptr_t qse_syshnd_t;
|
|
#define MIO_SYSHND_INVALID (~(mio_uintptr_t)0)
|
|
#else
|
|
typedef int mio_syshnd_t;
|
|
#define MIO_SYSHND_INVALID (-1)
|
|
#endif
|
|
|
|
typedef struct mio_devaddr_t mio_devaddr_t;
|
|
struct mio_devaddr_t
|
|
{
|
|
int len;
|
|
void* ptr;
|
|
};
|
|
|
|
#define MIO_ERRMSG_CAPA (2048)
|
|
|
|
/* [NOTE] ensure that it is a power of 2 */
|
|
#define MIO_LOG_CAPA_ALIGN 512
|
|
|
|
/* ========================================================================= */
|
|
|
|
typedef struct mio_t mio_t;
|
|
typedef struct mio_dev_t mio_dev_t;
|
|
typedef struct mio_dev_mth_t mio_dev_mth_t;
|
|
typedef struct mio_dev_evcb_t mio_dev_evcb_t;
|
|
|
|
typedef struct mio_q_t mio_q_t;
|
|
typedef struct mio_wq_t mio_wq_t;
|
|
typedef struct mio_cwq_t mio_cwq_t;
|
|
typedef mio_intptr_t mio_iolen_t; /* NOTE: this is a signed type */
|
|
|
|
typedef unsigned int mio_bitmask_t;
|
|
|
|
enum mio_errnum_t
|
|
{
|
|
MIO_ENOERR, /**< no error */
|
|
MIO_EGENERIC, /**< generic error */
|
|
|
|
MIO_ENOIMPL, /**< not implemented */
|
|
MIO_ESYSERR, /**< system error */
|
|
MIO_EINTERN, /**< internal error */
|
|
MIO_ESYSMEM, /**< insufficient system memory */
|
|
MIO_EOOMEM, /**< insufficient object memory */
|
|
MIO_ETYPE, /**< invalid class/type */
|
|
|
|
MIO_EINVAL, /**< invalid parameter or data */
|
|
MIO_ENOENT, /**< data not found */
|
|
MIO_EEXIST, /**< existing/duplicate data */
|
|
MIO_EBUSY,
|
|
MIO_EACCES,
|
|
MIO_EPERM, /**< operation not permitted */
|
|
MIO_ENOTDIR,
|
|
MIO_EINTR,
|
|
MIO_EPIPE,
|
|
MIO_EAGAIN,
|
|
MIO_EBADHND,
|
|
|
|
MIO_EMFILE, /* too many open files */
|
|
MIO_ENFILE,
|
|
|
|
MIO_EIOERR, /**< I/O error */
|
|
MIO_EECERR, /**< encoding conversion error */
|
|
MIO_EECMORE, /**< insufficient data for encoding conversion */
|
|
MIO_EBUFFULL, /**< buffer full */
|
|
|
|
MIO_ECONRF, /* connection refused */
|
|
MIO_ECONRS, /* connection reset */
|
|
MIO_ENOCAPA, /* no capability */
|
|
MIO_ETMOUT, /* timed out */
|
|
|
|
MIO_EDEVMAKE,
|
|
MIO_EDEVERR,
|
|
MIO_EDEVHUP
|
|
};
|
|
typedef enum mio_errnum_t mio_errnum_t;
|
|
|
|
struct mio_errinf_t
|
|
{
|
|
mio_errnum_t num;
|
|
mio_ooch_t msg[MIO_ERRMSG_CAPA];
|
|
};
|
|
typedef struct mio_errinf_t mio_errinf_t;
|
|
|
|
enum mio_option_t
|
|
{
|
|
MIO_TRAIT,
|
|
MIO_LOG_MASK,
|
|
MIO_LOG_MAXCAPA
|
|
};
|
|
typedef enum mio_option_t mio_option_t;
|
|
|
|
|
|
enum mio_stopreq_t
|
|
{
|
|
MIO_STOPREQ_NONE = 0,
|
|
MIO_STOPREQ_TERMINATION,
|
|
MIO_STOPREQ_WATCHER_ERROR
|
|
};
|
|
typedef enum mio_stopreq_t mio_stopreq_t;
|
|
|
|
/* ========================================================================= */
|
|
|
|
#define MIO_TMRIDX_INVALID ((mio_tmridx_t)-1)
|
|
|
|
typedef mio_oow_t mio_tmridx_t;
|
|
|
|
typedef struct mio_tmrjob_t mio_tmrjob_t;
|
|
|
|
typedef void (*mio_tmrjob_handler_t) (
|
|
mio_t* mio,
|
|
const mio_ntime_t* now,
|
|
mio_tmrjob_t* tmrjob
|
|
);
|
|
|
|
struct mio_tmrjob_t
|
|
{
|
|
void* ctx;
|
|
mio_ntime_t when;
|
|
mio_tmrjob_handler_t handler;
|
|
mio_tmridx_t* idxptr; /* pointer to the index holder */
|
|
};
|
|
|
|
/* ========================================================================= */
|
|
|
|
struct mio_dev_mth_t
|
|
{
|
|
/* ------------------------------------------------------------------ */
|
|
/* mandatory. called in mio_makedev() */
|
|
int (*make) (mio_dev_t* dev, void* ctx);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* mandatory. called in mio_killdev(). also called in mio_makedev() upon
|
|
* failure after make() success.
|
|
*
|
|
* when 'force' is 0, the return value of -1 causes the device to be a
|
|
* zombie. the kill method is called periodically on a zombie device
|
|
* until the method returns 0.
|
|
*
|
|
* when 'force' is 1, the called should not return -1. If it does, the
|
|
* method is called once more only with the 'force' value of 2.
|
|
*
|
|
* when 'force' is 2, the device is destroyed regardless of the return value.
|
|
*/
|
|
int (*kill) (mio_dev_t* dev, int force);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
mio_syshnd_t (*getsyshnd) (mio_dev_t* dev); /* mandatory. called in mio_makedev() after successful make() */
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* return -1 on failure, 0 if no data is availble, 1 otherwise.
|
|
* when returning 1, *len must be sent to the length of data read.
|
|
* if *len is set to 0, it's treated as EOF. */
|
|
int (*read) (mio_dev_t* dev, void* data, mio_iolen_t* len, mio_devaddr_t* srcaddr);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
int (*write) (mio_dev_t* dev, const void* data, mio_iolen_t* len, const mio_devaddr_t* dstaddr);
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
int (*ioctl) (mio_dev_t* dev, int cmd, void* arg);
|
|
|
|
};
|
|
|
|
struct mio_dev_evcb_t
|
|
{
|
|
/* return -1 on failure. 0 or 1 on success.
|
|
* when 0 is returned, it doesn't attempt to perform actual I/O.
|
|
* when 1 is returned, it attempts to perform actual I/O. */
|
|
int (*ready) (mio_dev_t* dev, int events);
|
|
|
|
/* return -1 on failure, 0 or 1 on success.
|
|
* when 0 is returned, the main loop stops the attempt to read more data.
|
|
* when 1 is returned, the main loop attempts to read more data without*/
|
|
int (*on_read) (mio_dev_t* dev, const void* data, mio_iolen_t len, const mio_devaddr_t* srcaddr);
|
|
|
|
/* return -1 on failure, 0 on success.
|
|
* wrlen is the length of data written. it is the length of the originally
|
|
* posted writing request for a stream device. For a non stream device, it
|
|
* may be shorter than the originally posted length. */
|
|
int (*on_write) (mio_dev_t* dev, mio_iolen_t wrlen, void* wrctx, const mio_devaddr_t* dstaddr);
|
|
};
|
|
|
|
struct mio_q_t
|
|
{
|
|
mio_q_t* next;
|
|
mio_q_t* prev;
|
|
};
|
|
|
|
#define MIO_Q_INIT(q) ((q)->next = (q)->prev = (q))
|
|
#define MIO_Q_TAIL(q) ((q)->prev)
|
|
#define MIO_Q_HEAD(q) ((q)->next)
|
|
#define MIO_Q_ISEMPTY(q) (MIO_Q_HEAD(q) == (q))
|
|
#define MIO_Q_ISNODE(q,x) ((q) != (x))
|
|
#define MIO_Q_ISHEAD(q,x) (MIO_Q_HEAD(q) == (x))
|
|
#define MIO_Q_ISTAIL(q,x) (MIO_Q_TAIL(q) == (x))
|
|
|
|
#define MIO_Q_NEXT(x) ((x)->next)
|
|
#define MIO_Q_PREV(x) ((x)->prev)
|
|
|
|
#define MIO_Q_LINK(p,x,n) do { \
|
|
mio_q_t* pp = (p), * nn = (n); \
|
|
(x)->prev = (p); \
|
|
(x)->next = (n); \
|
|
nn->prev = (x); \
|
|
pp->next = (x); \
|
|
} while (0)
|
|
|
|
#define MIO_Q_UNLINK(x) do { \
|
|
mio_q_t* pp = (x)->prev, * nn = (x)->next; \
|
|
nn->prev = pp; pp->next = nn; \
|
|
} while (0)
|
|
|
|
#define MIO_Q_REPL(o,n) do { \
|
|
mio_q_t* oo = (o), * nn = (n); \
|
|
nn->next = oo->next; \
|
|
nn->next->prev = nn; \
|
|
nn->prev = oo->prev; \
|
|
nn->prev->next = nn; \
|
|
} while (0)
|
|
|
|
/* insert an item at the back of the queue */
|
|
/*#define MIO_Q_ENQ(wq,x) MIO_Q_LINK(MIO_Q_TAIL(wq), x, MIO_Q_TAIL(wq)->next)*/
|
|
#define MIO_Q_ENQ(wq,x) MIO_Q_LINK(MIO_Q_TAIL(wq), x, wq)
|
|
|
|
/* remove an item in the front from the queue */
|
|
#define MIO_Q_DEQ(wq) MIO_Q_UNLINK(MIO_Q_HEAD(wq))
|
|
|
|
/* completed write queue */
|
|
struct mio_cwq_t
|
|
{
|
|
mio_cwq_t* next;
|
|
mio_cwq_t* prev;
|
|
|
|
mio_iolen_t olen;
|
|
void* ctx;
|
|
mio_dev_t* dev;
|
|
mio_devaddr_t dstaddr;
|
|
};
|
|
|
|
#define MIO_CWQ_INIT(cwq) ((cwq)->next = (cwq)->prev = (cwq))
|
|
#define MIO_CWQ_TAIL(cwq) ((cwq)->prev)
|
|
#define MIO_CWQ_HEAD(cwq) ((cwq)->next)
|
|
#define MIO_CWQ_ISEMPTY(cwq) (MIO_CWQ_HEAD(cwq) == (cwq))
|
|
#define MIO_CWQ_ISNODE(cwq,x) ((cwq) != (x))
|
|
#define MIO_CWQ_ISHEAD(cwq,x) (MIO_CWQ_HEAD(cwq) == (x))
|
|
#define MIO_CWQ_ISTAIL(cwq,x) (MIO_CWQ_TAIL(cwq) == (x))
|
|
#define MIO_CWQ_NEXT(x) ((x)->next)
|
|
#define MIO_CWQ_PREV(x) ((x)->prev)
|
|
#define MIO_CWQ_LINK(p,x,n) MIO_Q_LINK((mio_q_t*)p,(mio_q_t*)x,(mio_q_t*)n)
|
|
#define MIO_CWQ_UNLINK(x) MIO_Q_UNLINK((mio_q_t*)x)
|
|
#define MIO_CWQ_REPL(o,n) MIO_CWQ_REPL(o,n);
|
|
#define MIO_CWQ_ENQ(cwq,x) MIO_CWQ_LINK(MIO_CWQ_TAIL(cwq), (mio_q_t*)x, cwq)
|
|
#define MIO_CWQ_DEQ(cwq) MIO_CWQ_UNLINK(MIO_CWQ_HEAD(cwq))
|
|
|
|
/* write queue */
|
|
struct mio_wq_t
|
|
{
|
|
mio_wq_t* next;
|
|
mio_wq_t* prev;
|
|
|
|
mio_iolen_t olen; /* original data length */
|
|
mio_uint8_t* ptr; /* pointer to data */
|
|
mio_iolen_t len; /* remaining data length */
|
|
void* ctx;
|
|
mio_dev_t* dev; /* back-pointer to the device */
|
|
|
|
mio_tmridx_t tmridx;
|
|
mio_devaddr_t dstaddr;
|
|
};
|
|
|
|
#define MIO_WQ_INIT(wq) ((wq)->next = (wq)->prev = (wq))
|
|
#define MIO_WQ_TAIL(wq) ((wq)->prev)
|
|
#define MIO_WQ_HEAD(wq) ((wq)->next)
|
|
#define MIO_WQ_ISEMPTY(wq) (MIO_WQ_HEAD(wq) == (wq))
|
|
#define MIO_WQ_ISNODE(wq,x) ((wq) != (x))
|
|
#define MIO_WQ_ISHEAD(wq,x) (MIO_WQ_HEAD(wq) == (x))
|
|
#define MIO_WQ_ISTAIL(wq,x) (MIO_WQ_TAIL(wq) == (x))
|
|
#define MIO_WQ_NEXT(x) ((x)->next)
|
|
#define MIO_WQ_PREV(x) ((x)->prev)
|
|
#define MIO_WQ_LINK(p,x,n) MIO_Q_LINK((mio_q_t*)p,(mio_q_t*)x,(mio_q_t*)n)
|
|
#define MIO_WQ_UNLINK(x) MIO_Q_UNLINK((mio_q_t*)x)
|
|
#define MIO_WQ_REPL(o,n) MIO_WQ_REPL(o,n);
|
|
#define MIO_WQ_ENQ(wq,x) MIO_WQ_LINK(MIO_WQ_TAIL(wq), (mio_q_t*)x, wq)
|
|
#define MIO_WQ_DEQ(wq) MIO_WQ_UNLINK(MIO_WQ_HEAD(wq))
|
|
|
|
#define MIO_DEV_HEADERS \
|
|
mio_t* mio; \
|
|
mio_oow_t dev_size; \
|
|
int dev_capa; \
|
|
mio_dev_mth_t* dev_mth; \
|
|
mio_dev_evcb_t* dev_evcb; \
|
|
mio_ntime_t rtmout; \
|
|
mio_tmridx_t rtmridx; \
|
|
mio_wq_t wq; \
|
|
mio_oow_t cw_count; \
|
|
mio_dev_t* dev_prev; \
|
|
mio_dev_t* dev_next
|
|
|
|
struct mio_dev_t
|
|
{
|
|
MIO_DEV_HEADERS;
|
|
};
|
|
|
|
enum mio_dev_capa_t
|
|
{
|
|
MIO_DEV_CAPA_VIRTUAL = (1 << 0),
|
|
MIO_DEV_CAPA_IN = (1 << 1),
|
|
MIO_DEV_CAPA_OUT = (1 << 2),
|
|
/* #MIO_DEV_CAPA_PRI is meaningful only if #MIO_DEV_CAPA_IN is set */
|
|
MIO_DEV_CAPA_PRI = (1 << 3),
|
|
MIO_DEV_CAPA_STREAM = (1 << 4),
|
|
MIO_DEV_CAPA_OUT_QUEUED = (1 << 5),
|
|
|
|
/* internal use only. never set this bit to the dev_capa field */
|
|
MIO_DEV_CAPA_IN_DISABLED = (1 << 9),
|
|
MIO_DEV_CAPA_IN_CLOSED = (1 << 10),
|
|
MIO_DEV_CAPA_OUT_CLOSED = (1 << 11),
|
|
MIO_DEV_CAPA_IN_WATCHED = (1 << 12),
|
|
MIO_DEV_CAPA_OUT_WATCHED = (1 << 13),
|
|
MIO_DEV_CAPA_PRI_WATCHED = (1 << 14), /**< can be set only if MIO_DEV_CAPA_IN_WATCHED is set */
|
|
|
|
MIO_DEV_CAPA_ACTIVE = (1 << 15),
|
|
MIO_DEV_CAPA_HALTED = (1 << 16),
|
|
MIO_DEV_CAPA_ZOMBIE = (1 << 17),
|
|
|
|
/* internal use only */
|
|
MIO_DEV_RENEW_REQUIRED = (1 << 20)
|
|
};
|
|
typedef enum mio_dev_capa_t mio_dev_capa_t;
|
|
|
|
enum mio_dev_watch_cmd_t
|
|
{
|
|
MIO_DEV_WATCH_START,
|
|
MIO_DEV_WATCH_UPDATE,
|
|
MIO_DEV_WATCH_RENEW, /* automatic update */
|
|
MIO_DEV_WATCH_STOP
|
|
};
|
|
typedef enum mio_dev_watch_cmd_t mio_dev_watch_cmd_t;
|
|
|
|
enum mio_dev_event_t
|
|
{
|
|
MIO_DEV_EVENT_IN = (1 << 0),
|
|
MIO_DEV_EVENT_OUT = (1 << 1),
|
|
|
|
MIO_DEV_EVENT_PRI = (1 << 2),
|
|
MIO_DEV_EVENT_HUP = (1 << 3),
|
|
MIO_DEV_EVENT_ERR = (1 << 4)
|
|
};
|
|
typedef enum mio_dev_event_t mio_dev_event_t;
|
|
|
|
#define MIO_CWQFL_SIZE 16
|
|
#define MIO_CWQFL_ALIGN 16
|
|
|
|
/* =========================================================================
|
|
* MIO LOGGING
|
|
* ========================================================================= */
|
|
|
|
enum mio_log_mask_t
|
|
{
|
|
MIO_LOG_DEBUG = (1u << 0),
|
|
MIO_LOG_INFO = (1u << 1),
|
|
MIO_LOG_WARN = (1u << 2),
|
|
MIO_LOG_ERROR = (1u << 3),
|
|
MIO_LOG_FATAL = (1u << 4),
|
|
|
|
MIO_LOG_UNTYPED = (1u << 6), /* only to be used by MIO_DEBUGx() and MIO_INFOx() */
|
|
MIO_LOG_CORE = (1u << 7),
|
|
MIO_LOG_DEV = (1u << 8),
|
|
MIO_LOG_TIMER = (1u << 9),
|
|
|
|
MIO_LOG_ALL_LEVELS = (MIO_LOG_DEBUG | MIO_LOG_INFO | MIO_LOG_WARN | MIO_LOG_ERROR | MIO_LOG_FATAL),
|
|
MIO_LOG_ALL_TYPES = (MIO_LOG_UNTYPED | MIO_LOG_CORE | MIO_LOG_DEV | MIO_LOG_TIMER),
|
|
|
|
MIO_LOG_STDOUT = (1u << 14), /* write log messages to stdout without timestamp. MIO_LOG_STDOUT wins over MIO_LOG_STDERR. */
|
|
MIO_LOG_STDERR = (1u << 15) /* write log messages to stderr without timestamp. */
|
|
};
|
|
typedef enum mio_log_mask_t mio_log_mask_t;
|
|
|
|
/* all bits must be set to get enabled */
|
|
#define MIO_LOG_ENABLED(mio,mask) (((mio)->option.log_mask & (mask)) == (mask))
|
|
|
|
#define MIO_LOG0(mio,mask,fmt) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt); } while(0)
|
|
#define MIO_LOG1(mio,mask,fmt,a1) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1); } while(0)
|
|
#define MIO_LOG2(mio,mask,fmt,a1,a2) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2); } while(0)
|
|
#define MIO_LOG3(mio,mask,fmt,a1,a2,a3) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3); } while(0)
|
|
#define MIO_LOG4(mio,mask,fmt,a1,a2,a3,a4) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3, a4); } while(0)
|
|
#define MIO_LOG5(mio,mask,fmt,a1,a2,a3,a4,a5) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3, a4, a5); } while(0)
|
|
#define MIO_LOG6(mio,mask,fmt,a1,a2,a3,a4,a5,a6) do { if (MIO_LOG_ENABLED(mio,mask)) mio_logbfmt(mio, mask, fmt, a1, a2, a3, a4, a5, a6); } while(0)
|
|
|
|
#if defined(MIO_BUILD_RELEASE)
|
|
/* [NOTE]
|
|
* get rid of debugging message totally regardless of
|
|
* the log mask in the release build.
|
|
*/
|
|
# define MIO_DEBUG0(mio,fmt)
|
|
# define MIO_DEBUG1(mio,fmt,a1)
|
|
# define MIO_DEBUG2(mio,fmt,a1,a2)
|
|
# define MIO_DEBUG3(mio,fmt,a1,a2,a3)
|
|
# define MIO_DEBUG4(mio,fmt,a1,a2,a3,a4)
|
|
# define MIO_DEBUG5(mio,fmt,a1,a2,a3,a4,a5)
|
|
# define MIO_DEBUG6(mio,fmt,a1,a2,a3,a4,a5,a6)
|
|
#else
|
|
# define MIO_DEBUG0(mio,fmt) MIO_LOG0(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt)
|
|
# define MIO_DEBUG1(mio,fmt,a1) MIO_LOG1(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1)
|
|
# define MIO_DEBUG2(mio,fmt,a1,a2) MIO_LOG2(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2)
|
|
# define MIO_DEBUG3(mio,fmt,a1,a2,a3) MIO_LOG3(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3)
|
|
# define MIO_DEBUG4(mio,fmt,a1,a2,a3,a4) MIO_LOG4(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4)
|
|
# define MIO_DEBUG5(mio,fmt,a1,a2,a3,a4,a5) MIO_LOG5(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5)
|
|
# define MIO_DEBUG6(mio,fmt,a1,a2,a3,a4,a5,a6) MIO_LOG6(mio, MIO_LOG_DEBUG | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5, a6)
|
|
#endif
|
|
|
|
#define MIO_INFO0(mio,fmt) MIO_LOG0(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt)
|
|
#define MIO_INFO1(mio,fmt,a1) MIO_LOG1(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1)
|
|
#define MIO_INFO2(mio,fmt,a1,a2) MIO_LOG2(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2)
|
|
#define MIO_INFO3(mio,fmt,a1,a2,a3) MIO_LOG3(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3)
|
|
#define MIO_INFO4(mio,fmt,a1,a2,a3,a4) MIO_LOG4(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4)
|
|
#define MIO_INFO5(mio,fmt,a1,a2,a3,a4,a5) MIO_LOG5(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5)
|
|
#define MIO_INFO6(mio,fmt,a1,a2,a3,a4,a5,a6) MIO_LOG6(mio, MIO_LOG_INFO | MIO_LOG_UNTYPED, fmt, a1, a2, a3, a4, a5, a6)
|
|
|
|
/* ========================================================================= */
|
|
|
|
enum mio_sys_mux_cmd_t
|
|
{
|
|
MIO_SYS_MUX_CMD_INSERT = 0,
|
|
MIO_SYS_MUX_CMD_UPDATE = 1,
|
|
MIO_SYS_MUX_CMD_DELETE = 2
|
|
};
|
|
typedef enum mio_sys_mux_cmd_t mio_sys_mux_cmd_t;
|
|
|
|
|
|
typedef void (*mio_sys_mux_evtcb_t) (
|
|
mio_t* mio,
|
|
mio_dev_t* dev,
|
|
int events,
|
|
int rdhup
|
|
);
|
|
|
|
typedef struct mio_sys_mux_t mio_sys_mux_t;
|
|
typedef struct mio_sys_log_t mio_sys_log_t;
|
|
|
|
struct mio_t
|
|
{
|
|
mio_mmgr_t* mmgr;
|
|
mio_cmgr_t* cmgr;
|
|
mio_errnum_t errnum;
|
|
struct
|
|
{
|
|
union
|
|
{
|
|
mio_ooch_t ooch[MIO_ERRMSG_CAPA];
|
|
mio_bch_t bch[MIO_ERRMSG_CAPA];
|
|
mio_uch_t uch[MIO_ERRMSG_CAPA];
|
|
} tmpbuf;
|
|
mio_ooch_t buf[MIO_ERRMSG_CAPA];
|
|
mio_oow_t len;
|
|
} errmsg;
|
|
|
|
int shuterr;
|
|
|
|
struct
|
|
{
|
|
mio_bitmask_t trait;
|
|
mio_bitmask_t log_mask;
|
|
mio_oow_t log_maxcapa;
|
|
} option;
|
|
|
|
struct
|
|
{
|
|
mio_ooch_t* ptr;
|
|
mio_oow_t len;
|
|
mio_oow_t capa;
|
|
mio_bitmask_t last_mask;
|
|
mio_bitmask_t default_type_mask;
|
|
} log;
|
|
|
|
struct
|
|
{
|
|
struct
|
|
{
|
|
mio_ooch_t* ptr;
|
|
mio_oow_t capa;
|
|
mio_oow_t len;
|
|
} xbuf; /* buffer to support sprintf */
|
|
} sprintf;
|
|
|
|
mio_stopreq_t stopreq; /* stop request to abort mio_loop() */
|
|
|
|
struct
|
|
{
|
|
mio_dev_t* head;
|
|
mio_dev_t* tail;
|
|
} actdev; /* active devices */
|
|
|
|
struct
|
|
{
|
|
mio_dev_t* head;
|
|
mio_dev_t* tail;
|
|
} hltdev; /* halted devices */
|
|
|
|
struct
|
|
{
|
|
mio_dev_t* head;
|
|
mio_dev_t* tail;
|
|
} zmbdev; /* zombie devices */
|
|
|
|
mio_uint8_t bigbuf[65535]; /* TODO: make this dynamic depending on devices added. device may indicate a buffer size required??? */
|
|
|
|
struct
|
|
{
|
|
mio_oow_t capa;
|
|
mio_oow_t size;
|
|
mio_tmrjob_t* jobs;
|
|
} tmr;
|
|
|
|
mio_cwq_t cwq;
|
|
mio_cwq_t* cwqfl[MIO_CWQFL_SIZE]; /* list of free cwq objects */
|
|
|
|
/* platform specific fields below */
|
|
struct
|
|
{
|
|
mio_sys_mux_t* mux;
|
|
mio_sys_log_t* log;
|
|
} sys;
|
|
};
|
|
|
|
/* ========================================================================= */
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
MIO_EXPORT mio_t* mio_open (
|
|
mio_mmgr_t* mmgr,
|
|
mio_oow_t xtnsize,
|
|
mio_cmgr_t* cmgr,
|
|
mio_oow_t tmrcapa, /**< initial timer capacity */
|
|
mio_errinf_t* errinf
|
|
);
|
|
|
|
MIO_EXPORT void mio_close (
|
|
mio_t* mio
|
|
);
|
|
|
|
MIO_EXPORT int mio_init (
|
|
mio_t* mio,
|
|
mio_mmgr_t* mmgr,
|
|
mio_cmgr_t* cmgr,
|
|
mio_oow_t tmrcapa
|
|
);
|
|
|
|
MIO_EXPORT void mio_fini (
|
|
mio_t* mio
|
|
);
|
|
|
|
MIO_EXPORT int mio_getoption (
|
|
mio_t* mio,
|
|
mio_option_t id,
|
|
void* value
|
|
);
|
|
|
|
MIO_EXPORT int mio_setoption (
|
|
mio_t* mio,
|
|
mio_option_t id,
|
|
const void* value
|
|
);
|
|
|
|
#if defined(MIO_HAVE_INLINE)
|
|
static MIO_INLINE mio_mmgr_t* mio_getmmgr (mio_t* mio) { return mio->mmgr; }
|
|
static MIO_INLINE void* mio_getxtn (mio_t* mio) { return (void*)(mio + 1); }
|
|
|
|
static MIO_INLINE mio_cmgr_t* mio_getcmgr (mio_t* mio) { return mio->cmgr; }
|
|
static MIO_INLINE void mio_setcmgr (mio_t* mio, mio_cmgr_t* cmgr) { mio->cmgr = cmgr; }
|
|
|
|
static MIO_INLINE mio_errnum_t mio_geterrnum (mio_t* mio) { return mio->errnum; }
|
|
#else
|
|
# define mio_getmmgr(mio) ((mio)->mmgr)
|
|
# define mio_getxtn(mio) ((void*)((mio) + 1))
|
|
|
|
# define mio_getcmgr(mio) ((mio)->cmgr)
|
|
# define mio_setcmgr(mio,mgr) ((mio)->cmgr = (mgr))
|
|
|
|
# define mio_geterrnum(mio) ((mio)->errnum)
|
|
#endif
|
|
|
|
MIO_EXPORT void mio_seterrnum (
|
|
mio_t* mio,
|
|
mio_errnum_t errnum
|
|
);
|
|
|
|
MIO_EXPORT void mio_seterrwithsyserr (
|
|
mio_t* mio,
|
|
int syserr_type,
|
|
int syserr_code
|
|
);
|
|
|
|
MIO_EXPORT void mio_seterrbfmt (
|
|
mio_t* mio,
|
|
mio_errnum_t errnum,
|
|
const mio_bch_t* fmt,
|
|
...
|
|
);
|
|
|
|
MIO_EXPORT void mio_seterrufmt (
|
|
mio_t* mio,
|
|
mio_errnum_t errnum,
|
|
const mio_uch_t* fmt,
|
|
...
|
|
);
|
|
|
|
MIO_EXPORT void mio_seterrbfmtwithsyserr (
|
|
mio_t* mio,
|
|
int syserr_type,
|
|
int syserr_code,
|
|
const mio_bch_t* fmt,
|
|
...
|
|
);
|
|
|
|
MIO_EXPORT void mio_seterrufmtwithsyserr (
|
|
mio_t* mio,
|
|
int syserr_type,
|
|
int syserr_code,
|
|
const mio_uch_t* fmt,
|
|
...
|
|
);
|
|
|
|
MIO_EXPORT const mio_ooch_t* mio_geterrstr (
|
|
mio_t* mio
|
|
);
|
|
|
|
MIO_EXPORT const mio_ooch_t* mio_geterrmsg (
|
|
mio_t* mio
|
|
);
|
|
|
|
MIO_EXPORT void mio_geterrinf (
|
|
mio_t* mio,
|
|
mio_errinf_t* info
|
|
);
|
|
|
|
MIO_EXPORT const mio_ooch_t* mio_backuperrmsg (
|
|
mio_t* mio
|
|
);
|
|
|
|
|
|
MIO_EXPORT int mio_exec (
|
|
mio_t* mio
|
|
);
|
|
|
|
MIO_EXPORT int mio_loop (
|
|
mio_t* mio
|
|
);
|
|
|
|
MIO_EXPORT void mio_stop (
|
|
mio_t* mio,
|
|
mio_stopreq_t stopreq
|
|
);
|
|
|
|
MIO_EXPORT mio_dev_t* mio_makedev (
|
|
mio_t* mio,
|
|
mio_oow_t dev_size,
|
|
mio_dev_mth_t* dev_mth,
|
|
mio_dev_evcb_t* dev_evcb,
|
|
void* make_ctx
|
|
);
|
|
|
|
MIO_EXPORT void mio_killdev (
|
|
mio_t* mio,
|
|
mio_dev_t* dev
|
|
);
|
|
|
|
MIO_EXPORT int mio_dev_ioctl (
|
|
mio_dev_t* dev,
|
|
int cmd,
|
|
void* arg
|
|
);
|
|
|
|
MIO_EXPORT int mio_dev_watch (
|
|
mio_dev_t* dev,
|
|
mio_dev_watch_cmd_t cmd,
|
|
/** 0 or bitwise-ORed of #MIO_DEV_EVENT_IN and #MIO_DEV_EVENT_OUT */
|
|
int events
|
|
);
|
|
|
|
MIO_EXPORT int mio_dev_read (
|
|
mio_dev_t* dev,
|
|
int enabled
|
|
);
|
|
|
|
MIO_EXPORT int mio_dev_timedread (
|
|
mio_dev_t* dev,
|
|
int enabled,
|
|
const mio_ntime_t* tmout
|
|
);
|
|
|
|
/**
|
|
* The mio_dev_write() function posts a writing request.
|
|
* It attempts to write data immediately if there is no pending requests.
|
|
* If writing fails, it returns -1. If writing succeeds, it calls the
|
|
* on_write callback. If the callback fails, it returns -1. If the callback
|
|
* succeeds, it returns 1. If no immediate writing is possible, the request
|
|
* is enqueued to a pending request list. If enqueing gets successful,
|
|
* it returns 0. otherwise it returns -1.
|
|
*/
|
|
MIO_EXPORT int mio_dev_write (
|
|
mio_dev_t* dev,
|
|
const void* data,
|
|
mio_iolen_t len,
|
|
void* wrctx,
|
|
const mio_devaddr_t* dstaddr
|
|
);
|
|
|
|
|
|
MIO_EXPORT int mio_dev_timedwrite (
|
|
mio_dev_t* dev,
|
|
const void* data,
|
|
mio_iolen_t len,
|
|
const mio_ntime_t* tmout,
|
|
void* wrctx,
|
|
const mio_devaddr_t* dstaddr
|
|
);
|
|
|
|
MIO_EXPORT void mio_dev_halt (
|
|
mio_dev_t* dev
|
|
);
|
|
|
|
/* =========================================================================
|
|
* TIMER MANAGEMENT
|
|
* ========================================================================= */
|
|
|
|
/**
|
|
* The mio_instmrjob() function schedules a new event.
|
|
*
|
|
* \return #MIO_TMRIDX_INVALID on failure, valid index on success.
|
|
*/
|
|
|
|
MIO_EXPORT mio_tmridx_t mio_instmrjob (
|
|
mio_t* mio,
|
|
const mio_tmrjob_t* job
|
|
);
|
|
|
|
MIO_EXPORT mio_tmridx_t mio_updtmrjob (
|
|
mio_t* mio,
|
|
mio_tmridx_t index,
|
|
const mio_tmrjob_t* job
|
|
);
|
|
|
|
MIO_EXPORT void mio_deltmrjob (
|
|
mio_t* mio,
|
|
mio_tmridx_t index
|
|
);
|
|
|
|
/**
|
|
* The mio_gettmrjob() function returns the
|
|
* pointer to the registered event at the given index.
|
|
*/
|
|
MIO_EXPORT mio_tmrjob_t* mio_gettmrjob (
|
|
mio_t* mio,
|
|
mio_tmridx_t index
|
|
);
|
|
|
|
MIO_EXPORT int mio_gettmrjobdeadline (
|
|
mio_t* mio,
|
|
mio_tmridx_t index,
|
|
mio_ntime_t* deadline
|
|
);
|
|
|
|
/* =========================================================================
|
|
* SYSTEM MEMORY MANAGEMENT FUCNTIONS VIA MMGR
|
|
* ========================================================================= */
|
|
MIO_EXPORT void* mio_allocmem (
|
|
mio_t* mio,
|
|
mio_oow_t size
|
|
);
|
|
|
|
MIO_EXPORT void* mio_callocmem (
|
|
mio_t* mio,
|
|
mio_oow_t size
|
|
);
|
|
|
|
MIO_EXPORT void* mio_reallocmem (
|
|
mio_t* mio,
|
|
void* ptr,
|
|
mio_oow_t size
|
|
);
|
|
|
|
MIO_EXPORT void mio_freemem (
|
|
mio_t* mio,
|
|
void* ptr
|
|
);
|
|
|
|
/* =========================================================================
|
|
* STRING ENCODING CONVERSION
|
|
* ========================================================================= */
|
|
|
|
#if defined(MIO_OOCH_IS_UCH)
|
|
# define mio_convootobchars(mio,oocs,oocslen,bcs,bcslen) mio_convutobchars(mio,oocs,oocslen,bcs,bcslen)
|
|
# define mio_convbtooochars(mio,bcs,bcslen,oocs,oocslen) mio_convbtouchars(mio,bcs,bcslen,oocs,oocslen)
|
|
# define mio_convootobcstr(mio,oocs,oocslen,bcs,bcslen) mio_convutobcstr(mio,oocs,oocslen,bcs,bcslen)
|
|
# define mio_convbtooocstr(mio,bcs,bcslen,oocs,oocslen) mio_convbtoucstr(mio,bcs,bcslen,oocs,oocslen)
|
|
#else
|
|
# define mio_convootouchars(mio,oocs,oocslen,bcs,bcslen) mio_convbtouchars(mio,oocs,oocslen,bcs,bcslen)
|
|
# define mio_convutooochars(mio,bcs,bcslen,oocs,oocslen) mio_convutobchars(mio,bcs,bcslen,oocs,oocslen)
|
|
# define mio_convootoucstr(mio,oocs,oocslen,bcs,bcslen) mio_convbtoucstr(mio,oocs,oocslen,bcs,bcslen)
|
|
# define mio_convutooocstr(mio,bcs,bcslen,oocs,oocslen) mio_convutobcstr(mio,bcs,bcslen,oocs,oocslen)
|
|
#endif
|
|
|
|
MIO_EXPORT int mio_convbtouchars (
|
|
mio_t* mio,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t* bcslen,
|
|
mio_uch_t* ucs,
|
|
mio_oow_t* ucslen
|
|
);
|
|
|
|
MIO_EXPORT int mio_convutobchars (
|
|
mio_t* mio,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t* ucslen,
|
|
mio_bch_t* bcs,
|
|
mio_oow_t* bcslen
|
|
);
|
|
|
|
/**
|
|
* The mio_convbtoucstr() function converts a null-terminated byte string
|
|
* to a wide string.
|
|
*/
|
|
MIO_EXPORT int mio_convbtoucstr (
|
|
mio_t* mio,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t* bcslen,
|
|
mio_uch_t* ucs,
|
|
mio_oow_t* ucslen
|
|
);
|
|
|
|
|
|
/**
|
|
* The mio_convutobcstr() function converts a null-terminated wide string
|
|
* to a byte string.
|
|
*/
|
|
MIO_EXPORT int mio_convutobcstr (
|
|
mio_t* mio,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t* ucslen,
|
|
mio_bch_t* bcs,
|
|
mio_oow_t* bcslen
|
|
);
|
|
|
|
|
|
#if defined(MIO_OOCH_IS_UCH)
|
|
# define mio_dupootobcharswithheadroom(mio,hrb,oocs,oocslen,bcslen) mio_duputobcharswithheadroom(mio,hrb,oocs,oocslen,bcslen)
|
|
# define mio_dupbtooocharswithheadroom(mio,hrb,bcs,bcslen,oocslen) mio_dupbtoucharswithheadroom(mio,hrb,bcs,bcslen,oocslen)
|
|
# define mio_dupootobchars(mio,oocs,oocslen,bcslen) mio_duputobchars(mio,oocs,oocslen,bcslen)
|
|
# define mio_dupbtooochars(mio,bcs,bcslen,oocslen) mio_dupbtouchars(mio,bcs,bcslen,oocslen)
|
|
|
|
# define mio_dupootobcstrwithheadroom(mio,hrb,oocs,bcslen) mio_duputobcstrwithheadroom(mio,hrb,oocs,bcslen)
|
|
# define mio_dupbtooocstrwithheadroom(mio,hrb,bcs,oocslen) mio_dupbtoucstrwithheadroom(mio,hrb,bcs,oocslen)
|
|
# define mio_dupootobcstr(mio,oocs,bcslen) mio_duputobcstr(mio,oocs,bcslen)
|
|
# define mio_dupbtooocstr(mio,bcs,oocslen) mio_dupbtoucstr(mio,bcs,oocslen)
|
|
#else
|
|
# define mio_dupootoucharswithheadroom(mio,hrb,oocs,oocslen,ucslen) mio_dupbtoucharswithheadroom(mio,hrb,oocs,oocslen,ucslen)
|
|
# define mio_duputooocharswithheadroom(mio,hrb,ucs,ucslen,oocslen) mio_duputobcharswithheadroom(mio,hrb,ucs,ucslen,oocslen)
|
|
# define mio_dupootouchars(mio,oocs,oocslen,ucslen) mio_dupbtouchars(mio,oocs,oocslen,ucslen)
|
|
# define mio_duputooochars(mio,ucs,ucslen,oocslen) mio_duputobchars(mio,ucs,ucslen,oocslen)
|
|
|
|
# define mio_dupootoucstrwithheadroom(mio,hrb,oocs,ucslen) mio_dupbtoucstrwithheadroom(mio,hrb,oocs,ucslen)
|
|
# define mio_duputooocstrwithheadroom(mio,hrb,ucs,oocslen) mio_duputobcstrwithheadroom(mio,hrb,ucs,oocslen)
|
|
# define mio_dupootoucstr(mio,oocs,ucslen) mio_dupbtoucstr(mio,oocs,ucslen)
|
|
# define mio_duputooocstr(mio,ucs,oocslen) mio_duputobcstr(mio,ucs,oocslen)
|
|
#endif
|
|
|
|
|
|
MIO_EXPORT mio_uch_t* mio_dupbtoucharswithheadroom (
|
|
mio_t* mio,
|
|
mio_oow_t headroom_bytes,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t bcslen,
|
|
mio_oow_t* ucslen
|
|
);
|
|
|
|
MIO_EXPORT mio_bch_t* mio_duputobcharswithheadroom (
|
|
mio_t* mio,
|
|
mio_oow_t headroom_bytes,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t ucslen,
|
|
mio_oow_t* bcslen
|
|
);
|
|
|
|
MIO_EXPORT mio_uch_t* mio_dupbtouchars (
|
|
mio_t* mio,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t bcslen,
|
|
mio_oow_t* ucslen
|
|
);
|
|
|
|
MIO_EXPORT mio_bch_t* mio_duputobchars (
|
|
mio_t* mio,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t ucslen,
|
|
mio_oow_t* bcslen
|
|
);
|
|
|
|
|
|
MIO_EXPORT mio_uch_t* mio_dupbtoucstrwithheadroom (
|
|
mio_t* mio,
|
|
mio_oow_t headroom_bytes,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t* ucslen
|
|
);
|
|
|
|
MIO_EXPORT mio_bch_t* mio_duputobcstrwithheadroom (
|
|
mio_t* mio,
|
|
mio_oow_t headroom_bytes,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t* bcslen
|
|
);
|
|
|
|
MIO_EXPORT mio_uch_t* mio_dupbtoucstr (
|
|
mio_t* mio,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t* ucslen /* optional: length of returned string */
|
|
);
|
|
|
|
MIO_EXPORT mio_bch_t* mio_duputobcstr (
|
|
mio_t* mio,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t* bcslen /* optional: length of returned string */
|
|
);
|
|
|
|
|
|
#if defined(MIO_OOCH_IS_UCH)
|
|
# define mio_dupoochars(mio,oocs,oocslen) mio_dupuchars(mio,oocs,oocslen)
|
|
#else
|
|
# define mio_dupoochars(mio,oocs,oocslen) mio_dupbchars(mio,oocs,oocslen)
|
|
#endif
|
|
|
|
MIO_EXPORT mio_uch_t* mio_dupuchars (
|
|
mio_t* mio,
|
|
const mio_uch_t* ucs,
|
|
mio_oow_t ucslen
|
|
);
|
|
|
|
MIO_EXPORT mio_bch_t* mio_dupbchars (
|
|
mio_t* mio,
|
|
const mio_bch_t* bcs,
|
|
mio_oow_t bcslen
|
|
);
|
|
|
|
|
|
/* =========================================================================
|
|
* MIO VM LOGGING
|
|
* ========================================================================= */
|
|
|
|
MIO_EXPORT mio_ooi_t mio_logbfmt (
|
|
mio_t* mio,
|
|
mio_bitmask_t mask,
|
|
const mio_bch_t* fmt,
|
|
...
|
|
);
|
|
|
|
MIO_EXPORT mio_ooi_t mio_logufmt (
|
|
mio_t* mio,
|
|
mio_bitmask_t mask,
|
|
const mio_uch_t* fmt,
|
|
...
|
|
);
|
|
|
|
#if defined(MIO_OOCH_IS_UCH)
|
|
# define mio_logoofmt mio_logufmt
|
|
#else
|
|
# define mio_logoofmt mio_logbfmt
|
|
#endif
|
|
|
|
/* =========================================================================
|
|
* MISCELLANEOUS HELPER FUNCTIONS
|
|
* ========================================================================= */
|
|
|
|
MIO_EXPORT const mio_ooch_t* mio_errnum_to_errstr (
|
|
mio_errnum_t errnum
|
|
);
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
|
|
#endif
|