jsexec.c 25 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 2008 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 "js_rtpool.h"
49
#include "js_request.h"
50

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

54
55
56
JSRuntime*	js_runtime;
JSContext*	js_cx;
JSObject*	js_glob;
57
js_branch_t	branch;
58
59
scfg_t		scfg;
ulong		js_max_bytes=JAVASCRIPT_MAX_BYTES;
60
ulong		js_cx_stack=JAVASCRIPT_CONTEXT_STACK;
61
ulong		stack_limit=JAVASCRIPT_THREAD_STACK;
rswindell's avatar
rswindell committed
62
63
FILE*		confp;
FILE*		errfp;
64
FILE*		nulfp;
65
FILE*		statfp;
66
char		revision[16];
67
char		compiler[32];
68
69
char*		host_name=NULL;
char		host_name_buf[128];
70
BOOL		pause_on_exit=FALSE;
rswindell's avatar
rswindell committed
71
BOOL		pause_on_error=FALSE;
72
BOOL		terminated=FALSE;
73
BOOL		recycled;
74
int			log_level=DEFAULT_LOG_LEVEL;
75
int  		err_level=DEFAULT_ERR_LOG_LVL;
76
pthread_mutex_t output_mutex;
77
78
79
#if defined(__unix__)
BOOL		daemonize=FALSE;
#endif
80
81
82

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

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

rswindell's avatar
rswindell committed
99
100
void usage(FILE* fp)
{
101
102
	banner(fp);

rswindell's avatar
rswindell committed
103
104
	fprintf(fp,"\nusage: jsexec [-opts] [path]module[.js] [args]\n"
		"\navailable opts:\n\n"
105
		"\t-c<ctrl_dir>   specify path to Synchronet CTRL directory\n"
106
107
108
#if defined(__unix__)
		"\t-d             run in background (daemonize)\n"
#endif
109
110
		"\t-m<bytes>      set maximum heap size (default=%u bytes)\n"
		"\t-s<bytes>      set context stack size (default=%u bytes)\n"
rswindell's avatar
rswindell committed
111
		"\t-S<bytes>      set thread stack limit (default=%u, 0=unlimited)\n"
112
113
114
115
		"\t-b<limit>      set branch limit (default=%u, 0=unlimited)\n"
		"\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"
116
		"\t-u<mask>       set file creation permissions mask (in octal)\n"
117
		"\t-L<level>      set log level (default=%u)\n"
118
		"\t-E<level>      set error log level threshold (default=%d)\n"
119
		"\t-f             use non-buffered stream for console messages\n"
rswindell's avatar
rswindell committed
120
		"\t-a             append instead of overwriting message output files\n"
121
122
123
124
		"\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"
125
		"\t-v             display version details and exit\n"
126
		"\t-x             disable auto-termination on local abort signal\n"
127
128
129
		"\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
130
131
		,JAVASCRIPT_MAX_BYTES
		,JAVASCRIPT_CONTEXT_STACK
132
		,JAVASCRIPT_THREAD_STACK
133
		,JAVASCRIPT_BRANCH_LIMIT
134
135
		,JAVASCRIPT_YIELD_INTERVAL
		,JAVASCRIPT_GC_INTERVAL
136
		,DEFAULT_LOG_LEVEL
137
		,DEFAULT_ERR_LOG_LVL
138
		,_PATH_DEVNULL
139
		,_PATH_DEVNULL
rswindell's avatar
rswindell committed
140
141
		);
}
142

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

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

170
	if(level > log_level)
171
172
173
		return(0);

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

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

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

	pthread_mutex_unlock(&output_mutex);
196
197
198
    return(ret);
}

199
200
201
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
#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

234
#if defined(_WINSOCKAPI_)
235
236

WSADATA WSAData;
237
#define SOCKLIB_DESC WSAData.szDescription
238
239
240
241
242
243
244
static BOOL WSAInitialized=FALSE;

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

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

250
    lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
251
252
253
254
255
	return(FALSE);
}

#else /* No WINSOCK */

256
257
#define winsock_startup()	(TRUE)
#define SOCKLIB_DESC NULL
258
259
260

#endif

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

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

	if(code)
		fprintf(statfp,"\nReturning error code: %d\n",code);
275
276
277
278
279
280
	return(code);
}

void bail(int code)
{
	exit(do_bail(code));
281
282
}

283
284
285
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
286
287
    uintN		i=0;
	int32		level=LOG_INFO;
288
    JSString*	str=NULL;
deuce's avatar
deuce committed
289
	jsrefcount	rc;
290

291
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
292
293
		JS_ValueToInt32(cx,argv[i++],&level);

294
295
296
	for(; i<argc; i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
297
		rc=JS_SUSPENDREQUEST(cx);
298
		lprintf(level,"%s",JS_GetStringBytes(str));
299
		JS_RESUMEREQUEST(cx, rc);
300
301
	}

302
303
304
305
306
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);

307
308
309
    return(JS_TRUE);
}

310
311
312
313
314
315
static JSBool
js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*	buf;
	int		rd;
	int32	len=128;
deuce's avatar
deuce committed
316
	jsrefcount	rc;
317

318
319
	if(argc)
		JS_ValueToInt32(cx,argv[0],&len);
320
	if((buf=alloca(len))==NULL)
321
322
		return(JS_TRUE);

323
	rc=JS_SUSPENDREQUEST(cx);
324
	rd=fread(buf,sizeof(char),len,stdin);
325
	JS_RESUMEREQUEST(cx, rc);
326
327
328
329
330
331
332
333
334
335
336
337
338

	if(rd>=0)
		*rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,rd));

    return(JS_TRUE);
}

static JSBool
js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*	buf;
	char*	p;
	int32	len=128;
deuce's avatar
deuce committed
339
	jsrefcount	rc;
340

341
342
	if(argc)
		JS_ValueToInt32(cx,argv[0],&len);
343
	if((buf=alloca(len+1))==NULL)
344
345
		return(JS_TRUE);

346
	rc=JS_SUSPENDREQUEST(cx);
347
	p=fgets(buf,len+1,stdin);
348
	JS_RESUMEREQUEST(cx, rc);
349
350

	if(p!=NULL)
351
		*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,truncnl(p)));
352
353
354
355
356

    return(JS_TRUE);
}


357
static JSBool
358
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
359
360
{
    uintN		i;
361
    JSString*	str=NULL;
deuce's avatar
deuce committed
362
	jsrefcount	rc;
363
364
365
366

    for (i = 0; i < argc; i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
		    return(JS_FALSE);
367
		rc=JS_SUSPENDREQUEST(cx);
368
		fprintf(confp,"%s",JS_GetStringBytes(str));
369
		JS_RESUMEREQUEST(cx, rc);
370
371
	}

372
373
374
375
376
377
378
379
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
    return(JS_TRUE);
}

static JSBool
380
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
381
{
deuce's avatar
deuce committed
382
383
	jsrefcount	rc;

384
385
386
	if(!js_write(cx,obj,argc,argv,rval))
		return(JS_FALSE);

387
	rc=JS_SUSPENDREQUEST(cx);
388
	fprintf(confp,"\n");
389
	JS_RESUMEREQUEST(cx, rc);
390
391
392
393
394
395
    return(JS_TRUE);
}

static JSBool
js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
396
	char* p;
deuce's avatar
deuce committed
397
	jsrefcount	rc;
398

399
400
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
401
402
403
		return(JS_FALSE);
	}

404
	rc=JS_SUSPENDREQUEST(cx);
405
	fprintf(confp,"%s",p);
406
	JS_RESUMEREQUEST(cx, rc);
407
408
409

	*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));

410
	js_sprintf_free(p);
411
412
413
414
415
416
417
418

    return(JS_TRUE);
}

static JSBool
js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
deuce's avatar
deuce committed
419
	jsrefcount	rc;
420
421
422
423

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

424
	rc=JS_SUSPENDREQUEST(cx);
425
	fprintf(confp,"!%s\n",JS_GetStringBytes(str));
426
	JS_RESUMEREQUEST(cx, rc);
427

428
429
	*rval = argv[0];

430
431
432
433
434
435
436
    return(JS_TRUE);
}

static JSBool
js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
deuce's avatar
deuce committed
437
438
	char	 *	cstr;
	jsrefcount	rc;
439
440
441
442

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

deuce's avatar
deuce committed
443
	cstr = JS_GetStringBytes(str);
444
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
445
	printf("%s (Y/N)?", cstr);
446
	JS_RESUMEREQUEST(cx, rc);
447

448
449
450
451
452
453
454
455
456
457
	*rval = BOOLEAN_TO_JSVAL(FALSE);
	return(JS_TRUE);
}

static JSBool
js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char		instr[81];
    JSString *	prompt;
    JSString *	str;
deuce's avatar
deuce committed
458
	jsrefcount	rc;
459

460
461
462
	if(!JSVAL_IS_VOID(argv[0])) {
		if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
			return(JS_FALSE);
463
		rc=JS_SUSPENDREQUEST(cx);
464
		fprintf(confp,"%s: ",JS_GetStringBytes(prompt));
465
		JS_RESUMEREQUEST(cx, rc);
466
	}
467
468
469
470
471
472
473
474

	if(argc>1) {
		if((str=JS_ValueToString(cx, argv[1]))==NULL)
		    return(JS_FALSE);
		SAFECOPY(instr,JS_GetStringBytes(str));
	} else
		instr[0]=0;

475
	rc=JS_SUSPENDREQUEST(cx);
476
477
	if(!fgets(instr,sizeof(instr),stdin)) {
		JS_RESUMEREQUEST(cx, rc);
478
		return(JS_TRUE);
479
	}
480
	JS_RESUMEREQUEST(cx, rc);
481

482
	if((str=JS_NewStringCopyZ(cx, truncnl(instr)))==NULL)
483
484
485
486
487
488
	    return(JS_FALSE);

	*rval = STRING_TO_JSVAL(str);
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
489
490
491
492
static JSBool
js_chdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		p;
deuce's avatar
deuce committed
493
	jsrefcount	rc;
rswindell's avatar
rswindell committed
494
495
496
497
498
499

	if((p=JS_GetStringBytes(JS_ValueToString(cx, argv[0])))==NULL) {
		*rval = INT_TO_JSVAL(-1);
		return(JS_TRUE);
	}

500
	rc=JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
501
	*rval = BOOLEAN_TO_JSVAL(chdir(p)==0);
502
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
503
504
505
	return(JS_TRUE);
}

506
507
508
509
510
511
512
513
514
515
516
517
518
519
static JSBool
js_putenv(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		p;

	if((p=JS_GetStringBytes(JS_ValueToString(cx, argv[0])))==NULL) {
		*rval = INT_TO_JSVAL(-1);
		return(JS_TRUE);
	}

	*rval = BOOLEAN_TO_JSVAL(putenv(p)==0);
	return(JS_TRUE);
}

520
static jsSyncMethodSpec js_global_functions[] = {
521
	{"log",				js_log,				1},
522
523
	{"read",			js_read,            1},
	{"readln",			js_readln,          1},
524
    {"write",           js_write,           0},
525
526
    {"writeln",         js_writeln,         0},
    {"print",           js_writeln,         0},
527
528
529
530
    {"printf",          js_printf,          1},	
	{"alert",			js_alert,			1},
	{"prompt",			js_prompt,			1},
	{"confirm",			js_confirm,			1},
rswindell's avatar
rswindell committed
531
	{"chdir",			js_chdir,			1},
532
	{"putenv",			js_putenv,			1},
533
534
535
536
537
538
539
540
541
    {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
542
	jsrefcount	rc;
543

544
	rc=JS_SUSPENDREQUEST(cx);
545
	if(report==NULL) {
546
		lprintf(LOG_ERR,"!JavaScript: %s", message);
547
		JS_RESUMEREQUEST(cx, rc);
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
		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";
	} else
		warning="";

569
	lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
570
	JS_RESUMEREQUEST(cx, rc);
571
572
573
574
575
}

static JSBool
js_BranchCallback(JSContext *cx, JSScript *script)
{
576
    return(js_CommonBranchCallback(cx,&branch));
577
578
}

rswindell's avatar
rswindell committed
579
580
581
582
583
584
585
586
587
588
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);

589
590
591
592
	if(!JS_DefineProperty(cx, glob, "env", OBJECT_TO_JSVAL(js_env)
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
		return(FALSE);

rswindell's avatar
rswindell committed
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
	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
609
static BOOL js_init(char** environ)
610
{
611
612
	fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());

613
	fprintf(statfp,"JavaScript: Creating runtime: %lu bytes\n"
614
615
		,js_max_bytes);

616
	if((js_runtime = jsrt_GetNew(js_max_bytes, 5000, __FILE__, __LINE__))==NULL)
617
618
		return(FALSE);

619
	fprintf(statfp,"JavaScript: Initializing context (stack: %lu bytes)\n"
620
		,js_cx_stack);
621

622
    if((js_cx = JS_NewContext(js_runtime, js_cx_stack))==NULL)
623
		return(FALSE);
624
	JS_BEGINREQUEST(js_cx);
625

626
627
628
629
	if(stack_limit)
		fprintf(statfp,"JavaScript: Thread stack limit: %lu bytes\n"
			,stack_limit);

630
631
632
	JS_SetErrorReporter(js_cx, js_ErrorReporter);

	/* Global Object */
633
	if((js_glob=js_CreateCommonObjects(js_cx, &scfg, NULL, js_global_functions
634
635
636
		,time(NULL), host_name, SOCKLIB_DESC	/* system */
		,&branch								/* js */
		,NULL,INVALID_SOCKET					/* client */
637
		,NULL									/* server */
638
		))==NULL) {
639
		JS_ENDREQUEST(js_cx);
640
		return(FALSE);
641
	}
642

rswindell's avatar
rswindell committed
643
	/* Environment Object (associative array) */
644
	if(!js_CreateEnvObject(js_cx, js_glob, environ)) {
645
		JS_ENDREQUEST(js_cx);
rswindell's avatar
rswindell committed
646
		return(FALSE);
647
	}
rswindell's avatar
rswindell committed
648

649
	if(js_CreateUifcObject(js_cx, js_glob)==NULL) {
650
		JS_ENDREQUEST(js_cx);
651
		return(FALSE);
652
	}
653

654
	if(js_CreateConioObject(js_cx, js_glob)==NULL) {
655
		JS_ENDREQUEST(js_cx);
deuce's avatar
deuce committed
656
		return(FALSE);
657
	}
deuce's avatar
deuce committed
658

659
660
661
	return(TRUE);
}

662
663
664
665
666
667
668
static const char* js_ext(const char* fname)
{
	if(strchr(fname,'.')==NULL)
		return(".js");
	return("");
}

669
670
long js_exec(const char *fname, char** args)
{
671
	ulong		stack_frame;
672
	int			argc=0;
673
	uint		line_no;
674
	char		path[MAX_PATH+1];
675
	char		line[1024];
676
	char		rev_detail[256];
677
	size_t		len;
678
679
	char*		js_buf=NULL;
	size_t		js_buflen;
680
681
682
	JSScript*	js_script=NULL;
	JSString*	arg;
	JSObject*	argv;
683
	FILE*		fp=stdin;
684
	jsval		val;
rswindell's avatar
rswindell committed
685
686
	jsval		rval=JSVAL_VOID;
	int32		result=0;
687
688
	long double	start;
	long double	diff;
deuce's avatar
deuce committed
689

690
691
692
693
694
695
	if(fname!=NULL) {
		if(strcspn(fname,"/\\")==strlen(fname)) {
			sprintf(path,"%s%s%s",scfg.mods_dir,fname,js_ext(fname));
			if(scfg.mods_dir[0]==0 || !fexistcase(path))
				sprintf(path,"%s%s%s",scfg.exec_dir,fname,js_ext(fname));
		} else
696
			SAFECOPY(path,fname);
697
698

		if(!fexistcase(path)) {
699
			lprintf(LOG_ERR,"!Module file (%s) doesn't exist",path);
700
701
			return(-1); 
		}
702
703

		if((fp=fopen(path,"r"))==NULL) {
704
			lprintf(LOG_ERR,"!Error %d (%s) opening %s",errno,STRERROR(errno),path);
705
706
			return(-1);
		}
707
	}
708
709
	JS_ClearPendingException(js_cx);

710
711
712
713
714
715
716
717
	if(stack_limit) {
#if JS_STACK_GROWTH_DIRECTION > 0
		stack_frame=((ulong)&stack_frame)+stack_limit;
#else
		stack_frame=((ulong)&stack_frame)-stack_limit;
#endif
		JS_SetThreadStackLimit(js_cx, stack_frame);
	}
718
719

	argv=JS_NewArrayObject(js_cx, 0, NULL);
720
	JS_DefineProperty(js_cx, js_glob, "argv", OBJECT_TO_JSVAL(argv)
721
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
722
723
724
725
726
727
728
729
730

	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;
	}
731
	JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc)
732
733
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

734
735
736
737
	JS_DefineProperty(js_cx, js_glob, "jsexec_revision"
		,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,revision))
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);

738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
	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);

753
754
	branch.terminated=&terminated;

755
756
	JS_SetBranchCallback(js_cx, js_BranchCallback);

757
	if(fp==stdin) 	 /* Using stdin for script source */
758
		SAFECOPY(path,"stdin");
759

760
761
762
	fprintf(statfp,"Reading script from %s\n",path);
	line_no=0;
	js_buflen=0;
763
764
	while(!feof(fp)) {
		if(!fgets(line,sizeof(line),fp))
765
766
			break;
		line_no++;
767
#if defined(__unix__)	/* Support Unix Shell Scripts that start with #!/path/to/jsexec */
768
		if(line_no==1 && strncmp(line,"#!",2)==0)
769
			strcpy(line,"\n");	/* To keep line count correct */
770
#endif
771
772
		len=strlen(line);
		if((js_buf=realloc(js_buf,js_buflen+len))==NULL) {
773
			lprintf(LOG_ERR,"!Error allocating %u bytes of memory"
774
				,js_buflen+len);
775
776
			return(-1);
		}
777
778
		memcpy(js_buf+js_buflen,line,len);
		js_buflen+=len;
779
	}
780
781
782
	if(fp!=NULL && fp!=stdin)
		fclose(fp);

783
	start=xp_timer();
784
	if((js_script=JS_CompileScript(js_cx, js_glob, js_buf, js_buflen, fname==NULL ? NULL : path, 1))==NULL) {
785
		lprintf(LOG_ERR,"!Error compiling script from %s",path);
786
		return(-1);
787
	}
788
	if((diff=xp_timer()-start) > 0)
789
		mfprintf(statfp,"%s compiled in %.2Lf seconds"
790
			,path
791
			,diff);
792

793
	start=xp_timer();
794
	JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
795
796
	js_EvalOnExit(js_cx, js_glob, &branch);

797
	if((diff=xp_timer()-start) > 0)
798
		mfprintf(statfp,"%s executed in %.2Lf seconds"
799
			,path
800
			,diff);
801

802
803
	JS_GetProperty(js_cx, js_glob, "exit_code", &rval);

804
	if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval)) {
805
		mfprintf(statfp,"Using JavaScript exit_code: %s",JS_GetStringBytes(JS_ValueToString(js_cx,rval)));
806
807
		JS_ValueToInt32(js_cx,rval,&result);
	}
808
809
810
811
812

	JS_DestroyScript(js_cx, js_script);

	JS_GC(js_cx);

813
814
815
	if(js_buf!=NULL)
		free(js_buf);

rswindell's avatar
rswindell committed
816
	return(result);
817
818
}

deuce's avatar
deuce committed
819
void break_handler(int type)
820
{
821
	lprintf(LOG_NOTICE,"\n-> Terminated Locally (signal: %d)",type);
822
823
824
	terminated=TRUE;
}

825
826
void recycle_handler(int type)
{
827
	lprintf(LOG_NOTICE,"\n-> Recycled Locally (signal: %d)",type);
828
829
830
831
832
	recycled=TRUE;
	branch.terminated=&recycled;
}


833
834
835
#if defined(_WIN32)
BOOL WINAPI ControlHandler(DWORD CtrlType)
{
deuce's avatar
deuce committed
836
	break_handler((int)CtrlType);
837
838
839
840
	return TRUE;
}
#endif

841
842
843
/*********************/
/* Entry point (duh) */
/*********************/
rswindell's avatar
rswindell committed
844
int main(int argc, char **argv, char** environ)
845
{
rswindell's avatar
rswindell committed
846
847
848
	char	error[512];
	char*	module=NULL;
	char*	p;
rswindell's avatar
rswindell committed
849
	char*	omode="w";
rswindell's avatar
rswindell committed
850
	int		argn;
851
	long	result;
852
	ulong	exec_count=0;
853
	BOOL	loop=FALSE;
854
	BOOL	nonbuffered_con=FALSE;
855

rswindell's avatar
rswindell committed
856
857
	confp=stdout;
	errfp=stderr;
rswindell's avatar
rswindell committed
858
859
	if((nulfp=fopen(_PATH_DEVNULL,"w+"))==NULL) {
		perror(_PATH_DEVNULL);
860
		return(do_bail(-1));
rswindell's avatar
rswindell committed
861
	}
862
863
864
865
	if(isatty(fileno(stderr)))
		statfp=stderr;
	else	/* if redirected, don't send status messages to stderr */
		statfp=nulfp;
rswindell's avatar
rswindell committed
866

867
	branch.limit=JAVASCRIPT_BRANCH_LIMIT;
868
869
	branch.yield_interval=JAVASCRIPT_YIELD_INTERVAL;
	branch.gc_interval=JAVASCRIPT_GC_INTERVAL;
870
	branch.auto_terminate=TRUE;
871

872
	sscanf("$Revision$", "%*s %s", revision);
873
	DESCRIBE_COMPILER(compiler);
874
875
876
877

	memset(&scfg,0,sizeof(scfg));
	scfg.size=sizeof(scfg);

878
	if(!winsock_startup())
879
		return(do_bail(2));
880

881
	for(argn=1;argn<argc && module==NULL;argn++) {
rswindell's avatar
rswindell committed
882
		if(argv[argn][0]=='-') {
883
			p=argv[argn]+2;
rswindell's avatar
rswindell committed
884
			switch(argv[argn][1]) {
rswindell's avatar
rswindell committed
885
886
887
				case 'a':
					omode="a";
					break;
888
889
890
				case 'f':
					nonbuffered_con=TRUE;
					break;
rswindell's avatar
rswindell committed
891
				case 'm':
892
893
					if(*p==0) p=argv[++argn];
					js_max_bytes=strtoul(p,NULL,0);
rswindell's avatar
rswindell committed
894
895
					break;
				case 's':
896
897
					if(*p==0) p=argv[++argn];
					js_cx_stack=strtoul(p,NULL,0);
rswindell's avatar
rswindell committed
898
					break;
899
900
901
902
				case 'S':
					if(*p==0) p=argv[++argn];
					stack_limit=strtoul(p,NULL,0);
					break;
903
				case 'b':
904
905
					if(*p==0) p=argv[++argn];
					branch.limit=strtoul(p,NULL,0);
906
907
					break;
				case 'y':
908
909
					if(*p==0) p=argv[++argn];
					branch.yield_interval=strtoul(p,NULL,0);
910
911
					break;
				case 'g':
912
913
					if(*p==0) p=argv[++argn];
					branch.gc_interval=strtoul(p,NULL,0);
914
					break;
915
				case 'h':
916
917
918
919
					if(*p==0)
						gethostname(host_name=host_name_buf,sizeof(host_name_buf));
					else
						host_name=p;
920
					break;
921
922
923
924
				case 'u':
					if(*p==0) p=argv[++argn];
					umask(strtol(p,NULL,8));
					break;
925
				case 'L':
926
					if(*p==0) p=argv[++argn];
927
					log_level=strtol(p,NULL,0);
928
929
930
931
					break;
				case 'E':
					if(*p==0) p=argv[++argn];
					err_level=strtol(p,NULL,0);
932
					break;
rswindell's avatar
rswindell committed
933
				case 'e':
934
					if(*p==0) p=argv[++argn];
rswindell's avatar
rswindell committed
935
					if((errfp=fopen(p,omode))==NULL) {
936
						perror(p);
937
						return(do_bail(1));
938
939
940
941
					}
					break;
				case 'o':
					if(*p==0) p=argv[++argn];
rswindell's avatar
rswindell committed
942
					if((confp=fopen(p,omode))==NULL) {
943
						perror(p);
944
						return(do_bail(1));
945
					}
946
947
					break;
				case 'q':
948
					confp=nulfp;
949
					break;
950
951
952
				case 'n':
					statfp=nulfp;
					break;
953
				case 'x':
954
					branch.auto_terminate=FALSE;
955
					break;
956
957
958
				case 'l':
					loop=TRUE;
					break;
959
960
				case 'p':
					pause_on_exit=TRUE;
rswindell's avatar
rswindell committed
961
					break;
rswindell's avatar
rswindell committed
962
963
964
				case '!':
					pause_on_error=TRUE;
					break;
965
				case 'c':
966
967
					if(*p==0) p=argv[++argn];
					SAFECOPY(scfg.ctrl_dir,p);
968
					break;
969
970
971
				case 'v':
					banner(statfp);
					fprintf(statfp,"%s\n",(char *)JS_GetImplementationVersion());