js_file.c 27.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* js_file.c */

/* Synchronet JavaScript "File" Object */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright 2001 Rob Swindell - http://www.synchro.net/copyright.html		*
 *																			*
 * 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.	*
 ****************************************************************************/

#include "sbbs.h"
39
40
#include "md5.h"
#include "base64.h"
41
42
43
44
45

#ifdef JAVASCRIPT

typedef struct
{
46
	FILE*	fp;
47
48
	char	name[MAX_PATH+1];
	char	mode[4];
49
	uchar	etx;
50
51
52
53
54
	BOOL	external;	/* externally created, don't close */
	BOOL	debug;

} private_t;

55
static const char* getprivate_failure = "line %d %s JS_GetPrivate failed";
56
57
58
59
60
61
62
63
64
65

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);
66
67
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
68
69
    va_end(argptr);
	
70
	lprintf("%04u File %s%s",p->fp ? fileno(p->fp) : 0,error ? "ERROR: ":"",sbuf);
71
72
}

73
74
75
76
77
78
79
80
/* 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;
81
82
	else
		flags|=O_TEXT;
83
84
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

	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);
}
111
112
113
114
115
116
117

/* File Object Methods */

static JSBool
js_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		mode="w+";	/* default mode */
118
119
	BOOL		shareable=FALSE;
	int			file;
120
	uintN		i;
121
	jsint		bufsize=2*1024;
122
123
124
	JSString*	str;
	private_t*	p;

125
	*rval = JSVAL_FALSE;
126

127
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
128
		JS_ReportError(cx,getprivate_failure,WHERE);
129
		return(JS_FALSE);
130
	}
131
132
133

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

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

149
150
151
152
153
154
155
156
	if(shareable)
		p->fp=fopen(p->name,p->mode);
	else {
		if((file=nopen(p->name,fopenflags(p->mode)))!=-1) {
			if((p->fp=fdopen(file,p->mode))==NULL)
				close(file);
		}
	}
157
	if(p->fp!=NULL) {
158
		*rval = JSVAL_TRUE;
159
		dbprintf(FALSE, p, "opened: %s",p->name);
160
161
162
163
164
		if(!bufsize)
			setvbuf(p->fp,NULL,_IONBF,0);	/* no buffering */
		else
			setvbuf(p->fp,NULL,_IOFBF,bufsize);
	}
165
166
167
168
169
170
171
172
173
174

	return(JS_TRUE);
}


static JSBool
js_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	private_t*	p;

175
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
176
		JS_ReportError(cx,getprivate_failure,WHERE);
177
		return(JS_FALSE);
178
	}
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194

	*rval = JSVAL_VOID;

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

	fclose(p->fp);

	dbprintf(FALSE, p, "closed");

	p->fp=NULL; 

	return(JS_TRUE);
}

static JSBool
195
js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
196
197
{
	char*		cp;
198
	char*		buf;
199
	int32		len=512;
200
201
202
	JSString*	str;
	private_t*	p;

203
204
	*rval = JSVAL_NULL;

205
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
206
		JS_ReportError(cx,getprivate_failure,WHERE);
207
		return(JS_FALSE);
208
	}
209

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

213
	if(argc)
214
		JS_ValueToInt32(cx,argv[0],&len);
215

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
	if((buf=malloc(len+1))==NULL)
		return(JS_TRUE);

	len = fread(buf,1,len,p->fp);
	if(len<0) 
		len=0;
	buf[len]=0;

	if(p->etx) {
		cp=strchr(buf,p->etx);
		if(cp) *cp=0; 
	}

	str = JS_NewStringCopyZ(cx, buf);

	free(buf);

233
234
235
	if(str==NULL)
		return(JS_FALSE);

236
237
238
	*rval = STRING_TO_JSVAL(str);

	dbprintf(FALSE, p, "read %u bytes",len);
239
240
241
242
243
		
	return(JS_TRUE);
}

static JSBool
244
js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
245
{
246
247
	char*		cp;
	char*		buf;
248
	int32		len=512;
249
	JSString*	js_str;
250
251
	private_t*	p;

252
	*rval = JSVAL_NULL;
253

254
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
255
		JS_ReportError(cx,getprivate_failure,WHERE);
256
		return(JS_FALSE);
257
	}
258
259
260

	if(p->fp==NULL)
		return(JS_TRUE);
261
262
	
	if(argc)
263
		JS_ValueToInt32(cx,argv[0],&len);
264

265
266
267
268
	if((buf=malloc(len))==NULL)
		return(JS_TRUE);

	if(fgets(buf,len,p->fp)!=NULL) {
269
270
271
272
		len=strlen(buf);
		while(len>0 && (buf[len-1]=='\r' || buf[len-1]=='\n'))
			len--;
		buf[len]=0;
273
274
275
276
		if(p->etx) {
			cp=strchr(buf,p->etx);
			if(cp) *cp=0; 
		}
277
278
		if((js_str=JS_NewStringCopyZ(cx,buf))!=NULL)
			*rval = STRING_TO_JSVAL(js_str);
279
280
	}

281
282
	free(buf);

283
284
285
286
	return(JS_TRUE);
}

static JSBool
287
js_readbin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
288
289
290
291
292
293
294
{
	BYTE		b;
	WORD		w;
	DWORD		l;
	size_t		size=sizeof(DWORD);
	private_t*	p;

295
	*rval = INT_TO_JSVAL(-1);
296

297
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
298
		JS_ReportError(cx,getprivate_failure,WHERE);
299
		return(JS_FALSE);
300
	}
301
302
303
304

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

305
	if(argc) 
306
		JS_ValueToInt32(cx,argv[0],(int32*)&size);
307
308
309

	switch(size) {
		case sizeof(BYTE):
310
311
			if(fread(&b,1,size,p->fp)==size)
				*rval = INT_TO_JSVAL(b);
312
313
			break;
		case sizeof(WORD):
314
315
			if(fread(&w,1,size,p->fp)==size)
				*rval = INT_TO_JSVAL(w);
316
317
			break;
		case sizeof(DWORD):
318
			if(fread(&l,1,size,p->fp)==size)
319
				JS_NewNumberValue(cx,l,rval);
320
321
322
323
324
325
326
			break;
	}
		
	return(JS_TRUE);
}

static JSBool
327
js_readall(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
328
{
329
330
331
    jsint       len=0;
    jsval       line;
    JSObject*	array;
332
333
	private_t*	p;

334
335
	*rval = JSVAL_NULL;

336
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
337
		JS_ReportError(cx,getprivate_failure,WHERE);
338
		return(JS_FALSE);
339
	}
340

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

344
    array = JS_NewArrayObject(cx, 0, NULL);
345

346
347
348
349
    while(!feof(p->fp)) {
		js_readln(cx, obj, 0, NULL, &line); 
		if(line==JSVAL_NULL)
			break;
350
351
        if(!JS_SetElement(cx, array, len++, &line))
			break;
352
	}
353
    *rval = OBJECT_TO_JSVAL(array);
354

355
356
    return(JS_TRUE);
}
357

358
359
360
361
static JSBool
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		cp;
362
363
	size_t		len;	/* string length */
	size_t		tlen;	/* total length to write (may be greater than len) */
364
365
	JSString*	str;
	private_t*	p;
366

367
	*rval = JSVAL_FALSE;
368

369
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
370
		JS_ReportError(cx,getprivate_failure,WHERE);
371
		return(JS_FALSE);
372
	}
373
374
375
376
377
378
379

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

	str = JS_ValueToString(cx, argv[0]);
	cp = JS_GetStringBytes(str);
	len = strlen(cp);
380
381
	tlen = len;
	if(argc>1) {
382
		JS_ValueToInt32(cx,argv[1],(int32*)&tlen);
383
384
385
		if(len>tlen)
			len=tlen;
	}
386
	if(fwrite(cp,1,len,p->fp)==len) {
387
388
389
390
		if(tlen>len) {
			len=tlen-len;
			if((cp=malloc(len))==NULL) {
				dbprintf(TRUE, p, "malloc failure of %u bytes", len);
391
				*rval = JSVAL_FALSE;
392
393
394
395
396
397
398
				return(JS_TRUE);
			}
			memset(cp,p->etx,len);
			fwrite(cp,1,len,p->fp);
			free(cp);
		}
		dbprintf(FALSE, p, "wrote %u bytes",tlen);
399
		*rval = JSVAL_TRUE;
400
401
	} else 
		dbprintf(TRUE, p, "write of %u bytes failed",len);
402
403
404
405
406
		
	return(JS_TRUE);
}

static JSBool
407
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
408
{
409
410
411
412
	char*		cp="";
	JSString*	str;
	private_t*	p;

413
	*rval = JSVAL_FALSE;
414

415
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
416
		JS_ReportError(cx,getprivate_failure,WHERE);
417
		return(JS_FALSE);
418
	}
419
420
421
422
423

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

	if(argc) {
424
425
		if((str = JS_ValueToString(cx, argv[0]))==NULL) {
			JS_ReportError(cx,"JS_ValueToString failed");
426
			return(JS_FALSE);
427
		}
428
429
430
431
		cp = JS_GetStringBytes(str);
	}

	if(fprintf(p->fp,"%s\n",cp)!=0)
432
		*rval = JSVAL_TRUE;
433

434
435
	return(JS_TRUE);
}
436

437
static JSBool
438
js_writebin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
439
{
440
441
442
	BYTE		b;
	WORD		w;
	DWORD		l;
443
	int32		val=0;
444
	size_t		wr=0;
445
	size_t		size=sizeof(DWORD);
446
447
	private_t*	p;

448
	*rval = JSVAL_FALSE;
449

450
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
451
		JS_ReportError(cx,getprivate_failure,WHERE);
452
		return(JS_FALSE);
453
	}
454

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

458
	if(argc>1) 
459
		JS_ValueToInt32(cx,argv[1],(int32*)&size);
460

461
462
	switch(size) {
		case sizeof(BYTE):
463
464
			JS_ValueToInt32(cx,argv[0],&val);
			b = (BYTE)val;
465
			wr=fwrite(&b,1,size,p->fp);
466
467
			break;
		case sizeof(WORD):
468
469
			JS_ValueToInt32(cx,argv[0],&val);
			w = (WORD)val;
470
			wr=fwrite(&w,1,size,p->fp);
471
472
			break;
		case sizeof(DWORD):
473
474
			JS_ValueToInt32(cx,argv[0],&val);
			l = val;
475
476
477
478
479
			wr=fwrite(&l,1,size,p->fp);
			break;
		default:	
			/* unknown size */
			dbprintf(TRUE, p, "unsupported binary write size: %d",size);
480
481
			break;
	}
482
	if(wr==size)
483
		*rval = JSVAL_TRUE;
484
		
485
486
487
	return(JS_TRUE);
}

488
489
490
491
492
493
494
495
496
static JSBool
js_writeall(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    jsuint      i;
    jsuint      limit;
    JSObject*	array;
    jsval       elemval;
	private_t*	p;

497
	*rval = JSVAL_FALSE;
498

499
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
500
		JS_ReportError(cx,getprivate_failure,WHERE);
501
		return(JS_FALSE);
502
	}
503
504
505
506

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

507
	if(!JSVAL_IS_OBJECT(argv[0]))
508
509
510
511
		return(JS_TRUE);

    array = JSVAL_TO_OBJECT(argv[0]);

512
513
514
    if(!JS_IsArrayObject(cx, array))
		return(JS_TRUE);

515
516
    if(!JS_GetArrayLength(cx, array, &limit))
		return(JS_FALSE);
517

518
    *rval = JSVAL_TRUE;
519

520
521
522
523
    for(i=0;i<limit;i++) {
        if(!JS_GetElement(cx, array, i, &elemval))
			break;
        js_writeln(cx, obj, 1, &elemval, rval);
524
		if(*rval!=JSVAL_TRUE)
525
			break;
526
527
528
529
530
    }

    return(JS_TRUE);
}

531
static JSBool
532
js_lock(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
533
{
534
535
	int32		offset=0;
	int32		len=0;
536
537
	private_t*	p;

538
	*rval = JSVAL_FALSE;
539

540
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
541
		JS_ReportError(cx,getprivate_failure,WHERE);
542
		return(JS_FALSE);
543
	}
544
545
546
547

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

548
549
550
551
552
553
554
555
556
557
558
559
	/* offset */
	if(argc)
		JS_ValueToInt32(cx,argv[0],&offset);

	/* length */
	if(argc>1)
		JS_ValueToInt32(cx,argv[1],&len);

	if(len==0)
		len=filelength(fileno(p->fp))-offset;

	if(lock(fileno(p->fp),offset,len)==0)
560
		*rval = JSVAL_TRUE;
561

562
563
	return(JS_TRUE);
}
564

565
static JSBool
566
js_unlock(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
567
{
568
569
	int32		offset=0;
	int32		len=0;
570
571
	private_t*	p;

572
	*rval = JSVAL_FALSE;
573

574
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
575
		JS_ReportError(cx,getprivate_failure,WHERE);
576
		return(JS_FALSE);
577
	}
578
579
580
581

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

582
583
584
585
586
587
588
589
590
591
592
	/* offset */
	if(argc)
		JS_ValueToInt32(cx,argv[0],&offset);

	/* length */
	if(argc>1)
		JS_ValueToInt32(cx,argv[1],&len);

	if(len==0)
		len=filelength(fileno(p->fp))-offset;

593
	if(unlock(fileno(p->fp),offset,len)==0)
594
		*rval = JSVAL_TRUE;
595

596
597
598
	return(JS_TRUE);
}

599
600
601
602
603
static JSBool
js_delete(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	private_t*	p;

604
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
605
		JS_ReportError(cx,getprivate_failure,WHERE);
606
		return(JS_FALSE);
607
	}
608
609
610
611
612
613
614
615
616
617

	if(p->fp!=NULL) {	/* close it if it's open */
		fclose(p->fp);
		p->fp=NULL;
	}

	*rval = BOOLEAN_TO_JSVAL(remove(p->name)==0);

	return(JS_TRUE);
}
618
619
620
621
622
623

static JSBool
js_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	private_t*	p;

624
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
625
		JS_ReportError(cx,getprivate_failure,WHERE);
626
		return(JS_FALSE);
627
	}
628
629

	if(p->fp==NULL)
630
		*rval = JSVAL_FALSE;
631
632
633
634
635
636
637
638
639
640
641
	else 
		*rval = BOOLEAN_TO_JSVAL(fflush(p->fp)==0);

	return(JS_TRUE);
}

static JSBool
js_clear_error(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	private_t*	p;

642
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
643
		JS_ReportError(cx,getprivate_failure,WHERE);
644
		return(JS_FALSE);
645
	}
646
647

	if(p->fp==NULL)
648
		*rval = JSVAL_FALSE;
649
650
	else  {
		clearerr(p->fp);
651
		*rval = JSVAL_TRUE;
652
653
654
655
656
	}

	return(JS_TRUE);
}

657
658
659
660
661
662
663
664
665
666
static JSBool
js_fprintf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		cp;
    uintN		i;
	JSString *	fmt;
    JSString *	str;
	va_list		arglist[64];
	private_t*	p;

667
	*rval = JSVAL_FALSE;
668

669
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
670
		JS_ReportError(cx,getprivate_failure,WHERE);
671
		return(JS_FALSE);
672
	}
673
674
675
676

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

677
678
	if((fmt=JS_ValueToString(cx, argv[0]))==NULL) {
		JS_ReportError(cx,"JS_ValueToString failed");
679
		return(JS_FALSE);
680
	}
681
682
683
684
685

	memset(arglist,0,sizeof(arglist));	/* Initialize arglist to NULLs */

    for (i = 1; i < argc && i<sizeof(arglist)/sizeof(arglist[0]); i++) {
		if(JSVAL_IS_STRING(argv[i])) {
686
687
			if((str=JS_ValueToString(cx, argv[i]))==NULL) {
				JS_ReportError(cx,"JS_ValueToString failed");
688
			    return(JS_FALSE);
689
			}
690
691
692
693
694
695
696
697
698
699
			arglist[i-1]=JS_GetStringBytes(str);	/* exception here July-29-2002 */
		}
		else if(JSVAL_IS_DOUBLE(argv[i]))
			arglist[i-1]=(char*)(unsigned long)*JSVAL_TO_DOUBLE(argv[i]);
		else if(JSVAL_IS_INT(argv[i]) || JSVAL_IS_BOOLEAN(argv[i]))
			arglist[i-1]=(char *)JSVAL_TO_INT(argv[i]);
		else
			arglist[i-1]=NULL;
	}

700
701
	if((cp=JS_vsmprintf(JS_GetStringBytes(fmt),(char*)arglist))==NULL) {
		JS_ReportError(cx,"JS_vsmprintf failed");
702
		return(JS_FALSE);
703
	}
704
705
706
707
708
709
710

	*rval = INT_TO_JSVAL(fwrite(cp,1,strlen(cp),p->fp));
	JS_smprintf_free(cp);
	
    return(JS_TRUE);
}

711
712
713
714

/* File Object Properites */
enum {
	 FILE_PROP_NAME		
715
716
	,FILE_PROP_MODE
	,FILE_PROP_ETX
717
718
719
720
721
722
723
724
725
726
	,FILE_PROP_EXISTS	
	,FILE_PROP_DATE		
	,FILE_PROP_IS_OPEN	
	,FILE_PROP_EOF		
	,FILE_PROP_ERROR	
	,FILE_PROP_DESCRIPTOR
	,FILE_PROP_DEBUG	
	,FILE_PROP_POSITION	
	,FILE_PROP_LENGTH	
	,FILE_PROP_ATTRIBUTES
727
728
729
730
731
732
733
	/* dynamically calculated */
	,FILE_PROP_CHKSUM
	,FILE_PROP_CRC16
	,FILE_PROP_CRC32
	,FILE_PROP_BASE64
	,FILE_PROP_MD5_HEX
	,FILE_PROP_MD5_BASE64
734
735
};

rswindell's avatar
rswindell committed
736

737
738
739
740
741
static JSBool js_file_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
    jsint       tiny;
	private_t*	p;

742
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
743
		JS_ReportError(cx,getprivate_failure,WHERE);
744
		return(JS_FALSE);
745
	}
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765

    tiny = JSVAL_TO_INT(id);

	dbprintf(FALSE, p, "setting property %d",tiny);

	switch(tiny) {
		case FILE_PROP_DEBUG:
			p->debug = JSVAL_TO_BOOLEAN(*vp);
			break;
		case FILE_PROP_POSITION:
			if(p->fp!=NULL)
				fseek(p->fp,JSVAL_TO_INT(*vp),SEEK_SET);
			break;
		case FILE_PROP_LENGTH:
			if(p->fp!=NULL)
				chsize(fileno(p->fp),JSVAL_TO_INT(*vp));
			break;
		case FILE_PROP_ATTRIBUTES:
			CHMOD(p->name,JSVAL_TO_INT(*vp));
			break;
766
767
768
		case FILE_PROP_ETX:
			p->etx = (uchar)JSVAL_TO_INT(*vp);
			break;
769
770
771
772
773
774
775
	}

	return(JS_TRUE);
}

static JSBool js_file_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
776
777
778
779
780
781
	BYTE*		buf;
	long		l;
	long		len;
	long		offset;
	ulong		sum;
	BYTE		digest[MD5_DIGEST_SIZE];
782
    jsint       tiny;
783
	JSString*	js_str=NULL;
784
785
	private_t*	p;

786
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL) {
787
		JS_ReportError(cx,getprivate_failure,WHERE);
788
		return(JS_FALSE);
789
	}
790
791
792
793
794
795
796
797
798

    tiny = JSVAL_TO_INT(id);

#if 0 /* just too much */
	dbprintf(FALSE, sock, "getting property %d",tiny);
#endif

	switch(tiny) {
		case FILE_PROP_NAME:
799
800
801
			if((js_str=JS_NewStringCopyZ(cx, p->name))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
802
803
			break;
		case FILE_PROP_MODE:
804
805
806
			if((js_str=JS_NewStringCopyZ(cx, p->mode))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
807
808
809
			break;
		case FILE_PROP_EXISTS:
			if(p->fp)	/* open? */
810
				*vp = JSVAL_TRUE;
811
812
813
814
			else
				*vp = BOOLEAN_TO_JSVAL(fexist(p->name));
			break;
		case FILE_PROP_DATE:
815
			JS_NewNumberValue(cx,fdate(p->name),vp);
816
817
818
819
820
			break;
		case FILE_PROP_IS_OPEN:
			*vp = BOOLEAN_TO_JSVAL(p->fp!=NULL);
			break;
		case FILE_PROP_EOF:
821
822
823
			if(p->fp)
				*vp = BOOLEAN_TO_JSVAL(feof(p->fp)!=0);
			else
824
				*vp = JSVAL_TRUE;
825
826
			break;
		case FILE_PROP_ERROR:
827
828
829
830
			if(p->fp)
				*vp = INT_TO_JSVAL(ferror(p->fp));
			else
				*vp = INT_TO_JSVAL(0);
831
832
			break;
		case FILE_PROP_POSITION:
833
			if(p->fp)
834
				JS_NewNumberValue(cx,ftell(p->fp),vp);
835
836
			else
				*vp = INT_TO_JSVAL(-1);
837
838
839
			break;
		case FILE_PROP_LENGTH:
			if(p->fp)	/* open? */
840
				JS_NewNumberValue(cx,filelength(fileno(p->fp)),vp);
841
842
843
844
			else
				*vp = INT_TO_JSVAL(flength(p->name));
			break;
		case FILE_PROP_ATTRIBUTES:
845
			JS_NewNumberValue(cx,getfattr(p->name),vp);
846
847
848
849
850
			break;
		case FILE_PROP_DEBUG:
			*vp = INT_TO_JSVAL(p->debug);
			break;
		case FILE_PROP_DESCRIPTOR:
851
852
853
854
			if(p->fp)
				*vp = INT_TO_JSVAL(fileno(p->fp));
			else
				*vp = INT_TO_JSVAL(-1);
855
			break;
856
857
858
		case FILE_PROP_ETX:
			*vp = INT_TO_JSVAL(p->etx);
			break;
859
860
861
		case FILE_PROP_CHKSUM:
		case FILE_PROP_CRC16:
		case FILE_PROP_CRC32:
862
863
864
865
			*vp = JSVAL_ZERO;
			if(p->fp==NULL)
				break;
			/* fall-through */
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
		case FILE_PROP_BASE64:
		case FILE_PROP_MD5_HEX:
		case FILE_PROP_MD5_BASE64:
			*vp = JSVAL_VOID;
			if(p->fp==NULL)
				break;
			offset=ftell(p->fp);			/* save current file position */
			fseek(p->fp,0,SEEK_SET);
			len=filelength(fileno(p->fp));
			if(len<1)
				break;
			if((buf=malloc(len*2))==NULL)
				break;
			len=fread(buf,sizeof(BYTE),len,p->fp);
			if(len<1)
				break;

			switch(tiny) {
				case FILE_PROP_CHKSUM:
					for(sum=l=0;l<len;l++)
						sum+=buf[l];
					JS_NewNumberValue(cx,sum,vp);
					break;
				case FILE_PROP_CRC16:
890
					JS_NewNumberValue(cx,crc16(buf,len),vp);
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
					break;
				case FILE_PROP_CRC32:
					JS_NewNumberValue(cx,crc32(buf,len),vp);
					break;
				case FILE_PROP_BASE64:
					b64_encode(buf,len*2,buf,len);
					js_str=JS_NewStringCopyZ(cx, buf);
					break;
				case FILE_PROP_MD5_HEX:
					MD5_calc(digest,buf,len);
					MD5_hex(buf,digest);
					js_str=JS_NewStringCopyZ(cx, buf);
					break;
				case FILE_PROP_MD5_BASE64:
					MD5_calc(digest,buf,len);
					b64_encode(buf,len*2,digest,sizeof(digest));
					js_str=JS_NewStringCopyZ(cx, buf);
					break;
			}
			free(buf);
			fseek(p->fp,offset,SEEK_SET);	/* restore saved file position */
			if(js_str!=NULL)
				*vp = STRING_TO_JSVAL(js_str);
			break;
915
916
	}

917
	return(JS_TRUE);
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
}

#define FILE_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY

static struct JSPropertySpec js_file_properties[] = {
/*		 name				,tinyid					,flags,				getter,	setter	*/
	{	"name"				,FILE_PROP_NAME			,FILE_PROP_FLAGS,	NULL,NULL},
	{	"mode"				,FILE_PROP_MODE			,FILE_PROP_FLAGS,	NULL,NULL},
	{	"exists"			,FILE_PROP_EXISTS		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"date"				,FILE_PROP_DATE			,FILE_PROP_FLAGS,	NULL,NULL},
	{	"is_open"			,FILE_PROP_IS_OPEN		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"eof"				,FILE_PROP_EOF			,FILE_PROP_FLAGS,	NULL,NULL},
	{	"error"				,FILE_PROP_ERROR		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"descriptor"		,FILE_PROP_DESCRIPTOR	,FILE_PROP_FLAGS,	NULL,NULL},
	/* writeable */
933
	{	"etx"				,FILE_PROP_ETX			,JSPROP_ENUMERATE,  NULL,NULL},
934
935
936
937
	{	"debug"				,FILE_PROP_DEBUG		,JSPROP_ENUMERATE,	NULL,NULL},
	{	"position"			,FILE_PROP_POSITION		,JSPROP_ENUMERATE,	NULL,NULL},
	{	"length"			,FILE_PROP_LENGTH		,JSPROP_ENUMERATE,	NULL,NULL},
	{	"attributes"		,FILE_PROP_ATTRIBUTES	,JSPROP_ENUMERATE,	NULL,NULL},
938
939
940
941
942
943
944
	/* dynamically calculated */
	{	"chksum"			,FILE_PROP_CHKSUM		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"crc16"				,FILE_PROP_CRC16		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"crc32"				,FILE_PROP_CRC32		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"base64"			,FILE_PROP_BASE64		,FILE_PROP_FLAGS,	NULL,NULL},
	{	"md5_base64"		,FILE_PROP_MD5_BASE64	,FILE_PROP_FLAGS,	NULL,NULL},
	{	"md5_hex"			,FILE_PROP_MD5_HEX		,FILE_PROP_FLAGS,	NULL,NULL},
945
946
947
	{0}
};

rswindell's avatar
rswindell committed
948
949
950
951
952
953
954
#ifdef _DEBUG
static char* file_prop_desc[] = {
	 "filename specified in constructor - <small>READ ONLY</small>"
	,"mode string specified in <i>open</i> call - <small>READ ONLY</small>"
	,"<i>true</i> if the file exists - <small>READ ONLY</small>"
	,"last modified date/time (time_t format) - <small>READ ONLY</small>"
	,"<i>true</i> if the file has been opened successfully - <small>READ ONLY</small>"
955
	,"<i>true</i> if the current file position is at the <i>end of file</i> - <small>READ ONLY</small>"
rswindell's avatar
rswindell committed
956
957
958
959
960
961
962
	,"the last occurred error value (use clear_error to clear) - <small>READ ONLY</small>"
	,"the open file descriptor (advanced use only) - <small>READ ONLY</small>"
	,"end-of-text character (advanced use only), if non-zero used by <i>read</i>, <i>readln</i>, and <i>write</i>"
	,"<i>true</i> if debug output is enabled"
	,"the current file position (offset in bytes), change value to seek within file"
	,"the current length of the file (in bytes)"
	,"file mode/attributes"
963
964
965
966
967
968
	,"calculated 32-bit checksum of file contents - <small>READ ONLY</small>"
	,"calculated 16-bit CRC of file contents - <small>READ ONLY</small>"
	,"calculated 32-bit CRC of file contents - <small>READ ONLY</small>"
	,"base64-encoded file contents (string) - <small>READ ONLY</small>"
	,"base64-encoded calculated MD5 digest of file contents (string) - <small>READ ONLY</small>"
	,"hexadecimal-encoded calculated MD5 digest of file contents (string) - <small>READ ONLY</small>"
rswindell's avatar
rswindell committed
969
970
971
972
973
	,NULL
};
#endif


974
static jsMethodSpec js_file_functions[] = {
975
	{"open",			js_open,			1,	JSTYPE_BOOLEAN,	JSDOCSTR("[string mode, boolean shareable, number buflen]")
rswindell's avatar
rswindell committed
976
977
978
979
980
981
982
983
984
	,JSDOCSTR("open file, <i>shareable</i> defaults to <i>false</i>, <i>buflen</i> defaults to 2048 bytes, "
		"mode (default: <tt>w+</tt>) specifies the type of access requested for the file, as follows:<br>"
		"<tt>r&nbsp</tt> open for reading; if the file does not exist or cannot be found, the open call fails<br>"
		"<tt>w&nbsp</tt> open an empty file for writing; if the given file exists, its contents are destroyed<br>"
		"<tt>a&nbsp</tt> open for writing at the end of the file (appending); creates the file first if it doesnt exist<br>"
		"<tt>r+</tt> open for both reading and writing (the file must exist)<br>"
		"<tt>w+</tt> open an empty file for both reading and writing; if the given file exists, its contents are destroyed<br>"
		"<tt>a+</tt> open for reading and appending<br>"
		"<tt>b&nbsp</tt> open in binary (untranslated) mode; translations involving carriage-return and linefeed characters are suppressed (e.g. <tt>r+b</tt>)<br>"
rswindell's avatar
rswindell committed
985
		)
986
987
	},		
	{"close",			js_close,			0,	JSTYPE_VOID,	""
988
	,JSDOCSTR("close file")
989
	},		
990
991
	{"remove",			js_delete,			0,	JSTYPE_BOOLEAN, ""
	,JSDOCSTR("remove the file from the disk")
992
	},
993
	{"clearError",		js_clear_error,		0,	JSTYPE_ALIAS },
994
	{"clear_error",		js_clear_error,		0,	JSTYPE_BOOLEAN, ""
rswindell's avatar
rswindell committed
995
	,JSDOCSTR("clears the current error value (AKA clearError)")
996
997
	},
	{"flush",			js_flush,			0,	JSTYPE_BOOLEAN,	""
rswindell's avatar
rswindell committed
998
	,JSDOCSTR("flush/commit buffers to disk")
999
	},
1000
	{"lock",			js_lock,			2,	JSTYPE_BOOLEAN,	JSDOCSTR("[offset, length]")
rswindell's avatar
rswindell committed
1001
	,JSDOCSTR("lock file record for exclusive access (file must be opened <i>shareable</i>)")
1002
	},		
1003
	{"unlock",			js_unlock,			2,	JSTYPE_BOOLEAN,	JSDOCSTR("[offset, length]")
rswindell's avatar
rswindell committed
1004
	,JSDOCSTR("unlock file record for exclusive access")
1005
	},		
1006
1007
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[number maxlen]")
	,JSDOCSTR("read a string from file, maxlen defaults to 512 characters")
1008
	},
1009
1010
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[number maxlen]")
	,JSDOCSTR("read a line-feed terminated string, maxlen defaults 512 characters")
1011
	},		
1012
1013
	{"readBin",			js_readbin,			0,	JSTYPE_NUMBER,	JSDOCSTR("[number bytes]")
	,JSDOCSTR("read a binary integer from the file, default number of bytes is 4 (32-bits)")
1014
1015
	},
	{"readAll",			js_readall,			0,	JSTYPE_ARRAY,	""
1016
	,JSDOCSTR("read all lines into an array of strings")
1017
	},
1018
1019
	{"write",			js_write,			1,	JSTYPE_BOOLEAN,	JSDOCSTR("string text [,number len]")
	,JSDOCSTR("write a string to the file")
1020
	},
1021
1022
	{"writeln",			js_writeln,			0,	JSTYPE_BOOLEAN, JSDOCSTR("[string text]")
	,JSDOCSTR("write a line-feed terminated string to the file")
1023
	},
1024
1025
	{"writeBin",		js_writebin,		1,	JSTYPE_BOOLEAN,	JSDOCSTR("number value [,number bytes]")
	,JSDOCSTR("write a binary integer to the file, default number of bytes is 4 (32-bits)")
1026
	},
1027
1028
	{"writeAll",		js_writeall,		0,	JSTYPE_BOOLEAN,	JSDOCSTR("array lines")
	,JSDOCSTR("write an array of strings to file")
1029
	},		
1030
	{"printf",			js_fprintf,			0,	JSTYPE_NUMBER,	JSDOCSTR("string format [,args]")
rswindell's avatar
rswindell committed
1031
	,JSDOCSTR("write a formatted string to the file (ala fprintf)")
1032
	},		
1033
1034
1035
	{0}
};

1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
/* File Destructor */

static void js_finalize_file(JSContext *cx, JSObject *obj)
{
	private_t* p;
	
	if((p=(private_t*)JS_GetPrivate(cx,obj))==NULL)
		return;

	if(p->external==JS_FALSE && p->fp!=NULL)
		fclose(p->fp);

	dbprintf(FALSE, p, "closed: %s",p->name);

	free(p);

	JS_SetPrivate(cx, obj, NULL);
}

static JSClass js_file_class = {
     "File"					/* name			*/
    ,JSCLASS_HAS_PRIVATE	/* flags		*/
	,JS_PropertyStub		/* addProperty	*/
	,JS_PropertyStub		/* delProperty	*/
	,js_file_get			/* getProperty	*/
	,js_file_set			/* setProperty	*/
	,JS_EnumerateStub		/* enumerate	*/
	,JS_ResolveStub			/* resolve		*/
	,JS_ConvertStub			/* convert		*/
	,js_finalize_file		/* finalize		*/
};

/* File Constructor (creates file descriptor) */

static JSBool
js_file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	JSString*	str;
	private_t*	p;

	if((str = JS_ValueToString(cx, argv[0]))==NULL) {
1077
		JS_ReportError(cx,"No filename specified");
1078
1079
1080
1081
1082
1083
		return(JS_FALSE);
	}

	*rval = JSVAL_VOID;

	if((p=(private_t*)calloc(1,sizeof(private_t)))==NULL) {
1084
		JS_ReportError(cx,"calloc failed");
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
		return(JS_FALSE);
	}

	SAFECOPY(p->name,JS_GetStringBytes(str));

	if(!JS_SetPrivate(cx, obj, p)) {
		dbprintf(TRUE, p, "JS_SetPrivate failed");
		return(JS_FALSE);
	}

1095
	if(!js_DefineMethods(cx, obj, js_file_functions, FALSE)) {
1096
1097
1098
1099
1100
		dbprintf(TRUE, p, "js_DefineMethods failed");
		return(JS_FALSE);
	}

#ifdef _DEBUG
rswindell's avatar
rswindell committed
1101
	js_DescribeObject(cx,obj,"Class used for opening/creating files on the local file system");
rswindell's avatar
rswindell committed
1102
	js_DescribeConstructor(cx,obj,"To create a new File object: <tt>var f = new File(filename)</tt>");
rswindell's avatar
rswindell committed
1103
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", file_prop_desc, <