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 31.7 KB
Newer Older
rswindell's avatar
rswindell committed
1
/* Execute a Synchronet JavaScript module from the command-line */
2 3

/* $Id$ */
rswindell's avatar
rswindell committed
4
// vi: tabstop=4
5 6 7 8 9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
37
#ifndef JAVASCRIPT
38
#define JAVASCRIPT
rswindell's avatar
rswindell committed
39
#endif
40

deuce's avatar
deuce committed
41
#ifdef __unix__
42
#define _WITH_GETLINE
deuce's avatar
deuce committed
43
#include <signal.h>
deuce's avatar
deuce committed
44
#include <termios.h>
deuce's avatar
deuce committed
45 46
#endif

47
#include "sbbs.h"
48
#include "ciolib.h"
49
#include "ini_file.h"
50
#include "js_rtpool.h"
51
#include "js_request.h"
52
#include "jsdebug.h"
53

54
#define DEFAULT_LOG_LEVEL	LOG_DEBUG	/* Display all LOG levels */
55
#define DEFAULT_ERR_LOG_LVL	LOG_WARNING
56

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

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

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

rswindell's avatar
rswindell committed
112 113
void usage(FILE* fp)
{
114 115
	banner(fp);

116
	fprintf(fp,"\nusage: " PROG_NAME_LC " [-opts] [path]module[.js] [args]\n"
rswindell's avatar
rswindell committed
117
		"\navailable opts:\n\n"
118
#ifdef JSDOOR
119
		"    -c<ctrl_dir>   specify path to CTRL directory\n"
120
#else
121
		"    -c<ctrl_dir>   specify path to Synchronet CTRL directory\n"
122
#endif
123
		"    -C             do not change the current working directory (to CTRL dir)\n"
124
#if defined(__unix__)
125
		"    -d             run in background (daemonize)\n"
126
#endif
127 128 129 130 131
		"    -m<bytes>      set maximum heap size (default=%u bytes)\n"
		"    -s<bytes>      set context stack 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"
132
#ifdef JSDOOR
133
		"    -h[hostname]   use local or specified host name\n"
134
#else
135
		"    -h[hostname]   use local or specified host name (instead of SCFG value)\n"
136
#endif
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
		"    -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"
		"    -e<filename>   send error messages to file in addition to stderr\n"
		"    -o<filename>   send console messages to file instead of stdout\n"
		"    -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"
		"    -D             debugs the script\n"
rswindell's avatar
rswindell committed
153 154
		,JAVASCRIPT_MAX_BYTES
		,JAVASCRIPT_CONTEXT_STACK
155
		,JAVASCRIPT_TIME_LIMIT
156 157
		,JAVASCRIPT_YIELD_INTERVAL
		,JAVASCRIPT_GC_INTERVAL
158
		,DEFAULT_LOG_LEVEL
159
		,DEFAULT_ERR_LOG_LVL
160
		,load_path_list
161
		,_PATH_DEVNULL
162
		,_PATH_DEVNULL
rswindell's avatar
rswindell committed
163 164
		);
}
165

166 167 168 169 170 171 172 173 174
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
175
		    | ENABLE_MOUSE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_QUICK_EDIT_MODE);
176
#else
177
		#warning "Can't cook the tty on this platform"
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
#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
205 206
		SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 0);
#else
207
		#warning "Can't set the tty as raw on this platform"
208 209 210 211
#endif
	}
}

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
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);
}

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

239
	if(level > log_level)
240 241 242
		return(0);

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

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

256
	if(level<=err_level) {
257
		ret=fprintf(errfp,"%s\n",sbuf);
258
		if(errfp!=stderr)
259 260
			ret=fprintf(statfp,"%s\n",sbuf);
	}
261 262
	if(level>err_level)
		ret=fprintf(statfp,"%s\n",sbuf);
263 264

	pthread_mutex_unlock(&output_mutex);
265 266 267
    return(ret);
}

268 269 270 271 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
#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

303
#if defined(_WINSOCKAPI_)
304 305

WSADATA WSAData;
306
#define SOCKLIB_DESC WSAData.szDescription
307 308 309 310 311 312 313
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
314
/*		fprintf(statfp,"%s %s\n",WSAData.szDescription, WSAData.szSystemStatus); */
315 316 317 318
		WSAInitialized=TRUE;
		return(TRUE);
	}

319
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
320 321 322 323 324
	return(FALSE);
}

#else /* No WINSOCK */

325 326
#define winsock_startup()	(TRUE)
#define SOCKLIB_DESC NULL
327 328 329

#endif

330
static int do_bail(int code)
331
{
332
#if defined(_WINSOCKAPI_)
333
	if(WSAInitialized && WSACleanup()!=0) 
334
		lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
335 336
#endif

rswindell's avatar
rswindell committed
337
	if(pause_on_exit || (code && pause_on_error)) {
338
		fprintf(statfp,"\nHit enter to continue...");
339 340
		getchar();
	}
341 342 343

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
344
	cooked_tty();
345 346 347 348 349 350
	return(code);
}

void bail(int code)
{
	exit(do_bail(code));
351 352
}

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

364 365
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

366 367 368 369
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
370

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

383
	if(str==NULL)
384
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
385
	else
386
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
387

388 389 390
    return(JS_TRUE);
}

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

400 401
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

409
	rc=JS_SUSPENDREQUEST(cx);
410
	rd=fread(buf,sizeof(char),len,stdin);
411
	JS_RESUMEREQUEST(cx, rc);
412 413

	if(rd>=0)
414
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd)));
deuce's avatar
deuce committed
415
	free(buf);
416 417 418 419 420

    return(JS_TRUE);
}

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

429 430
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

438
	rc=JS_SUSPENDREQUEST(cx);
439
	cooked_tty();
440
	p=fgets(buf,len+1,stdin);
441
	raw_tty();
442
	JS_RESUMEREQUEST(cx, rc);
443 444

	if(p!=NULL)
445
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p))));
deuce's avatar
deuce committed
446
	free(buf);
447 448 449 450 451

    return(JS_TRUE);
}


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

462 463
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

476
	if(str==NULL)
477
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
478
	else
479
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
480 481 482 483
    return(JS_TRUE);
}

static JSBool
484
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
485
{
deuce's avatar
deuce committed
486 487
	jsrefcount	rc;

488 489
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
490
	if(!js_write(cx,argc,arglist))
491 492
		return(JS_FALSE);

493
	rc=JS_SUSPENDREQUEST(cx);
494
	fprintf(confp,"\n");
495
	JS_RESUMEREQUEST(cx, rc);
496 497 498 499
    return(JS_TRUE);
}

static JSBool
500
jse_printf(JSContext *cx, uintN argc, jsval *arglist)
501
{
502
	jsval *argv=JS_ARGV(cx, arglist);
503
	char* p;
deuce's avatar
deuce committed
504
	jsrefcount	rc;
505

506 507
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

508 509
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
510 511 512
		return(JS_FALSE);
	}

513
	rc=JS_SUSPENDREQUEST(cx);
514
	fprintf(confp,"%s",p);
515
	JS_RESUMEREQUEST(cx, rc);
516

517
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
518

519
	js_sprintf_free(p);
520 521 522 523 524

    return(JS_TRUE);
}

static JSBool
525
js_alert(JSContext *cx, uintN argc, jsval *arglist)
526
{
527
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
528
	jsrefcount	rc;
deuce's avatar
deuce committed
529
	char		*line;
530

531 532
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
533
	JSVALUE_TO_MSTRING(cx, argv[0], line, NULL);
deuce's avatar
deuce committed
534
	if(line==NULL)
535 536
	    return(JS_FALSE);

537
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
538
	fprintf(confp,"!%s\n",line);
deuce's avatar
deuce committed
539
	free(line);
540
	JS_RESUMEREQUEST(cx, rc);
541

542
	JS_SET_RVAL(cx, arglist, argv[0]);
543

544 545 546 547
    return(JS_TRUE);
}

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

557 558
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

559 560 561
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

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

574 575
	p=instr;
	SKIP_WHITESPACE(p);
576
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='n'));
577 578 579 580
	return(JS_TRUE);
}

static JSBool
581
js_deny(JSContext *cx, uintN argc, jsval *arglist)
582
{
583
	jsval *argv=JS_ARGV(cx, arglist);
584 585 586 587 588 589
    JSString *	str;
	char	 *	cstr;
	char     *	p;
	jsrefcount	rc;
	char		instr[81];

590 591
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

592 593 594
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

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

	p=instr;
	SKIP_WHITESPACE(p);
609
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='y'));
610 611 612 613
	return(JS_TRUE);
}

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

622 623
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

	if(argc>1) {
deuce's avatar
deuce committed
636 637
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
		HANDLE_PENDING(cx);
638 639 640
	} else
		instr[0]=0;

641
	rc=JS_SUSPENDREQUEST(cx);
642
	cooked_tty();
643
	if(!fgets(instr,sizeof(instr),stdin)) {
644
		raw_tty();
645
		JS_RESUMEREQUEST(cx, rc);
646
		return(JS_TRUE);
647
	}
648
	raw_tty();
649
	JS_RESUMEREQUEST(cx, rc);
650

651
	if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL)
652 653
	    return(JS_FALSE);

654
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
655 656 657
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
658
static JSBool
659
js_chdir(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
660
{
661
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
662
	char*		p;
deuce's avatar
deuce committed
663
	jsrefcount	rc;
deuce's avatar
deuce committed
664
	BOOL		ret;
rswindell's avatar
rswindell committed
665

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

673
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
674 675
	ret=chdir(p)==0;
	free(p);
676
	JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
677
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
rswindell's avatar
rswindell committed
678 679 680
	return(JS_TRUE);
}

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

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

deuce's avatar
deuce committed
698 699 700 701 702
	rc=JS_SUSPENDREQUEST(cx);
	ret=putenv(strdup(p))==0;
	free(p);
	JS_RESUMEREQUEST(cx, rc);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
703 704 705
	return(JS_TRUE);
}

706
static jsSyncMethodSpec js_global_functions[] = {
707
	{"log",				js_log,				1},
708
	{"read",			js_read,            1},
deuce's avatar
deuce committed
709 710 711 712
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
	,311
	},
713
    {"write",           js_write,           0},
714 715
    {"writeln",         js_writeln,         0},
    {"print",           js_writeln,         0},
716
    {"printf",          jse_printf,          1},	
717 718 719
	{"alert",			js_alert,			1},
	{"prompt",			js_prompt,			1},
	{"confirm",			js_confirm,			1},
720
	{"deny",			js_deny,			1},
rswindell's avatar
rswindell committed
721
	{"chdir",			js_chdir,			1},
722
	{"putenv",			js_putenv,			1},
723 724 725 726 727 728 729 730 731
    {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
732
	jsrefcount	rc;
733
	int		log_level;
734

735
	rc=JS_SUSPENDREQUEST(cx);
736
	if(report==NULL) {
737
		lprintf(LOG_ERR,"!JavaScript: %s", message);
738
		JS_RESUMEREQUEST(cx, rc);
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
		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";
757 758 759
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
760
		warning="";
761
	}
762

763
	lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
764
	JS_RESUMEREQUEST(cx, rc);
765 766
}

767 768 769 770 771 772
static JSBool
js_OperationCallback(JSContext *cx)
{
	JSBool	ret;

	JS_SetOperationCallback(cx, NULL);
773
	ret=js_CommonOperationCallback(cx, &cb);
774 775 776
	JS_SetOperationCallback(cx, js_OperationCallback);
	return ret;
}
777

rswindell's avatar
rswindell committed
778 779 780 781 782 783 784 785 786 787
static BOOL js_CreateEnvObject(JSContext* cx, JSObject* glob, char** env)
{
	char		name[256];
	char*		val;
	uint		i;
	JSObject*	js_env;

	if((js_env=JS_NewObject(js_cx, NULL, NULL, glob))==NULL)
		return(FALSE);

788 789 790 791
	if(!JS_DefineProperty(cx, glob, "env", OBJECT_TO_JSVAL(js_env)
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
		return(FALSE);

rswindell's avatar
rswindell committed
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
	for(i=0;env[i]!=NULL;i++) {
		SAFECOPY(name,env[i]);
		truncstr(name,"=");
		val=strchr(env[i],'=');
		if(val==NULL)
			val="";
		else
			val++;
		if(!JS_DefineProperty(cx, js_env, name, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,val))
			,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
			return(FALSE);
	}

	return(TRUE);
}

808
static BOOL js_init(char** env)
809
{
810
	memset(&startup,0,sizeof(startup));
811
	SAFECOPY(startup.load_path, load_path_list);
812

813 814
	fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());

815
	fprintf(statfp,"JavaScript: Creating runtime: %lu bytes\n"
816 817
		,js_max_bytes);