jsexec.c 27.4 KB
Newer Older
1
2
/* jsexec.c */

rswindell's avatar
rswindell committed
3
/* Execute a Synchronet JavaScript module from the command-line */
4
5
6
7
8
9
10

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
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
37
 *																			*
 * 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
38
#ifndef JAVASCRIPT
39
#define JAVASCRIPT
rswindell's avatar
rswindell committed
40
#endif
41

deuce's avatar
deuce committed
42
43
44
45
#ifdef __unix__
#include <signal.h>
#endif

46
#include "sbbs.h"
47
#include "ciolib.h"
48
#include "ini_file.h"
49
#include "js_rtpool.h"
50
#include "js_request.h"
51

52
#define DEFAULT_LOG_LEVEL	LOG_DEBUG	/* Display all LOG levels */
53
#define DEFAULT_ERR_LOG_LVL	LOG_WARNING
54

55
js_startup_t	startup;
56
57
58
JSRuntime*	js_runtime;
JSContext*	js_cx;
JSObject*	js_glob;
59
js_callback_t	cb;
60
61
scfg_t		scfg;
ulong		js_max_bytes=JAVASCRIPT_MAX_BYTES;
62
ulong		js_cx_stack=JAVASCRIPT_CONTEXT_STACK;
rswindell's avatar
rswindell committed
63
64
FILE*		confp;
FILE*		errfp;
65
FILE*		nulfp;
66
FILE*		statfp;
67
char		revision[16];
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
pthread_mutex_t output_mutex;
79
80
81
#if defined(__unix__)
BOOL		daemonize=FALSE;
#endif
82
83
char		orig_cwd[MAX_PATH+1];

84
85
void banner(FILE* fp)
{
86
	fprintf(fp,"\nJSexec v%s%c-%s (rev %s)%s - "
87
88
89
90
		"Execute Synchronet JavaScript Module\n"
		,VERSION,REVISION
		,PLATFORM_DESC
		,revision
91
92
93
94
95
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
96
		);
97
98
99

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

rswindell's avatar
rswindell committed
102
103
void usage(FILE* fp)
{
104
105
	banner(fp);

rswindell's avatar
rswindell committed
106
107
	fprintf(fp,"\nusage: jsexec [-opts] [path]module[.js] [args]\n"
		"\navailable opts:\n\n"
108
		"\t-c<ctrl_dir>   specify path to Synchronet CTRL directory\n"
109
110
111
#if defined(__unix__)
		"\t-d             run in background (daemonize)\n"
#endif
112
113
		"\t-m<bytes>      set maximum heap size (default=%u bytes)\n"
		"\t-s<bytes>      set context stack size (default=%u bytes)\n"
114
		"\t-t<limit>      set time limit (default=%u, 0=unlimited)\n"
115
116
117
		"\t-y<interval>   set yield interval (default=%u, 0=never)\n"
		"\t-g<interval>   set garbage collection interval (default=%u, 0=never)\n"
		"\t-h[hostname]   use local or specified host name (instead of SCFG value)\n"
118
		"\t-u<mask>       set file creation permissions mask (in octal)\n"
119
		"\t-L<level>      set log level (default=%u)\n"
120
		"\t-E<level>      set error log level threshold (default=%u)\n"
121
		"\t-i<path_list>  set load() comma-sep search path list (default=\"%s\")\n"
122
		"\t-f             use non-buffered stream for console messages\n"
rswindell's avatar
rswindell committed
123
		"\t-a             append instead of overwriting message output files\n"
124
125
126
127
		"\t-e<filename>   send error messages to file in addition to stderr\n"
		"\t-o<filename>   send console messages to file instead of stdout\n"
		"\t-n             send status messages to %s instead of stderr\n"
		"\t-q             send console messages to %s instead of stdout\n"
128
		"\t-v             display version details and exit\n"
129
		"\t-x             disable auto-termination on local abort signal\n"
130
131
132
		"\t-l             loop until intentionally terminated\n"
		"\t-p             wait for keypress (pause) on exit\n"
		"\t-!             wait for keypress (pause) on error\n"
rswindell's avatar
rswindell committed
133
134
		,JAVASCRIPT_MAX_BYTES
		,JAVASCRIPT_CONTEXT_STACK
135
		,JAVASCRIPT_TIME_LIMIT
136
137
		,JAVASCRIPT_YIELD_INTERVAL
		,JAVASCRIPT_GC_INTERVAL
138
		,DEFAULT_LOG_LEVEL
139
		,DEFAULT_ERR_LOG_LVL
140
		,load_path_list
141
		,_PATH_DEVNULL
142
		,_PATH_DEVNULL
rswindell's avatar
rswindell committed
143
144
		);
}
145

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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);
}

166
/* Log printf */
167
int lprintf(int level, const char *fmt, ...)
168
169
170
{
	va_list argptr;
	char sbuf[1024];
deuce's avatar
deuce committed
171
	int ret=0;
172

173
	if(level > log_level)
174
175
176
		return(0);

    va_start(argptr,fmt);
177
    ret=vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
178
179
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
180
181
#if defined(__unix__)
	if(daemonize) {
182
		syslog(level,"%s",sbuf);
183
184
185
		return(ret);
	}
#endif
186
187
188
189

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

190
	if(level<=err_level) {
191
		ret=fprintf(errfp,"%s\n",sbuf);
192
193
194
195
		if(errfp!=stderr && confp!=stdout)
			ret=fprintf(statfp,"%s\n",sbuf);
	}
	if(level>err_level || errfp!=stderr)
196
		ret=fprintf(confp,"%s\n",sbuf);
197
198

	pthread_mutex_unlock(&output_mutex);
199
200
201
    return(ret);
}

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#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

237
#if defined(_WINSOCKAPI_)
238
239

WSADATA WSAData;
240
#define SOCKLIB_DESC WSAData.szDescription
241
242
243
244
245
246
247
static BOOL WSAInitialized=FALSE;

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

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
248
/*		fprintf(statfp,"%s %s\n",WSAData.szDescription, WSAData.szSystemStatus); */
249
250
251
252
		WSAInitialized=TRUE;
		return(TRUE);
	}

253
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
254
255
256
257
258
	return(FALSE);
}

#else /* No WINSOCK */

259
260
#define winsock_startup()	(TRUE)
#define SOCKLIB_DESC NULL
261
262
263

#endif

264
static int do_bail(int code)
265
{
266
#if defined(_WINSOCKAPI_)
267
	if(WSAInitialized && WSACleanup()!=0) 
268
		lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
269
270
#endif

rswindell's avatar
rswindell committed
271
	if(pause_on_exit || (code && pause_on_error)) {
272
		fprintf(statfp,"\nHit enter to continue...");
273
274
		getchar();
	}
275
276
277

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
278
279
280
281
282
283
	return(code);
}

void bail(int code)
{
	exit(do_bail(code));
284
285
}

286
static JSBool
287
js_log(JSContext *cx, uintN argc, jsval *arglist)
288
{
289
	jsval *argv=JS_ARGV(cx, arglist);
290
291
    uintN		i=0;
	int32		level=LOG_INFO;
292
    JSString*	str=NULL;
deuce's avatar
deuce committed
293
	jsrefcount	rc;
deuce's avatar
deuce committed
294
	char		*logstr;
295

296
297
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

298
299
300
301
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
302

303
304
305
	for(; i<argc; i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
306
		JSSTRING_TO_STRING(cx, str, logstr, NULL);
deuce's avatar
deuce committed
307
308
		if(logstr==NULL)
			return(JS_FALSE);
309
		rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
310
		lprintf(level,"%s",logstr);
311
		JS_RESUMEREQUEST(cx, rc);
312
313
	}

deuce's avatar
deuce committed
314
	if(logstr==NULL)
315
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
316
	else
317
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
318

319
320
321
    return(JS_TRUE);
}

322
static JSBool
323
js_read(JSContext *cx, uintN argc, jsval *arglist)
324
{
325
	jsval *argv=JS_ARGV(cx, arglist);
326
327
328
	char*	buf;
	int		rd;
	int32	len=128;
deuce's avatar
deuce committed
329
	jsrefcount	rc;
330

331
332
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

333
334
335
336
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
337
	if((buf=alloca(len))==NULL)
338
339
		return(JS_TRUE);

340
	rc=JS_SUSPENDREQUEST(cx);
341
	rd=fread(buf,sizeof(char),len,stdin);
342
	JS_RESUMEREQUEST(cx, rc);
343
344

	if(rd>=0)
345
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd)));
346
347
348
349
350

    return(JS_TRUE);
}

static JSBool
351
js_readln(JSContext *cx, uintN argc, jsval *arglist)
352
{
353
	jsval *argv=JS_ARGV(cx, arglist);
354
355
356
	char*	buf;
	char*	p;
	int32	len=128;
deuce's avatar
deuce committed
357
	jsrefcount	rc;
358

359
360
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

361
362
363
364
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
365
	if((buf=alloca(len+1))==NULL)
366
367
		return(JS_TRUE);

368
	rc=JS_SUSPENDREQUEST(cx);
369
	p=fgets(buf,len+1,stdin);
370
	JS_RESUMEREQUEST(cx, rc);
371
372

	if(p!=NULL)
373
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p))));
374
375
376
377
378

    return(JS_TRUE);
}


379
static JSBool
380
js_write(JSContext *cx, uintN argc, jsval *arglist)
381
{
382
	jsval *argv=JS_ARGV(cx, arglist);
383
    uintN		i;
384
    JSString*	str=NULL;
deuce's avatar
deuce committed
385
	jsrefcount	rc;
deuce's avatar
deuce committed
386
	char		*line;
387

388
389
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

390
    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
391
		if((str=JS_ValueToString(cx, argv[0]))==NULL)
392
		    return(JS_FALSE);
393
		JSSTRING_TO_STRING(cx, str, line, NULL);
deuce's avatar
deuce committed
394
395
		if(line==NULL)
			return(JS_FALSE);
396
		rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
397
		fprintf(confp,"%s",line);
398
		JS_RESUMEREQUEST(cx, rc);
399
400
	}

401
	if(str==NULL)
402
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
403
	else
404
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
405
406
407
408
    return(JS_TRUE);
}

static JSBool
409
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
410
{
deuce's avatar
deuce committed
411
412
	jsrefcount	rc;

413
414
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

deuce's avatar
deuce committed
415
	if(!js_write(cx,argc,arglist))
416
417
		return(JS_FALSE);

418
	rc=JS_SUSPENDREQUEST(cx);
419
	fprintf(confp,"\n");
420
	JS_RESUMEREQUEST(cx, rc);
421
422
423
424
    return(JS_TRUE);
}

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

431
432
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

433
434
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
435
436
437
		return(JS_FALSE);
	}

438
	rc=JS_SUSPENDREQUEST(cx);
439
	fprintf(confp,"%s",p);
440
	JS_RESUMEREQUEST(cx, rc);
441

442
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
443

444
	js_sprintf_free(p);
445
446
447
448
449

    return(JS_TRUE);
}

static JSBool
450
js_alert(JSContext *cx, uintN argc, jsval *arglist)
451
{
452
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
453
	jsrefcount	rc;
deuce's avatar
deuce committed
454
	char		*line;
455

456
457
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

458
	JSVALUE_TO_STRING(cx, argv[0], line, NULL);
deuce's avatar
deuce committed
459
	if(line==NULL)
460
461
	    return(JS_FALSE);

462
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
463
	fprintf(confp,"!%s\n",line);
464
	JS_RESUMEREQUEST(cx, rc);
465

466
	JS_SET_RVAL(cx, arglist, argv[0]);
467

468
469
470
471
    return(JS_TRUE);
}

static JSBool
472
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
473
{
474
	jsval *argv=JS_ARGV(cx, arglist);
475
    JSString *	str;
deuce's avatar
deuce committed
476
	char	 *	cstr;
477
	char     *	p;
deuce's avatar
deuce committed
478
	jsrefcount	rc;
479
	char		instr[81]="y";
480

481
482
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

483
484
485
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

486
	JSSTRING_TO_STRING(cx, str, cstr, NULL);
487
	printf("%s (Y/n)? ", cstr);
488
	rc=JS_SUSPENDREQUEST(cx);
489
	fgets(instr,sizeof(instr),stdin);
490
	JS_RESUMEREQUEST(cx, rc);
491

492
493
	p=instr;
	SKIP_WHITESPACE(p);
494
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='n'));
495
496
497
498
	return(JS_TRUE);
}

static JSBool
499
js_deny(JSContext *cx, uintN argc, jsval *arglist)
500
{
501
	jsval *argv=JS_ARGV(cx, arglist);
502
503
504
505
506
507
    JSString *	str;
	char	 *	cstr;
	char     *	p;
	jsrefcount	rc;
	char		instr[81];

508
509
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

510
511
512
	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

513
	JSSTRING_TO_STRING(cx, str, cstr, NULL);
514
515
516
517
518
519
520
	printf("%s (N/y)? ", cstr);
	rc=JS_SUSPENDREQUEST(cx);
	fgets(instr,sizeof(instr),stdin);
	JS_RESUMEREQUEST(cx, rc);

	p=instr;
	SKIP_WHITESPACE(p);
521
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(tolower(*p)!='y'));
522
523
524
525
	return(JS_TRUE);
}

static JSBool
526
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
527
{
528
	jsval *argv=JS_ARGV(cx, arglist);
529
530
	char		instr[81];
    JSString *	str;
deuce's avatar
deuce committed
531
	jsrefcount	rc;
deuce's avatar
deuce committed
532
	char		*prstr;
533

534
535
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

536
	if(argc>0 && !JSVAL_IS_VOID(argv[0])) {
537
		JSVALUE_TO_STRING(cx, argv[0], prstr, NULL);
538
		if(prstr==NULL)
539
			return(JS_FALSE);
540
		rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
541
		fprintf(confp,"%s: ",prstr);
542
		JS_RESUMEREQUEST(cx, rc);
543
	}
544
545

	if(argc>1) {
546
		JSVALUE_TO_STRING(cx, argv[1], prstr, NULL);
547
		if(prstr==NULL)
548
		    return(JS_FALSE);
deuce's avatar
deuce committed
549
		SAFECOPY(instr,prstr);
550
551
552
	} else
		instr[0]=0;

553
	rc=JS_SUSPENDREQUEST(cx);
554
555
	if(!fgets(instr,sizeof(instr),stdin)) {
		JS_RESUMEREQUEST(cx, rc);
556
		return(JS_TRUE);
557
	}
558
	JS_RESUMEREQUEST(cx, rc);
559

560
	if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL)
561
562
	    return(JS_FALSE);

563
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
564
565
566
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
567
static JSBool
568
js_chdir(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
569
{
570
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
571
	char*		p;
deuce's avatar
deuce committed
572
	jsrefcount	rc;
rswindell's avatar
rswindell committed
573

574
	JSVALUE_TO_STRING(cx, argv[0], p, NULL);
deuce's avatar
deuce committed
575
	if(p==NULL) {
576
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
rswindell's avatar
rswindell committed
577
578
579
		return(JS_TRUE);
	}

580
	rc=JS_SUSPENDREQUEST(cx);
581
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(chdir(p)==0));
582
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
583
584
585
	return(JS_TRUE);
}

586
static JSBool
587
js_putenv(JSContext *cx, uintN argc, jsval *arglist)
588
{
589
	jsval *argv=JS_ARGV(cx, arglist);
590
	char*		p=NULL;
591

592
593
	if(argc)
		JSVALUE_TO_STRING(cx, argv[0], p, NULL);
deuce's avatar
deuce committed
594
	if(p==NULL) {
595
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
596
597
598
		return(JS_TRUE);
	}

599
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(putenv(strdup(p))==0));
600
601
602
	return(JS_TRUE);
}

603
static jsSyncMethodSpec js_global_functions[] = {
604
	{"log",				js_log,				1},
605
	{"read",			js_read,            1},
deuce's avatar
deuce committed
606
607
608
609
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
	,311
	},
610
    {"write",           js_write,           0},
611
612
    {"writeln",         js_writeln,         0},
    {"print",           js_writeln,         0},
613
614
615
616
    {"printf",          js_printf,          1},	
	{"alert",			js_alert,			1},
	{"prompt",			js_prompt,			1},
	{"confirm",			js_confirm,			1},
617
	{"deny",			js_deny,			1},
rswindell's avatar
rswindell committed
618
	{"chdir",			js_chdir,			1},
619
	{"putenv",			js_putenv,			1},
620
621
622
623
624
625
626
627
628
    {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
629
	jsrefcount	rc;
630
	int		log_level;
631

632
	rc=JS_SUSPENDREQUEST(cx);
633
	if(report==NULL) {
634
		lprintf(LOG_ERR,"!JavaScript: %s", message);
635
		JS_RESUMEREQUEST(cx, rc);
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
		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";
654
655
656
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
657
		warning="";
658
	}
659

660
	lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
661
	JS_RESUMEREQUEST(cx, rc);
662
663
}

664
665
666
667
668
669
static JSBool
js_OperationCallback(JSContext *cx)
{
	JSBool	ret;

	JS_SetOperationCallback(cx, NULL);
670
	ret=js_CommonOperationCallback(cx, &cb);
671
672
673
	JS_SetOperationCallback(cx, js_OperationCallback);
	return ret;
}
674

rswindell's avatar
rswindell committed
675
676
677
678
679
680
681
682
683
684
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);

685
686
687
688
	if(!JS_DefineProperty(cx, glob, "env", OBJECT_TO_JSVAL(js_env)
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
		return(FALSE);

rswindell's avatar
rswindell committed
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
	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);
}

rswindell's avatar
rswindell committed
705
static BOOL js_init(char** environ)
706
{
707
	memset(&startup,0,sizeof(startup));
708
	SAFECOPY(startup.load_path, load_path_list);
709

710
711
	fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());

712
	fprintf(statfp,"JavaScript: Creating runtime: %lu bytes\n"
713
714
		,js_max_bytes);

715
	if((js_runtime = jsrt_GetNew(js_max_bytes, 5000, __FILE__, __LINE__))==NULL)
716
717
		return(FALSE);

718
	fprintf(statfp,"JavaScript: Initializing context (stack: %lu bytes)\n"
719
		,js_cx_stack);
720

721
    if((js_cx = JS_NewContext(js_runtime, js_cx_stack))==NULL)
722
		return(FALSE);
723
	JS_BEGINREQUEST(js_cx);
724
725
726
727

	JS_SetErrorReporter(js_cx, js_ErrorReporter);

	/* Global Object */
728
	if(!js_CreateCommonObjects(js_cx, &scfg, NULL, js_global_functions
729
		,time(NULL), host_name, SOCKLIB_DESC	/* system */
730
		,&cb,&startup						/* js */
731
		,NULL,INVALID_SOCKET					/* client */
732
		,NULL									/* server */
733
734
		,&js_glob
		)) {
735
		JS_ENDREQUEST(js_cx);
736
		return(FALSE);
737
	}
738

rswindell's avatar
rswindell committed
739
	/* Environment Object (associative array) */
740
	if(!js_CreateEnvObject(js_cx, js_glob, environ)) {
741
		JS_ENDREQUEST(js_cx);
rswindell's avatar
rswindell committed
742
		return(FALSE);
743
	}
rswindell's avatar
rswindell committed
744

745
	if(js_CreateUifcObject(js_cx, js_glob)==NULL) {
746
		JS_ENDREQUEST(js_cx);
747
		return(FALSE);
748
	}
749

750
	if(js_CreateConioObject(js_cx, js_glob)==NULL) {
751
		JS_ENDREQUEST(js_cx);
deuce's avatar
deuce committed
752
		return(FALSE);
753
	}
deuce's avatar
deuce committed
754

755
756
757
	return(TRUE);
}

758
759
760
761
762
763
764
static const char* js_ext(const char* fname)
{
	if(strchr(fname,'.')==NULL)
		return(".js");
	return("");
}

765
766
767
long js_exec(const char *fname, char** args)
{
	int			argc=0;
768
	uint		line_no;
769
	char		path[MAX_PATH+1];
770
	char		line[1024];
771
	char		rev_detail[256];
772
	size_t		len;
773
774
	char*		js_buf=NULL;
	size_t		js_buflen;
775
	JSObject*	js_script=NULL;
776
777
	JSString*	arg;
	JSObject*	argv;
778
	FILE*		fp=stdin;
779
	jsval		val;
rswindell's avatar
rswindell committed
780
781
	jsval		rval=JSVAL_VOID;
	int32		result=0;
782
783
	long double	start;
	long double	diff;
deuce's avatar
deuce committed
784

785
	if(fname!=NULL) {
786
787
788
789
		if(isfullpath(fname)) {
			SAFECOPY(path,fname);
		}
		else {
790
791
792
793
794
795
			SAFEPRINTF3(path,"%s%s%s",orig_cwd,fname,js_ext(fname));
			if(!fexistcase(path)) {
				SAFEPRINTF3(path,"%s%s%s",scfg.mods_dir,fname,js_ext(fname));
				if(scfg.mods_dir[0]==0 || !fexistcase(path))
					SAFEPRINTF3(path,"%s%s%s",scfg.exec_dir,fname,js_ext(fname));
			}
796
		}
797
798

		if(!fexistcase(path)) {
799
			lprintf(LOG_ERR,"!Module file (%s) doesn't exist",path);
800
801
			return(-1); 
		}
802
803

		if((fp=fopen(path,"r"))==NULL) {
804
			lprintf(LOG_ERR,"!Error %d (%s) opening %s",errno,STRERROR(errno),path);
805
806
			return(-1);
		}
807
	}
808
809
	JS_ClearPendingException(js_cx);

810
	argv=JS_NewArrayObject(js_cx, 0, NULL);
811
	JS_DefineProperty(js_cx, js_glob, "argv", OBJECT_TO_JSVAL(argv)
812
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
813
814
815
816
817
818
819
820
821

	for(argc=0;args[argc];argc++) {
		arg = JS_NewStringCopyZ(js_cx, args[argc]);
		if(arg==NULL)
			break;
		val=STRING_TO_JSVAL(arg);
		if(!JS_SetElement(js_cx, argv, argc, &val))
			break;
	}
822
	JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc)
823
824
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

825
826
827
828
	JS_DefineProperty(js_cx, js_glob, "jsexec_revision"
		,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,revision))
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
	sprintf(rev_detail,"JSexec %s%s  "
		"Compiled %s %s with %s"
		,revision
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,__DATE__, __TIME__, compiler
		);

	JS_DefineProperty(js_cx, js_glob, "jsexec_revision_detail"
		,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,rev_detail))
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

844
	cb.terminated=&terminated;
845

846
	JS_SetOperationCallback(js_cx, js_OperationCallback);
847

848
	if(fp==stdin) 	 /* Using stdin for script source */
849
		SAFECOPY(path,"stdin");
850

851
852
853
	fprintf(statfp,"Reading script from %s\n",path);
	line_no=0;
	js_buflen=0;
854
855
	while(!feof(fp)) {
		if(!fgets(line,sizeof(line),fp))
856
857
			break;
		line_no++;
858
#if defined(__unix__)	/* Support Unix Shell Scripts that start with #!/path/to/jsexec */
859
		if(line_no==1 && strncmp(line,"#!",2)==0)
860
			strcpy(line,"\n");	/* To keep line count correct */
861
#endif
862
863
		len=strlen(line);
		if((js_buf=realloc(js_buf,js_buflen+len))==NULL) {
864
			lprintf(LOG_ERR,"!Error allocating %u bytes of memory"
865
				,js_buflen+len);
866
867
			return(-1);
		}
868
869
		memcpy(js_buf+js_buflen,line,len);
		js_buflen+=len;
870
	}
871
872
873
	if(fp!=NULL && fp!=stdin)
		fclose(fp);

874
	start=xp_timer();
875
	if((js_script=JS_CompileScript(js_cx, js_glob, js_buf, js_buflen, fname==NULL ? NULL : path, 1))==NULL) {
876
		lprintf(LOG_ERR,"!Error compiling script from %s",path);
877
		return(-1);
878
	}
879
	if((diff=xp_timer()-start) > 0)
880
		mfprintf(statfp,"%s compiled in %.2Lf seconds"
881
			,path
882
			,diff);
883

884
	js_PrepareToExecute(js_cx, js_glob, fname==NULL ? NULL : path, orig_cwd);
rswindell's avatar