Skip to content
Snippets Groups Projects
js_com.c 19.8 KiB
Newer Older
/* Synchronet JavaScript "COM" Object */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "comio.h"
#include "js_request.h"

#ifdef JAVASCRIPT

typedef struct
{
	COM_HANDLE	com;
	BOOL		external;	/* externally created, don't close */
	BOOL		is_open;
	BOOL		network_byte_order;
	BOOL		debug;
	BOOL		dtr;
	long		baud_rate;
	int			last_error;
	char		*dev;

} private_t;

static void dbprintf(BOOL error, private_t* p, char* fmt, ...)
{
	va_list argptr;
	char sbuf[1024];

	if(p==NULL || (!p->debug /*&& !error */))
		return;

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
	
	lprintf(LOG_DEBUG,"%s %s%s",p->dev,error ? "ERROR: ":"",sbuf);
}

/* COM Destructor */

static void js_finalize_com(JSContext *cx, JSObject *obj)
{
	private_t* p;

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL)
		return;

	if(p->external==FALSE && p->com!=COM_HANDLE_INVALID) {
		comClose(p->com);
		dbprintf(FALSE, p, "closed");
	}

	if(p->dev)
		free(p->dev);
	free(p);

	JS_SetPrivate(cx, obj, NULL);
}


/* COM Object Methods */

js_close(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	private_t*	p;
	jsrefcount	rc;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
		return(JS_FALSE);
	}

	if(p->com==COM_HANDLE_INVALID)
		return(JS_TRUE);

	rc=JS_SUSPENDREQUEST(cx);
	comClose(p->com);

	p->last_error = COM_ERROR_VALUE;

	dbprintf(FALSE, p, "closed");

	p->com = COM_HANDLE_INVALID; 
	p->is_open = FALSE;
	JS_RESUMEREQUEST(cx, rc);

	return(JS_TRUE);
}

static JSBool
js_open(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
		return(JS_FALSE);
	}

	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "opening port %s", p->dev);

	p->com=comOpen(p->dev);

	if(p->com==COM_HANDLE_INVALID) {
		p->last_error=COM_ERROR_VALUE;
		dbprintf(TRUE, p, "connect failed with error %d", p->last_error);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		JS_RESUMEREQUEST(cx, rc);
		return(JS_TRUE);
	}

	comSetBaudRate(p->com, p->baud_rate);

	p->is_open = TRUE;
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	dbprintf(FALSE, p, "connected to port %s", p->dev);
	JS_RESUMEREQUEST(cx, rc);

	return(JS_TRUE);
}

static JSBool
js_send(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	size_t		len = 0;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
	if(!js_argc(cx, argc, 1))
		return JS_FALSE;

	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	JSVALUE_TO_MSTRING(cx, argv[0], cp, &len);
	HANDLE_PENDING(cx, cp);

	rc=JS_SUSPENDREQUEST(cx);
	if(cp && comWriteBuf(p->com,(uint8_t *)cp,len)==len) {
		dbprintf(FALSE, p, "sent %lu bytes",len);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
		p->last_error=COM_ERROR_VALUE;
		dbprintf(TRUE, p, "send of %lu bytes failed",len);
	if(cp)
		free(cp);
	JS_RESUMEREQUEST(cx, rc);
		
	return(JS_TRUE);
}

static JSBool
js_sendfile(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	private_t*	p;
	jsrefcount	rc;
	char		*buf;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
	if(!js_argc(cx, argc, 1))
		return JS_FALSE;

	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
	HANDLE_PENDING(cx, fname);
deuce's avatar
deuce committed
	if(fname==NULL) {
		JS_ReportError(cx,"Failure reading filename");
		return(JS_FALSE);
	}

	rc=JS_SUSPENDREQUEST(cx);
	if((file=nopen(fname,O_RDONLY|O_BINARY))==-1) {
		JS_RESUMEREQUEST(cx, rc);
		free(fname);
	free(fname);
	len=filelength(file);
	if(len < 1) {
		close(file);
		return(JS_TRUE);
	}
	if((buf=malloc((size_t)len))==NULL) {
		close(file);
		return(JS_TRUE);
	}
	if(read(file,buf,(uint)len)!=len) {
		free(buf);
		close(file);
		return(JS_TRUE);
	}
	close(file);

	if(comWriteBuf(p->com,(uint8_t *)buf,(size_t)len)==len) {
		dbprintf(FALSE, p, "sent %ld bytes",len);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
		p->last_error=COM_ERROR_VALUE;
		dbprintf(TRUE, p, "send of %ld bytes failed",len);
	}
	free(buf);

	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}

static JSBool
js_sendbin(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	BYTE		b;
	WORD		w;
	DWORD		l;
	int32		val=0;
	size_t		wr=0;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
	if(!js_argc(cx, argc, 1))
		return JS_FALSE;

	if(!JS_ValueToInt32(cx,argv[0],&val))
		return JS_FALSE;

	if(!JS_ValueToInt32(cx,argv[1],&size))

	rc=JS_SUSPENDREQUEST(cx);
	switch(size) {
		case sizeof(BYTE):
			b = (BYTE)val;
			wr=comWriteBuf(p->com,&b,size);
			break;
		case sizeof(WORD):
			w = (WORD)val;
			if(p->network_byte_order)
				w=htons(w);
			wr=comWriteBuf(p->com,(BYTE*)&w,size);
			break;
		case sizeof(DWORD):
			l = val;
			if(p->network_byte_order)
				l=htonl(l);
			wr=comWriteBuf(p->com,(BYTE*)&l,size);
			break;
		default:	
			/* unknown size */
			dbprintf(TRUE, p, "unsupported binary write size: %d",size);
			break;
	}
	if(wr==size) {
		dbprintf(FALSE, p, "sent %u bytes (binary)",size);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
		p->last_error=COM_ERROR_VALUE;
		dbprintf(TRUE, p, "send of %u bytes (binary) failed",size);
	}
		
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


static JSBool
js_recv(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	char*		buf;
	int32		len=512;
	JSString*	str;
	jsrefcount	rc;
	int32		timeout=30;	/* seconds */
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
		if(!JS_ValueToInt32(cx,argv[1],&timeout))
			return JS_FALSE;
	}
deuce's avatar
deuce committed
	if((buf=(char*)malloc(len+1))==NULL) {
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
		return(JS_FALSE);
	}

	rc=JS_SUSPENDREQUEST(cx);
	len = comReadBuf(p->com,buf,len,NULL,timeout);
	JS_RESUMEREQUEST(cx, rc);
	if(len<0) {
		p->last_error=COM_ERROR_VALUE;
deuce's avatar
deuce committed
		free(buf);
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
		return(JS_TRUE);
	}
	buf[len]=0;

	str = JS_NewStringCopyN(cx, buf, len);
deuce's avatar
deuce committed
	free(buf);
	if(str==NULL)
		return(JS_FALSE);

	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "received %u bytes",len);
	JS_RESUMEREQUEST(cx, rc);
	
	return(JS_TRUE);
}

static JSBool
js_recvline(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	char*		buf;
	int			i;
	int32		len=512;
	int32		timeout=30;	/* seconds */
	JSString*	str;
	private_t*	p;
	jsrefcount	rc;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
deuce's avatar
deuce committed
	if((buf=(char*)malloc(len+1))==NULL) {
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
		return(JS_FALSE);
	}

deuce's avatar
deuce committed
		if(!JS_ValueToInt32(cx,argv[1],&timeout)) {
			free(buf);
deuce's avatar
deuce committed
		}

	rc=JS_SUSPENDREQUEST(cx);

	i=comReadLine(p->com, buf, len+1, timeout);
	
	if(i>0 && buf[i-1]=='\r')
		buf[i-1]=0;
	else
		buf[i]=0;

	JS_RESUMEREQUEST(cx, rc);
	str = JS_NewStringCopyZ(cx, buf);
deuce's avatar
deuce committed
	free(buf);
	if(str==NULL)
		return(JS_FALSE);

	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "received %u bytes (recvline) lasterror=%d"
	JS_RESUMEREQUEST(cx, rc);
		
	return(JS_TRUE);
}

static JSBool
js_recvbin(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	int			rd=0;
	private_t*	p;
	jsrefcount	rc;
	int32		timeout=30;	/* seconds */

	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
		if(!JS_ValueToInt32(cx,argv[0],&size))
		if(!JS_ValueToInt32(cx,argv[1],&timeout))

	rc=JS_SUSPENDREQUEST(cx);
	switch(size) {
		case sizeof(BYTE):
deuce's avatar
deuce committed
			if((rd=comReadBuf(p->com,(char*)&b,size,NULL,timeout))==size)
				JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(b));
			break;
		case sizeof(WORD):
deuce's avatar
deuce committed
			if((rd=comReadBuf(p->com,(char*)&w,size,NULL,timeout))==size) {
				if(p->network_byte_order)
					w=ntohs(w);
				JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(w));
deuce's avatar
deuce committed
			if((rd=comReadBuf(p->com,(char*)&l,size,NULL,timeout))==size) {
				if(p->network_byte_order)
					l=ntohl(l);
				JS_SET_RVAL(cx, arglist,UINT_TO_JSVAL(l));
		p->last_error=COM_ERROR_VALUE;
		
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}

/* COM Object Properites */
enum {
	 COM_PROP_LAST_ERROR
	,COM_PROP_IS_OPEN
	,COM_PROP_DEBUG
	,COM_PROP_DESCRIPTOR
	,COM_PROP_NETWORK_ORDER
	,COM_PROP_BAUD_RATE
	,COM_PROP_DEVICE
	,COM_PROP_DTR
	,COM_PROP_CTS
	,COM_PROP_DSR
	,COM_PROP_RING
	,COM_PROP_DCD

};

#ifdef BUILD_JSDOCS
static const char* com_prop_desc[] = {
Rob Swindell's avatar
Rob Swindell committed
	 "Error status for the last COM operation that failed - <small>READ ONLY</small>"
	,"<tt>true</tt> if port is in a connected state - <small>READ ONLY</small>"
Rob Swindell's avatar
Rob Swindell committed
	,"Enable debug logging"
	,"COM handle (advanced uses only)"
	,"<tt>true</tt> if binary data is to be sent in Network Byte Order (big end first), default is <tt>true</tt>"
	,"COM port Baud rate"
	,"Device name"
	,"Data Terminal Ready"
	,"Clear To Send"
	,"Data Set Ready"
	,"Ring Indicator"
	,"Data Carrier Detect"
	,NULL
};
#endif

static JSBool js_com_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
    jsint       tiny;
	private_t*	p;
	jsrefcount	rc;
	double		d;

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		// Prototype access
		return(JS_TRUE);
	}

    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);

	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "setting property %d",tiny);
	JS_RESUMEREQUEST(cx, rc);

	switch(tiny) {
		case COM_PROP_DEBUG:
			JS_ValueToBoolean(cx,*vp,&(p->debug));
			break;
		case COM_PROP_DESCRIPTOR:
			p->is_open=TRUE;
			break;
		case COM_PROP_LAST_ERROR:
			break;
		case COM_PROP_BAUD_RATE:
			if(JS_ValueToNumber(cx,*vp,&d)) {
				p->baud_rate=(long)d;
				rc=JS_SUSPENDREQUEST(cx);
				if(p->is_open)
					comSetBaudRate(p->com, p->baud_rate);
				JS_RESUMEREQUEST(cx, rc);
			}
			break;
		case COM_PROP_NETWORK_ORDER:
			JS_ValueToBoolean(cx,*vp,&(p->network_byte_order));
			break;
		case COM_PROP_DTR:
			JS_ValueToBoolean(cx,*vp,&(p->dtr));
			if(p->is_open) {
				if(p->dtr)
					comRaiseDTR(p->com);
				else
					comLowerDTR(p->com);
			}
			else
				p->dtr=FALSE;
			break;

	}

	return(JS_TRUE);
}

static JSBool js_com_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
    jsint       tiny;
	private_t*	p;
	JSString*	js_str;
	jsrefcount	rc;
	long		baud_rate;
	int			ms;

	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
		// Protoype access
		return(JS_TRUE);
	}

    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);

	rc=JS_SUSPENDREQUEST(cx);
#if 0 /* just too much */
	dbprintf(FALSE, p, "getting property %d",tiny);
#endif

	switch(tiny) {
		case COM_PROP_LAST_ERROR:
			*vp = INT_TO_JSVAL(p->last_error);
			break;
		case COM_PROP_IS_OPEN:
			break;
		case COM_PROP_DEBUG:
			*vp = BOOLEAN_TO_JSVAL(p->debug);
			break;
		case COM_PROP_DESCRIPTOR:
			*vp = INT_TO_JSVAL((int)p->com);
			break;
		case COM_PROP_NETWORK_ORDER:
			*vp = BOOLEAN_TO_JSVAL(p->network_byte_order);
			break;
		case COM_PROP_BAUD_RATE:
			baud_rate=comGetBaudRate(p->com);
			*vp=UINT_TO_JSVAL(baud_rate);
			break;
		case COM_PROP_DEVICE:
			JS_RESUMEREQUEST(cx, rc);
			if((js_str=JS_NewStringCopyZ(cx,p->dev))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
			rc=JS_SUSPENDREQUEST(cx);
			break;
		case COM_PROP_DTR:
			*vp = BOOLEAN_TO_JSVAL(p->dtr);
			break;
		case COM_PROP_CTS:
			ms=comGetModemStatus(p->com);
			*vp = BOOLEAN_TO_JSVAL(ms & COM_CTS);
			break;
		case COM_PROP_DSR:
			ms=comGetModemStatus(p->com);
			*vp = BOOLEAN_TO_JSVAL(ms & COM_DSR);
			break;
		case COM_PROP_RING:
			ms=comGetModemStatus(p->com);
			*vp = BOOLEAN_TO_JSVAL(ms & COM_RING);
			break;
		case COM_PROP_DCD:
			ms=comGetModemStatus(p->com);
			*vp = BOOLEAN_TO_JSVAL(ms & COM_DCD);
			break;

	}

	JS_RESUMEREQUEST(cx, rc);
	return(TRUE);
}

#define COM_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY

static jsSyncPropertySpec js_com_properties[] = {
/*		 name				,tinyid					,flags,				ver	*/

	{	"error"				,COM_PROP_LAST_ERROR	,COM_PROP_FLAGS,	315 },
	{	"last_error"		,COM_PROP_LAST_ERROR	,JSPROP_READONLY,	315 },	/* alias */
	{	"is_open"			,COM_PROP_IS_OPEN		,COM_PROP_FLAGS,	315 },
	{	"debug"				,COM_PROP_DEBUG			,JSPROP_ENUMERATE,	315 },
	{	"descriptor"		,COM_PROP_DESCRIPTOR	,JSPROP_ENUMERATE,	315 },
	{	"network_byte_order",COM_PROP_NETWORK_ORDER	,JSPROP_ENUMERATE,	315 },
	{	"baud_rate"			,COM_PROP_BAUD_RATE		,JSPROP_ENUMERATE,	315 },
	{	"device"			,COM_PROP_DEVICE		,COM_PROP_FLAGS,	315 },
	{	"dtr"				,COM_PROP_DTR			,JSPROP_ENUMERATE,	315 },
	{	"cts"				,COM_PROP_CTS			,COM_PROP_FLAGS,	315 },
	{	"dsr"				,COM_PROP_DSR			,COM_PROP_FLAGS,	315 },
	{	"ring"				,COM_PROP_RING			,COM_PROP_FLAGS,	315 },
	{	"dcd"				,COM_PROP_DCD			,COM_PROP_FLAGS,	315 },
	{0}
};

static jsSyncMethodSpec js_com_functions[] = {
	{"close",		js_close,		0,	JSTYPE_VOID,	""
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Close the port immediately")
	,315
	},
	{"open",     js_open,     2,	JSTYPE_BOOLEAN,	JSDOCSTR("")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Connect to a COM port")
	,315
	},
	{"write",		js_send,		1,	JSTYPE_ALIAS },
	{"send",		js_send,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("data")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Send a string (AKA write)")
	,315
	},
	{"sendfile",	js_sendfile,	1,	JSTYPE_BOOLEAN,	JSDOCSTR("path/filename")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Send an entire file over the port")
	,315
	},
	{"writeBin",	js_sendbin,		1,	JSTYPE_ALIAS },
	{"sendBin",		js_sendbin,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("value [,bytes=4]")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Send a binary integer over the port, default number of bytes is 4 (32-bits)")
	,315
	},
	{"read",		js_recv,		1,	JSTYPE_ALIAS },
	{"recv",		js_recv,		1,	JSTYPE_STRING,	JSDOCSTR("[maxlen=512 [,timeout=30]]")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Receive a string, default maxlen is 512 characters, default timeout is 30 seconds (AKA read)")
	,315
	},
	{"readline",	js_recvline,	0,	JSTYPE_ALIAS },
	{"readln",		js_recvline,	0,	JSTYPE_ALIAS },
	{"recvline",	js_recvline,	0,	JSTYPE_STRING,	JSDOCSTR("[maxlen=512] [,timeout=30.0]")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Receive a line-feed terminated string, default maxlen is 512 characters, default timeout is 30 seconds (AKA readline and readln)")
	,315
	},
	{"readBin",		js_recvbin,		0,	JSTYPE_ALIAS },
	{"recvBin",		js_recvbin,		0,	JSTYPE_NUMBER,	JSDOCSTR("[bytes=4 [,timeout=30]")
Rob Swindell's avatar
Rob Swindell committed
	,JSDOCSTR("Receive a binary integer from the port, default number of bytes is 4 (32-bits), default timeout is 30 seconds")
static JSBool js_com_resolve(JSContext *cx, JSObject *obj, jsid id)
deuce's avatar
deuce committed
	JSBool			ret;
deuce's avatar
deuce committed
	if(id != JSID_VOID && id != JSID_EMPTY) {
		jsval idval;
		
		JS_IdToValue(cx, id, &idval);
deuce's avatar
deuce committed
		if(JSVAL_IS_STRING(idval)) {
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
			HANDLE_PENDING(cx, name);
deuce's avatar
deuce committed
		}
deuce's avatar
deuce committed
	}
deuce's avatar
deuce committed
	ret=js_SyncResolve(cx, obj, name, js_com_properties, js_com_functions, NULL, 0);
	if(name)
		free(name);
	return ret;
}

static JSBool js_com_enumerate(JSContext *cx, JSObject *obj)
{
deuce's avatar
deuce committed
	return(js_com_resolve(cx, obj, JSID_VOID));
     "COM"				/* name			*/
    ,JSCLASS_HAS_PRIVATE	/* flags		*/
	,JS_PropertyStub		/* addProperty	*/
	,JS_PropertyStub		/* delProperty	*/
	,js_com_get				/* getProperty	*/
	,js_com_set				/* setProperty	*/
	,js_com_enumerate		/* enumerate	*/
	,js_com_resolve			/* resolve		*/
	,JS_ConvertStub			/* convert		*/
	,js_finalize_com		/* finalize		*/
};

/* COM Constructor (creates COM descriptor) */

static JSBool
js_com_constructor(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj;
	jsval *argv=JS_ARGV(cx, arglist);
	obj=JS_NewObject(cx, &js_com_class, NULL, NULL);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
	if(argc > 0) {
		JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
		HANDLE_PENDING(cx, fname);
deuce's avatar
deuce committed
	if(argc==0 || fname==NULL) {
		JS_ReportError(cx,"Failure reading port name");
		return(JS_FALSE);
	}

	if((p=(private_t*)malloc(sizeof(private_t)))==NULL) {
		JS_ReportError(cx,"malloc failed");
		free(fname);
		return(JS_FALSE);
	}
	memset(p,0,sizeof(private_t));

	p->dev=fname;
	p->network_byte_order = TRUE;
	p->baud_rate = 9600;
	p->com = COM_HANDLE_INVALID;

	if(!JS_SetPrivate(cx, obj, p)) {
		JS_ReportError(cx,"JS_SetPrivate failed");
		return(JS_FALSE);
	}

#ifdef BUILD_JSDOCS
	js_DescribeSyncObject(cx,obj,"Class used for serial port communications",31501);
	js_DescribeSyncConstructor(cx,obj,"To create a new COM object: "
		"<tt>var c = new COM('<i>device</i>')</tt><br>"
		"where <i>device</i> = <tt>COMx</tt> (e.g. COM1) for Win32 or <tt>/dev/ttyXY</tt> for *nix (e.g. /dev/ttyu0)"
		);
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", com_prop_desc, JSPROP_READONLY);
#endif

	dbprintf(FALSE, p, "object constructed");
	return(JS_TRUE);
}

JSObject* js_CreateCOMClass(JSContext* cx, JSObject* parent)
{
	JSObject*	comobj;

	comobj = JS_InitClass(cx, parent, NULL
		,&js_com_class
		,js_com_constructor
		,0	/* number of constructor args */
		,NULL /* props, specified in constructor */
		,NULL /* funcs, specified in constructor */
		,NULL,NULL);

	return(comobj);
}

JSObject* js_CreateCOMObject(JSContext* cx, JSObject* parent, const char *name, COM_HANDLE com)
{
	JSObject*	obj;
	private_t*	p;

	obj = JS_DefineObject(cx, parent, name, &js_com_class, NULL
		,JSPROP_ENUMERATE|JSPROP_READONLY);

	if(obj==NULL)
		return(NULL);

	if((p=(private_t*)malloc(sizeof(private_t)))==NULL)
		return(NULL);
	memset(p,0,sizeof(private_t));

	p->com = com;
	p->external = TRUE;
	p->network_byte_order = TRUE;

	if(!JS_SetPrivate(cx, obj, p)) {
		dbprintf(TRUE, p, "JS_SetPrivate failed");
		return(NULL);
	}

	dbprintf(FALSE, p, "object created");

	return(obj);
}

#endif	/* JAVSCRIPT */