Posted to tcl by stu at Fri Jan 26 16:00:19 GMT 2018view pretty

/*
 * Copyright (c) 2014 Stuart Cassoff <stwo@users.sourceforge.net>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


/*
 * tjsndio.c
 *
 * Tcl / Jim interface to sndio(3).
 *
 */

/*
 * Notes:
 *
 * sio_open() sets FD_CLOEXEC
 *
 */

#ifdef __cplusplus
extern "C" {
#endif

#include <stdlib.h>
#include <string.h>
#include <sndio.h>

#define COMBIEN(Z) ((int) (sizeof(Z) / sizeof(Z[0])))

#ifdef FOR_JIM
#include <jim-subcmd.h>
# define EZT_OK JIM_OK
# define EZT_ERROR JIM_ERR
# define Ezt_IncrRefCount(O) Jim_IncrRefCount((O))
# define Ezt_DecrRefCount(O) Jim_DecrRefCount(interp,(O))
# define Ezt_SetResult(O) Jim_SetResult(interp,(O))
# define Ezt_Obj Jim_Obj
# define Ezt_Interp Jim_Interp
# define Ezt_NewInt(I) Ezt_NewIntObj(interp,(I))
# define Ezt_GetInt(I,P) Ezt_GetIntFromJimObj(interp,(I),(P))
# define Ezt_NewWide(I) Jim_NewWideObj(interp,(I))
# define Ezt_GetWide Jim_GetWide
# define Ezt_NewString(S,L) Jim_NewStringObj(interp,(S),(L))
# define Ezt_GetString Jim_GetString
# define Ezt_GetBytes Jim_GetString
# define Ezt_NewList() Jim_NewListObj(interp, NULL, 0)
# define Ezt_ListAppend(L,O) Jim_ListAppendElement(interp,(L),(O))
# define Ezt_NewDict() Jim_NewDictObj(interp, NULL, 0)
# define Ezt_DictPut(D,KO,VO) Jim_DictAddElement(interp,(D),(KO),(VO))
# define ezt_wide jim_wide
# define EZT_SUBCMD(S) int (S) (Ezt_Interp *interp, int objc, Ezt_Obj *const *objv)
# define EZT_CMD(S) int (S) (Ezt_Interp *interp, int objc, Ezt_Obj *const *objv)
# define ezt_subcmd_type jim_subcmd_type
#else
# include <tcl.h>
# define EZT_OK TCL_OK
# define EZT_ERROR TCL_ERROR
# define Ezt_IncrRefCount(O) Tcl_IncrRefCount((O))
# define Ezt_DecrRefCount(O) Tcl_DecrRefCount((O))
# define Ezt_SetResult(O) Tcl_SetObjResult(interp,(O))
# define Ezt_Obj Tcl_Obj
# define Ezt_Interp Tcl_Interp
# define Ezt_NewInt Tcl_NewIntObj
# define Ezt_GetInt(I,P) Tcl_GetIntFromObj(interp,(I),(P))
# define Ezt_NewWide Tcl_NewWideIntObj
# define Ezt_GetWide Tcl_GetWideIntFromObj
# define Ezt_NewString Tcl_NewStringObj
# define Ezt_GetString Tcl_GetStringFromObj
# define Ezt_GetBytes Tcl_GetByteArrayFromObj
# define Ezt_NewList() Tcl_NewListObj(0,NULL)
# define Ezt_ListAppend(L,O) Tcl_ListObjAppendElement(interp,(L),(O))
# define Ezt_NewDict() Tcl_NewDictObj()
# define Ezt_DictPut(D,KO,VO) Tcl_DictObjPut(interp,(D),(KO),(VO))
# define ezt_wide TclWideInt
# define EZT_SUBCMD(S) int (S) (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[])
# define EZT_CMD EZT_SUBCMD
typedef struct {
	const char *name;
	const char *args;
	Tcl_ObjCmdProc *proc;
	short minargs;
	short maxargs;
	unsigned short flags;
} ezt_subcmd_type;
#endif

#ifdef FOR_JIM
static Jim_Obj *
Ezt_NewIntObj (Jim_Interp *interp, int i) {
	return Jim_NewWideObj(interp, (ezt_wide) i);
}
static int
Ezt_GetIntFromJimObj (Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr) {
	jim_wide w;
	if (Jim_GetWide(interp, objPtr, &w) != JIM_OK) { return JIM_ERR; }
	*intPtr = (int) w;
	return JIM_OK;
}
#endif

typedef struct TJsio {
	struct sio_hdl *hd;
	Ezt_Obj *name;
} TJsio;

static TJsio TJ;
static TJsio *tj = &TJ;


static EZT_SUBCMD(TJsio_Sndio_Open);
static EZT_SUBCMD(TJsio_Sndio_Close);
static EZT_SUBCMD(TJsio_Sndio_Cap);
static EZT_SUBCMD(TJsio_Sndio_Start);
static EZT_SUBCMD(TJsio_Sndio_Stop);
static EZT_SUBCMD(TJsio_Sndio_Write);
static EZT_SUBCMD(TJsio_Sndio_Configure);
static EZT_SUBCMD(TJsio_GOpts_Cmd);
static EZT_SUBCMD(TJsio_SOpts_Cmd);

static const ezt_subcmd_type TJsio_Cmds[] = {
	{ "open"      , NULL  , TJsio_Sndio_Open      , 0,  0, 0 },
	{ "close"     , NULL  , TJsio_Sndio_Close     , 0,  0, 0 },
	{ "cap"       , NULL  , TJsio_Sndio_Cap       , 0,  0, 0 },
	{ "start"     , NULL  , TJsio_Sndio_Start     , 0,  0, 0 },
	{ "stop"      , NULL  , TJsio_Sndio_Stop      , 0,  0, 0 },
	{ "write"     , "buf" , TJsio_Sndio_Write     , 1,  1, 0 },
	{ "configure" , NULL  , TJsio_Sndio_Configure , 0, -1, 0 },
	{ "gopts"     , NULL  , TJsio_GOpts_Cmd       , 0,  0, 0 },
	{ "sopts"     , NULL  , TJsio_SOpts_Cmd       , 0,  0, 0 },
	{ NULL        , NULL  , NULL                  , 0,  0, 0 }
};

/***/

enum tjsioOptions {
	TJSIO_OPT_BITS     = (1 <<  0),
	TJSIO_OPT_BPS      = (1 <<  1),
	TJSIO_OPT_SIG      = (1 <<  2),
	TJSIO_OPT_LE       = (1 <<  3),
	TJSIO_OPT_MSB      = (1 <<  4),
	TJSIO_OPT_PCHAN    = (1 <<  5),
	TJSIO_OPT_RCHAN    = (1 <<  6),
	TJSIO_OPT_RATE     = (1 <<  7),
	TJSIO_OPT_APPBUFSZ = (1 <<  8),
	TJSIO_OPT_BUFSZ    = (1 <<  9),
	TJSIO_OPT_ROUND    = (1 << 10),
	TJSIO_OPT_XRUN     = (1 << 11),
	TJZIO_ALL          = (
		0
		| TJSIO_OPT_BITS
		| TJSIO_OPT_BPS
		| TJSIO_OPT_SIG
		| TJSIO_OPT_LE
		| TJSIO_OPT_MSB
		| TJSIO_OPT_PCHAN
		| TJSIO_OPT_RCHAN
		| TJSIO_OPT_RATE
		| TJSIO_OPT_APPBUFSZ
		| TJSIO_OPT_BUFSZ
		| TJSIO_OPT_ROUND
		| TJSIO_OPT_XRUN
	),
	TJSIO_OPT_INVALID  = -1
};

typedef struct TJsio_OptVals {
	int rate;
} TJsio_OptVals;

enum tjsioOptionFlags {
	TJSIO_OPTFLAG_RD = (1 << 0),
	TJSIO_OPTFLAG_WR = (1 << 1),
	TJSIO_OPTFLAG_CR = (1 << 2),
	TJSIO_OPTFLAG_RW  = (TJSIO_OPTFLAG_WR | TJSIO_OPTFLAG_RD),
	TJSIO_OPTFLAG_IV = 0
};

typedef struct TJsio_Option {
	const char* name;
	enum tjsioOptions id;
	enum tjsioOptionFlags flags;
} TJsio_Option;

static TJsio_Option TJsio_Options[] = {
	{ "-bits"     , TJSIO_OPT_BITS     , TJSIO_OPTFLAG_RW },
	{ "-bps"      , TJSIO_OPT_BPS      , TJSIO_OPTFLAG_RW },
	{ "-sig"      , TJSIO_OPT_SIG      , TJSIO_OPTFLAG_RW },
	{ "-le"       , TJSIO_OPT_LE       , TJSIO_OPTFLAG_RW },
	{ "-msb"      , TJSIO_OPT_MSB      , TJSIO_OPTFLAG_RW },
	{ "-pchan"    , TJSIO_OPT_PCHAN    , TJSIO_OPTFLAG_RW },
	{ "-rchan"    , TJSIO_OPT_RCHAN    , TJSIO_OPTFLAG_RW },
	{ "-rate"     , TJSIO_OPT_RATE     , TJSIO_OPTFLAG_RW },
	{ "-appbufsz" , TJSIO_OPT_APPBUFSZ , TJSIO_OPTFLAG_RW },
	{ "-bufsz"    , TJSIO_OPT_BUFSZ    , TJSIO_OPTFLAG_RW },
	{ "-round"    , TJSIO_OPT_ROUND    , TJSIO_OPTFLAG_RW },
	{ "-xrun"     , TJSIO_OPT_XRUN     , TJSIO_OPTFLAG_RW },
	{ NULL        , TJSIO_OPT_INVALID  , TJSIO_OPTFLAG_IV }
};


#define TJSIO_XRUN_BAD (SIO_ERROR + SIO_IGNORE + SIO_SYNC)

typedef struct TJsio_Xrun {
	const char *name;
	unsigned int val;
} TJsio_Xrun;

static TJsio_Xrun TJsio_Xruns[] = {
	{ "error"  , SIO_ERROR  },
	{ "ignore" , SIO_IGNORE },
	{ "sync"   , SIO_SYNC   },
	{ NULL     , 0          }
};

const char *
TJsio_XrunToStr (unsigned int val) {
	TJsio_Xrun *x;
	for (x = &TJsio_Xruns[0]; x->name != NULL; x++) {
		if (x->val == val) {
			return x->name;
		}
	}
	return NULL;
}

unsigned int
TJsio_StrToXrun (const char *name) {
	TJsio_Xrun *x;
	for (x = &TJsio_Xruns[0]; x->name != NULL; x++) {
		if (strcmp(name, x->name) == 0) {
			return x->val;
		}
	}
	return TJSIO_XRUN_BAD;
}
/***/


static
EZT_SUBCMD(TJsio_Sndio_Open) {
	const char *name = SIO_DEVANY;
	int mode = SIO_PLAY;

	if (tj->hd != NULL) {
		Ezt_SetResult(tj->name);
		return EZT_OK;
	}

	tj->hd = sio_open(name, mode, 0);
	if (tj->hd == NULL) {
		/*Jim_SetResultFormatted(interp, "couldn't open \"%s\"", name);*/
		return EZT_ERROR;
	}

	tj->name = Ezt_NewString(name, -1);
	Ezt_IncrRefCount(tj->name);

	Ezt_SetResult(tj->name);

	return EZT_OK;
}


static
EZT_SUBCMD(TJsio_Sndio_Close) {
	sio_close(tj->hd);
	tj->hd = NULL;
	Ezt_DecrRefCount(tj->name);
	return EZT_OK;
}


static
EZT_SUBCMD(TJsio_Sndio_Cap) {
	struct sio_cap cap;
	int i, j;
	Ezt_Obj *l, *d;
	Ezt_Obj *rates, *rchans, *pchans, *encs, *denc;
	unsigned int rate, rchan, pchan, enc;

	if (tj->hd == NULL) {
		return EZT_ERROR;
	}

	if (!sio_getcap(tj->hd, &cap)) {
		/*Jim_SetResultFormatted(interp, "couldn't cat cap from \"%s\"", "cow");*/
		return EZT_ERROR;
	}

	l = Ezt_NewList();
	Ezt_IncrRefCount(l);

	for (i = 0; i < cap.nconf; i++) {
		d = Ezt_NewDict(); Ezt_IncrRefCount(d);
		rates  = Ezt_NewList(); Ezt_IncrRefCount(rates);
		rchans = Ezt_NewList(); Ezt_IncrRefCount(rchans);
		pchans = Ezt_NewList(); Ezt_IncrRefCount(pchans);
		encs   = Ezt_NewList(); Ezt_IncrRefCount(encs);
		for (
			rate  = cap.confs[i].rate,
			rchan = cap.confs[i].rchan,
			pchan = cap.confs[i].pchan,
			enc   = cap.confs[i].enc,
			j = 0
		;
			rate || rchan || pchan || enc
		;
			rate >>= 1, rchan >>= 1, pchan >>= 1, enc >>= 1,
			j++
		) {
			if (rate & 1) {
				Ezt_ListAppend(rates, Ezt_NewInt(cap.rate[j]));
			}
			if (rchan & 1) {
				Ezt_ListAppend(rchans, Ezt_NewInt(cap.rchan[j]));
			}
			if (pchan & 1) {
				Ezt_ListAppend(pchans, Ezt_NewInt(cap.pchan[j]));
			}
			if (enc & 1) {
				denc = Ezt_NewDict();
				Ezt_IncrRefCount(denc);
				Ezt_DictPut(denc, Ezt_NewString("-bits", -1), Ezt_NewInt(cap.enc[j].bits));
				Ezt_DictPut(denc, Ezt_NewString("-bps",  -1), Ezt_NewInt(cap.enc[j].bps));
				Ezt_DictPut(denc, Ezt_NewString("-sig",  -1), Ezt_NewInt(cap.enc[j].sig));
				Ezt_DictPut(denc, Ezt_NewString("-le",   -1), Ezt_NewInt(cap.enc[j].le));
				Ezt_DictPut(denc, Ezt_NewString("-msb",  -1), Ezt_NewInt(cap.enc[j].msb));
				Ezt_ListAppend(encs, denc);
				Ezt_DecrRefCount(denc);
			}
		}
		Ezt_DictPut(d, Ezt_NewString("rates" , -1), rates ); Ezt_DecrRefCount(rates);
		Ezt_DictPut(d, Ezt_NewString("rchans", -1), rchans); Ezt_DecrRefCount(rchans);
		Ezt_DictPut(d, Ezt_NewString("pchans", -1), pchans); Ezt_DecrRefCount(pchans);
		Ezt_DictPut(d, Ezt_NewString("encs"  , -1), encs  ); Ezt_DecrRefCount(encs);
		Ezt_ListAppend(l, d);
		Ezt_DecrRefCount(d);
	}

	Ezt_SetResult(l);
	Ezt_DecrRefCount(l);

	return EZT_OK;
}



static
EZT_SUBCMD(TJsio_Sndio_Start) {
	if (tj->hd == NULL) {
		return EZT_ERROR;
	}
	if (!sio_start(tj->hd)) {
		return EZT_ERROR;
	}
	return EZT_OK;
}


static
EZT_SUBCMD(TJsio_Sndio_Stop) {
	if (tj->hd == NULL) {
		return EZT_ERROR;
	}
	if (!sio_stop(tj->hd)) {
		return EZT_ERROR;
	}
	return EZT_OK;
}


static
EZT_SUBCMD(TJsio_Sndio_Write) {
	const char *buf;
	int len;
#if FOR_TCL
objc--; objv++;
#endif
	if (tj->hd == NULL) {
		return EZT_ERROR;
	}
	buf = Ezt_GetBytes(objv[0], &len);
	sio_write(tj->hd, buf, len);
	return EZT_OK;
}


/* Returned obj will have refcount 1 */
static Ezt_Obj *
TJsio_ListOptions (
	Ezt_Interp *interp,
	enum tjsioOptionFlags mask
) {
	int i;
	Ezt_Obj *o;
	o = Ezt_NewList();
	Ezt_IncrRefCount(o);
	for (i = 0; i < COMBIEN(TJsio_Options) - 1; i++) {
		if ((TJsio_Options[i].flags & mask) != mask) { continue; }
		Ezt_ListAppend(o, Ezt_NewString(TJsio_Options[i].name,-1));
	}
	return o;
}

static int
TJsio_DoListOptions (
	Ezt_Interp *interp,
	enum tjsioOptionFlags mask
) {
	Ezt_Obj *o;
	o = TJsio_ListOptions(interp, mask);
	Ezt_SetResult(o);
	Ezt_DecrRefCount(o);
	return EZT_OK;
}

static
EZT_SUBCMD(TJsio_GOpts_Cmd) {
	return TJsio_DoListOptions(interp, TJSIO_OPTFLAG_RD);
}

static
EZT_SUBCMD(TJsio_SOpts_Cmd) {
	return TJsio_DoListOptions(interp, TJSIO_OPTFLAG_WR);
}

static Ezt_Obj *
TJsio_SndioParToObj (
	Ezt_Interp *interp,
	struct sio_par *p,
	enum tjsioOptions opt
) {
	unsigned int val;

	switch (opt) {
	case TJSIO_OPT_BITS     : val = p->bits     ; break;
	case TJSIO_OPT_BPS      : val = p->bps      ; break;
	case TJSIO_OPT_SIG      : val = p->sig      ; break;
	case TJSIO_OPT_LE       : val = p->le       ; break;
	case TJSIO_OPT_MSB      : val = p->msb      ; break;
	case TJSIO_OPT_PCHAN    : val = p->pchan    ; break;
	case TJSIO_OPT_RCHAN    : val = p->rchan    ; break;
	case TJSIO_OPT_RATE     : val = p->rate     ; break;
	case TJSIO_OPT_APPBUFSZ : val = p->appbufsz ; break;
	case TJSIO_OPT_BUFSZ    : val = p->bufsz    ; break;
	case TJSIO_OPT_ROUND    : val = p->round    ; break;
	case TJSIO_OPT_XRUN:
		return Ezt_NewString(TJsio_XrunToStr(p->xrun), -1);
	default:
		return NULL;
	}

	return Ezt_NewInt(val);
}

static int
TJsio_ObjToSndioPar (
	Ezt_Interp *interp,
	struct sio_par *p,
	enum tjsioOptions opt,
	Ezt_Obj *optVal
) {
	unsigned int val;

	if (opt == TJSIO_OPT_XRUN) {
		if ((val = TJsio_StrToXrun(Ezt_GetString(optVal, NULL))) == TJSIO_XRUN_BAD) { return EZT_ERROR; }
	} else {
		if (Ezt_GetInt(optVal, &val) != EZT_OK) { return EZT_ERROR; }
	}
	
	switch (opt) {
	case TJSIO_OPT_BITS:     p->bits     = val; break;
	case TJSIO_OPT_BPS:      p->bps      = val; break;
	case TJSIO_OPT_SIG:      p->sig      = val; break;
	case TJSIO_OPT_LE:       p->le       = val; break;
	case TJSIO_OPT_MSB:      p->msb      = val; break;
	case TJSIO_OPT_PCHAN:    p->pchan    = val; break;
	case TJSIO_OPT_RCHAN:    p->rchan    = val; break;
	case TJSIO_OPT_RATE:     p->rate     = val; break;
	case TJSIO_OPT_APPBUFSZ: p->appbufsz = val; break;
	case TJSIO_OPT_BUFSZ:    p->bufsz    = val; break;
	case TJSIO_OPT_ROUND:    p->round    = val; break;
	case TJSIO_OPT_XRUN:     p->xrun     = val; break;
	default:
		return EZT_ERROR;
	}

	return EZT_OK;
}

static int
Ezt_FindOption (
	Ezt_Interp *interp,
	Ezt_Obj *option,
	enum tjsioOptionFlags mask,
	int *where
) {
	const char *optionStr = Ezt_GetString(option, NULL);
	int i;
	for (i = 0; i < COMBIEN(TJsio_Options) - 1; i++) {
		if ((TJsio_Options[i].flags & mask) != mask) { continue; }
		if (strcmp(optionStr, TJsio_Options[i].name) != 0) { continue; }
		*where = i;
		return EZT_OK;
	}
/*	return Duft_BadOption(interp, optionStr, mask, "option");*/
return EZT_ERROR;
}

static
EZT_SUBCMD(TJsio_Sndio_Configure) {
	struct sio_par par;
	int i;

#if FOR_TCL
objc--; objv++;
#endif

	if (tj->hd == NULL) {
		return EZT_ERROR;
	}

	if ((objc > 1) && ((objc % 2) == 1)) {
		/*Ezt_WrongNumArgs(interp, 1, objv, "id ?-option value ...?");*/
		return EZT_ERROR;
	}

	sio_initpar(&par);

	if (objc > 1) {
		int j,k;
		for (j=0,k=1; k < objc; j+=2,k+=2) {
			if (Ezt_FindOption(interp, objv[j], TJSIO_OPTFLAG_RD, &i) != EZT_OK) {
				return EZT_ERROR;
			}
			if (TJsio_ObjToSndioPar(interp, &par, TJsio_Options[i].id, objv[k]) != EZT_OK) {
				return EZT_ERROR;
			}
		}
		if (!sio_setpar(tj->hd, &par)) {
			return EZT_ERROR;
		}
		return EZT_OK;
	}

	if (!sio_getpar(tj->hd, &par)) {
		return EZT_ERROR;
	}

	if (objc == 0) {
		Ezt_Obj *d;
		d = Ezt_NewDict();
		Ezt_IncrRefCount(d);
		for (i = 0; i < COMBIEN(TJsio_Options) - 1; i++) {
			Ezt_DictPut(d, Ezt_NewString(TJsio_Options[i].name, -1), TJsio_SndioParToObj(interp, &par, TJsio_Options[i].id));
		}
		Ezt_SetResult(d);
		Ezt_DecrRefCount(d);
		return EZT_OK;
	}

	if (Ezt_FindOption(interp, objv[0], TJSIO_OPTFLAG_RD, &i) != EZT_OK) {
		return EZT_ERROR;
	}
	Ezt_SetResult(TJsio_SndioParToObj(interp, &par, TJsio_Options[i].id));

	return EZT_OK;
}

/***/

#ifdef FOR_JIM
int
Jim_sndioInit (Ezt_Interp *interp) {
	if (Jim_CreateCommand(interp, "sndio", Jim_SubCmdProc, (void *) TJsio_Cmds, NULL) != EZT_OK) { return EZT_ERROR; }
	if (Jim_PackageProvide(interp, PACKAGE_NAME, PACKAGE_VERSION, JIM_ERRMSG) != EZT_OK) { return EZT_ERROR; }
	return EZT_OK;
}
#else


#define MIN_TCL_VERSION "8.5"
#define TJSIO_NS "::sndio"
#define TJSIO_EN "sndio"
#define TJSIO_VER "0.1"

static int
TJsio_CommonInit (
	Tcl_Interp *interp
) {
	if (Tcl_InitStubs       (interp, MIN_TCL_VERSION, 0)        == NULL)   { return TCL_ERROR; }
	if (Tcl_PkgRequire      (interp, "Tcl", MIN_TCL_VERSION, 0) == NULL)   { return TCL_ERROR; }
	if (Tcl_CreateNamespace (interp, TJSIO_NS, NULL, NULL)      == NULL)   { return TCL_ERROR; }
	if (Tcl_PkgProvide      (interp, "sndio", TJSIO_VER)        != TCL_OK) { return TCL_ERROR; }
	return TCL_OK;
}


EXTERN int
Sndio_Init (
	Tcl_Interp *interp
) {
	Tcl_Namespace *ns;
	const ezt_subcmd_type *c;
	Tcl_Obj *o;
	Tcl_Obj *map;

	if (TJsio_CommonInit(interp) != TCL_OK) { return TCL_ERROR; }

	if ((ns = Tcl_FindNamespace(interp, TJSIO_NS, NULL, TCL_LEAVE_ERR_MSG)) == NULL) { return TCL_ERROR; }
	map = Tcl_NewDictObj();
	for (c = &TJsio_Cmds[0]; c->name != NULL; c++) {
		o = Tcl_ObjPrintf("%s%s", TJSIO_NS, c->name);
		Tcl_IncrRefCount(o);
		if (Tcl_CreateObjCommand(interp, Tcl_GetString(o), c->proc, NULL, NULL) == NULL) { Tcl_DecrRefCount(o); return TCL_ERROR; }
		Tcl_DictObjPut(interp, map, Tcl_NewStringObj(c->name, -1), o);
		Tcl_DecrRefCount(o);
		if (Tcl_Export(interp, ns, c->name, 0) != TCL_OK) { return TCL_ERROR; }
	}
	if (Tcl_SetEnsembleMappingDict(interp, Tcl_CreateEnsemble(interp, TJSIO_EN, ns, TCL_ENSEMBLE_PREFIX), map) != TCL_OK) { return TCL_ERROR; };
	if (Tcl_Export(interp, ns, TJSIO_EN, 0) != TCL_OK) { return TCL_ERROR; }
	return TCL_OK;
}

EXTERN int
Sndio_SafeInit (
	Tcl_Interp *interp
) {
	return TJsio_CommonInit(interp);
}
#endif


#ifdef __cplusplus
}
#endif


/* EOF */