Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

js_com.c 19.9 KB
Newer Older
1 2 3 4 5 6
/* 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)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *																			*
 * 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"
23
#include "comio.h"
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
#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 */

82 83
extern JSClass js_com_class;

84
static JSBool
85
js_close(JSContext *cx, uintN argc, jsval *arglist)
86
{
87
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
88 89 90
	private_t*	p;
	jsrefcount	rc;

91 92
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

93
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
94 95 96 97 98 99 100 101 102
		return(JS_FALSE);
	}

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

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

103
	p->last_error = COM_ERROR_VALUE;
104 105 106 107 108 109 110 111 112 113 114

	dbprintf(FALSE, p, "closed");

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

	return(JS_TRUE);
}

static JSBool
115
js_open(JSContext *cx, uintN argc, jsval *arglist)
116
{
117
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
118 119 120
	private_t*	p;
	jsrefcount	rc;

121 122
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

123
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_com_class))==NULL) {
124 125 126 127 128 129 130 131 132
		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) {
133 134
		p->last_error=COM_ERROR_VALUE;
		dbprintf(TRUE, p, "connect failed with error %d", p->last_error);
135
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
136 137 138 139 140 141 142
		JS_RESUMEREQUEST(cx, rc);
		return(JS_TRUE);
	}

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

	p->is_open = TRUE;
143
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
144 145 146 147 148 149 150
	dbprintf(FALSE, p, "connected to port %s", p->dev);
	JS_RESUMEREQUEST(cx, rc);

	return(JS_TRUE);
}

static JSBool
151
js_send(JSContext *cx, uintN argc, jsval *arglist)
152
{
153 154
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
155
	char*		cp = NULL;
156
	size_t		len = 0;
157 158 159
	private_t*	p;
	jsrefcount	rc;

160 161
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

166 167 168
	if(!js_argc(cx, argc, 1))
		return JS_FALSE;

169
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
170

171
	JSVALUE_TO_MSTRING(cx, argv[0], cp, &len);
172
	HANDLE_PENDING(cx, cp);
173 174

	rc=JS_SUSPENDREQUEST(cx);
175
	if(cp && comWriteBuf(p->com,(uint8_t *)cp,len)==len) {
176
		dbprintf(FALSE, p, "sent %u bytes",len);
177
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
178
	} else {
179
		p->last_error=COM_ERROR_VALUE;
180 181
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
182 183
	if(cp)
		free(cp);
184 185 186 187 188 189
	JS_RESUMEREQUEST(cx, rc);
		
	return(JS_TRUE);
}

static JSBool
190
js_sendfile(JSContext *cx, uintN argc, jsval *arglist)
191
{
192 193
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
194
	off_t		len;
195
	int			file;
196
	char*		fname = NULL;
197 198 199 200
	private_t*	p;
	jsrefcount	rc;
	char		*buf;

201 202
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

207 208 209
	if(!js_argc(cx, argc, 1))
		return JS_FALSE;

210
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
211

212
	JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
213
	HANDLE_PENDING(cx, fname);
deuce's avatar
deuce committed
214
	if(fname==NULL) {
215 216 217 218 219 220 221
		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);
222
		free(fname);
223 224 225
		return(JS_TRUE);
	}

226
	free(fname);
227
	len=filelength(file);
228 229 230 231
	if(len < 1) {
		close(file);
		return(JS_TRUE);
	}
232
	if((buf=malloc((size_t)len))==NULL) {
233 234 235
		close(file);
		return(JS_TRUE);
	}
236
	if(read(file,buf,(uint)len)!=len) {
237 238 239 240 241 242
		free(buf);
		close(file);
		return(JS_TRUE);
	}
	close(file);

243
	if(comWriteBuf(p->com,(uint8_t *)buf,(size_t)len)==len) {
244
		dbprintf(FALSE, p, "sent %u bytes",len);
245
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
246
	} else {
247
		p->last_error=COM_ERROR_VALUE;
248 249 250 251 252 253 254 255 256
		dbprintf(TRUE, p, "send of %u bytes failed",len);
	}
	free(buf);

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

static JSBool
257
js_sendbin(JSContext *cx, uintN argc, jsval *arglist)
258
{
259 260
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
261 262 263 264 265
	BYTE		b;
	WORD		w;
	DWORD		l;
	int32		val=0;
	size_t		wr=0;
266
	int32		size=sizeof(DWORD);
267 268 269
	private_t*	p;
	jsrefcount	rc;

270
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
271

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

276 277 278 279 280 281
	if(!js_argc(cx, argc, 1))
		return JS_FALSE;

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

282
	if(!JS_ValueToInt32(cx,argv[1],&size))
283
		return JS_FALSE;
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309

	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);
310
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
311
	} else {
312
		p->last_error=COM_ERROR_VALUE;
313 314 315 316 317 318 319 320 321
		dbprintf(TRUE, p, "send of %u bytes (binary) failed",size);
	}
		
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


static JSBool
322
js_recv(JSContext *cx, uintN argc, jsval *arglist)
323
{
324 325
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
326 327 328 329 330
	char*		buf;
	int32		len=512;
	JSString*	str;
	jsrefcount	rc;
	int32		timeout=30;	/* seconds */
331
	private_t*	p;
332

333 334
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

339
	if(argc) {
340 341 342
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
343

344
	if(argc>1) {
345 346 347
		if(!JS_ValueToInt32(cx,argv[1],&timeout))
			return JS_FALSE;
	}
348

deuce's avatar
deuce committed
349
	if((buf=(char*)malloc(len+1))==NULL) {
350 351 352 353 354 355 356 357
		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) {
358
		p->last_error=COM_ERROR_VALUE;
deuce's avatar
deuce committed
359
		free(buf);
360
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
361 362 363 364 365
		return(JS_TRUE);
	}
	buf[len]=0;

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

370
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
371 372 373 374 375 376 377 378
	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "received %u bytes",len);
	JS_RESUMEREQUEST(cx, rc);
	
	return(JS_TRUE);
}

static JSBool
379
js_recvline(JSContext *cx, uintN argc, jsval *arglist)
380
{
381 382
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
383 384 385 386 387 388 389 390
	char*		buf;
	int			i;
	int32		len=512;
	int32		timeout=30;	/* seconds */
	JSString*	str;
	private_t*	p;
	jsrefcount	rc;

391 392
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

397
	if(argc) {
398 399 400
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
401

deuce's avatar
deuce committed
402
	if((buf=(char*)malloc(len+1))==NULL) {
403 404 405 406
		JS_ReportError(cx,"Error allocating %u bytes",len+1);
		return(JS_FALSE);
	}

407
	if(argc>1) {
deuce's avatar
deuce committed
408 409
		if(!JS_ValueToInt32(cx,argv[1],&timeout)) {
			free(buf);
410
			return JS_FALSE;
deuce's avatar
deuce committed
411
		}
412
	}
413 414 415 416 417 418 419 420 421 422 423 424

	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
425
	free(buf);
426 427 428
	if(str==NULL)
		return(JS_FALSE);

429
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
430 431
	rc=JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "received %u bytes (recvline) lasterror=%d"
432
		,i,COM_ERROR_VALUE);
433 434 435 436 437 438
	JS_RESUMEREQUEST(cx, rc);
		
	return(JS_TRUE);
}

static JSBool
439
js_recvbin(JSContext *cx, uintN argc, jsval *arglist)
440
{
441 442
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
443 444 445
	BYTE		b;
	WORD		w;
	DWORD		l;
446
	int32		size=sizeof(DWORD);
447 448 449 450 451
	int			rd=0;
	private_t*	p;
	jsrefcount	rc;
	int32		timeout=30;	/* seconds */

452
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
453

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

458
	if(argc) {
459
		if(!JS_ValueToInt32(cx,argv[0],&size))
460 461
			return JS_FALSE;
	}
462

463
	if(argc>1) {
464
		if(!JS_ValueToInt32(cx,argv[1],&timeout))
465 466
			return JS_FALSE;
	}
467 468 469 470

	rc=JS_SUSPENDREQUEST(cx);
	switch(size) {
		case sizeof(BYTE):
deuce's avatar
deuce committed
471
			if((rd=comReadBuf(p->com,(char*)&b,size,NULL,timeout))==size)
472
				JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(b));
473 474
			break;
		case sizeof(WORD):
deuce's avatar
deuce committed
475
			if((rd=comReadBuf(p->com,(char*)&w,size,NULL,timeout))==size) {
476 477
				if(p->network_byte_order)
					w=ntohs(w);
478
				JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(w));
479 480 481
			}
			break;
		case sizeof(DWORD):
deuce's avatar
deuce committed
482
			if((rd=comReadBuf(p->com,(char*)&l,size,NULL,timeout))==size) {
483 484
				if(p->network_byte_order)
					l=ntohl(l);
485
				JS_SET_RVAL(cx, arglist,UINT_TO_JSVAL(l));
486 487 488 489 490
			}
			break;
	}

	if(rd!=size)
491
		p->last_error=COM_ERROR_VALUE;
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
		
	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 char* com_prop_desc[] = {
	 "error status for the last COM operation that failed - <small>READ ONLY</small>"
	,"<i>true</i> if port is in a connected state - <small>READ ONLY</small>"
	,"enable debug logging"
	,"COM handle (advanced uses only)"
	,"<i>true</i> if binary data is to be sent in Network Byte Order (big end first), default is <i>true</i>"
	,"COM port Baud rate"
	,"Device name"
	,"Data Terminal Ready"
	,"Clear To Send"
	,"Data Set Ready"
	,"Ring Indicator"
	,"Data Carrier Detect"
	,NULL
};
#endif

532
static JSBool js_com_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
533
{
534
	jsval idval;
535 536 537 538
    jsint       tiny;
	private_t*	p;
	jsrefcount	rc;
	double		d;
539
	int32		i;
540 541 542 543 544 545

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

546 547
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
548 549 550 551 552 553 554 555 556 557

	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:
558
			if(!JS_ValueToInt32(cx,*vp,&i))
559
				return JS_FALSE;
560
			p->com=(COM_HANDLE)i;
561 562 563
			p->is_open=TRUE;
			break;
		case COM_PROP_LAST_ERROR:
564
			if(!JS_ValueToInt32(cx,*vp,&i))
565
				return JS_FALSE;
566
			p->last_error=i;
567 568
			break;
		case COM_PROP_BAUD_RATE:
569 570 571 572 573 574 575
			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);
			}
576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
			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);
}

597
static JSBool js_com_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
598
{
599
	jsval idval;
600 601 602 603 604 605 606 607 608 609 610 611
    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);
	}

612 613
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
614 615 616 617 618 619 620 621 622 623 624

	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:
625
			if(p->is_open)
626
				*vp = JSVAL_TRUE;
627 628
			else
				*vp = JSVAL_FALSE;
629 630 631 632 633
			break;
		case COM_PROP_DEBUG:
			*vp = BOOLEAN_TO_JSVAL(p->debug);
			break;
		case COM_PROP_DESCRIPTOR:
634
			*vp = INT_TO_JSVAL((int)p->com);
635 636 637 638 639 640
			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);
641
			*vp=UINT_TO_JSVAL(baud_rate);
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
			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,	""
	,JSDOCSTR("close the port immediately")
	,315
	},
	{"open",     js_open,     2,	JSTYPE_BOOLEAN,	JSDOCSTR("")
	,JSDOCSTR("connect to a COM port")
	,315
	},
	{"write",		js_send,		1,	JSTYPE_ALIAS },
	{"send",		js_send,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("data")
	,JSDOCSTR("send a string (AKA write)")
	,315
	},
	{"sendfile",	js_sendfile,	1,	JSTYPE_BOOLEAN,	JSDOCSTR("path/filename")
	,JSDOCSTR("send an entire file over the port")
	,315
	},
	{"writeBin",	js_sendbin,		1,	JSTYPE_ALIAS },
	{"sendBin",		js_sendbin,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("value [,bytes=<tt>4</tt>]")
	,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=<tt>512</tt> [,timeout=<tt>30</tt>]]")
	,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=<tt>512</tt>] [,timeout=<tt>30.0</tt>]")
	,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=<tt>4</tt> [,timeout=<tt>30</tt>]")
	,JSDOCSTR("receive a binary integer from the port, default number of bytes is 4 (32-bits), default timeout is 30 seconds")
	,315
	},
	{0}
};

739
static JSBool js_com_resolve(JSContext *cx, JSObject *obj, jsid id)
740 741
{
	char*			name=NULL;
deuce's avatar
deuce committed
742
	JSBool			ret;
743

deuce's avatar
deuce committed
744 745 746 747
	if(id != JSID_VOID && id != JSID_EMPTY) {
		jsval idval;
		
		JS_IdToValue(cx, id, &idval);
deuce's avatar
deuce committed
748 749
		if(JSVAL_IS_STRING(idval)) {
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
750
			HANDLE_PENDING(cx, name);
deuce's avatar
deuce committed
751
		}
deuce's avatar
deuce committed
752
	}
753

deuce's avatar
deuce committed
754 755 756 757
	ret=js_SyncResolve(cx, obj, name, js_com_properties, js_com_functions, NULL, 0);
	if(name)
		free(name);
	return ret;
758 759 760 761
}

static JSBool js_com_enumerate(JSContext *cx, JSObject *obj)
{
deuce's avatar
deuce committed
762
	return(js_com_resolve(cx, obj, JSID_VOID));
763 764
}

765
JSClass js_com_class = {
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
     "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
781
js_com_constructor(JSContext *cx, uintN argc, jsval *arglist)
782
{
783
	JSObject *obj;
784
	jsval *argv=JS_ARGV(cx, arglist);
785
	private_t* p;
786
	char*		fname = NULL;
787

788 789
	obj=JS_NewObject(cx, &js_com_class, NULL, NULL);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
deuce's avatar
deuce committed
790
	if(argc > 0) {
791
		JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
792
		HANDLE_PENDING(cx, fname);
deuce's avatar
deuce committed
793
	}
deuce's avatar
deuce committed
794
	if(argc==0 || fname==NULL) {
795 796 797 798 799 800
		JS_ReportError(cx,"Failure reading port name");
		return(JS_FALSE);
	}

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

806
	p->dev=fname;
807 808
	p->network_byte_order = TRUE;
	p->baud_rate = 9600;
809
	p->com = COM_HANDLE_INVALID;
810 811 812 813 814 815 816

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

#ifdef BUILD_JSDOCS
817
	js_DescribeSyncObject(cx,obj,"Class used for serial port communications",31501);
818
	js_DescribeSyncConstructor(cx,obj,"To create a new COM object: "
819 820
		"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)"
821 822 823 824 825 826 827 828
		);
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", com_prop_desc, JSPROP_READONLY);
#endif

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

Rob Swindell's avatar
Rob Swindell committed
829
JSObject* js_CreateCOMClass(JSContext* cx, JSObject* parent)
830 831 832 833 834 835 836 837 838 839 840 841 842 843
{
	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);
}

Rob Swindell's avatar
Rob Swindell committed
844
JSObject* js_CreateCOMObject(JSContext* cx, JSObject* parent, const char *name, COM_HANDLE com)
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
{
	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 */