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
291
292
293
294
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;
#ifdef __unix__
	fd_set		rd;
	struct	timeval tv = {0, 0};
#endif

295
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
296
297
298
299
300
301
		return(JS_FALSE);
	}

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

302
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
303
304
305
306
307
308
309
310
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
342
343
344
345
346
347
		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__
	if (timeout >= 0) {
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout%1000)*1000;
	}
	FD_ZERO(&rd);
	FD_SET(fileno(p->fp), &rd);
	if (select(fileno(p->fp)+1, &rd, NULL, NULL, timeout < 0 ? NULL : &tv) == 1)
		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;
}

348
349
350
351
352
353
354
355
356
357
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;
358
359
	int		fd;
	off_t		pos;
360
361
362

	JS_SET_RVAL(cx, arglist, JSVAL_NULL);

363
364
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
		
365
366
367
368
369
370
		return(JS_FALSE);
	}

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

371
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
372
373
374
375
376
377
378
379
380
381
382
		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);
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
412
413
414
415
416
417
	// 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);
418
	len = read(fileno(p->fp),buf,len);
419
	fseek(p->fp, pos + (len >= 0 ? len : 0), SEEK_SET);
420
	dbprintf(FALSE, p, "read %u raw bytes",len);
421
	if(len<0)
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
		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);
}


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

453
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
454

455
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
456
		return(JS_FALSE);
457
	}
458

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

462
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
463
464
465
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return(JS_FALSE);
	} else {
466
		rc=JS_SUSPENDREQUEST(cx);
467
468
		len=(long)filelength(fileno(p->fp));
		offset=(long)ftell(p->fp);
469
470
		if(offset>0)
			len-=offset;
471
		JS_RESUMEREQUEST(cx, rc);
472
473
474
	}
	if(len<0)
		len=512;
475

476
	if((buf=malloc(len+1))==NULL)
477
478
		return(JS_TRUE);

479
	rc=JS_SUSPENDREQUEST(cx);
480
	len = fread(buf,1,len,p->fp);
481
	dbprintf(FALSE, p, "read %u bytes",len);
482
	if(len<0)
483
484
485
486
487
		len=0;
	buf[len]=0;

	if(p->etx) {
		cp=strchr(buf,p->etx);
488
		if(cp) *cp=0;
489
		len=strlen(buf);
490
491
	}

492
493
494
	if(p->rot13)
		rot13(buf);

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

518
	str = JS_NewStringCopyN(cx, buf, len);
519
	free(buf);
520

521
522
523
	if(str==NULL)
		return(JS_FALSE);

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

526
527
528
529
	return(JS_TRUE);
}

static JSBool
530
js_readln(JSContext *cx, uintN argc, jsval *arglist)
531
{
532
533
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
534
535
	char*		cp;
	char*		buf;
536
	int32		len=512;
537
	JSString*	js_str;
538
	private_t*	p;
deuce's avatar
deuce committed
539
	jsrefcount	rc;
540

541
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
542

543
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
544
		return(JS_FALSE);
545
	}
546
547
548

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

550
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
551
552
553
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return(JS_FALSE);
	}
554

555
	if((buf=malloc(len + 1))==NULL)
deuce's avatar
deuce committed
556
		return(JS_FALSE);
557

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

	return(JS_TRUE);
}

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

599
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
600

601
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
602
		return(JS_FALSE);
603
	}
604
605
606
607

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

608
	if(argc && !JSVAL_NULL_OR_VOID(argv[0])) {
609
		if(!JS_ValueToInt32(cx,argv[0],&size))
610
			return(JS_FALSE);
611
		if(argc>1 && !JSVAL_NULL_OR_VOID(argv[1])) {
612
			if(!JS_ValueToInt32(cx,argv[1],&count))
613
614
				return(JS_FALSE);
		}
615
	}
616

617
	rc=JS_SUSPENDREQUEST(cx);
618
619
620
	if(size != sizeof(BYTE) && size != sizeof(WORD) && size != sizeof(DWORD)) {
		/* unknown size */
		dbprintf(TRUE, p, "unsupported binary read size: %d",size);
621
		JS_RESUMEREQUEST(cx, rc);
622
623
624
625
626
627
		return(JS_TRUE);
	}

	buffer=malloc(size*count);
	if(buffer==NULL) {
		dbprintf(TRUE, p, "malloc failure of %u bytes", size*count);
628
		JS_RESUMEREQUEST(cx, rc);
629
630
631
632
633
634
635
636
637
638
		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):
639
					JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*b));
640
641
					break;
				case sizeof(WORD):
642
643
644
645
					if (p->network_byte_order)
						*w = BE_SHORT(*w);
					else
						*w = LE_SHORT(*w);
646
					JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*w));
647
648
					break;
				case sizeof(DWORD):
649
650
651
652
					if (p->network_byte_order)
						*l = BE_LONG(*l);
					else
						*l = LE_LONG(*l);
653
					JS_SET_RVAL(cx, arglist, UINT_TO_JSVAL(*l));
654
					break;
655
			}
656
657
658
		}
	}
	else {
659
		JS_RESUMEREQUEST(cx, rc);
660
661
662
663
664
665
666
667
    	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):
668
669
670
671
					if (p->network_byte_order)
						*w = BE_SHORT(*w);
					else
						*w = LE_SHORT(*w);
672
673
674
					v = INT_TO_JSVAL(*(w++));
					break;
				case sizeof(DWORD):
675
676
677
678
					if (p->network_byte_order)
						*l = BE_LONG(*l);
					else
						*l = LE_LONG(*l);
679
					v=UINT_TO_JSVAL(*(l++));
680
					break;
681
			}
deuce's avatar
deuce committed
682
        	if(!JS_SetElement(cx, array, i, &v)) {
683
				rc=JS_SUSPENDREQUEST(cx);
684
				goto end;
deuce's avatar
deuce committed
685
			}
686
		}
687
    	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
688
	}
689
690
691

end:
	free(buffer);
692
	JS_RESUMEREQUEST(cx, rc);
693
694
695
696
	return(JS_TRUE);
}

static JSBool
697
js_readall(JSContext *cx, uintN argc, jsval *arglist)
698
{
699
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
700
701
    jsint       len=0;
    JSObject*	array;
702
703
	private_t*	p;

704
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
705

706
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
707
		return(JS_FALSE);
708
	}
709

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

713
    array = JS_NewArrayObject(cx, 0, NULL);
714

715
    while(!feof(p->fp)) {
716
717
		js_readln(cx, argc, arglist);
		if(JS_RVAL(cx, arglist)==JSVAL_NULL)
718
			break;
719
        if(!JS_SetElement(cx, array, len++, &JS_RVAL(cx, arglist)))
720
			break;
721
	}
722
    JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
723

724
725
    return(JS_TRUE);
}
726

727
static jsval get_value(JSContext *cx, char* value, bool blanks)
728
729
730
731
732
{
	char*	p;
	BOOL	f=FALSE;
	jsval	val;

733
	if(value==NULL || (*value==0 && !blanks))
734
735
736
737
		return(JSVAL_VOID);

	/* integer or float? */
	for(p=value;*p;p++) {
738
		if(*p=='.' && !f)
739
			f=TRUE;
740
		else if(!IS_DIGIT(*p))
741
742
			break;
	}
743
	if(p!=value && *p==0) {
744
745
746
747
		if(f)
			val=DOUBLE_TO_JSVAL(atof(value));
		else
			val=DOUBLE_TO_JSVAL((double)strtoul(value,NULL,10));
748
749
750
		return(val);
	}
	/* hexadecimal number? */
751
	if(!strncmp(value,"0x",2)) {
752
		for(p=value+2;*p;p++)
753
			if(!isxdigit((uchar)*p))
754
				break;
755
		if(*p==0) {
756
			val=DOUBLE_TO_JSVAL((double)strtoul(value,NULL,0));
757
758
759
760
761
762
763
764
765
766
767
768
769
			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
770
771
772
773
static double js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
{
	jsval	rval;

deuce's avatar
deuce committed
774
	if(!JS_CallFunctionName(cx, obj, "getTime", 0, NULL, &rval)) {
deuce's avatar
deuce committed
775
		return ((double)time(NULL))*1000;
deuce's avatar
deuce committed
776
	}
deuce's avatar
deuce committed
777
778
779
	return JSVAL_TO_DOUBLE(rval);
}

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

802
803
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

804
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
805
806
807
		return(JS_FALSE);
	}

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

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

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

	return(JS_TRUE);
}

920
static JSBool
921
js_iniSetValue_internal(JSContext *cx, JSObject *obj, uintN argc, jsval* argv, str_list_t* list)
922
{
rswindell's avatar
rswindell committed
923
	char*	section=ROOT_SECTION;
924
	char*	key=NULL;
925
926
927
928
	char*	result=NULL;
	int32	i;
	jsval	value=argv[2];
	private_t*	p;
929
	JSObject*	value_obj;
deuce's avatar
deuce committed
930
931
932
	jsrefcount	rc;
	char*		cstr;
	time_t		tt;
933

934
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
935
936
937
		return(JS_FALSE);
	}

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

941
	if(argc && argv[0]!=JSVAL_VOID && argv[0]!=JSVAL_NULL)
942
943
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
	JSVALUE_TO_MSTRING(cx, argv[1], key, NULL);
944
	if(JS_IsExceptionPending(cx)) {
945
946
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
947
948
		return JS_FALSE;
	}
949

deuce's avatar
deuce committed
950
	if(value==JSVAL_VOID) { 	/* unspecified value */
951
		rc=JS_SUSPENDREQUEST(cx);
952
		result = iniSetString(list,section,key,"",&p->ini_style);
953
		JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
954
	}
deuce's avatar
deuce committed
955
	else if(JSVAL_IS_BOOLEAN(value)) {
956
		result = iniSetBool(list,section,key,JSVAL_TO_BOOLEAN(value),&p->ini_style);
deuce's avatar
deuce committed
957
958
	}
	else if(JSVAL_IS_DOUBLE(value)) {
959
		result = iniSetFloat(list,section,key,JSVAL_TO_DOUBLE(value),&p->ini_style);
deuce's avatar
deuce committed
960
961
	}
	else if(JSVAL_IS_NUMBER(value)) {
962
		if(!JS_ValueToInt32(cx,value,&i)) {
963
964
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
965
966
			return JS_FALSE;
		}
deuce's avatar
deuce committed
967
		rc=JS_SUSPENDREQUEST(cx);
968
		result = iniSetInteger(list,section,key,i,&p->ini_style);
deuce's avatar
deuce committed
969
		JS_RESUMEREQUEST(cx, rc);
970
	} else if(JSVAL_IS_OBJECT(value)
deuce's avatar
deuce committed
971
972
973
974
			&& (value_obj = JSVAL_TO_OBJECT(value))!=NULL
			&& (strcmp("Date",JS_GetClass(cx, value_obj)->name)==0)) {
		tt=(time_t)(js_DateGetMsecSinceEpoch(cx,value_obj)/1000.0);
		rc=JS_SUSPENDREQUEST(cx);
975
		result = iniSetDateTime(list,section,key,/* include_time */TRUE, tt, &p->ini_style);
deuce's avatar
deuce committed
976
977
		JS_RESUMEREQUEST(cx, rc);
	} else {
978
		cstr=NULL;
979
		JSVALUE_TO_MSTRING(cx, value, cstr, NULL);
980
		if(JS_IsExceptionPending(cx)) {
981
982
983
			FREE_AND_NULL(cstr);
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
984
985
			return JS_FALSE;
		}
deuce's avatar
deuce committed
986
		rc=JS_SUSPENDREQUEST(cx);
987
		result = iniSetString(list,section,key, cstr, &p->ini_style);
988
		FREE_AND_NULL(cstr);
deuce's avatar
deuce committed
989
		JS_RESUMEREQUEST(cx, rc);
990
	}
991
992
	FREE_AND_NULL(section);
	FREE_AND_NULL(key);
993

994
	return(result != NULL);
995
996
}

deuce's avatar
deuce committed
997
998
999
1000
1001
static JSBool
js_iniSetValue(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
1002
1003
1004
1005
1006
	jsval	rval=JSVAL_FALSE;
	private_t*	p;
	str_list_t	list;
	jsrefcount	rc;

1007
	if((p=(private_t*)js_GetClassPrivate(cx, obj, &js_file_class))==NULL) {
1008
1009
1010
		return(JS_FALSE);
	}

1011
	if(p->fp==NULL)
1012
1013
1014
1015
1016
1017
1018
1019
1020
		return(JS_TRUE);

	rc=JS_SUSPENDREQUEST(cx);
	if((list=iniReadFile(p->fp)) != NULL) {
		if(js_iniSetValue_internal(cx, obj, argc, argv, &list))
			rval = BOOLEAN_TO_JSVAL(iniWriteFile(p->fp,list));
	}
	strListFree(&list);
	JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
1021
1022

	JS_SET_RVAL(cx, arglist, rval);
1023
	return JS_TRUE;
deuce's avatar
deuce committed
1024
1025
}

1026
static JSBool
1027
js_iniRemoveKey(JSContext *cx, uintN argc, jsval *arglist)
1028
{
1029
1030
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);