js_file.c 80.3 KB
Newer Older
1
2
3
4
5
6
/* Synchronet JavaScript "File" Object */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
23
#include "xpendian.h"
24
25
#include "md5.h"
#include "base64.h"
26
#include "uucode.h"
27
#include "yenc.h"
28
#include "ini_file.h"
29
#include <stdbool.h>
30

31
32
33
34
#if !defined(__unix__)
	#include <conio.h>		/* for kbhit() */
#endif

35
36
#ifdef JAVASCRIPT

37
#include "js_request.h"
38

39
40
typedef struct
{
41
	FILE*	fp;
42
43
	char	name[MAX_PATH+1];
	char	mode[4];
44
	uchar	etx;
45
	BOOL	debug;
46
	BOOL	rot13;
47
	BOOL	yencoded;
48
	BOOL	uuencoded;
49
50
	BOOL	b64encoded;
	BOOL	network_byte_order;
51
	BOOL	pipe;		/* Opened with popen() use pclose() to close */
52
	ini_style_t ini_style;
53
54
55
56
57
58
59
60
61
62
63
64

} private_t;

static void dbprintf(BOOL error, private_t* p, char* fmt, ...)
{
	va_list argptr;
	char sbuf[1024];

	if(p==NULL || (!p->debug && !error))
		return;

    va_start(argptr,fmt);
65
66
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
67
    va_end(argptr);
68

69
	lprintf(LOG_DEBUG,"%04d File %s%s",p->fp ? fileno(p->fp) : 0,error ? "ERROR: ":"",sbuf);
70
71
}

72
73
74
75
76
77
78
/* Converts fopen() style 'mode' string into open() style 'flags' integer */
static int fopenflags(char *mode)
{
	int flags=0;

	if(strchr(mode,'b'))
		flags|=O_BINARY;
79
80
	else
		flags|=O_TEXT;
81

82
83
84
	if(strchr(mode,'x'))
		flags|=O_EXCL;

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
	if(strchr(mode,'w')) {
		flags|=O_CREAT|O_TRUNC;
		if(strchr(mode,'+'))
			flags|=O_RDWR;
		else
			flags|=O_WRONLY;
		return(flags);
	}

	if(strchr(mode,'a')) {
		flags|=O_CREAT|O_APPEND;
		if(strchr(mode,'+'))
			flags|=O_RDWR;
		else
			flags|=O_WRONLY;
		return(flags);
	}

	if(strchr(mode,'r')) {
		if(strchr(mode,'+'))
			flags|=O_RDWR;
		else
			flags|=O_RDONLY;
	}

	return(flags);
}
112
113
114

/* File Object Methods */

115
extern JSClass js_file_class;
116
static JSBool
117
js_open(JSContext *cx, uintN argc, jsval *arglist)
118
{
119
120
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
121
	BOOL		shareable=FALSE;
122
	int			file = -1;
123
	uintN		i;
124
	jsint		bufsize=2*1024;
125
126
	JSString*	str;
	private_t*	p;
deuce's avatar
deuce committed
127
	jsrefcount	rc;
128

129
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
130
		return(JS_FALSE);
131
	}
132

133
134
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);

135
	if(p->fp!=NULL)
136
		return(JS_TRUE);
137

138
	SAFECOPY(p->mode,"w+");		/* default mode */
139
140
141
	for(i=0;i<argc;i++) {
		if(JSVAL_IS_STRING(argv[i])) {	/* mode */
			if((str = JS_ValueToString(cx, argv[i]))==NULL) {
142
143
				JS_ReportError(cx,"Invalid mode specified: %s",str);
				return(JS_TRUE);
144
			}
145
146
147
			JSSTRING_TO_STRBUF(cx, str, p->mode, sizeof(p->mode), NULL);
		}
		else if(JSVAL_IS_BOOLEAN(argv[i]))	/* shareable */
148
			shareable=JSVAL_TO_BOOLEAN(argv[i]);
149
150
151
152
		else if(JSVAL_IS_NUMBER(argv[i])) {	/* bufsize */
			if(!JS_ValueToInt32(cx,argv[i],&bufsize))
				return(JS_FALSE);
		}
153
154
	}

155
	rc=JS_SUSPENDREQUEST(cx);
156
157
158
159
	if(shareable)
		p->fp=fopen(p->name,p->mode);
	else {
		if((file=nopen(p->name,fopenflags(p->mode)))!=-1) {
160
161
162
163
			char *fdomode=strdup(p->mode);
			char *e=fdomode;

			if(fdomode && e) {
164
165
				/* Remove deprecated (never-worked, non-standard) 'e'xclusive mode char (and warn): */
				for(e=strchr(fdomode, 'e'); e ; e=strchr(e, 'e')) {
166
					JS_ReportWarning(cx, "Deprecated file open mode: 'e'");
167
168
169
170
					memmove(e, e+1, strlen(e));
				}
				/* Remove (C11 standard) 'x'clusive mode char to avoid MSVC assertion: */
				for(e=strchr(fdomode, 'x'); e ; e=strchr(e, 'x'))
171
					memmove(e, e+1, strlen(e));
172
173
174
				if((p->fp=fdopen(file,fdomode)) == NULL) {
					JS_ReportWarning(cx, "fdopen(%s, %s) ERROR %d: %s", p->name, fdomode, errno, strerror(errno));
				}
175
176
			}
			free(fdomode);
177
178
		}
	}
179
	if(p->fp!=NULL) {
180
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
181
		dbprintf(FALSE, p, "opened: %s",p->name);
182
183
		if(!bufsize)
			setvbuf(p->fp,NULL,_IONBF,0);	/* no buffering */
184
185
186
187
188
		else {
#ifdef _WIN32
			if(bufsize < 2)
				bufsize = 2;
#endif
189
			setvbuf(p->fp,NULL,_IOFBF,bufsize);
190
		}
191
192
	} else if(file >= 0)
		close(file);
193
	JS_RESUMEREQUEST(cx, rc);
194
195
196
197

	return(JS_TRUE);
}

198
static JSBool
199
js_popen(JSContext *cx, uintN argc, jsval *arglist)
200
{
201
202
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
203
204
205
206
	uintN		i;
	jsint		bufsize=2*1024;
	JSString*	str;
	private_t*	p;
deuce's avatar
deuce committed
207
	jsrefcount	rc;
208

209
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
210

211
212
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
		
213
214
215
		return(JS_FALSE);
	}

216
	if(p->fp!=NULL)
217
218
		return(JS_TRUE);

219
	SAFECOPY(p->mode,"r+");	/* default mode */
220
221
222
223
224
225
	for(i=0;i<argc;i++) {
		if(JSVAL_IS_STRING(argv[i])) {	/* mode */
			if((str = JS_ValueToString(cx, argv[i]))==NULL) {
				JS_ReportError(cx,"Invalid mode specified: %s",str);
				return(JS_TRUE);
			}
226
			JSSTRING_TO_STRBUF(cx, str, p->mode, sizeof(p->mode), NULL);
227
228
229
230
231
232
233
		}
		else if(JSVAL_IS_NUMBER(argv[i])) {	/* bufsize */
			if(!JS_ValueToInt32(cx,argv[i],&bufsize))
				return(JS_FALSE);
		}
	}

234
	rc=JS_SUSPENDREQUEST(cx);
235
236
237
	p->fp=popen(p->name,p->mode);
	if(p->fp!=NULL) {
		p->pipe=TRUE;
238
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
239
240
241
242
243
244
		dbprintf(FALSE, p, "popened: %s",p->name);
		if(!bufsize)
			setvbuf(p->fp,NULL,_IONBF,0);	/* no buffering */
		else
			setvbuf(p->fp,NULL,_IOFBF,bufsize);
	}
245
	JS_RESUMEREQUEST(cx, rc);
246
247
248

	return(JS_TRUE);
}
249
250

static JSBool
251
js_close(JSContext *cx, uintN argc, jsval *arglist)
252
{
253
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
254
	private_t*	p;
deuce's avatar
deuce committed
255
	jsrefcount	rc;
256

257
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
258
		return(JS_FALSE);
259
	}
260

261
262
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

263
264
265
	if(p->fp==NULL)
		return(JS_TRUE);

266
	rc=JS_SUSPENDREQUEST(cx);
267
268
269
270
271
272
#ifdef __unix__
	if(p->pipe)
		pclose(p->fp);
	else
#endif
		fclose(p->fp);
273

274
	p->fp=NULL;
275
276
	dbprintf(FALSE, p, "closed: %s", p->name);

277
	JS_RESUMEREQUEST(cx, rc);
278
279
280
281

	return(JS_TRUE);
}

282
283
284
285
286
287
288
289
290
static JSBool
js_raw_pollin(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	private_t*	p;
	jsrefcount	rc;
	int32		timeout = -1;

291
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
292
293
294
295
296
297
		return(JS_FALSE);
	}

	if(p->fp==NULL)
		return(JS_TRUE);

298
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
299
300
301
302
303
304
305
		if(!JS_ValueToInt32(cx,argv[0],&timeout))
			return(JS_FALSE);
	}

	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(FALSE));
	rc=JS_SUSPENDREQUEST(cx);
#ifdef __unix__
Deucе's avatar
Deucе committed
306
307
308
309
310
	/*
	 * TODO: macOS poll() page has the ominous statement that "The poll() system call currently does not support devices."
	 *       But, since we don't support OS X in Synchronet that's likely OK?
	 */
	if (socket_readable(fileno(p->fp), timeout))
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE));
#else
	while(timeout) {
		if (isatty(fileno(p->fp))) {
			if (kbhit()) {
				JS_RESUMEREQUEST(cx, rc);
				JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE));
				rc=JS_SUSPENDREQUEST(cx);
				break;
			}
			SLEEP(1);
			if (timeout > 0)
				timeout--;
		}
		else {
			if (!eof(fileno(p->fp))) {
				JS_RESUMEREQUEST(cx, rc);
				JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE));
				rc=JS_SUSPENDREQUEST(cx);
				break;
			}
			SLEEP(1);
			if (timeout > 0)
				timeout--;
		}
	}
#endif
	JS_RESUMEREQUEST(cx, rc);
	return JS_TRUE;
}

342
343
344
345
346
347
348
349
350
351
static JSBool
js_raw_read(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	char*		buf;
	int32		len;
	JSString*	str;
	private_t*	p;
	jsrefcount	rc;
352
353
	int		fd;
	off_t		pos;
354
355
356

	JS_SET_RVAL(cx, arglist, JSVAL_NULL);

357
358
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
		
359
360
361
362
363
364
		return(JS_FALSE);
	}

	if(p->fp==NULL)
		return(JS_TRUE);

365
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
366
367
368
369
370
371
372
373
374
375
376
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return(JS_FALSE);
	} else
		len = 1;
	if(len<0)
		len=1;

	if((buf=malloc(len))==NULL)
		return(JS_TRUE);

	rc=JS_SUSPENDREQUEST(cx);
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
	// https://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_05.html#tag_02_05_01
	/* For the first handle, the first applicable condition below applies. After the actions
	 * required below are taken, if the handle is still open, the application can close it.
	 *   If it is a file descriptor, no action is required.
	 *   If the only further action to be performed on any handle to this open file descriptor
         *      is to close it, no action need be taken.
	 *   If it is a stream which is unbuffered, no action need be taken.
	 *   If it is a stream which is line buffered, and the last byte written to the stream was a
	 *      <newline> (that is, as if a: putc('\n') was the most recent operation on that stream),
	 *      no action need be taken.
	 *   If it is a stream which is open for writing or appending (but not also open for reading),
	 *      the application shall either perform an fflush(), or the stream shall be closed.
	 *   If the stream is open for reading and it is at the end of the file ( feof() is true), no
	 *      action need be taken.
	 *   If the stream is open with a mode that allows reading and the underlying open file
	 *      description refers to a device that is capable of seeking, the application shall
	 *      either perform an fflush(), or the stream shall be closed.
	 * Otherwise, the result is undefined.
	 * For the second handle:
	 *   If any previous active handle has been used by a function that explicitly changed the file
	 *      offset, except as required above for the first handle, the application shall perform an
	 *      lseek() or fseek() (as appropriate to the type of handle) to an appropriate location.
	 */
	/*
	 * Since we don't want to overcomplicate this, it basically boils down to:
	 * Call fflush() on the stream, lseek() on the descriptor, diddle the descriptor, then fseek() the
	 * stream.
	 *
	 * The only option bit is the fflush() on the stream, but it never hurts and is sometimes
	 * required by POSIX.
	 */
	fflush(p->fp);
	pos = ftell(p->fp);
	fd = fileno(p->fp);
	lseek(fd, pos, SEEK_SET);
412
	len = read(fileno(p->fp),buf,len);
413
	fseek(p->fp, pos + (len >= 0 ? len : 0), SEEK_SET);
414
	dbprintf(FALSE, p, "read %u raw bytes",len);
415
	if(len<0)
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
		len=0;

	JS_RESUMEREQUEST(cx, rc);

	str = JS_NewStringCopyN(cx, buf, len);
	free(buf);

	if(str==NULL)
		return(JS_FALSE);

	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));

	return(JS_TRUE);
}


432
static JSBool
433
js_read(JSContext *cx, uintN argc, jsval *arglist)
434
{
435
436
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
437
	char*		cp;
438
	char*		buf;
439
	char*		uubuf;
440
441
	int32		len;
	int32		offset;
442
	int32		uulen;
443
444
	JSString*	str;
	private_t*	p;
deuce's avatar
deuce committed
445
	jsrefcount	rc;
446

447
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
448

449
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
450
		return(JS_FALSE);
451
	}
452

453
454
	if(p->fp==NULL)
		return(JS_TRUE);
455

456
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
457
458
459
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return(JS_FALSE);
	} else {
460
		rc=JS_SUSPENDREQUEST(cx);
461
462
		len=(long)filelength(fileno(p->fp));
		offset=(long)ftell(p->fp);
463
464
		if(offset>0)
			len-=offset;
465
		JS_RESUMEREQUEST(cx, rc);
466
467
468
	}
	if(len<0)
		len=512;
469

470
	if((buf=malloc(len+1))==NULL)
471
472
		return(JS_TRUE);

473
	rc=JS_SUSPENDREQUEST(cx);
474
	len = fread(buf,1,len,p->fp);
475
	dbprintf(FALSE, p, "read %u bytes",len);
476
	if(len<0)
477
478
479
480
481
		len=0;
	buf[len]=0;

	if(p->etx) {
		cp=strchr(buf,p->etx);
482
		if(cp) *cp=0;
483
		len=strlen(buf);
484
485
	}

486
487
488
	if(p->rot13)
		rot13(buf);

489
	if(p->uuencoded || p->b64encoded || p->yencoded) {
490
		uulen=len*2;
deuce's avatar
deuce committed
491
		if((uubuf=malloc(uulen))==NULL) {
492
			free(buf);
493
			JS_RESUMEREQUEST(cx, rc);
494
			return(JS_TRUE);
495
		}
496
497
		if(p->uuencoded)
			uulen=uuencode(uubuf,uulen,buf,len);
498
499
		else if(p->yencoded)
			uulen=yencode(uubuf,uulen,buf,len);
500
501
		else
			uulen=b64_encode(uubuf,uulen,buf,len);
502
		if(uulen>=0) {
503
			free(buf);
504
			buf=uubuf;
505
			len=uulen;
deuce's avatar
deuce committed
506
		}
507
508
		else
			free(uubuf);
509
	}
510
	JS_RESUMEREQUEST(cx, rc);
511

512
	str = JS_NewStringCopyN(cx, buf, len);
513
	free(buf);
514

515
516
517
	if(str==NULL)
		return(JS_FALSE);

518
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
519

520
521
522
523
	return(JS_TRUE);
}

static JSBool
524
js_readln(JSContext *cx, uintN argc, jsval *arglist)
525
{
526
527
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
528
529
	char*		cp;
	char*		buf;
530
	int32		len=512;
531
	JSString*	js_str;
532
	private_t*	p;
deuce's avatar
deuce committed
533
	jsrefcount	rc;
534

535
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
536

537
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
538
		return(JS_FALSE);
539
	}
540
541
542

	if(p->fp==NULL)
		return(JS_TRUE);
543

544
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
545
546
547
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return(JS_FALSE);
	}
548

549
	if((buf=malloc(len + 1))==NULL)
deuce's avatar
deuce committed
550
		return(JS_FALSE);
551

552
	rc=JS_SUSPENDREQUEST(cx);
553
	if(fgets(buf,len + 1,p->fp)!=NULL) {
554
555
556
557
		len=strlen(buf);
		while(len>0 && (buf[len-1]=='\r' || buf[len-1]=='\n'))
			len--;
		buf[len]=0;
558
559
		if(p->etx) {
			cp=strchr(buf,p->etx);
560
			if(cp) *cp=0;
561
		}
562
563
		if(p->rot13)
			rot13(buf);
564
		JS_RESUMEREQUEST(cx, rc);
565
		if((js_str=JS_NewStringCopyZ(cx,buf))!=NULL)	/* exception here Feb-12-2005 */
566
			JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
567
568
	} else {
		JS_RESUMEREQUEST(cx, rc);
569
	}
deuce's avatar
deuce committed
570
	free(buf);
571
572
573
574
575

	return(JS_TRUE);
}

static JSBool
576
js_readbin(JSContext *cx, uintN argc, jsval *arglist)
577
{
578
579
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
580
581
582
	BYTE		*b;
	WORD		*w;
	DWORD		*l;
583
	int32		size=sizeof(DWORD);
584
	private_t*	p;
585
	int32		count=1;
586
587
	size_t		retlen;
	void		*buffer=NULL;
rswindell's avatar
rswindell committed
588
	size_t		i;
589
590
    JSObject*	array;
    jsval       v;
deuce's avatar
deuce committed
591
	jsrefcount	rc;
592

593
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
594

595
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
596
		return(JS_FALSE);
597
	}
598
599
600
601

	if(p->fp==NULL)
		return(JS_TRUE);

602
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
603
		if(!JS_ValueToInt32(cx,argv[0],&size))
604
			return(JS_FALSE);
605
		if(argc>1 && !JSVAL_NULL_OR_VOID(argv[1])) {
606
			if(!JS_ValueToInt32(cx,argv[1],&count))
607
608
				return(JS_FALSE);
		}
609
	}
610

611
	rc=JS_SUSPENDREQUEST(cx);
612
613
614
	if(size != sizeof(BYTE) && size != sizeof(WORD) && size != sizeof(DWORD)) {
		/* unknown size */
		dbprintf(TRUE, p, "unsupported binary read size: %d",size);
615
		JS_RESUMEREQUEST(cx, rc);
616
617
618
619
620
621
		return(JS_TRUE);
	}

	buffer=malloc(size*count);
	if(buffer==NULL) {
		dbprintf(TRUE, p, "malloc failure of %u bytes", size*count);
622
		JS_RESUMEREQUEST(cx, rc);
623
624
625
626
627
628
629
630
631
632
		return(JS_FALSE);
	}
	b=buffer;
	w=buffer;
	l=buffer;
	retlen=fread(buffer, size, count, p->fp);
	if(count==1) {
		if(retlen==1) {
			switch(size) {
				case sizeof(BYTE):
633
					JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*b));
634
635
					break;
				case sizeof(WORD):
636
637
638
639
					if (p->network_byte_order)
						*w = BE_SHORT(*w);
					else
						*w = LE_SHORT(*w);
640
					JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*w));
641
642
					break;
				case sizeof(DWORD):
643
644
645
646
					if (p->network_byte_order)
						*l = BE_LONG(*l);
					else
						*l = LE_LONG(*l);
647
					JS_SET_RVAL(cx, arglist, UINT_TO_JSVAL(*l));
648
					break;
649
			}
650
651
652
		}
	}
	else {
653
		JS_RESUMEREQUEST(cx, rc);
654
655
656
657
658
659
660
661
    	array = JS_NewArrayObject(cx, 0, NULL);

		for(i=0; i<retlen; i++) {
			switch(size) {
				case sizeof(BYTE):
					v = INT_TO_JSVAL(*(b++));
					break;
				case sizeof(WORD):
662
663
664
665
					if (p->network_byte_order)
						*w = BE_SHORT(*w);
					else
						*w = LE_SHORT(*w);
666
667
668
					v = INT_TO_JSVAL(*(w++));
					break;
				case sizeof(DWORD):
669
670
671
672
					if (p->network_byte_order)
						*l = BE_LONG(*l);
					else
						*l = LE_LONG(*l);
673
					v=UINT_TO_JSVAL(*(l++));
674
					break;
675
			}
deuce's avatar
deuce committed
676
        	if(!JS_SetElement(cx, array, i, &v)) {
677
				rc=JS_SUSPENDREQUEST(cx);
678
				goto end;
deuce's avatar
deuce committed
679
			}
680
		}
681
    	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
682
	}
683
684
685

end:
	free(buffer);
686
	JS_RESUMEREQUEST(cx, rc);
687
688
689
690
	return(JS_TRUE);
}

static JSBool
691
js_readall(JSContext *cx, uintN argc, jsval *arglist)
692
{
693
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
694
695
    jsint       len=0;
    JSObject*	array;
696
697
	private_t*	p;

698
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
699

700
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
701
		return(JS_FALSE);
702
	}
703

704
705
	if(p->fp==NULL)
		return(JS_TRUE);
706

707
    array = JS_NewArrayObject(cx, 0, NULL);
708

709
    while(!feof(p->fp)) {
710
711
		js_readln(cx, argc, arglist);
		if(JS_RVAL(cx, arglist)==JSVAL_NULL)
712
			break;
713
        if(!JS_SetElement(cx, array, len++, &JS_RVAL(cx, arglist)))
714
			break;
715
	}
716
    JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
717

718
719
    return(JS_TRUE);
}
720

721
static jsval get_value(JSContext *cx, char* value, bool blanks)
722
723
724
725
726
{
	char*	p;
	BOOL	f=FALSE;
	jsval	val;

727
	if(value==NULL || (*value==0 && !blanks))
728
729
730
731
		return(JSVAL_VOID);

	/* integer or float? */
	for(p=value;*p;p++) {
732
		if(*p=='.' && !f)
733
			f=TRUE;
734
		else if(!IS_DIGIT(*p))
735
736
			break;
	}
737
	if(p!=value && *p==0) {
738
739
740
741
		if(f)
			val=DOUBLE_TO_JSVAL(atof(value));
		else
			val=DOUBLE_TO_JSVAL((double)strtoul(value,NULL,10));
742
743
744
		return(val);
	}
	/* hexadecimal number? */
745
	if(!strncmp(value,"0x",2)) {
746
		for(p=value+2;*p;p++)
747
			if(!isxdigit((uchar)*p))
748
				break;
749
		if(*p==0) {
750
			val=DOUBLE_TO_JSVAL((double)strtoul(value,NULL,0));
751
752
753
754
755
756
757
758
759
760
761
762
763
			return(val);
		}
	}
	/* Boolean? */
	if(!stricmp(value,"true"))
		return(JSVAL_TRUE);
	if(!stricmp(value,"false"))
		return(JSVAL_FALSE);

	/* String */
	return(STRING_TO_JSVAL(JS_NewStringCopyZ(cx,value)));
}

deuce's avatar
deuce committed
764
765
766
767
static double js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
{
	jsval	rval;

deuce's avatar
deuce committed
768
	if(!JS_CallFunctionName(cx, obj, "getTime", 0, NULL, &rval)) {
deuce's avatar
deuce committed
769
		return ((double)time(NULL))*1000;
deuce's avatar
deuce committed
770
	}
deuce's avatar
deuce committed
771
772
773
	return JSVAL_TO_DOUBLE(rval);
}

774
static JSBool
775
js_iniGetValue(JSContext *cx, uintN argc, jsval *arglist)
776
{
777
778
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
779
	char*	section=ROOT_SECTION;
780
	char*	key;
781
	char**	list;
782
	char	buf[INI_MAX_VALUE_LEN];
783
784
	int32	i;
	jsval	val;
785
786
	jsval	dflt=argv[2];
	private_t*	p;
787
	JSObject*	array;
788
789
	JSObject*	dflt_obj;
	JSObject*	date_obj;
deuce's avatar
deuce committed
790
791
792
	jsrefcount	rc;
	double		dbl;
	time_t		tt;
793
	char*		cstr=NULL;
deuce's avatar
deuce committed
794
	char*		cstr2;
795

796
797
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

798
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
799
800
801
		return(JS_FALSE);
	}

802
	if(argc && argv[0]!=JSVAL_VOID && argv[0]!=JSVAL_NULL)
803
804
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
	JSVALUE_TO_MSTRING(cx, argv[1], key, NULL);
805
	if(JS_IsExceptionPending(cx)) {
806
807
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
deuce's avatar
deuce committed
808
		return JS_FALSE;
809
	}
810
811
812
813
814
815
	/*
	 * Although section can be NULL (ie: root), a NULL key will cause a
	 * segfault.
	 */
	if(key==NULL) {
		JS_ReportError(cx, "Invalid NULL key specified");
816
		FREE_AND_NULL(section);
817
818
		return JS_FALSE;
	}
819

820
	str_list_t ini = iniReadFile(p->fp);
821
	if(argc < 3 || dflt==JSVAL_VOID) {	/* unspecified default value */
822
		rc=JS_SUSPENDREQUEST(cx);
823
		cstr=iniGetString(ini,section,key,NULL,buf);
824
825
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
826
		strListFree(&ini);
827
		JS_RESUMEREQUEST(cx, rc);
828
		JS_SET_RVAL(cx, arglist, get_value(cx, cstr, /* blanks */false));
829
830
831
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
832
833
	if(JSVAL_IS_BOOLEAN(dflt)) {
		JS_SET_RVAL(cx,arglist,BOOLEAN_TO_JSVAL(
834
			iniGetBool(ini,section,key,JSVAL_TO_BOOLEAN(dflt))));
deuce's avatar
deuce committed
835
836
837
838
	}
	else if(JSVAL_IS_OBJECT(dflt)) {
		if((dflt_obj = JSVAL_TO_OBJECT(dflt))!=NULL && (strcmp("Date",JS_GetClass(cx, dflt_obj)->name)==0)) {
			tt=(time_t)(js_DateGetMsecSinceEpoch(cx,dflt_obj)/1000.0);
839
			rc=JS_SUSPENDREQUEST(cx);
840
			dbl=(double)iniGetDateTime(ini,section,key,tt);
deuce's avatar
deuce committed
841
			dbl *= 1000;
842
			JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
843
844
			date_obj = JS_NewDateObjectMsec(cx, dbl);
			if(date_obj!=NULL) {
deuce's avatar
deuce committed
845
				JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(date_obj));
deuce's avatar
deuce committed
846
			}
deuce's avatar
deuce committed
847
848
		}
		else {
849
		    array = JS_NewArrayObject(cx, 0, NULL);
850
			cstr=NULL;
851
			JSVALUE_TO_MSTRING(cx, dflt, cstr, NULL);
852
			if(JS_IsExceptionPending(cx)) {
853
854
855
				FREE_AND_NULL(cstr);
				FREE_AND_NULL(section);
				FREE_AND_NULL(key);
856
				strListFree(&ini);
857
858
				return JS_FALSE;
			}
859
			rc=JS_SUSPENDREQUEST(cx);
860
			list=iniGetStringList(ini,section,key,",",cstr);
861
			FREE_AND_NULL(cstr);
862
			JS_RESUMEREQUEST(cx, rc);
863
864
865
866
867
			for(i=0;list && list[i];i++) {
				val=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,list[i]));
				if(!JS_SetElement(cx, array, i, &val))
					break;
			}
868
			rc=JS_SUSPENDREQUEST(cx);
869
			iniFreeStringList(list);
870
			JS_RESUMEREQUEST(cx, rc);
871
			JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
deuce's avatar
deuce committed
872
873
874
875
		}
	}
	else if(JSVAL_IS_DOUBLE(dflt)) {
		rc=JS_SUSPENDREQUEST(cx);
876
		dbl=iniGetFloat(ini,section,key,JSVAL_TO_DOUBLE(dflt));
deuce's avatar
deuce committed
877
878
879
880
		JS_RESUMEREQUEST(cx, rc);
		JS_SET_RVAL(cx, arglist,DOUBLE_TO_JSVAL(dbl));
	}
	else if(JSVAL_IS_NUMBER(dflt)) {
881
		if(!JS_ValueToInt32(cx,dflt,&i)) {
882
883
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
884
			strListFree(&ini);
deuce's avatar
deuce committed
885
			return(JS_FALSE);
886
		}
deuce's avatar
deuce committed
887
		rc=JS_SUSPENDREQUEST(cx);
888
		i=iniGetInteger(ini,section,key,i);
deuce's avatar
deuce committed
889
890
891
		JS_RESUMEREQUEST(cx, rc);
		JS_SET_RVAL(cx, arglist,INT_TO_JSVAL(i));
	} else {
892
		cstr=NULL;
893
		JSVALUE_TO_MSTRING(cx, dflt, cstr, NULL);
894
		if(JS_IsExceptionPending(cx)) {
895
896
897
			FREE_AND_NULL(cstr);
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
898
			strListFree(&ini);
899
900
			return JS_FALSE;
		}
deuce's avatar
deuce committed
901
		rc=JS_SUSPENDREQUEST(cx);
902
		cstr2=iniGetString(ini,section,key,cstr,buf);
deuce's avatar
deuce committed
903
904
		JS_RESUMEREQUEST(cx, rc);
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, cstr2)));
905
		FREE_AND_NULL(cstr);
906
	}
907
908
	FREE_AND_NULL(section);
	FREE_AND_NULL(key);
909
	strListFree(&ini);
910
911
912
913

	return(JS_TRUE);
}

914
static JSBool
915
js_iniSetValue_internal(JSContext *cx, JSObject *obj, uintN argc, jsval* argv, str_list_t* list)
916
{
rswindell's avatar
rswindell committed
917
	char*	section=ROOT_SECTION;
918
	char*	key=NULL;