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_console.cpp 67.5 KB
Newer Older
1 2 3 4 5 6
/* Synchronet JavaScript "Console" Object */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
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 "js_request.h"
24 25 26 27 28 29 30

#ifdef JAVASCRIPT

/*****************************/
/* Console Object Properites */
/*****************************/
enum {
31
	 CON_PROP_STATUS
32
	,CON_PROP_MOUSE_MODE
33
	,CON_PROP_LNCNTR
34
	,CON_PROP_COLUMN
35
	,CON_PROP_LASTLINELEN
rswindell's avatar
rswindell committed
36
	,CON_PROP_ATTR
37
	,CON_PROP_TOS
rswindell's avatar
rswindell committed
38
	,CON_PROP_ROW
39
	,CON_PROP_ROWS
rswindell's avatar
rswindell committed
40
	,CON_PROP_COLUMNS
rswindell's avatar
rswindell committed
41
	,CON_PROP_TABSTOP
42
	,CON_PROP_AUTOTERM
rswindell's avatar
rswindell committed
43
	,CON_PROP_TERMINAL
44 45
	,CON_PROP_TERM_TYPE
	,CON_PROP_CHARSET
46
	,CON_PROP_CTERM_VERSION
rswindell's avatar
rswindell committed
47
	,CON_PROP_WORDWRAP
48
	,CON_PROP_QUESTION
49 50
	,CON_PROP_INACTIV_WARN
	,CON_PROP_INACTIV_HANGUP
51
	,CON_PROP_TIMEOUT			/* User inactivity timeout reference */
52
	,CON_PROP_TIMELEFT_WARN		/* low timeleft warning counter */
53
	,CON_PROP_ABORTED
rswindell's avatar
rswindell committed
54 55
	,CON_PROP_ABORTABLE
	,CON_PROP_TELNET_MODE
56
	,CON_PROP_GETSTR_OFFSET
57
	,CON_PROP_CTRLKEY_PASSTHRU
58 59 60 61 62
	/* read only */
	,CON_PROP_INBUF_LEVEL
	,CON_PROP_INBUF_SPACE
	,CON_PROP_OUTBUF_LEVEL
	,CON_PROP_OUTBUF_SPACE
63 64
	,CON_PROP_KEYBUF_LEVEL
	,CON_PROP_KEYBUF_SPACE
65 66

	,CON_PROP_OUTPUT_RATE
rswindell's avatar
rswindell committed
67 68
};

69
extern JSClass js_console_class;
70
static JSBool js_console_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
71
{
72 73
	jsval		idval;
	int32		val;
74
    jsint       tiny;
75
	JSString*	js_str;
76 77
	sbbs_t*		sbbs;

78
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, obj, &js_console_class))==NULL)
79 80
		return(JS_FALSE);

81 82
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
83 84 85 86 87

	switch(tiny) {
		case CON_PROP_STATUS:
			val=sbbs->console;
			break;
88 89 90
		case CON_PROP_MOUSE_MODE:
			val=sbbs->mouse_mode;
			break;
91 92 93
		case CON_PROP_LNCNTR:
			val=sbbs->lncntr;
			break;
94 95 96
		case CON_PROP_COLUMN:
			val=sbbs->column;
			break;
97 98 99
		case CON_PROP_LASTLINELEN:
			val=sbbs->lastlinelen;
			break;
rswindell's avatar
rswindell committed
100 101 102
		case CON_PROP_ATTR:
			val=sbbs->curatr;
			break;
103
		case CON_PROP_TOS:
rswindell's avatar
rswindell committed
104 105 106 107
			val=sbbs->row == 0;
			break;
		case CON_PROP_ROW:
			val=sbbs->row;
108 109 110 111
			break;
		case CON_PROP_ROWS:
			val=sbbs->rows;
			break;
rswindell's avatar
rswindell committed
112 113 114
		case CON_PROP_COLUMNS:
			val=sbbs->cols;
			break;
rswindell's avatar
rswindell committed
115 116 117
		case CON_PROP_TABSTOP:
			val=sbbs->tabstop;
			break;
118 119 120
		case CON_PROP_AUTOTERM:
			val=sbbs->autoterm;
			break;
rswindell's avatar
rswindell committed
121 122 123 124 125
		case CON_PROP_TERMINAL:
			if((js_str=JS_NewStringCopyZ(cx, sbbs->terminal))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
			return(JS_TRUE);
126 127 128 129 130 131 132 133 134 135
		case CON_PROP_TERM_TYPE:
			if((js_str=JS_NewStringCopyZ(cx, sbbs->term_type()))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
			return(JS_TRUE);
		case CON_PROP_CHARSET:
			if((js_str=JS_NewStringCopyZ(cx, sbbs->term_charset()))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
			return(JS_TRUE);
136 137 138
		case CON_PROP_CTERM_VERSION:
			val=sbbs->cterm_version;
			break;
139 140 141 142 143 144 145

		case CON_PROP_INACTIV_WARN:
			val=sbbs->cfg.sec_warn;
			break;
		case CON_PROP_INACTIV_HANGUP:
			val=sbbs->cfg.sec_hangup;
			break;
146
		case CON_PROP_TIMEOUT:
147
			val=(int32)sbbs->timeout;
148 149 150 151
			break;
		case CON_PROP_TIMELEFT_WARN:
			val=sbbs->timeleft_warn;
			break;
152
		case CON_PROP_ABORTED:
153 154
			*vp=BOOLEAN_TO_JSVAL(INT_TO_BOOL(sbbs->sys_status&SS_ABORT));
			return(JS_TRUE);
rswindell's avatar
rswindell committed
155
		case CON_PROP_ABORTABLE:
156 157
			*vp=BOOLEAN_TO_JSVAL(INT_TO_BOOL(sbbs->rio_abortable));
			return(JS_TRUE);
rswindell's avatar
rswindell committed
158 159 160
		case CON_PROP_TELNET_MODE:
			val=sbbs->telnet_mode;
			break;
161 162 163
		case CON_PROP_GETSTR_OFFSET:
			val=sbbs->getstr_offset;
			break;
rswindell's avatar
rswindell committed
164
		case CON_PROP_WORDWRAP:
165 166 167
			if((js_str=JS_NewStringCopyZ(cx, sbbs->wordwrap))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
rswindell's avatar
rswindell committed
168
			return(JS_TRUE);
169
		case CON_PROP_QUESTION:
170 171 172
			if((js_str=JS_NewStringCopyZ(cx, sbbs->question))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
173
			return(JS_TRUE);
174 175 176
		case CON_PROP_CTRLKEY_PASSTHRU:
			val=sbbs->cfg.ctrlkey_passthru;
			break;
177 178 179 180 181 182 183 184 185 186 187 188
		case CON_PROP_INBUF_LEVEL:
			val=RingBufFull(&sbbs->inbuf);
			break;
		case CON_PROP_INBUF_SPACE:
			val=RingBufFree(&sbbs->inbuf);
			break;
		case CON_PROP_OUTBUF_LEVEL:
			val=RingBufFull(&sbbs->outbuf);
			break;
		case CON_PROP_OUTBUF_SPACE:
			val=RingBufFree(&sbbs->outbuf);
			break;
189 190 191
		case CON_PROP_OUTPUT_RATE:
			val = sbbs->cur_output_rate;
			break;
192 193 194 195 196 197
		case CON_PROP_KEYBUF_LEVEL:
			val = sbbs->keybuf_level();
			break;
		case CON_PROP_KEYBUF_SPACE:
			val = sbbs->keybuf_space();
			break;
198

199 200 201 202 203 204 205 206 207
		default:
			return(JS_TRUE);
	}

	*vp = INT_TO_JSVAL(val);

	return(JS_TRUE);
}

208
static JSBool js_console_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
209
{
210
	jsval idval;
211
	int32		val=0;
212 213
    jsint       tiny;
	sbbs_t*		sbbs;
214
	JSString*	str;
deuce's avatar
deuce committed
215
	jsrefcount	rc;
deuce's avatar
deuce committed
216
	char		*sval;
217

218
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, obj, &js_console_class))==NULL)
219 220
		return(JS_FALSE);

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

224 225 226 227
	if(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_BOOLEAN(*vp)) {
		if(!JS_ValueToInt32(cx, *vp, &val))
			return JS_FALSE;
	}
228 229 230 231 232

	switch(tiny) {
		case CON_PROP_STATUS:
			sbbs->console=val;
			break;
233 234 235
		case CON_PROP_MOUSE_MODE:
			sbbs->set_mouse(val);
			break;
236 237 238
		case CON_PROP_LNCNTR:
			sbbs->lncntr=val;
			break;
239 240 241
		case CON_PROP_COLUMN:
			sbbs->column=val;
			break;
242 243 244
		case CON_PROP_LASTLINELEN:
			sbbs->lastlinelen=val;
			break;
rswindell's avatar
rswindell committed
245
		case CON_PROP_ATTR:
246
			if(JSVAL_IS_STRING(*vp)) {
deuce's avatar
deuce committed
247
				JSVALUE_TO_MSTRING(cx, *vp, sval, NULL);
deuce's avatar
deuce committed
248
				if(sval==NULL)
249
					break;
deuce's avatar
deuce committed
250
				val=attrstr(sval);
deuce's avatar
deuce committed
251
				free(sval);
252
			}
253
			rc=JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
254
			sbbs->attr(val);
255
			JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
256
			break;
rswindell's avatar
rswindell committed
257 258 259
		case CON_PROP_ROW:
			if(val >= 0 && val < TERM_ROWS_MAX)
				sbbs->row = val;
260 261
			break;
		case CON_PROP_ROWS:
rswindell's avatar
rswindell committed
262 263
			if(val >= TERM_ROWS_MIN && val <= TERM_ROWS_MAX)
				sbbs->rows=val;
264
			break;
rswindell's avatar
rswindell committed
265
		case CON_PROP_COLUMNS:
rswindell's avatar
rswindell committed
266 267 268 269 270
			if(val >= TERM_COLS_MIN && val <= TERM_COLS_MAX)
				sbbs->cols=val;
			break;
		case CON_PROP_TABSTOP:
			sbbs->tabstop=val;
rswindell's avatar
rswindell committed
271
			break;
272 273 274
		case CON_PROP_AUTOTERM:
			sbbs->autoterm=val;
			break;
rswindell's avatar
rswindell committed
275
		case CON_PROP_TERMINAL:
deuce's avatar
deuce committed
276
			JSVALUE_TO_MSTRING(cx, *vp, sval, NULL);
deuce's avatar
deuce committed
277
			if(sval==NULL)
rswindell's avatar
rswindell committed
278
				break;
deuce's avatar
deuce committed
279
			SAFECOPY(sbbs->terminal,sval);
deuce's avatar
deuce committed
280
			free(sval);
rswindell's avatar
rswindell committed
281
			break;
282 283 284
		case CON_PROP_CTERM_VERSION:
			sbbs->cterm_version = val;
			break;
285 286 287 288 289 290
		case CON_PROP_INACTIV_WARN:
			sbbs->cfg.sec_warn = (uint16_t)val;
			break;
		case CON_PROP_INACTIV_HANGUP:
			sbbs->cfg.sec_hangup = (uint16_t)val;
			break;
291 292 293 294 295 296
		case CON_PROP_TIMEOUT:
			sbbs->timeout=val;
			break;
		case CON_PROP_TIMELEFT_WARN:
			sbbs->timeleft_warn=val;
			break;
297 298 299 300 301 302
		case CON_PROP_ABORTED:
			if(val)
				sbbs->sys_status|=SS_ABORT;
			else
				sbbs->sys_status&=~SS_ABORT;
			break;
rswindell's avatar
rswindell committed
303
		case CON_PROP_ABORTABLE:
304
			sbbs->rio_abortable=val
rswindell's avatar
rswindell committed
305
				? true:false; // This is a dumb bool conversion to make BC++ happy
rswindell's avatar
rswindell committed
306 307 308 309
			break;
		case CON_PROP_TELNET_MODE:
			sbbs->telnet_mode=val;
			break;
310 311 312
		case CON_PROP_GETSTR_OFFSET:
			sbbs->getstr_offset=val;
			break;
313
		case CON_PROP_QUESTION:
deuce's avatar
deuce committed
314
			JSVALUE_TO_MSTRING(cx, *vp, sval, NULL);
deuce's avatar
deuce committed
315
			if(sval==NULL)
316
				break;
deuce's avatar
deuce committed
317
			SAFECOPY(sbbs->question,sval);
deuce's avatar
deuce committed
318
			free(sval);
319
			break;
320
		case CON_PROP_CTRLKEY_PASSTHRU:
321 322 323
			if(JSVAL_IS_STRING(*vp)) {
				char *s;

324 325
				if((str=JS_ValueToString(cx, *vp))==NULL)
					break;
deuce's avatar
deuce committed
326 327 328
				JSSTRING_TO_MSTRING(cx, str, s, NULL);
				if(s==NULL)
					break;
329
				val=str_to_bits(sbbs->cfg.ctrlkey_passthru, s);
deuce's avatar
deuce committed
330
				free(s);
331
			}
332 333
			sbbs->cfg.ctrlkey_passthru=val;
			break;
334 335 336
		case CON_PROP_OUTPUT_RATE:
			sbbs->set_output_rate((enum sbbs_t::output_rate)val);
			break;
337

338 339 340 341 342 343 344
		default:
			return(JS_TRUE);
	}

	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
345
#define CON_PROP_FLAGS JSPROP_ENUMERATE
346

347 348 349 350
static jsSyncPropertySpec js_console_properties[] = {
/*		 name				,tinyid						,flags			,ver	*/

	{	"status"			,CON_PROP_STATUS			,CON_PROP_FLAGS	,310},
351
	{	"mouse_mode"		,CON_PROP_MOUSE_MODE		,CON_PROP_FLAGS, 31800},
352
	{	"line_counter"		,CON_PROP_LNCNTR 			,CON_PROP_FLAGS	,310},
rswindell's avatar
rswindell committed
353
	{	"current_row"		,CON_PROP_ROW				,CON_PROP_FLAGS ,31800},
354
	{	"current_column"	,CON_PROP_COLUMN			,CON_PROP_FLAGS ,315},
355
	{	"last_line_length"	,CON_PROP_LASTLINELEN		,CON_PROP_FLAGS	,317},
356
	{	"attributes"		,CON_PROP_ATTR				,CON_PROP_FLAGS	,310},
rswindell's avatar
rswindell committed
357
	{	"top_of_screen"		,CON_PROP_TOS				,JSPROP_ENUMERATE|JSPROP_READONLY	,310},
358 359
	{	"screen_rows"		,CON_PROP_ROWS				,CON_PROP_FLAGS	,310},
	{	"screen_columns"	,CON_PROP_COLUMNS			,CON_PROP_FLAGS	,311},
rswindell's avatar
rswindell committed
360
	{	"tabstop"			,CON_PROP_TABSTOP			,CON_PROP_FLAGS	,31700},
361 362
	{	"autoterm"			,CON_PROP_AUTOTERM			,CON_PROP_FLAGS	,310},
	{	"terminal"			,CON_PROP_TERMINAL			,CON_PROP_FLAGS ,311},
363 364
	{	"type"				,CON_PROP_TERM_TYPE			,JSPROP_ENUMERATE|JSPROP_READONLY ,31702},
	{	"charset"			,CON_PROP_CHARSET			,JSPROP_ENUMERATE|JSPROP_READONLY ,31702},
365
	{	"cterm_version"		,CON_PROP_CTERM_VERSION		,CON_PROP_FLAGS ,317},
366 367
	{	"inactivity_warning",CON_PROP_INACTIV_WARN		,CON_PROP_FLAGS, 31401},
	{	"inactivity_hangup"	,CON_PROP_INACTIV_HANGUP	,CON_PROP_FLAGS, 31401},
368 369 370 371 372 373 374 375 376
	{	"timeout"			,CON_PROP_TIMEOUT			,CON_PROP_FLAGS	,310},
	{	"timeleft_warning"	,CON_PROP_TIMELEFT_WARN		,CON_PROP_FLAGS	,310},
	{	"aborted"			,CON_PROP_ABORTED			,CON_PROP_FLAGS	,310},
	{	"abortable"			,CON_PROP_ABORTABLE			,CON_PROP_FLAGS	,310},
	{	"telnet_mode"		,CON_PROP_TELNET_MODE		,CON_PROP_FLAGS	,310},
	{	"wordwrap"			,CON_PROP_WORDWRAP			,JSPROP_ENUMERATE|JSPROP_READONLY ,310},
	{	"question"			,CON_PROP_QUESTION			,CON_PROP_FLAGS ,310},
	{	"getstr_offset"		,CON_PROP_GETSTR_OFFSET		,CON_PROP_FLAGS ,311},
	{	"ctrlkey_passthru"	,CON_PROP_CTRLKEY_PASSTHRU	,CON_PROP_FLAGS	,310},
377 378 379 380
	{	"input_buffer_level",CON_PROP_INBUF_LEVEL		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
	{	"input_buffer_space",CON_PROP_INBUF_SPACE		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
	{	"output_buffer_level",CON_PROP_OUTBUF_LEVEL		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
	{	"output_buffer_space",CON_PROP_OUTBUF_SPACE		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
381
	{	"output_rate"		,CON_PROP_OUTPUT_RATE		,JSPROP_ENUMERATE, 31702},
382 383
	{	"keyboard_buffer_level",CON_PROP_KEYBUF_LEVEL	,JSPROP_ENUMERATE|JSPROP_READONLY, 31800},
	{	"keyboard_buffer_space",CON_PROP_KEYBUF_SPACE	,JSPROP_ENUMERATE|JSPROP_READONLY, 31800},
384 385 386
	{0}
};

387
#ifdef BUILD_JSDOCS
388
static const char* con_prop_desc[] = {
389
	 "status bit-field (see <tt>CON_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
390
	,"mouse mode bit-field (see <tt>MOUSE_MODE_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
391
	,"current 0-based line counter (used for automatic screen pause)"
rswindell's avatar
rswindell committed
392
	,"current 0-based row counter"
393
	,"current 0-based column counter (used to auto-increment <i>line_counter</i> when screen wraps)"
394
	,"length of last line sent to terminal (before a carriage-return or line-wrap)"
395
	,"current display attributes (set with number or string value)"
rswindell's avatar
rswindell committed
396
	,"set to <i>true</i> if the terminal cursor is already at the top of the screen - <small>READ ONLY</small>"
397 398
	,"number of remote terminal screen rows (in lines)"
	,"number of remote terminal screen columns (in character cells)"
rswindell's avatar
rswindell committed
399
	,"current tab stop interval (tab size), in columns"
400
	,"bit-field of automatically detected terminal settings "
401
		"(see <tt>USER_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
402 403 404
	,"terminal type description, possibly supplied by client (e.g. 'ANSI')"
	,"terminal type (i.e. 'ANSI', 'RIP', 'PETSCII', or 'DUMB')"
	,"terminal character set (i.e. 'UTF-8', 'CP437', 'CBM-ASCII', or 'US-ASCII')"
405
	,"detected CTerm (SyncTERM) version as an integer > 1000 where major version is cterm_version / 1000 and minor version is cterm_version % 1000"
406 407 408 409
	,"number of seconds before displaying warning (Are you really there?) due to user/keyboard inactivity"
	,"number of seconds before disconnection due to user/keyboard inactivity"
	,"user/keyboard inactivity timeout reference value (time_t format)"
	,"number of low time-left (5 or fewer minutes remaining) warnings displayed to user"
410
	,"input/output has been aborted"
411
	,"remote output can be asynchronously aborted with Ctrl-C"
412
	,"current Telnet mode bit-field (see <tt>TELNET_MODE_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
413
	,"word-wrap buffer (used by getstr) - <small>READ ONLY</small>"
414
	,"current yes/no question (set by yesno and noyes)"
415
	,"cursor position offset for use with <tt>getstr(K_USEOFFSET)</tt>"
416
	,"control key pass-through bit-mask, set bits represent control key combinations "
417 418
		"<i>not</i> handled by <tt>inkey()</tt> method "
		"This may optionally be specified as a string of characters. "
deuce's avatar
deuce committed
419
		"The format of this string is [+-][@-_]. If neither plus nor minus is "
420
		"the first character, the value will be replaced by one constructed "
deuce's avatar
deuce committed
421
		"from the string. A + indicates that characters following will be "
422
		"added to the set, and a - indicates they should be removed. "
rswindell's avatar
rswindell committed
423
		"ex: <tt>console.ctrlkey_passthru=\"-UP+AB\"</tt> will clear CTRL-U and "
424
		"CTRL-P and set CTRL-A and CTRL-B."
425 426 427 428
	,"number of bytes currently in the input buffer (from the remote client) - <small>READ ONLY</small>"
	,"number of bytes available in the input buffer	- <small>READ ONLY</small>"
	,"number of bytes currently in the output buffer (from the local server) - <small>READ ONLY</small>"
	,"number of bytes available in the output buffer - <small>READ ONLY</small>"
429
	,"emulated serial data output rate, in bits-per-second (0 = unlimited)"
430 431
	,"number of characters current in the keyboard input buffer (from <tt>ungetstr</tt>) - <small>READ ONLY</small>"
	,"number of characters spaces available in the keyboard input buffer - <small>READ ONLY</small>"
432
	,NULL
433 434 435
};
#endif

436 437 438 439 440
/**************************/
/* Console Object Methods */
/**************************/

static JSBool
441
js_inkey(JSContext *cx, uintN argc, jsval *arglist)
442
{
443
	jsval *argv=JS_ARGV(cx, arglist);
444
	char		key[2];
445
	int			ch;
446 447
	int32		mode=0;
	int32		timeout=0;
448 449
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
450
	jsrefcount	rc;
451

452
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
453 454
		return(JS_FALSE);

455
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
456

457 458 459 460 461 462 463 464
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&mode))
			return JS_FALSE;
	}
	if(argc>1) {
		if(!JS_ValueToInt32(cx,argv[1],&timeout))
			return JS_FALSE;
	}
465
	rc=JS_SUSPENDREQUEST(cx);
466 467 468 469 470 471 472 473
	ch = sbbs->inkey(mode,timeout);
	if(ch != NOINP) {
		key[0]=ch;
		key[1]=0;
		if((js_str = JS_NewStringCopyZ(cx, key))==NULL)
			return(JS_FALSE);
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
	}
474
	JS_RESUMEREQUEST(cx, rc);
475 476 477 478
    return(JS_TRUE);
}

static JSBool
479
js_getkey(JSContext *cx, uintN argc, jsval *arglist)
480
{
481
	jsval *argv=JS_ARGV(cx, arglist);
482
	char		key[2];
483
	int32		mode=0;
484 485
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
486
	jsrefcount	rc;
487

488
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
489 490
		return(JS_FALSE);

491 492
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

493 494 495 496
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&mode))
			return JS_FALSE;
	}
497
	rc=JS_SUSPENDREQUEST(cx);
498
	key[0]=sbbs->getkey(mode);
499
	JS_RESUMEREQUEST(cx, rc);
500 501
	key[1]=0;

502 503 504
	if((js_str = JS_NewStringCopyZ(cx, key))==NULL)
		return(JS_FALSE);

505
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
506 507 508
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
509 510 511 512 513 514 515 516
static JSBool
js_getbyte(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	int32		timeout=0;
	sbbs_t*		sbbs;
	jsrefcount	rc;

517
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
rswindell's avatar
rswindell committed
518 519
		return JS_FALSE;

520 521
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);

rswindell's avatar
rswindell committed
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0], &timeout))
			return JS_FALSE;
	}
	rc=JS_SUSPENDREQUEST(cx);
	int32 byte = sbbs->incom(timeout);
	JS_RESUMEREQUEST(cx, rc);

	if(byte != NOINP)
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(byte));
    return JS_TRUE;
}

static JSBool
js_putbyte(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	int32		byte=0;
	sbbs_t*		sbbs;
	jsrefcount	rc;

543
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
rswindell's avatar
rswindell committed
544 545 546 547 548 549 550 551 552 553 554 555 556 557
		return JS_FALSE;

	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0], &byte))
			return JS_FALSE;
	}
	rc=JS_SUSPENDREQUEST(cx);
	int result = sbbs->outcom(byte);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, result == 0 ? JSVAL_TRUE : JSVAL_FALSE);
    return JS_TRUE ;
}

rswindell's avatar
rswindell committed
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
static JSBool
js_add_hotspot(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	sbbs_t*		sbbs;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
		return JS_FALSE;

	if(argc < 1) {
		JS_ReportError(cx, "Invalid number of arguments to function");
		return JS_FALSE;
	}

	JSString*	js_str = JS_ValueToString(cx, argv[0]);
	if(js_str == NULL)
		return JS_FALSE;
577
	bool hungry = true;
rswindell's avatar
rswindell committed
578 579 580 581
	int32 min_x = -1;
	int32 max_x = -1;
	int32 y = -1;
	uintN argn = 1;
582 583 584 585
	if(argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
		hungry = JSVAL_TO_BOOLEAN(argv[argn]);
		argn++;
	}
rswindell's avatar
rswindell committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
	if(argc > argn) {
		if(!JS_ValueToInt32(cx,argv[argn], &min_x))
			return JS_FALSE;
		argn++;
	}
	if(argc > argn) {
		if(!JS_ValueToInt32(cx,argv[argn], &max_x))
			return JS_FALSE;
		argn++;
	}
	if(argc > argn) {
		if(!JS_ValueToInt32(cx,argv[argn], &y))
			return JS_FALSE;
		argn++;
	}
	char* p = NULL;
	JSSTRING_TO_MSTRING(cx, js_str, p, NULL);
	if(p == NULL)
		return JS_FALSE;
605
	sbbs->add_hotspot(p, hungry, min_x, max_x, y);
rswindell's avatar
rswindell committed
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
	free(p);
    return JS_TRUE;
}

static JSBool js_scroll_hotspots(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	sbbs_t*		sbbs;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
		return JS_FALSE;

	int32 rows = 1;
	if(argc > 0 && !JS_ValueToInt32(cx,argv[0], &rows))
		return JS_FALSE;
	sbbs->scroll_hotspots(rows);
    return JS_TRUE;
}

static JSBool js_clear_hotspots(JSContext *cx, uintN argc, jsval *arglist)
{
	sbbs_t*		sbbs;

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
		return JS_FALSE;

	sbbs->clear_hotspots();
    return JS_TRUE;
}
rswindell's avatar
rswindell committed
639

rswindell's avatar
rswindell committed
640
static JSBool
641
js_handle_ctrlkey(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
642
{
643
	jsval *argv=JS_ARGV(cx, arglist);
644
	char		key;
rswindell's avatar
rswindell committed
645
	int32		mode=0;
rswindell's avatar
rswindell committed
646
	sbbs_t*		sbbs;
deuce's avatar
deuce committed
647
	jsrefcount	rc;
deuce's avatar
deuce committed
648
	char		*keystr;
rswindell's avatar
rswindell committed
649

650
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
rswindell's avatar
rswindell committed
651 652
		return(JS_FALSE);

653 654
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);

655 656 657
	if(JSVAL_IS_INT(argv[0]))
		key=(char)JSVAL_TO_INT(argv[0]);
	else {
deuce's avatar
deuce committed
658
		JSVALUE_TO_ASTRING(cx, argv[0], keystr, 2, NULL);
deuce's avatar
deuce committed
659
		if(keystr==NULL)
660
			return(JS_FALSE);
deuce's avatar
deuce committed
661
		key=keystr[0];
662
	}
rswindell's avatar
rswindell committed
663

664 665 666 667
	if(argc>1) {
		if(!JS_ValueToInt32(cx, argv[1], &mode))
			return JS_FALSE;
	}
rswindell's avatar
rswindell committed
668

669
	rc=JS_SUSPENDREQUEST(cx);
670
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->handle_ctrlkey(key,mode)==0));
671
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
672 673 674
    return(JS_TRUE);
}

675
static JSBool
676
js_getstr(JSContext *cx, uintN argc, jsval *arglist)
677
{
678
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
679
	char		*p,*p2;
680
	int32		mode=0;
681
	uintN		i;
682
	int32		maxlen=0;
683 684
	sbbs_t*		sbbs;
    JSString*	js_str=NULL;
deuce's avatar
deuce committed
685
	jsrefcount	rc;
686
	str_list_t	history = NULL;
687

688
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
689 690
		return(JS_FALSE);

691 692
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

693
	for(i=0;i<argc;i++) {
694
		if(JSVAL_IS_NUMBER(argv[i])) {
695
			if(!maxlen) {
696
				if(!JS_ValueToInt32(cx,argv[i],&maxlen))
697 698 699
					return JS_FALSE;
			}
			else {
700
				if(!JS_ValueToInt32(cx,argv[i],&mode))
701 702
					return JS_FALSE;
			}
703
		}
704
		else if(JSVAL_IS_STRING(argv[i])) {
705 706 707 708
			js_str = JS_ValueToString(cx, argv[i]);
			if (!js_str)
			    return(JS_FALSE);
		}
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
		else if(JSVAL_IS_OBJECT(argv[i])) {
			JSObject* array = JSVAL_TO_OBJECT(argv[i]);
			jsuint len=0;
			if(!JS_GetArrayLength(cx, array, &len))
				return JS_FALSE;
			history = (str_list_t)alloca(sizeof(char*) * (len + 1));
			memset(history, 0, sizeof(char*) * (len + 1));
			for(jsuint j=0; j < len; j++) {
				jsval		val;
				if(!JS_GetElement(cx, array, j, &val))
					break;
				JSString* hist = JS_ValueToString(cx, val);
				if (hist == NULL)
					return JS_FALSE;
				char* cstr = NULL;
				JSSTRING_TO_ASTRING(cx, hist, cstr, (uint)(maxlen ? maxlen : 80), NULL);
				if(cstr == NULL)
					return JS_FALSE;
				history[j] = cstr;
			}
		}
730 731 732 733 734 735 736
	}

	if(!maxlen) maxlen=128;

	if((p=(char *)calloc(1,maxlen+1))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
737
	if(js_str!=NULL) {
deuce's avatar
deuce committed
738
		JSSTRING_TO_MSTRING(cx, js_str, p2, NULL);
739 740
		if(p2==NULL) {
			free(p);
deuce's avatar
deuce committed
741
			return JS_FALSE;
742
		}
deuce's avatar
deuce committed
743
		sprintf(p,"%.*s",(int)maxlen,p2);
deuce's avatar
deuce committed
744
		free(p2);
deuce's avatar
deuce committed
745
	}
746

747
	rc=JS_SUSPENDREQUEST(cx);
748
	sbbs->getstr(p, maxlen, mode, history);
749
	JS_RESUMEREQUEST(cx, rc);
750 751 752 753 754

	js_str = JS_NewStringCopyZ(cx, p);

	free(p);

755 756 757
	if(js_str==NULL)
		return(JS_FALSE);

758
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
759 760 761 762
    return(JS_TRUE);
}

static JSBool
763
js_getnum(JSContext *cx, uintN argc, jsval *arglist)
764
{
765
	jsval *argv=JS_ARGV(cx, arglist);
766
	uint32_t	maxnum=~0;
767
	int32		dflt=0;
768
	sbbs_t*		sbbs;
deuce's avatar
deuce committed
769
	jsrefcount	rc;
770

771
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
772 773
		return(JS_FALSE);

774 775
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

776 777 778 779 780
	if(argc && JSVAL_IS_NUMBER(argv[0])) {
		if(!JS_ValueToInt32(cx,argv[0],(int32*)&maxnum))
			return JS_FALSE;
	}
	if(argc>1 && JSVAL_IS_NUMBER(argv[1])) {
781
		if(!JS_ValueToInt32(cx,argv[1],&dflt))
782 783
			return JS_FALSE;
	}
784

785
	rc=JS_SUSPENDREQUEST(cx);
786
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(sbbs->getnum(maxnum,dflt)));
787
	JS_RESUMEREQUEST(cx, rc);
788 789 790 791
    return(JS_TRUE);
}

static JSBool
792
js_getkeys(JSContext *cx, uintN argc, jsval *arglist)
793
{
794
	jsval *argv=JS_ARGV(cx, arglist);
795 796
	char		key[2];
	uintN		i;
797
	int32		val;
798 799
	uint32		maxnum = ~0;
	bool		maxnum_specified = false;
800
	long		mode = K_UPPER;
801 802
	sbbs_t*		sbbs;
    JSString*	js_str=NULL;
803
	char*		cstr=NULL;
deuce's avatar
deuce committed
804
	jsrefcount	rc;
805

806
	if((sbbs=(sbbs_t*)js_GetClassPrivate(cx, JS_THIS_OBJECT(cx, arglist), &js_console_class))==NULL)
807 808
		return(JS_FALSE);

809 810
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

811
	for(i=0;i<argc;i++) {
812
		if(JSVAL_IS_NUMBER(argv[i])) {
813 814
			if(!JS_ValueToInt32(cx, argv[i], &val)) {
				free(cstr);
815
				return JS_FALSE;
816
			}
817 818
			if(!maxnum_specified) {
				maxnum_specified = true;
819
				maxnum = val;
820
			} else
821
				mode = val;
822 823 824 825
			continue;
		}
		if(JSVAL_IS_STRING(argv[i])) {
			js_str = JS_ValueToString(cx, argv[i]);
826
			free(cstr);
827 828 829
			JSSTRING_TO_MSTRING(cx, js_str, cstr, NULL);
			if(cstr==NULL)
				return JS_FALSE;
830 831 832
		}
	}

833
	rc=JS_SUSPENDREQUEST(cx);
834
	val=sbbs->getkeys(cstr, maxnum, mode);
835
	free(cstr);
836
	JS_RESUMEREQUEST(cx, rc);
837 838

	if(val==-1) {			// abort
839
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(0));
840 841
	} else if(val<0) {		// number
		val&=~0x80000000;
842
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(val));
843 844 845
	} else {				// key
		key[0]=(uchar)val;
		key[1]=0;
846 847
		if((js_str = JS_NewStringCopyZ(cx, key))==NULL)
			return(JS_FALSE);
848
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
849 850 851 852 853 854
	}

    return(JS_TRUE);
}

static JSBool
855
js_gettemplate(JSContext *cx, uintN argc, jsval *arglist)
856
{
857
	jsval *argv=JS_ARGV(cx, arglist);
858
	char		str[128];
859
	int32		mode=0;
860 861 862 863
	uintN		i;
	sbbs_t*		sbbs;
    JSString*	js_str=NULL;
    JSString*	js_fmt=NULL;