jsexec.c 34.4 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_INFO
55
#define DEFAULT_ERR_LOG_LVL	LOG_WARNING
56

57
58
59
60
61
62
63
static const char*	strJavaScriptMaxBytes		="JavaScriptMaxBytes";
static const char*	strJavaScriptContextStack	="JavaScriptContextStack";
static const char*	strJavaScriptTimeLimit		="JavaScriptTimeLimit";
static const char*	strJavaScriptGcInterval		="JavaScriptGcInterval";
static const char*	strJavaScriptYieldInterval	="JavaScriptYieldInterval";
static const char*	strJavaScriptLoadPath		="JavaScriptLoadPath";

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

103
104
void banner(FILE* fp)
{
105
	fprintf(fp,"\n" PROG_NAME " v%s%c-%s (rev %s)%s - "
106
107
108
109
		"Execute Synchronet JavaScript Module\n"
		,VERSION,REVISION
		,PLATFORM_DESC
		,revision
110
111
112
113
114
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
115
		);
116
117
118

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

rswindell's avatar
rswindell committed
121
122
void usage(FILE* fp)
{
123
124
	banner(fp);

125
	fprintf(fp,"\nusage: " PROG_NAME_LC " [-opts] [path]module[.js] [args]\n"
rswindell's avatar
rswindell committed
126
		"\navailable opts:\n\n"
127
#ifdef JSDOOR
128
		"    -c<ctrl_dir>   specify path to CTRL directory\n"
129
#else
130
		"    -c<ctrl_dir>   specify path to Synchronet CTRL directory\n"
131
#endif
132
		"    -C             do not change the current working directory (to CTRL dir)\n"
133
#if defined(__unix__)
134
		"    -d             run in background (daemonize)\n"
135
#endif
136
137
138
139
140
		"    -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"
141
#ifdef JSDOOR
142
		"    -h[hostname]   use local or specified host name\n"
143
#else
144
		"    -h[hostname]   use local or specified host name (instead of SCFG value)\n"
145
#endif
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
		"    -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
162
163
		,JAVASCRIPT_MAX_BYTES
		,JAVASCRIPT_CONTEXT_STACK
164
		,JAVASCRIPT_TIME_LIMIT
165
166
		,JAVASCRIPT_YIELD_INTERVAL
		,JAVASCRIPT_GC_INTERVAL
167
		,DEFAULT_LOG_LEVEL
168
		,DEFAULT_ERR_LOG_LVL
169
		,load_path_list
170
		,_PATH_DEVNULL
171
		,_PATH_DEVNULL
rswindell's avatar
rswindell committed
172
173
		);
}
174

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

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
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);
}

241
/* Log printf */
242
int lprintf(int level, const char *fmt, ...)
243
244
245
{
	va_list argptr;
	char sbuf[1024];
deuce's avatar
deuce committed
246
	int ret=0;
247

248
	if(level > log_level)
249
250
251
		return(0);

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

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

265
	if(level<=err_level) {
266
		ret=fprintf(errfp,"%s\n",sbuf);
267
		if(errfp!=stderr)
268
269
			ret=fprintf(statfp,"%s\n",sbuf);
	}
270
271
	if(level>err_level)
		ret=fprintf(statfp,"%s\n",sbuf);
272
273

	pthread_mutex_unlock(&output_mutex);
274
275
276
    return(ret);
}

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
307
308
309
310
311
#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

312
#if defined(_WINSOCKAPI_)
313
314

WSADATA WSAData;
315
#define SOCKLIB_DESC WSAData.szDescription
316
317
318
319
320
321
322
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
323
/*		fprintf(statfp,"%s %s\n",WSAData.szDescription, WSAData.szSystemStatus); */
324
325
326
327
		WSAInitialized=TRUE;
		return(TRUE);
	}

328
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
329
330
331
332
333
	return(FALSE);
}

#else /* No WINSOCK */

334
335
#define winsock_startup()	(TRUE)
#define SOCKLIB_DESC NULL
336
337
338

#endif

339
static int do_bail(int code)
340
{
341
#if defined(_WINSOCKAPI_)
342
	if(WSAInitialized && WSACleanup()!=0) 
343
		lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
344
345
#endif

346
	cooked_tty();
rswindell's avatar
rswindell committed
347
	if(pause_on_exit || (code && pause_on_error)) {
348
		fprintf(statfp,"\nHit enter to continue...");
349
350
		getchar();
	}
351
352
353

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
354
355
356
357
358
359
	return(code);
}

void bail(int code)
{
	exit(do_bail(code));
360
361
}

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

373
374
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

375
376
377
378
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
379

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

392
	if(str==NULL)
393
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
394
	else
395
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
396

397
398
399
    return(JS_TRUE);
}

400
static JSBool
401
js_read(JSContext *cx, uintN argc, jsval *arglist)
402
{
403
	jsval *argv=JS_ARGV(cx, arglist);
404
405
406
	char*	buf;
	int		rd;
	int32	len=128;
deuce's avatar
deuce committed
407
	jsrefcount	rc;
408

409
410
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

411
412
413
414
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
deuce's avatar
deuce committed
415
	if((buf=malloc(len))==NULL)
416
417
		return(JS_TRUE);

418
	rc=JS_SUSPENDREQUEST(cx);
419
	rd=fread(buf,sizeof(char),len,stdin);
420
	JS_RESUMEREQUEST(cx, rc);
421
422

	if(rd>=0)
423
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd)));
deuce's avatar
deuce committed
424
	free(buf);
425
426
427
428
429

    return(JS_TRUE);
}

static JSBool
430
js_readln(JSContext *cx, uintN argc, jsval *arglist)
431
{
432
	jsval *argv=JS_ARGV(cx, arglist);
433
434
435
	char*	buf;
	char*	p;
	int32	len=128;
deuce's avatar
deuce committed
436
	jsrefcount	rc;
437

438
439
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

440
441
442
443
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
deuce's avatar
deuce committed
444
	if((buf=malloc(len+1))==NULL)
445
446
		return(JS_TRUE);

447
	rc=JS_SUSPENDREQUEST(cx);
448
	cooked_tty();
449
	p=fgets(buf,len+1,stdin);
450
	raw_tty();
451
	JS_RESUMEREQUEST(cx, rc);
452
453

	if(p!=NULL)
454
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p))));
deuce's avatar
deuce committed
455
	free(buf);
456
457
458
459
460

    return(JS_TRUE);
}


461
static JSBool
462
js_write(JSContext *cx, uintN argc, jsval *arglist)
463
{
464
	jsval *argv=JS_ARGV(cx, arglist);
465
    uintN		i;
466
    JSString*	str=NULL;
deuce's avatar
deuce committed
467
	jsrefcount	rc;
deuce's avatar
deuce committed
468
469
	char		*line=NULL;
	size_t		line_sz=0;
470

471
472
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

485
	if(str==NULL)
486
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
487
	else
488
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
489
490
491
492
    return(JS_TRUE);
}

static JSBool
493
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
494
{
deuce's avatar
deuce committed
495
496
	jsrefcount	rc;

497
498
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
499
	if(!js_write(cx,argc,arglist))
500
501
		return(JS_FALSE);

502
	rc=JS_SUSPENDREQUEST(cx);
503
	fprintf(confp,"\n");
504
	JS_RESUMEREQUEST(cx, rc);
505
506
507
508
    return(JS_TRUE);
}

static JSBool
509
jse_printf(JSContext *cx, uintN argc, jsval *arglist)
510
{
511
	jsval *argv=JS_ARGV(cx, arglist);
512
	char* p;
deuce's avatar
deuce committed
513
	jsrefcount	rc;
514

515
516
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

517
518
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
519
520
521
		return(JS_FALSE);
	}

522
	rc=JS_SUSPENDREQUEST(cx);
523
	fprintf(confp,"%s",p);
524
	JS_RESUMEREQUEST(cx, rc);
525

526
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
527

528
	js_sprintf_free(p);
529
530
531
532
533

    return(JS_TRUE);
}

static JSBool
534
js_alert(JSContext *cx, uintN argc, jsval *arglist)
535
{
536
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
537
	jsrefcount	rc;
deuce's avatar
deuce committed
538
	char		*line;
539

540
541
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
542
	JSVALUE_TO_MSTRING(cx, argv[0], line, NULL);
deuce's avatar
deuce committed
543
	if(line==NULL)
544
545
	    return(JS_FALSE);

546
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
547
	fprintf(confp,"!%s\n",line);
deuce's avatar
deuce committed
548
	free(line);
549
	JS_RESUMEREQUEST(cx, rc);
550

551
	JS_SET_RVAL(cx, arglist, argv[0]);
552

553
554
555
556
    return(JS_TRUE);
}

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

566
567
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

568
569
570
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

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

583
584
	p=instr;
	SKIP_WHITESPACE(p);
585
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='n'));
586
587
588
589
	return(JS_TRUE);
}

static JSBool
590
js_deny(JSContext *cx, uintN argc, jsval *arglist)
591
{
592
	jsval *argv=JS_ARGV(cx, arglist);
593
    JSString *	str;
594
	char	 *	cstr = NULL;
595
596
597
598
	char     *	p;
	jsrefcount	rc;
	char		instr[81];

599
600
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

601
602
603
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

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

	p=instr;
	SKIP_WHITESPACE(p);
618
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='y'));
619
620
621
622
	return(JS_TRUE);
}

static JSBool
623
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
624
{
625
	jsval *argv=JS_ARGV(cx, arglist);
626
	char		instr[256];
627
    JSString *	str;
deuce's avatar
deuce committed
628
	jsrefcount	rc;
629
	char		*prstr = NULL;
630

631
632
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

	if(argc>1) {
deuce's avatar
deuce committed
645
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
646
		HANDLE_PENDING(cx, NULL);
647
648
649
	} else
		instr[0]=0;

650
	rc=JS_SUSPENDREQUEST(cx);
651
	cooked_tty();
652
	if(!fgets(instr,sizeof(instr),stdin)) {
653
		raw_tty();
654
		JS_RESUMEREQUEST(cx, rc);
655
		return(JS_TRUE);
656
	}
657
	raw_tty();
658
	JS_RESUMEREQUEST(cx, rc);
659

660
	if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL)
661
662
	    return(JS_FALSE);

663
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
664
665
666
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
667
static JSBool
668
js_chdir(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
669
{
670
	jsval *argv=JS_ARGV(cx, arglist);
671
	char*		p = NULL;
deuce's avatar
deuce committed
672
	jsrefcount	rc;
deuce's avatar
deuce committed
673
	BOOL		ret;
rswindell's avatar
rswindell committed
674

deuce's avatar
deuce committed
675
	JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
676
	HANDLE_PENDING(cx, p);
deuce's avatar
deuce committed
677
	if(p==NULL) {
678
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
rswindell's avatar
rswindell committed
679
680
681
		return(JS_TRUE);
	}

682
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
683
684
	ret=chdir(p)==0;
	free(p);
685
	JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
686
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
rswindell's avatar
rswindell committed
687
688
689
	return(JS_TRUE);
}

690
static JSBool
691
js_putenv(JSContext *cx, uintN argc, jsval *arglist)
692
{
693
	jsval *argv=JS_ARGV(cx, arglist);
694
	char*		p=NULL;
deuce's avatar
deuce committed
695
696
	BOOL		ret;
	jsrefcount	rc;
697

deuce's avatar
deuce committed
698
699
	if(argc) {
		JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
700
		HANDLE_PENDING(cx, p);
deuce's avatar
deuce committed
701
	}
deuce's avatar
deuce committed
702
	if(p==NULL) {
703
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
704
705
706
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
707
708
709
710
711
	rc=JS_SUSPENDREQUEST(cx);
	ret=putenv(strdup(p))==0;
	free(p);
	JS_RESUMEREQUEST(cx, rc);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(ret));
712
713
714
	return(JS_TRUE);
}

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

744
	rc=JS_SUSPENDREQUEST(cx);
745
	if(report==NULL) {
746
		lprintf(LOG_ERR,"!JavaScript: %s", message);
747
		JS_RESUMEREQUEST(cx, rc);
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
		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";
766
767
768
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
769
		warning="";
770
	}
771

772
	lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
773
	JS_RESUMEREQUEST(cx, rc);
774
775
}

776
777
778
779
780
781
static JSBool
js_OperationCallback(JSContext *cx)
{
	JSBool	ret;

	JS_SetOperationCallback(cx, NULL);
782
	ret=js_CommonOperationCallback(cx, &cb);
783
784
785
	JS_SetOperationCallback(cx, js_OperationCallback);
	return ret;
}
786

rswindell's avatar
rswindell committed
787
788
789
790
791
792
793
794
795
796
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);

797
798
799
800
	if(!JS_DefineProperty(cx, glob, "env", OBJECT_TO_JSVAL(js_env)
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
		return(FALSE);

rswindell's avatar
rswindell committed
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
	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);
}

817
static BOOL js_init(char** env)
818
{
819
	memset(&startup,0,sizeof(startup));
820
	SAFECOPY(startup.load_path, load_path_list);
821

822
823
	fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());

824
	fprintf(statfp,"JavaScript: Creating runtime: %lu bytes\n"
825
826
		,js_max_bytes);

827
	if((js_runtime = jsrt_GetNew(js_max_bytes, 5000, __FILE__, __LINE__))==NULL)
828
829
		return(FALSE);

830
	fprintf(statfp,"JavaScript: Initializing context (stack: %lu bytes)\n"
831
		,js_cx_stack);
832

833
    if((js_cx = JS_NewContext(js_runtime, js_cx_stack))==NULL)
834
		return(FALSE);
835
#ifdef JSDOOR
836
	JS_SetOptions(js_cx, JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_COMPILE_N_GO | JSOPTION_PROFILING);
837
#endif
838
	JS_BEGINREQUEST(js_cx);
839
840
841
842

	JS_SetErrorReporter(js_cx, js_ErrorReporter);

	/* Global Object */
843
	if(!js_CreateCommonObjects(js_cx, &scfg, NULL, js_global_functions
844
		,time(NULL), host_name, SOCKLIB_DESC	/* system */
845
		,&cb,&startup						/* js */
846
		,NULL,INVALID_SOCKET,-1					/* client */
847
		,NULL									/* server */
848
849
		,&js_glob
		)) {
850
		JS_ENDREQUEST(js_cx);
851
		return(FALSE);
852
	}
853

rswindell's avatar
rswindell committed
854
	/* Environment Object (associative array) */
855
	if(!js_CreateEnvObject(js_cx, js_glob, env)) {
856
		JS_ENDREQUEST(js_cx);
rswindell's avatar
rswindell committed
857
		return(FALSE);
858
	}
rswindell's avatar
rswindell committed
859

860
	if(js_CreateUifcObject(js_cx, js_glob)==NULL) {
861
		JS_ENDREQUEST(js_cx);
862
		return(FALSE);
863
	}
864

865
	if(js_CreateConioObject(js_cx, js_glob)==NULL) {
866
		JS_ENDREQUEST(js_cx);
deuce's avatar
deuce committed
867
		return(FALSE);
868
	}
deuce's avatar
deuce committed
869

870
	/* STDIO objects */
871
	if(!js_CreateFileObject(js_cx, js_glob, "stdout", STDOUT_FILENO, "w")) {
872
873
874
875
		JS_ENDREQUEST(js_cx);
		return(FALSE);
	}

876
	if(!js_CreateFileObject(js_cx, js_glob, "stdin", STDIN_FILENO, "r")) {
877
878
879
880
		JS_ENDREQUEST(js_cx);
		return(FALSE);
	}

881
	if(!js_CreateFileObject(js_cx, js_glob, "stderr", STDERR_FILENO, "w")) {
882
883
884
885
		JS_ENDREQUEST(js_cx);
		return(FALSE);
	}

886
887
888
	return(TRUE);
}

889