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

jsexec.c 35.8 KB
Newer Older
rswindell's avatar
rswindell committed
1
/* Execute a Synchronet JavaScript module from the command-line */
2 3 4 5 6

/****************************************************************************
 * @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
 *																			*
 * 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.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
22
#ifndef JAVASCRIPT
23
#define JAVASCRIPT
rswindell's avatar
rswindell committed
24
#endif
25

deuce's avatar
deuce committed
26
#ifdef __unix__
27
#define _WITH_GETLINE
deuce's avatar
deuce committed
28
#include <signal.h>
deuce's avatar
deuce committed
29
#include <termios.h>
deuce's avatar
deuce committed
30 31
#endif

32
#define STARTUP_INI_JSOPT_BITDESC_TABLE
33
#include "sbbs.h"
34
#include "ciolib.h"
35
#include "ini_file.h"
36
#include "js_rtpool.h"
37
#include "js_request.h"
38
#include "jsdebug.h"
39 40
#include "git_branch.h"
#include "git_hash.h"
41

42
#define DEFAULT_LOG_LEVEL	LOG_INFO
43
#define DEFAULT_ERR_LOG_LVL	LOG_WARNING
44

45 46 47 48
static const char*	strJavaScriptMaxBytes		="JavaScriptMaxBytes";
static const char*	strJavaScriptTimeLimit		="JavaScriptTimeLimit";
static const char*	strJavaScriptGcInterval		="JavaScriptGcInterval";
static const char*	strJavaScriptYieldInterval	="JavaScriptYieldInterval";
49
static const char*	strJavaScriptOptions		="JavaScriptOptions";
50

51
js_startup_t	startup;
52 53 54
JSRuntime*	js_runtime;
JSContext*	js_cx;
JSObject*	js_glob;
55
js_callback_t	cb;
56
scfg_t		scfg;
57
char*		text[TOTAL_TEXT];
58
ulong		js_max_bytes=JAVASCRIPT_MAX_BYTES;
59
#ifndef JSDOOR
60
ulong		js_opts = JAVASCRIPT_OPTIONS;
61
#else
Deucе's avatar
Deucе committed
62
ulong		js_opts = JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_COMPILE_N_GO | JSOPTION_PROFILING;
63
#endif
rswindell's avatar
rswindell committed
64 65
FILE*		confp;
FILE*		errfp;
66
FILE*		nulfp;
67
FILE*		statfp;
68
char		compiler[32];
69 70
char*		host_name=NULL;
char		host_name_buf[128];
71
char*		load_path_list=JAVASCRIPT_LOAD_PATH;
72
BOOL		pause_on_exit=FALSE;
rswindell's avatar
rswindell committed
73
BOOL		pause_on_error=FALSE;
74
BOOL		terminated=FALSE;
75
BOOL		recycled;
76
int			log_level=DEFAULT_LOG_LEVEL;
77
int  		err_level=DEFAULT_ERR_LOG_LVL;
78
long		umask_val = -1;
79
pthread_mutex_t output_mutex;
80 81
#if defined(__unix__)
BOOL		daemonize=FALSE;
82
struct termios	orig_term;
83
#endif
84
char		orig_cwd[MAX_PATH+1];
deuce's avatar
deuce committed
85
BOOL		debugger=FALSE;
86 87 88 89 90 91
#ifndef PROG_NAME
#define PROG_NAME "JSexec"
#endif
#ifndef PROG_NAME_LC
#define PROG_NAME_LC "jsexec"
#endif
92

93 94
void banner(FILE* fp)
{
95
	fprintf(fp,"\n" PROG_NAME " v%s%c-%s %s/%s%s - "
96 97 98
		"Execute Synchronet JavaScript Module\n"
		,VERSION,REVISION
		,PLATFORM_DESC
99
		,GIT_BRANCH, GIT_HASH
100 101 102 103 104
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
105
		);
106 107 108

	fprintf(fp, "Compiled %s %s with %s\n"
		,__DATE__, __TIME__, compiler);
109
}
110

111
void usage()
rswindell's avatar
rswindell committed
112
{
Rob Swindell's avatar
Rob Swindell committed
113
	banner(stdout);
114

115 116 117
	fprintf(stdout, "\nusage: " PROG_NAME_LC " [-opts] [[path/]module[.js] [args]\n"
		"   or: " PROG_NAME_LC " [-opts] -r js-expression [args]\n"
		"   or: " PROG_NAME_LC " -v\n"
rswindell's avatar
rswindell committed
118
		"\navailable opts:\n\n"
119
		"    -r<expression> run (compile and execute) JavaScript expression\n"
120
#ifdef JSDOOR
121
		"    -c<ctrl_dir>   specify path to CTRL directory\n"
122
#else
123
		"    -c<ctrl_dir>   specify path to Synchronet CTRL directory\n"
124
#endif
125
		"    -C             do not change the current working directory (to CTRL dir)\n"
126
#if defined(__unix__)
127
		"    -d             run in background (daemonize)\n"
128
#endif
129 130 131 132
		"    -m<bytes>      set maximum heap size (default=%u bytes)\n"
		"    -t<limit>      set time limit (default=%u, 0=unlimited)\n"
		"    -y<interval>   set yield interval (default=%u, 0=never)\n"
		"    -g<interval>   set garbage collection interval (default=%u, 0=never)\n"
133
#ifdef JSDOOR
134
		"    -h[hostname]   use local or specified host name\n"
135
#else
136
		"    -h[hostname]   use local or specified host name (instead of SCFG value)\n"
137
#endif
138 139 140 141 142 143
		"    -u<mask>       set file creation permissions mask (in octal)\n"
		"    -L<level>      set log level (default=%u)\n"
		"    -E<level>      set error log level threshold (default=%u)\n"
		"    -i<path_list>  set load() comma-sep search path list (default=\"%s\")\n"
		"    -f             use non-buffered stream for console messages\n"
		"    -a             append instead of overwriting message output files\n"
Rob Swindell's avatar
Rob Swindell committed
144 145
		"    -A             send all messages to stdout\n"
		"    -A<filename>   send all messages to file instead of stdout/stderr\n"
146 147
		"    -e<filename>   send error messages to file in addition to stderr\n"
		"    -o<filename>   send console messages to file instead of stdout\n"
148 149
		"    -S<filename>   send status messages to file instead of stderr\n"
		"    -S             send status messages to stdout\n"
150 151 152 153 154 155 156
		"    -n             send status messages to %s instead of stderr\n"
		"    -q             send console messages to %s instead of stdout\n"
		"    -v             display version details and exit\n"
		"    -x             disable auto-termination on local abort signal\n"
		"    -l             loop until intentionally terminated\n"
		"    -p             wait for keypress (pause) on exit\n"
		"    -!             wait for keypress (pause) on error\n"
157
		"    -D             load the script into an interactive debugger\n"
rswindell's avatar
rswindell committed
158
		,JAVASCRIPT_MAX_BYTES
159
		,JAVASCRIPT_TIME_LIMIT
160 161
		,JAVASCRIPT_YIELD_INTERVAL
		,JAVASCRIPT_GC_INTERVAL
162
		,DEFAULT_LOG_LEVEL
163
		,DEFAULT_ERR_LOG_LVL
164
		,load_path_list
165
		,_PATH_DEVNULL
166
		,_PATH_DEVNULL
rswindell's avatar
rswindell committed
167 168
		);
}
169

170 171 172 173 174 175 176 177 178
static void
cooked_tty(void)
{
	if (isatty(fileno(stdin))) {
#ifdef __unix__
		tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
#elif defined _WIN32
		SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_ECHO_INPUT
		    | ENABLE_EXTENDED_FLAGS | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT
179
		    | ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE);
180
#else
181
		#warning "Can't cook the tty on this platform"
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
#endif
	}
}

#ifdef __unix__
static void raw_input(struct termios *t)
{
#ifdef JSDOOR
	t->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
	t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
#else
	t->c_iflag &= ~(IMAXBEL|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
	t->c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
#endif
}
#endif

static void
raw_tty(void)
{
	if (isatty(fileno(stdin))) {
#ifdef __unix__
		struct termios term = orig_term;

		raw_input(&term);
		tcsetattr(fileno(stdin), TCSANOW, &term);
#elif defined _WIN32
deuce's avatar
deuce committed
209 210
		SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0);
#else
211
		#warning "Can't set the tty as raw on this platform"
212 213 214 215
#endif
	}
}

216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
int mfprintf(FILE* fp, char *fmt, ...)
{
	va_list argptr;
	char sbuf[1024];
	int ret=0;

    va_start(argptr,fmt);
    ret=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);

	/* Mutex-protect stdout/stderr */
	pthread_mutex_lock(&output_mutex);

	ret = fprintf(fp, "%s\n", sbuf);

	pthread_mutex_unlock(&output_mutex);
    return(ret);
}

236
/* Log printf */
237
int lprintf(int level, const char *fmt, ...)
238 239 240
{
	va_list argptr;
	char sbuf[1024];
deuce's avatar
deuce committed
241
	int ret=0;
242

243
	if(level > log_level)
244 245 246
		return(0);

    va_start(argptr,fmt);
247
    ret=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
248 249
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
250 251
#if defined(__unix__)
	if(daemonize) {
252
		syslog(level,"%s",sbuf);
253 254 255
		return(ret);
	}
#endif
256 257 258 259

	/* Mutex-protect stdout/stderr */
	pthread_mutex_lock(&output_mutex);

260
	if(level<=err_level) {
261
		ret=fprintf(errfp,"%s\n",sbuf);
262
		if(errfp!=stderr)
263 264
			ret=fprintf(statfp,"%s\n",sbuf);
	}
265 266
	if(level>err_level)
		ret=fprintf(statfp,"%s\n",sbuf);
267 268

	pthread_mutex_unlock(&output_mutex);
269 270 271
    return(ret);
}

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
#if defined(__unix__) && defined(NEEDS_DAEMON)
/****************************************************************************/
/* Daemonizes the process                                                   */
/****************************************************************************/
int
daemon(int nochdir, int noclose)
{
    int fd;

    switch (fork()) {
    case -1:
        return (-1);
    case 0:
        break;
    default:
        _exit(0);
    }

    if (setsid() == -1)
        return (-1);

    if (!nochdir)
        (void)chdir("/");

    if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
        (void)dup2(fd, STDIN_FILENO);
        (void)dup2(fd, STDOUT_FILENO);
        (void)dup2(fd, STDERR_FILENO);
        if (fd > 2)
            (void)close(fd);
    }
    return (0);
}
#endif

307
#if defined(_WINSOCKAPI_)
308 309

WSADATA WSAData;
310
#define SOCKLIB_DESC WSAData.szDescription
311 312 313 314 315 316 317
static BOOL WSAInitialized=FALSE;

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
318
/*		fprintf(statfp,"%s %s\n",WSAData.szDescription, WSAData.szSystemStatus); */
319 320 321 322
		WSAInitialized=TRUE;
		return(TRUE);
	}

323
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
324 325 326 327 328
	return(FALSE);
}

#else /* No WINSOCK */

329 330
#define winsock_startup()	(TRUE)
#define SOCKLIB_DESC NULL
331 332 333

#endif

334
static int do_bail(int code)
335
{
336
#if defined(_WINSOCKAPI_)
337
	if(WSAInitialized && WSACleanup()!=0) 
338
		lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
339 340
#endif

341
	cooked_tty();
rswindell's avatar
rswindell committed
342
	if(pause_on_exit || (code && pause_on_error)) {
343
		fprintf(statfp,"\nHit enter to continue...");
344 345
		getchar();
	}
346 347 348

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
349 350 351 352 353 354
	return(code);
}

void bail(int code)
{
	exit(do_bail(code));
355 356
}

357
static JSBool
358
js_log(JSContext *cx, uintN argc, jsval *arglist)
359
{
360
	jsval *argv=JS_ARGV(cx, arglist);
361 362
    uintN		i=0;
	int32		level=LOG_INFO;
363
    JSString*	str=NULL;
deuce's avatar
deuce committed
364
	jsrefcount	rc;
deuce's avatar
deuce committed
365 366
	char		*logstr=NULL;
	size_t		logstr_sz=0;
367

368 369
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

370 371 372 373
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
374

375 376 377
	for(; i<argc; i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
378
		JSSTRING_TO_RASTRING(cx, str, logstr, &logstr_sz, NULL);
deuce's avatar
deuce committed
379 380
		if(logstr==NULL)
			return(JS_FALSE);
381
		rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
382
		lprintf(level,"%s",logstr);
383
		JS_RESUMEREQUEST(cx, rc);
384
		FREE_AND_NULL(logstr);
385 386
	}

387
	if(str==NULL)
388
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
389
	else
390
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
391

392 393 394
    return(JS_TRUE);
}

395
static JSBool
396
js_read(JSContext *cx, uintN argc, jsval *arglist)
397
{
398
	jsval *argv=JS_ARGV(cx, arglist);
399 400 401
	char*	buf;
	int		rd;
	int32	len=128;
deuce's avatar
deuce committed
402
	jsrefcount	rc;
403

404 405
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

406 407 408 409
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
deuce's avatar
deuce committed
410
	if((buf=malloc(len))==NULL)
411 412
		return(JS_TRUE);

413
	rc=JS_SUSPENDREQUEST(cx);
414
	rd=fread(buf,sizeof(char),len,stdin);
415
	JS_RESUMEREQUEST(cx, rc);
416 417

	if(rd>=0)
418
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd)));
deuce's avatar
deuce committed
419
	free(buf);
420 421 422 423 424

    return(JS_TRUE);
}

static JSBool
425
js_readln(JSContext *cx, uintN argc, jsval *arglist)
426
{
427
	jsval *argv=JS_ARGV(cx, arglist);
428 429 430
	char*	buf;
	char*	p;
	int32	len=128;
deuce's avatar
deuce committed
431
	jsrefcount	rc;
432

433 434
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

435 436 437 438
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
deuce's avatar
deuce committed
439
	if((buf=malloc(len+1))==NULL)
440 441
		return(JS_TRUE);

442
	rc=JS_SUSPENDREQUEST(cx);
443
	cooked_tty();
444
	p=fgets(buf,len+1,stdin);
445
	raw_tty();
446
	JS_RESUMEREQUEST(cx, rc);
447 448

	if(p!=NULL)
449
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p))));
deuce's avatar
deuce committed
450
	free(buf);
451 452 453 454 455

    return(JS_TRUE);
}


456
static JSBool
457
js_write(JSContext *cx, uintN argc, jsval *arglist)
458
{
459
	jsval *argv=JS_ARGV(cx, arglist);
460
    uintN		i;
461
    JSString*	str=NULL;
deuce's avatar
deuce committed
462
	jsrefcount	rc;
deuce's avatar
deuce committed
463 464
	char		*line=NULL;
	size_t		line_sz=0;
465

466 467
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

468
    for (i = 0; i < argc; i++) {
469
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
470
		    return(JS_FALSE);
deuce's avatar
deuce committed
471
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
472 473
		if(line==NULL)
			return(JS_FALSE);
474
		rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
475
		fprintf(confp,"%s",line);
476
		FREE_AND_NULL(line);
477
		JS_RESUMEREQUEST(cx, rc);
478 479
	}

480
	if(str==NULL)
481
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
482
	else
483
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
484 485 486 487
    return(JS_TRUE);
}

static JSBool
488
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
489
{
deuce's avatar
deuce committed
490 491
	jsrefcount	rc;

492 493
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
494
	if(!js_write(cx,argc,arglist))
495 496
		return(JS_FALSE);

497
	rc=JS_SUSPENDREQUEST(cx);
498
	fprintf(confp,"\n");
499
	JS_RESUMEREQUEST(cx, rc);
500 501 502 503
    return(JS_TRUE);
}

static JSBool
504
jse_printf(JSContext *cx, uintN argc, jsval *arglist)
505
{
506
	jsval *argv=JS_ARGV(cx, arglist);
507
	char* p;
deuce's avatar
deuce committed
508
	jsrefcount	rc;
509

510 511
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

512 513
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
514 515 516
		return(JS_FALSE);
	}

517
	rc=JS_SUSPENDREQUEST(cx);
518
	fprintf(confp,"%s",p);
519
	JS_RESUMEREQUEST(cx, rc);
520

521
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
522

523
	js_sprintf_free(p);
524 525 526 527 528

    return(JS_TRUE);
}

static JSBool
529
js_alert(JSContext *cx, uintN argc, jsval *arglist)
530
{
531
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
532
	jsrefcount	rc;
deuce's avatar
deuce committed
533
	char		*line;
534

535 536
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
537
	JSVALUE_TO_MSTRING(cx, argv[0], line, NULL);
deuce's avatar
deuce committed
538
	if(line==NULL)
539 540
	    return(JS_FALSE);

541
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
542
	fprintf(confp,"!%s\n",line);
deuce's avatar
deuce committed
543
	free(line);
544
	JS_RESUMEREQUEST(cx, rc);
545

546
	JS_SET_RVAL(cx, arglist, argv[0]);
547

548 549 550 551
    return(JS_TRUE);
}

static JSBool
552
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
553
{
554
	jsval *argv=JS_ARGV(cx, arglist);
555
    JSString *	str;
556
	char	 *	cstr = NULL;
557
	char     *	p;
deuce's avatar
deuce committed
558
	jsrefcount	rc;
559
	char		instr[81]="y";
560

561 562
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

563 564 565
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

deuce's avatar
deuce committed
566
	JSSTRING_TO_MSTRING(cx, str, cstr, NULL);
567
	HANDLE_PENDING(cx, cstr);
deuce's avatar
deuce committed
568 569
	if(cstr==NULL)
		return JS_TRUE;
570
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
571 572
	printf("%s (Y/n)? ", cstr);
	free(cstr);
573
	cooked_tty();
574
	fgets(instr,sizeof(instr),stdin);
575
	raw_tty();
576
	JS_RESUMEREQUEST(cx, rc);
577

578 579
	p=instr;
	SKIP_WHITESPACE(p);
580
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='n'));
581 582 583 584
	return(JS_TRUE);
}

static JSBool
585
js_deny(JSContext *cx, uintN argc, jsval *arglist)
586
{
587
	jsval *argv=JS_ARGV(cx, arglist);
588
    JSString *	str;
589
	char	 *	cstr = NULL;
590 591 592 593
	char     *	p;
	jsrefcount	rc;
	char		instr[81];

594 595
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

596 597 598
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

deuce's avatar
deuce committed
599
	JSSTRING_TO_MSTRING(cx, str, cstr, NULL);
600
	HANDLE_PENDING(cx, cstr);
deuce's avatar
deuce committed
601 602
	if(cstr==NULL)
		return JS_TRUE;
603
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
604 605
	printf("%s (N/y)? ", cstr);
	free(cstr);
606
	cooked_tty();
607
	fgets(instr,sizeof(instr),stdin);
608
	raw_tty();
609 610 611 612
	JS_RESUMEREQUEST(cx, rc);

	p=instr;
	SKIP_WHITESPACE(p);
613
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='y'));
614 615 616 617
	return(JS_TRUE);
}

static JSBool
618
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
619
{
620
	jsval *argv=JS_ARGV(cx, arglist);
621
	char		instr[256];
622
    JSString *	str;
deuce's avatar
deuce committed
623
	jsrefcount	rc;
624
	char		*prstr = NULL;
625

626 627
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

628
	if(argc>0 && !JSVAL_IS_VOID(argv[0])) {
deuce's avatar
deuce committed
629
		JSVALUE_TO_MSTRING(cx, argv[0], prstr, NULL);
630
		HANDLE_PENDING(cx, prstr);
631
		if(prstr==NULL)
632
			return(JS_FALSE);
633
		rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
634
		fprintf(confp,"%s: ",prstr);
deuce's avatar
deuce committed
635
		free(prstr);
636
		JS_RESUMEREQUEST(cx, rc);
637
	}
638 639

	if(argc>1) {
deuce's avatar
deuce committed
640
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
641
		HANDLE_PENDING(cx, NULL);
642 643 644
	} else
		instr[0]=0;

645
	rc=JS_SUSPENDREQUEST(cx);
646
	cooked_tty();
647
	if(!fgets(instr,sizeof(instr),stdin)) {
648
		raw_tty();
649
		JS_RESUMEREQUEST(cx, rc);
650
		return(JS_TRUE);
651
	}
652
	raw_tty();
653
	JS_RESUMEREQUEST(cx, rc);
654

655
	if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL)
656 657
	    return(JS_FALSE);

658
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
659 660 661
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
662
static JSBool
663
js_chdir(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
664
{
665
	jsval *argv=JS_ARGV(cx, arglist);
666
	char*		p = NULL;
deuce's avatar
deuce committed
667
	jsrefcount	rc;
deuce's avatar
deuce committed
668
	BOOL		ret;
rswindell's avatar
rswindell committed
669

deuce's avatar
deuce committed
670
	JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
671
	HANDLE_PENDING(cx, p);
deuce's avatar
deuce committed
672
	if(p==NULL) {
673
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
rswindell's avatar
rswindell committed
674 675 676
		return(JS_TRUE);
	}

677
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
678 679
	ret=chdir(p)==0;
	free(p);
680
	JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
681
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
rswindell's avatar
rswindell committed
682 683 684
	return(JS_TRUE);
}

685
static JSBool
686
js_putenv(JSContext *cx, uintN argc, jsval *arglist)
687
{
688
	jsval *argv=JS_ARGV(cx, arglist);
689
	char*		p=NULL;
deuce's avatar
deuce committed
690 691
	BOOL		ret;
	jsrefcount	rc;
692

deuce's avatar
deuce committed
693 694
	if(argc) {
		JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
695
		HANDLE_PENDING(cx, p);
deuce's avatar
deuce committed
696
	}
deuce's avatar
deuce committed
697
	if(p==NULL) {
698
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
699 700 701
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
702 703 704 705 706
	rc=JS_SUSPENDREQUEST(cx);
	ret=putenv(strdup(p))==0;
	free(p);
	JS_RESUMEREQUEST(cx, rc);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
707 708 709
	return(JS_TRUE);
}

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
// Forked from mozilla/js/src/shell/js.cpp: AssertEq()
static JSBool
js_AssertEq(JSContext *cx, uintN argc, jsval *vp)
{
    if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) {
        JS_ReportError(cx, "assertEq: missing or invalid args");
        return JS_FALSE;
    }

    jsval *argv = JS_ARGV(cx, vp);
    JSBool same;
    if (!JS_SameValue(cx, argv[0], argv[1], &same))
        return JS_FALSE;
    if (!same) {
		JS_ReportError(cx, "not equal");
        return JS_FALSE;
    }
    JS_SET_RVAL(cx, vp, JSVAL_VOID);
    return JS_TRUE;
}


732
static jsSyncMethodSpec js_global_functions[] = {
733
	{"log",				js_log,				1},
734
	{"read",			js_read,            1},
deuce's avatar
deuce committed
735 736 737 738
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
	,311
	},
739
    {"write",           js_write,           0},
740 741
    {"writeln",         js_writeln,         0},
    {"print",           js_writeln,         0},
742
    {"printf",          jse_printf,         1},	
743 744 745
	{"alert",			js_alert,			1},
	{"prompt",			js_prompt,			1},
	{"confirm",			js_confirm,			1},
746
	{"deny",			js_deny,			1},
rswindell's avatar
rswindell committed
747
	{"chdir",			js_chdir,			1},
748
	{"putenv",			js_putenv,			1},
749
	{"assertEq",		js_AssertEq,		2},
750 751 752 753 754 755 756 757 758
    {0}
};

static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	const char*	warning;
deuce's avatar
deuce committed
759
	jsrefcount	rc;
760
	int		log_level;
761

762
	rc=JS_SUSPENDREQUEST(cx);
763
	if(report==NULL) {
764
		lprintf(LOG_ERR,"!JavaScript: %s", message);
765
		JS_RESUMEREQUEST(cx, rc);
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
		return;
    }

	if(report->filename)
		sprintf(file," %s",report->filename);
	else
		file[0]=0;

	if(report->lineno)
		sprintf(line," line %d",report->lineno);
	else
		line[0]=0;

	if(JSREPORT_IS_WARNING(report->flags)) {
		if(JSREPORT_IS_STRICT(report->flags))
			warning="strict warning";
		else
			warning="warning";