Skip to content
Snippets Groups Projects
js_file.c 87.7 KiB
Newer Older
/* 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)		*
 *																			*
 * Copyright 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										*
 *																			*
 * 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"
#include "xpendian.h"
	#include <conio.h>      /* for kbhit() */
	FILE* fp;
	char name[MAX_PATH + 1];
	char mode[7];
	uchar etx;
	BOOL debug;
	BOOL rot13;
	BOOL yencoded;
	BOOL uuencoded;
	BOOL b64encoded;
	BOOL network_byte_order;
	BOOL pipe;          /* Opened with popen() use pclose() to close */
	ini_style_t ini_style;

} private_t;

static void dbprintf(BOOL error, private_t* p, char* fmt, ...)
{
	va_list argptr;
	if (p == NULL || (!p->debug && !error))
	va_start(argptr, fmt);
	vsnprintf(sbuf, sizeof(sbuf), fmt, argptr);
	sbuf[sizeof(sbuf) - 1] = 0;
	va_end(argptr);
	lprintf(LOG_DEBUG, "%04d File %s%s", p->fp ? fileno(p->fp) : 0, error ? "ERROR: ":"", sbuf);
/* Converts fopen() style 'mode' string into open() style 'flags' integer */
static int fopenflags(char *mode)
{
	if (strchr(mode, 'b'))
		flags |= O_BINARY;
	if (strchr(mode, 'x'))
		flags |= O_EXCL;
	if (strchr(mode, 'w')) {
		flags |= O_CREAT | O_TRUNC;
		if (strchr(mode, '+'))
			flags |= O_RDWR;
	if (strchr(mode, 'a')) {
		flags |= O_CREAT | O_APPEND;
		if (strchr(mode, '+'))
			flags |= O_RDWR;
	if (strchr(mode, 'r')) {
		if (strchr(mode, '+'))
			flags |= O_RDWR;
js_open(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	BOOL       shareable = FALSE;
	int        file = -1;
	uintN      i;
	jsint      bufsize = 2 * 1024;
	JSString*  str;
	private_t* p;
	jsrefcount rc;

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	SAFECOPY(p->mode, "w+");     /* default mode */
	for (i = 0; i < argc; i++) {
		if (JSVAL_IS_STRING(argv[i])) {  /* mode */
			if ((str = JS_ValueToString(cx, argv[i])) == NULL)
				return JS_FALSE;
			JSSTRING_TO_STRBUF(cx, str, p->mode, sizeof(p->mode), NULL);
		}
		else if (JSVAL_IS_BOOLEAN(argv[i]))  /* shareable */
			shareable = JSVAL_TO_BOOLEAN(argv[i]);
		else if (JSVAL_IS_NUMBER(argv[i])) { /* bufsize */
			if (!JS_ValueToInt32(cx, argv[i], &bufsize))
	rc = JS_SUSPENDREQUEST(cx);
	if (shareable)
		p->fp = fopen(p->name, p->mode);
		if ((file = nopen(p->name, fopenflags(p->mode))) != -1) {
			char *fdomode = strdup(p->mode);
			char *e = fdomode;
			if (fdomode && e) {
				/* Remove deprecated (never-worked, non-standard) 'e'xclusive mode char (and warn): */
				for (e = strchr(fdomode, 'e'); e ; e = strchr(e, 'e')) {
					JS_ReportWarning(cx, "Deprecated file open mode: 'e'");
					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'))
					memmove(e, e + 1, strlen(e));
				if ((p->fp = fdopen(file, fdomode)) == NULL) {
					JS_ReportWarning(cx, "fdopen(%s, %s) ERROR %d: %s", p->name, fdomode, errno, strerror(errno));
				}
	if (p->fp != NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
		dbprintf(FALSE, p, "opened: %s", p->name);
		if (!bufsize)
			setvbuf(p->fp, NULL, _IONBF, 0); /* no buffering */
			setvbuf(p->fp, NULL, _IOFBF, bufsize);
	} else if (file >= 0)
js_popen(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	uintN      i;
	jsint      bufsize = 2 * 1024;
	JSString*  str;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {

	SAFECOPY(p->mode, "r+"); /* default mode */
	for (i = 0; i < argc; i++) {
		if (JSVAL_IS_STRING(argv[i])) {  /* mode */
			if ((str = JS_ValueToString(cx, argv[i])) == NULL)
				return JS_FALSE;
			JSSTRING_TO_STRBUF(cx, str, p->mode, sizeof(p->mode), NULL);
		else if (JSVAL_IS_NUMBER(argv[i])) { /* bufsize */
			if (!JS_ValueToInt32(cx, argv[i], &bufsize))
	rc = JS_SUSPENDREQUEST(cx);
	p->fp = popen(p->name, p->mode);
	if (p->fp != NULL) {
		p->pipe = TRUE;
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
		dbprintf(FALSE, p, "popened: %s", p->name);
		if (!bufsize)
			setvbuf(p->fp, NULL, _IONBF, 0); /* no buffering */
			setvbuf(p->fp, NULL, _IOFBF, bufsize);
js_close(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	private_t* p;
	jsrefcount rc;
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	rc = JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "closed: %s", p->name);

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;
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && !JSVAL_NULL_OR_VOID(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &timeout))
	}

	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(FALSE));
	rc = JS_SUSPENDREQUEST(cx);
Deucе's avatar
Deucе committed
	/*
	 * 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))
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE));
#else
		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;
}

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;
	int        fd;
	off_t      pos;

	JS_SET_RVAL(cx, arglist, JSVAL_NULL);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {

	if (argc && !JSVAL_NULL_OR_VOID(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &len))
	if ((buf = malloc(len)) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
	// 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 = ftello(p->fp);
		len = 0;
	else {
		fd = fileno(p->fp);
		lseek(fd, pos, SEEK_SET);
		len = read(fileno(p->fp), buf, len);
		fseeko(p->fp, pos + (len >= 0 ? len : 0), SEEK_SET);
		dbprintf(FALSE, p, "read %d raw bytes", len);
		if (len < 0)
			len = 0;
	JS_RESUMEREQUEST(cx, rc);

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


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

js_read(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      cp;
	char*      buf;
	char*      uubuf;
	int32      len;
	int32      offset;
	int32      uulen;
	JSString*  str;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && !JSVAL_NULL_OR_VOID(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &len))
		rc = JS_SUSPENDREQUEST(cx);
		len = (long)filelength(fileno(p->fp));
		offset = (long)ftell(p->fp);
		if (offset > 0)
			len -= offset;
	if ((buf = malloc(len + 1)) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
	len = fread(buf, 1, len, p->fp);
	dbprintf(FALSE, p, "read %u bytes", len);
	if (len < 0)
		len = 0;
	buf[len] = 0;
	if (p->etx) {
		cp = strchr(buf, p->etx);
	if (p->uuencoded || p->b64encoded || p->yencoded) {
		uulen = len * 2;
		if ((uubuf = malloc(uulen)) == NULL) {
		if (p->uuencoded)
			uulen = uuencode(uubuf, uulen, buf, len);
		else if (p->yencoded)
			uulen = yencode(uubuf, uulen, buf, len);
			uulen = b64_encode(uubuf, uulen, buf, len);
		if (uulen >= 0) {
			buf = uubuf;
			len = uulen;
deuce's avatar
deuce committed
		}
	str = JS_NewStringCopyN(cx, buf, len);
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
js_readln(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      cp;
	char*      buf;
	int32      len = 512;
	JSString*  js_str;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && !JSVAL_NULL_OR_VOID(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &len))
	if ((buf = malloc(len + 1)) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
	if (fgets(buf, len + 1, p->fp) != NULL) {
		len = strlen(buf);
		while (len > 0 && (buf[len - 1] == '\r' || buf[len - 1] == '\n'))
		buf[len] = 0;
		if (p->etx) {
			cp = strchr(buf, p->etx);
		if ((js_str = JS_NewStringCopyZ(cx, buf)) != NULL)    /* exception here Feb-12-2005 */
			JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
deuce's avatar
deuce committed
	free(buf);
js_readbin(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	BYTE *     b;
	WORD *     w;
	DWORD *    l;
	uint64_t * q;
	int32      size = sizeof(DWORD);
	private_t* p;
	int32      count = 1;
	size_t     retlen;
	void *     buffer = NULL;
	size_t     i;
	JSObject*  array;
	jsval      v;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(-1));
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && !JSVAL_NULL_OR_VOID(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &size))
		if (argc > 1 && !JSVAL_NULL_OR_VOID(argv[1])) {
			if (!JS_ValueToInt32(cx, argv[1], &count))
	rc = JS_SUSPENDREQUEST(cx);
	if (size != sizeof(BYTE) && size != sizeof(WORD) && size != sizeof(DWORD) && size != sizeof(uint64_t)) {
		dbprintf(TRUE, p, "unsupported binary read size: %d", size);
	buffer = malloc(size * count);
	if (buffer == NULL) {
		dbprintf(TRUE, p, "malloc failure of %u bytes", size * count);
	b = buffer;
	w = buffer;
	l = buffer;
	q = buffer;
	retlen = fread(buffer, size, count, p->fp);
	if (count == 1) {
		if (retlen == 1) {
			switch (size) {
					JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*b));
					if (p->network_byte_order)
						*w = BE_SHORT(*w);
					else
						*w = LE_SHORT(*w);
					JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*w));
					break;
				case sizeof(DWORD):
					if (p->network_byte_order)
						*l = BE_LONG(*l);
					else
						*l = LE_LONG(*l);
					JS_SET_RVAL(cx, arglist, UINT_TO_JSVAL(*l));
					if (p->network_byte_order)
						*q = BE_INT64(*q);
					else
						*q = LE_INT64(*q);
					JS_SET_RVAL(cx, arglist, DOUBLE_TO_JSVAL((double)*q));
		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):
					if (p->network_byte_order)
						*w = BE_SHORT(*w);
					else
						*w = LE_SHORT(*w);
					v = INT_TO_JSVAL(*(w++));
					break;
				case sizeof(DWORD):
					if (p->network_byte_order)
						*l = BE_LONG(*l);
					else
						*l = LE_LONG(*l);
					v = UINT_TO_JSVAL(*(l++));
					if (p->network_byte_order)
						*q = BE_INT64(*q);
					else
						*q = LE_INT64(*q);
					v = DOUBLE_TO_JSVAL((double)*(q++));
			if (!JS_SetElement(cx, array, i, &v)) {
				rc = JS_SUSPENDREQUEST(cx);
		JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
js_readall(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsint      len = 0;
	JSObject*  array;
	private_t* p;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	array = JS_NewArrayObject(cx, 0, NULL);
	while (!feof(p->fp)) {
		if (JS_RVAL(cx, arglist) == JSVAL_NULL)
		if (!JS_SetElement(cx, array, len++, &JS_RVAL(cx, arglist)))
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
static jsval get_value(JSContext *cx, char* value, bool blanks)
	char* p;
	BOOL  f = FALSE;
	jsval val;
	if (value == NULL || (*value == 0 && !blanks))
	for (p = value; *p; p++) {
		if (*p == '.' && !f)
			f = TRUE;
		else if (!IS_DIGIT(*p))
	if (p != value && *p == 0) {
		if (f)
			val = DOUBLE_TO_JSVAL(atof(value));
			val = UINT_TO_JSVAL(strtoul(value, NULL, 10));
	if (!strncmp(value, "0x", 2)) {
		for (p = value + 2; *p; p++)
			if (!isxdigit((uchar) * p))
		if (*p == 0) {
			val = UINT_TO_JSVAL(strtoul(value, NULL, 0));
	if (!stricmp(value, "true"))
	if (!stricmp(value, "false"))
	return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value));
deuce's avatar
deuce committed
static double js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)
{
deuce's avatar
deuce committed

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

js_iniGetValue(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      section = ROOT_SECTION;
	char*      key;
	char**     list;
	char       buf[INI_MAX_VALUE_LEN];
	int32      i;
	jsval      val;
	jsval      dflt = argv[2];
	private_t* p;
	JSObject*  array;
	JSObject*  dflt_obj;
	JSObject*  date_obj;
	jsrefcount rc;
	double     dbl;
	time_t     tt;
	char*      cstr = NULL;
	char*      cstr2;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL)
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
	JSVALUE_TO_MSTRING(cx, argv[1], key, NULL);
	if (JS_IsExceptionPending(cx)) {
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
deuce's avatar
deuce committed
		return JS_FALSE;
	/*
	 * Although section can be NULL (ie: root), a NULL key will cause a
	 * segfault.
	 */
		JS_ReportError(cx, "Invalid NULL key specified");
	str_list_t ini = iniReadFile(p->fp);
	if (argc < 3 || dflt == JSVAL_VOID) {  /* unspecified default value */
		rc = JS_SUSPENDREQUEST(cx);
		cstr = iniGetString(ini, section, key, NULL, buf);
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
		strListFree(&ini);
		JS_SET_RVAL(cx, arglist, get_value(cx, cstr, /* blanks */ false));
	if (JSVAL_IS_BOOLEAN(dflt)) {
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(
						iniGetBool(ini, section, key, JSVAL_TO_BOOLEAN(dflt))));
deuce's avatar
deuce committed
	}
	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);
			rc = JS_SUSPENDREQUEST(cx);
			dbl = (double)iniGetDateTime(ini, section, key, tt);
deuce's avatar
deuce committed
			dbl *= 1000;
deuce's avatar
deuce committed
			date_obj = JS_NewDateObjectMsec(cx, dbl);
			if (date_obj != NULL) {
deuce's avatar
deuce committed
				JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(date_obj));
deuce's avatar
deuce committed
			}
deuce's avatar
deuce committed
		}
		else {
			array = JS_NewArrayObject(cx, 0, NULL);
			cstr = NULL;
			JSVALUE_TO_MSTRING(cx, dflt, cstr, NULL);
			if (JS_IsExceptionPending(cx)) {
				FREE_AND_NULL(cstr);
				FREE_AND_NULL(section);
				FREE_AND_NULL(key);
				strListFree(&ini);
			rc = JS_SUSPENDREQUEST(cx);
			list = iniGetStringList(ini, section, key, ",", cstr);
			for (i = 0; list && list[i]; i++) {
				val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, list[i]));
				if (!JS_SetElement(cx, array, i, &val))
			rc = JS_SUSPENDREQUEST(cx);
			JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
deuce's avatar
deuce committed
		}
	}
	else if (JSVAL_IS_DOUBLE(dflt)) {
		rc = JS_SUSPENDREQUEST(cx);
		dbl = iniGetFloat(ini, section, key, JSVAL_TO_DOUBLE(dflt));
deuce's avatar
deuce committed
		JS_RESUMEREQUEST(cx, rc);
		JS_SET_RVAL(cx, arglist, DOUBLE_TO_JSVAL(dbl));
deuce's avatar
deuce committed
	}
	else if (JSVAL_IS_NUMBER(dflt)) {
		if (!JS_ValueToInt32(cx, dflt, &i)) {
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
			strListFree(&ini);
		rc = JS_SUSPENDREQUEST(cx);
		i = iniGetInteger(ini, section, key, i);
deuce's avatar
deuce committed
		JS_RESUMEREQUEST(cx, rc);
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(i));
deuce's avatar
deuce committed
	} else {
		JSVALUE_TO_MSTRING(cx, dflt, cstr, NULL);
		if (JS_IsExceptionPending(cx)) {
			FREE_AND_NULL(cstr);
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
			strListFree(&ini);
		rc = JS_SUSPENDREQUEST(cx);
		cstr2 = iniGetString(ini, section, key, cstr, buf);
deuce's avatar
deuce committed
		JS_RESUMEREQUEST(cx, rc);
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, cstr2)));
		FREE_AND_NULL(cstr);
	FREE_AND_NULL(section);
	FREE_AND_NULL(key);
	strListFree(&ini);
js_iniSetValue_internal(JSContext *cx, JSObject *obj, uintN argc, jsval* argv, str_list_t* list)
	char*      section = ROOT_SECTION;
	char*      key = NULL;
	char*      result = NULL;
	int32      i;
	jsval      value = argv[2];
	private_t* p;
	JSObject*  value_obj;
	jsrefcount rc;
	char*      cstr;
	time_t     tt;

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL)
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
	JSVALUE_TO_MSTRING(cx, argv[1], key, NULL);
	if (JS_IsExceptionPending(cx)) {
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
	if (value == JSVAL_VOID) {     /* unspecified value */
		rc = JS_SUSPENDREQUEST(cx);
		result = iniSetString(list, section, key, "", &p->ini_style);
	else if (JSVAL_IS_BOOLEAN(value)) {
		result = iniSetBool(list, section, key, JSVAL_TO_BOOLEAN(value), &p->ini_style);
deuce's avatar
deuce committed
	}
	else if (JSVAL_IS_DOUBLE(value)) {
		result = iniSetFloat(list, section, key, JSVAL_TO_DOUBLE(value), &p->ini_style);
deuce's avatar
deuce committed
	}
	else if (JSVAL_IS_NUMBER(value)) {
		if (!JS_ValueToInt32(cx, value, &i)) {
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
		rc = JS_SUSPENDREQUEST(cx);
		result = iniSetInteger(list, section, key, i, &p->ini_style);
deuce's avatar
deuce committed
		JS_RESUMEREQUEST(cx, rc);
	} else if (JSVAL_IS_OBJECT(value)
	           && (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);
		result = iniSetDateTime(list, section, key, /* include_time */ TRUE, tt, &p->ini_style);
deuce's avatar
deuce committed
		JS_RESUMEREQUEST(cx, rc);
	} else {
		JSVALUE_TO_MSTRING(cx, value, cstr, NULL);
		if (JS_IsExceptionPending(cx)) {
			FREE_AND_NULL(cstr);
			FREE_AND_NULL(section);
			FREE_AND_NULL(key);
		rc = JS_SUSPENDREQUEST(cx);
		result = iniSetString(list, section, key, cstr, &p->ini_style);
deuce's avatar
deuce committed
		JS_RESUMEREQUEST(cx, rc);
	FREE_AND_NULL(section);
	FREE_AND_NULL(key);
	return result != NULL;
deuce's avatar
deuce committed
static JSBool
js_iniSetValue(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	jsval      rval = JSVAL_FALSE;
	private_t* p;
	str_list_t list;
	jsrefcount rc;

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	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

	JS_SET_RVAL(cx, arglist, rval);
deuce's avatar
deuce committed
}

js_iniRemoveKey(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      section = ROOT_SECTION;
	char*      key = NULL;
	private_t* p;
	str_list_t list;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL) {
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
	JSVALUE_TO_MSTRING(cx, argv[1], key, NULL);
	if (JS_IsExceptionPending(cx)) {
		FREE_AND_NULL(key);
		FREE_AND_NULL(section);
		return JS_FALSE;
	}
		JS_ReportError(cx, "Invalid NULL key specified");
	rc = JS_SUSPENDREQUEST(cx);
	if ((list = iniReadFile(p->fp)) == NULL) {
		FREE_AND_NULL(section);
		FREE_AND_NULL(key);
	if (iniRemoveKey(&list, section, key))
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list)));
js_iniRemoveSection(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      section = ROOT_SECTION;
	private_t* p;
	str_list_t list;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL) {
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
		HANDLE_PENDING(cx, section);
	rc = JS_SUSPENDREQUEST(cx);
	if ((list = iniReadFile(p->fp)) == NULL) {
	if (iniRemoveSection(&list, section))
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list)));
static JSBool
js_iniRemoveSections(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      prefix = NULL;
	private_t* p;
	str_list_t list;
	jsrefcount rc;

	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
		return JS_FALSE;
	if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL) {
		JSVALUE_TO_MSTRING(cx, argv[0], prefix, NULL);
		HANDLE_PENDING(cx, prefix);
	}

	rc = JS_SUSPENDREQUEST(cx);
	if ((list = iniReadFile(p->fp)) == NULL) {
		JS_RESUMEREQUEST(cx, rc);
		FREE_AND_NULL(prefix);
		return JS_TRUE;
	}

	if (iniRemoveSections(&list, prefix))
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list)));

	FREE_AND_NULL(prefix);
	strListFree(&list);
	JS_RESUMEREQUEST(cx, rc);

	return JS_TRUE;
}
js_iniGetSections(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      prefix = NULL;
	char**     list;
	jsint      i;
	jsval      val;
	JSObject*  array;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
		JSVALUE_TO_MSTRING(cx, argv[0], prefix, NULL);
		HANDLE_PENDING(cx, prefix);
	array = JS_NewArrayObject(cx, 0, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	str_list_t ini = iniReadFile(p->fp);
	list = iniGetSectionList(ini, prefix);
	strListFree(&ini);
	for (i = 0; list && list[i]; i++) {
		val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, list[i]));
		if (!JS_SetElement(cx, array, i, &val))
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
js_iniGetKeys(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      section = ROOT_SECTION;
	char**     list;
	jsint      i;
	jsval      val;
	JSObject*  array;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL) {
		JSVALUE_TO_MSTRING(cx, argv[0], section, NULL);
		HANDLE_PENDING(cx, section);
	array = JS_NewArrayObject(cx, 0, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	str_list_t ini = iniReadFile(p->fp);
	list = iniGetKeyList(ini, section);
	strListFree(&ini);
	for (i = 0; list && list[i]; i++) {
		val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, list[i]));
		if (!JS_SetElement(cx, array, i, &val))
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
js_iniGetObject(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *       obj = JS_THIS_OBJECT(cx, arglist);
	jsval *          argv = JS_ARGV(cx, arglist);
	char*            section = ROOT_SECTION;
	jsint            i;
	JSObject*        object;
	private_t*       p;
	jsrefcount       rc;
	bool             lowercase = false;
	bool             blanks = false;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc > argn && !JSVAL_IS_BOOLEAN(argv[argn]) && !JSVAL_NULL_OR_VOID(argv[argn])) {
		JSVALUE_TO_MSTRING(cx, argv[argn], section, NULL);
		HANDLE_PENDING(cx, section);
	if (argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
		lowercase = JSVAL_TO_BOOLEAN(argv[argn]);
		argn++;
	if (argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
		blanks = JSVAL_TO_BOOLEAN(argv[argn]);
		argn++;
	}
	rc = JS_SUSPENDREQUEST(cx);
	str_list_t ini = iniReadFile(p->fp);
	list = iniGetNamedStringList(ini, section);
	strListFree(&ini);
	object = JS_NewObject(cx, NULL, NULL, obj);
	for (i = 0; list && list[i]; i++) {
		if (lowercase)
		JS_DefineProperty(cx, object, list[i]->name
		                  , get_value(cx, list[i]->value, blanks)
		                  , NULL, NULL, JSPROP_ENUMERATE);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(object));
js_iniSetObject(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	jsint      i;
	JSObject*  object;
	JSIdArray* id_array;
	jsval      set_argv[3];
	jsval      rval;
	char*      cp;
	private_t* p;
	str_list_t list;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	set_argv[0] = argv[0];    /* section */
	if (!JSVAL_IS_OBJECT(argv[1]) || argv[1] == JSVAL_NULL)
	object = JSVAL_TO_OBJECT(argv[1]);
	if (object == NULL || (id_array = JS_Enumerate(cx, object)) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
	if ((list = iniReadFile(p->fp)) == NULL) {
		JS_DestroyIdArray(cx, id_array);
		return JS_TRUE;
	}
	JS_RESUMEREQUEST(cx, rc);

	rval = JSVAL_TRUE;
	for (i = 0; i < id_array->length; i++)  {
		JS_IdToValue(cx, id_array->vector[i], &set_argv[1]);
		JSVALUE_TO_MSTRING(cx, set_argv[1], cp, NULL);
		if (cp == NULL) {
			JS_DestroyIdArray(cx, id_array);
			JS_ReportError(cx, "Invalid NULL property");
			return JS_FALSE;
		}
		if (JS_IsExceptionPending(cx)) {
			JS_DestroyIdArray(cx, id_array);
		(void)JS_GetProperty(cx, object, cp, &set_argv[2]);
		if (!js_iniSetValue_internal(cx, obj, 3, set_argv, &list)) {
	rc = JS_SUSPENDREQUEST(cx);
	if (rval == JSVAL_TRUE)
		rval = BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list));
	strListFree(&list);
	JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
	JS_SET_RVAL(cx, arglist, rval);
	JS_DestroyIdArray(cx, id_array);
js_iniGetAllObjects(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *       obj = JS_THIS_OBJECT(cx, arglist);
	jsval *          argv = JS_ARGV(cx, arglist);
	const char *     name_def = "name";
	char*            name = (char *)name_def;
	char*            sec_name;
	char*            prefix = NULL;
	char**           sec_list;
	str_list_t       ini;
	jsint            i, k;
	jsval            val;
	JSObject*        array;
	JSObject*        object;
	private_t*       p;
	jsrefcount       rc;
	bool             lowercase = false;
	bool             blanks = false;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc > argn && JSVAL_IS_STRING(argv[argn])) {
		JSVALUE_TO_MSTRING(cx, argv[argn], name, NULL);
		HANDLE_PENDING(cx, name);
			JS_ReportError(cx, "Invalid name argument");
			return JS_FALSE;
		}
		argn++;
	}
	if (argc > argn && JSVAL_IS_STRING(argv[argn])) {
		JSVALUE_TO_MSTRING(cx, argv[argn], prefix, NULL);
		argn++;
	}
	if (argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
		lowercase = JSVAL_TO_BOOLEAN(argv[argn]);
		argn++;
	if (argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
		blanks = JSVAL_TO_BOOLEAN(argv[argn]);
		argn++;
	}
	if (JS_IsExceptionPending(cx)) {
		if (name != name_def)
			free(name);
		return JS_FALSE;
	}
	array = JS_NewArrayObject(cx, 0, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	ini = iniReadFile(p->fp);
	sec_list = iniGetSectionList(ini, prefix);
	for (i = 0; sec_list && sec_list[i]; i++) {
		object = JS_NewObject(cx, NULL, NULL, obj);
		sec_name = sec_list[i];
		if (prefix != NULL)
			sec_name += strlen(prefix);
		if (lowercase)
		JS_DefineProperty(cx, object, name
		                  , STRING_TO_JSVAL(JS_NewStringCopyZ(cx, sec_name))
		                  , NULL, NULL, JSPROP_ENUMERATE);
		rc = JS_SUSPENDREQUEST(cx);
		key_list = iniGetNamedStringList(ini, sec_list[i]);
		for (k = 0; key_list && key_list[k]; k++) {
			if (lowercase)
				strlwr(key_list[k]->name);
			JS_DefineProperty(cx, object, key_list[k]->name
			                  , get_value(cx, key_list[k]->value, blanks)
			                  , NULL, NULL, JSPROP_ENUMERATE);
		rc = JS_SUSPENDREQUEST(cx);
		iniFreeNamedStringList(key_list);
		val = OBJECT_TO_JSVAL(object);
		if (!JS_SetElement(cx, array, i, &val))
	rc = JS_SUSPENDREQUEST(cx);
	if (name != name_def)
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
js_iniSetAllObjects(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *  obj = JS_THIS_OBJECT(cx, arglist);
	jsval *     argv = JS_ARGV(cx, arglist);
	const char *name_def = "name";
	char*       name = (char *)name_def;
	jsuint      i;
	jsint       j;
	jsuint      count;
	JSObject*   array;
	JSObject*   object;
	jsval       oval;
	jsval       set_argv[3];
	JSIdArray*  id_array;
	jsval       rval;
	char*       cp = NULL;
	str_list_t  list;
	jsrefcount  rc;
	private_t*  p;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0]))
	array = JSVAL_TO_OBJECT(argv[0]);
	if (array == NULL || !JS_IsArrayObject(cx, array))
	if (!JS_GetArrayLength(cx, array, &count))
		JSVALUE_TO_MSTRING(cx, argv[1], name, NULL);
		HANDLE_PENDING(cx, name);
			JS_ReportError(cx, "Invalid NULL name property");
			return JS_FALSE;
		}
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
		if (name != name_def)
			free(name);
	if (p->fp == NULL) {
		if (name != name_def)
			free(name);
	rc = JS_SUSPENDREQUEST(cx);
	if ((list = iniReadFile(p->fp)) == NULL) {
		if (name != name_def)
	for (i = 0; i < count && rval == JSVAL_TRUE; i++)  {
		if (!JS_GetElement(cx, array, i, &oval))
		if (!JSVAL_IS_OBJECT(oval))  /* must be an array of objects */
		object = JSVAL_TO_OBJECT(oval);
		if (object == NULL || !JS_GetProperty(cx, object, name, &set_argv[0]))
		if ((id_array = JS_Enumerate(cx, object)) == NULL) {
			if (name != name_def)
		for (j = 0; j < id_array->length; j++)  {
			JS_IdToValue(cx, id_array->vector[j], &set_argv[1]);
			JSVALUE_TO_MSTRING(cx, set_argv[1], cp, NULL);
			if (JS_IsExceptionPending(cx)) {
				JS_DestroyIdArray(cx, id_array);
				if (name != name_def)
					free(name);
				return JS_FALSE;
			}
			if (strcmp(cp, name) == 0) {
			if (!JS_GetProperty(cx, object, cp, &set_argv[2])) {
				FREE_AND_NULL(cp);
				continue;
			}
			FREE_AND_NULL(cp);  /* Moved from before JS_GetProperty() call */
			if (!js_iniSetValue_internal(cx, obj, 3, set_argv, &list)) {
		JS_DestroyIdArray(cx, id_array);
	if (name != name_def)
	rc = JS_SUSPENDREQUEST(cx);
	if (rval == JSVAL_TRUE)
		rval = BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list));
	strListFree(&list);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, rval);

static JSBool
js_iniReadAll(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	private_t* p;
	jsrefcount rc;

	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL)
	JSObject* array = JS_NewArrayObject(cx, 0, NULL);
	if (array == NULL)
	rc = JS_SUSPENDREQUEST(cx);
	str_list_t list = iniReadFile(p->fp);
	JS_RESUMEREQUEST(cx, rc);
	for (size_t i = 0; list != NULL && list[i] != NULL; i++) {
		JSString* js_str;
		if ((js_str = JS_NewStringCopyZ(cx, list[i])) == NULL)
		jsval     val = STRING_TO_JSVAL(js_str);
		if (!JS_SetElement(cx, array, i, &val))
			break;
	}
	strListFree(&list);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array));
static JSBool
js_raw_write(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      cp = NULL;
	size_t     len;     /* string length */
	JSString*  str;
	private_t* p;
	jsrefcount rc;
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if ((str = JS_ValueToString(cx, argv[0])) == NULL)

	JSSTRING_TO_MSTRING(cx, str, cp, &len);
	HANDLE_PENDING(cx, cp);
	rc = JS_SUSPENDREQUEST(cx);
	if (write(fileno(p->fp), cp, len) == (size_t)len) {
		dbprintf(FALSE, p, "wrote %lu raw bytes", len);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	} else {
		free(cp);
		dbprintf(TRUE, p, "raw write of %lu bytes failed", len);
js_write(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      cp = NULL;
	char*      uubuf = NULL;
	size_t     len;     /* string length */
	int        decoded_len;
	size_t     tlen;    /* total length to write (may be greater than len) */
	int32      i;
	JSString*  str;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if ((str = JS_ValueToString(cx, argv[0])) == NULL)
	JSSTRING_TO_MSTRING(cx, str, cp, &len);
	HANDLE_PENDING(cx, cp);
	rc = JS_SUSPENDREQUEST(cx);
	if ((p->uuencoded || p->b64encoded || p->yencoded)
	    && len && (uubuf = malloc(len)) != NULL) {
		if (p->uuencoded)
			decoded_len = uudecode(uubuf, len, cp, len);
		else if (p->yencoded)
			decoded_len = ydecode(uubuf, len, cp, len);
			decoded_len = b64_decode(uubuf, len, cp, len);
		if (decoded_len < 0) {
		len = decoded_len;
	tlen = len;
	if (argc > 1 && !JSVAL_NULL_OR_VOID(argv[1])) {
		if (!JS_ValueToInt32(cx, argv[1], &i)) {
		tlen = i;
		if (len > tlen)
			len = tlen;
	rc = JS_SUSPENDREQUEST(cx);
	if (fwrite(cp, 1, len, p->fp) == (size_t)len) {
		if (tlen > len) {
			len = tlen - len;
			if ((cp = malloc(len)) == NULL) {
				JS_ReportError(cx, "malloc failure of %u bytes", len);
			memset(cp, p->etx, len);
			if (fwrite(cp, 1, len, p->fp) < len) {
				free(cp);
				JS_RESUMEREQUEST(cx, rc);
				return JS_TRUE;
			}
		dbprintf(FALSE, p, "wrote %lu bytes", (ulong)tlen);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
		dbprintf(TRUE, p, "write of %lu bytes failed", (ulong)len);
deuce's avatar
deuce committed
js_writeln_internal(JSContext *cx, JSObject *obj, jsval *arg, jsval *rval)
	const char *cp_def = "";
	char*       cp = (char *)cp_def;
	JSString*   str;
	private_t*  p;
	jsrefcount  rc;
deuce's avatar
deuce committed
	*rval = JSVAL_FALSE;
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (arg) {
		if ((str = JS_ValueToString(cx, *arg)) == NULL) {
			JS_ReportError(cx, "JS_ValueToString failed");
		JSSTRING_TO_MSTRING(cx, str, cp, NULL);
		HANDLE_PENDING(cx, cp);
		if (cp == NULL)
			cp = (char *)cp_def;
	rc = JS_SUSPENDREQUEST(cx);
	if (p->rot13)
	if (fprintf(p->fp, "%s\n", cp) != 0)
deuce's avatar
deuce committed
		*rval = JSVAL_TRUE;
deuce's avatar
deuce committed
static JSBool
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject *obj = JS_THIS_OBJECT(cx, arglist);
	jsval *   argv = JS_ARGV(cx, arglist);
	jsval     rval;
	JSBool    ret;
deuce's avatar
deuce committed

	if (argc) {
		ret = js_writeln_internal(cx, obj, &argv[0], &rval);
deuce's avatar
deuce committed
	}
	else {
		ret = js_writeln_internal(cx, obj, NULL, &rval);
deuce's avatar
deuce committed
	}
	JS_SET_RVAL(cx, arglist, rval);

deuce's avatar
deuce committed
}

static JSBool
js_writebin(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj = JS_THIS_OBJECT(cx, arglist);
	jsval *   argv = JS_ARGV(cx, arglist);
		uint8_t *b;
		uint16_t *w;
		uint32_t *l;
		uint64_t *q;
		int8_t *sb;
		int16_t *sw;
		int32_t *sl;
		int64_t *sq;
	size_t     wr = 0;
	int32      size = sizeof(int32_t);
	jsuint     count = 1;
	void *     buffer;
	private_t* p;
	JSObject*  array = NULL;
	jsval      elemval;
	jsdouble   val = 0;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (JSVAL_IS_OBJECT(argv[0]) && !JSVAL_IS_NULL(argv[0])) {
		array = JSVAL_TO_OBJECT(argv[0]);
		if (array != NULL && JS_IsArrayObject(cx, array)) {
			if (!JS_GetArrayLength(cx, array, &count))
	if (array == NULL) {
		if (!JS_ValueToNumber(cx, argv[0], &val))
	if (argc > 1 && !JSVAL_NULL_OR_VOID(argv[1])) {
		if (!JS_ValueToInt32(cx, argv[1], &size))
	if (size != sizeof(BYTE) && size != sizeof(WORD) && size != sizeof(DWORD) && size != sizeof(uint64_t)) {
		rc = JS_SUSPENDREQUEST(cx);
		dbprintf(TRUE, p, "unsupported binary write size: %d", size);
	buffer = calloc(size, count);
	if (buffer == NULL) {
		rc = JS_SUSPENDREQUEST(cx);
		dbprintf(TRUE, p, "malloc failure of %u bytes", size * count);
	o.b = buffer;
	if (array == NULL) {
		switch (size) {
				if (val < 0)
					*o.sb = (int8_t)val;
					*o.b = (uint8_t)val;
				if (val < 0)
					*o.sw = (int16_t)val;
					*o.w = (uint16_t)val;
				if (p->network_byte_order)
				if (val < 0)
					*o.sl = (int32_t)val;
					*o.l = (uint32_t)val;
				if (p->network_byte_order)
					*o.sq = (int64_t)val;
				else
					*o.q = (uint64_t)val;
				if (p->network_byte_order)
					*o.q = BE_INT64(*o.q);
				else
					*o.q = LE_INT64(*o.q);
		for (wr = 0; wr < count; wr++) {
			if (!JS_GetElement(cx, array, wr, &elemval))
			if (!JS_ValueToNumber(cx, elemval, &val))
					if (val < 0)
						*o.sb = (int8_t)val;
						*o.b = (uint8_t)val;
					if (val < 0)
						*o.sw = (int16_t)val;
						*o.w = (uint16_t)val;
					if (p->network_byte_order)
					if (val < 0)
						*o.sl = (int32_t)val;
						*o.l = (uint32_t)val;
					if (p->network_byte_order)
						*o.sq = (int64_t)val;
					else
						*o.q = (uint64_t)val;
					if (p->network_byte_order)
						*o.q = BE_INT64(*o.q);
					else
						*o.q = LE_INT64(*o.q);
	rc = JS_SUSPENDREQUEST(cx);
	wr = fwrite(buffer, size, count, p->fp);
	if (wr == count)
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_writeall(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	jsuint     i;
	jsuint     limit;
	JSObject*  array;
	jsval      elemval;
	private_t* p;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0]))
	array = JSVAL_TO_OBJECT(argv[0]);
	if (array == NULL || !JS_IsArrayObject(cx, array))
	if (!JS_GetArrayLength(cx, array, &limit))
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
	for (i = 0; i < limit; i++) {
		jsval rval;
deuce's avatar
deuce committed

		if (!JS_GetElement(cx, array, i, &elemval))
		js_writeln_internal(cx, obj, &elemval, &rval);
		JS_SET_RVAL(cx, arglist, rval);
		if (rval != JSVAL_TRUE)
js_lock(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	off_t      offset = 0;
	off_t      len = 0;
	private_t* p;
	jsrefcount rc;
	jsdouble   val;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc) {
		if (!JS_ValueToNumber(cx, argv[0], &val))
		offset = (off_t)val;
	if (argc > 1) {
		if (!JS_ValueToNumber(cx, argv[1], &val))
	rc = JS_SUSPENDREQUEST(cx);
	if (len == 0)
		len = filelength(fileno(p->fp)) - offset;
	if (lock(fileno(p->fp), offset, len) == 0)
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_unlock(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	off_t      offset = 0;
	off_t      len = 0;
	private_t* p;
	jsrefcount rc;
	jsdouble   val;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc) {
		if (!JS_ValueToNumber(cx, argv[0], &val))
		offset = (off_t)val;
	if (argc > 1) {
		if (!JS_ValueToNumber(cx, argv[1], &val))
	rc = JS_SUSPENDREQUEST(cx);
	if (len == 0)
		len = filelength(fileno(p->fp)) - offset;
	if (unlock(fileno(p->fp), offset, len) == 0)
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
static JSBool
js_delete(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (p->fp != NULL) {   /* close it if it's open */
		fclose(p->fp);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(remove(p->name) == 0));
js_flush(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	rc = JS_SUSPENDREQUEST(cx);
	if (p->fp == NULL)
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(fflush(p->fp) == 0));
js_rewind(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	rc = JS_SUSPENDREQUEST(cx);
	if (p->fp == NULL)
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_truncate(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	private_t* p;
	int32      len = 0;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if (argc && !JSVAL_NULL_OR_VOID(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &len))
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (p->fp != NULL && chsize(fileno(p->fp), len) == 0) {
		fseek(p->fp, len, SEEK_SET);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_clear_error(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	rc = JS_SUSPENDREQUEST(cx);
	if (p->fp == NULL)
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	else  {
		clearerr(p->fp);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_fprintf(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      cp;
	private_t* p;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) {
	if ((cp = js_sprintf(cx, 0, argc, argv)) == NULL) {
		JS_ReportError(cx, "js_sprintf failed");
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(fwrite(cp, 1, strlen(cp), p->fp)));
/* File Object Properties */
	FILE_PROP_NAME
	, FILE_PROP_MODE
	, FILE_PROP_ETX
	, 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
	, FILE_PROP_YENCODED
	, FILE_PROP_UUENCODED
	, FILE_PROP_B64ENCODED
	, FILE_PROP_ROT13
	, FILE_PROP_NETWORK_ORDER
	, FILE_PROP_CHKSUM
	, FILE_PROP_CRC16
	, FILE_PROP_CRC32
	, FILE_PROP_MD5_HEX
	, FILE_PROP_MD5_B64
	, FILE_PROP_SHA1_HEX
	, FILE_PROP_SHA1_B64
	, FILE_INI_KEY_LEN
	, FILE_INI_KEY_PREFIX
	, FILE_INI_SECTION_SEPARATOR
	, FILE_INI_VALUE_SEPARATOR
	, FILE_INI_BIT_SEPARATOR
	, FILE_INI_LITERAL_SEPARATOR
static JSBool js_file_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
	jsval      idval;
	int        result;
	int32      i = 0;
	uint32     u = 0;
	jsint      tiny;
	private_t* p;
	jsrefcount rc;
	char*      str = NULL;

	if ((p = (private_t*)JS_GetInstancePrivate(cx, obj, &js_file_class, NULL)) == NULL) {
	JS_IdToValue(cx, id, &idval);
	tiny = JSVAL_TO_INT(idval);
	rc = JS_SUSPENDREQUEST(cx);
	dbprintf(FALSE, p, "setting property %d", tiny);
		case FILE_PROP_DEBUG:
			JS_ValueToBoolean(cx, *vp, &(p->debug));
			JS_ValueToBoolean(cx, *vp, &(p->yencoded));
			JS_ValueToBoolean(cx, *vp, &(p->uuencoded));
			JS_ValueToBoolean(cx, *vp, &(p->b64encoded));
			JS_ValueToBoolean(cx, *vp, &(p->rot13));
			JS_ValueToBoolean(cx, *vp, &(p->network_byte_order));
		case FILE_PROP_POSITION:
			if (p->fp != NULL) {
				if (!JS_ValueToECMAUint32(cx, *vp, &u))
				rc = JS_SUSPENDREQUEST(cx);
			if (!JS_ValueToECMAUint32(cx, *vp, &u))
			rc = JS_SUSPENDREQUEST(cx);
		case FILE_PROP_LENGTH:
			if (p->fp != NULL) {
				if (!JS_ValueToECMAUint32(cx, *vp, &u))
				rc = JS_SUSPENDREQUEST(cx);
				result = chsize(fileno(p->fp), u);
					JS_ReportError(cx, "Error %d changing file size", errno);
					return JS_FALSE;
				}
			break;
		case FILE_PROP_ATTRIBUTES:
			if (!JS_ValueToInt32(cx, *vp, &i))
			rc = JS_SUSPENDREQUEST(cx);
			(void)CHMOD(p->name, i);
		case FILE_PROP_ETX:
			if (!JS_ValueToInt32(cx, *vp, &i))
			if (!JS_ValueToInt32(cx, *vp, &i))
			p->ini_style.key_len = i;
			break;
		case FILE_INI_KEY_PREFIX:
			FREE_AND_NULL(p->ini_style.key_prefix);
			if (!JSVAL_NULL_OR_VOID(*vp)) {
				JSVALUE_TO_MSTRING(cx, *vp, str, NULL);
				HANDLE_PENDING(cx, str);
				p->ini_style.key_prefix = str;
			}
			break;
		case FILE_INI_SECTION_SEPARATOR:
			FREE_AND_NULL(p->ini_style.section_separator);
			if (!JSVAL_NULL_OR_VOID(*vp)) {
				JSVALUE_TO_MSTRING(cx, *vp, str, NULL);
				HANDLE_PENDING(cx, str);
				p->ini_style.section_separator = str;
			}
			break;
		case FILE_INI_VALUE_SEPARATOR:
			FREE_AND_NULL(p->ini_style.value_separator);
			if (!JSVAL_NULL_OR_VOID(*vp)) {
				JSVALUE_TO_MSTRING(cx, *vp, str, NULL);
				HANDLE_PENDING(cx, str);
				p->ini_style.value_separator = str;
			}
			break;
		case FILE_INI_BIT_SEPARATOR:
			FREE_AND_NULL(p->ini_style.bit_separator);
			if (!JSVAL_NULL_OR_VOID(*vp)) {
				JSVALUE_TO_MSTRING(cx, *vp, str, NULL);
				HANDLE_PENDING(cx, str);
				p->ini_style.bit_separator = str;
			}
			break;
		case FILE_INI_LITERAL_SEPARATOR:
			FREE_AND_NULL(p->ini_style.literal_separator);
			if (!JSVAL_NULL_OR_VOID(*vp)) {
				JSVALUE_TO_MSTRING(cx, *vp, str, NULL);
				HANDLE_PENDING(cx, str);
				p->ini_style.literal_separator = str;
			}
static JSBool js_file_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
	jsval      idval;
	char       str[128];
	char*      s = NULL;
	size_t     i;
	size_t     rd;
	off_t      offset;
	ulong      sum = 0;
	ushort     c16 = 0;
	uint32     c32 = ~0;
	MD5        md5_ctx;
	SHA1_CTX   sha1_ctx;
	BYTE       block[4096];
	BYTE       digest[SHA1_DIGEST_SIZE];
	jsint      tiny;
	JSString*  js_str = NULL;
	private_t* p;
	jsrefcount rc;
	time_t     tt;
	off_t      lng;
	int        in;

	if ((p = (private_t*)JS_GetPrivate(cx, obj)) == NULL)
	JS_IdToValue(cx, id, &idval);
	tiny = JSVAL_TO_INT(idval);
		case FILE_PROP_NAME:
			break;
		case FILE_PROP_MODE:
			break;
		case FILE_PROP_EXISTS:
			if (p->fp)   /* open? */
deuce's avatar
deuce committed
			else {
				rc = JS_SUSPENDREQUEST(cx);
				*vp = BOOLEAN_TO_JSVAL(fexistcase(p->name));
			break;
		case FILE_PROP_DATE:
			rc = JS_SUSPENDREQUEST(cx);
			tt = fdate(p->name);
			*vp = DOUBLE_TO_JSVAL((double)tt);
			break;
		case FILE_PROP_IS_OPEN:
			*vp = BOOLEAN_TO_JSVAL(p->fp != NULL);
			break;
		case FILE_PROP_EOF:
			if (p->fp) {
				rc = JS_SUSPENDREQUEST(cx);
				*vp = BOOLEAN_TO_JSVAL(feof(p->fp) != 0);
			break;
		case FILE_PROP_ERROR:
			if (p->fp) {
				rc = JS_SUSPENDREQUEST(cx);
				*vp = INT_TO_JSVAL(ferror(p->fp));
			break;
		case FILE_PROP_POSITION:
			if (p->fp) {
				rc = JS_SUSPENDREQUEST(cx);
				lng = ftell(p->fp);
				*vp = DOUBLE_TO_JSVAL((double)lng);
			break;
		case FILE_PROP_LENGTH:
			rc = JS_SUSPENDREQUEST(cx);
			if (p->fp)   /* open? */
deuce's avatar
deuce committed
				lng = filelength(fileno(p->fp));
deuce's avatar
deuce committed
				lng = flength(p->name);
			*vp = DOUBLE_TO_JSVAL((double)lng);
			break;
		case FILE_PROP_ATTRIBUTES:
			rc = JS_SUSPENDREQUEST(cx);
			in = getfmode(p->name);
			*vp = INT_TO_JSVAL(in);
			break;
		case FILE_PROP_DEBUG:
			*vp = BOOLEAN_TO_JSVAL(p->debug);
			break;
		case FILE_PROP_YENCODED:
			*vp = BOOLEAN_TO_JSVAL(p->yencoded);
			break;
		case FILE_PROP_UUENCODED:
			*vp = BOOLEAN_TO_JSVAL(p->uuencoded);
		case FILE_PROP_B64ENCODED:
			*vp = BOOLEAN_TO_JSVAL(p->b64encoded);
			break;
		case FILE_PROP_ROT13:
			*vp = BOOLEAN_TO_JSVAL(p->rot13);
			break;
		case FILE_PROP_NETWORK_ORDER:
			*vp = BOOLEAN_TO_JSVAL(p->network_byte_order);
			break;
		case FILE_PROP_DESCRIPTOR:
				*vp = INT_TO_JSVAL(fileno(p->fp));
			else
				*vp = INT_TO_JSVAL(-1);
		case FILE_PROP_ETX:
			*vp = INT_TO_JSVAL(p->etx);
			break;
		case FILE_PROP_CHKSUM:
		case FILE_PROP_CRC16:
		case FILE_PROP_CRC32:
			*vp = JSVAL_ZERO;
		case FILE_PROP_SHA1_HEX:
		case FILE_PROP_SHA1_B64:
			rc = JS_SUSPENDREQUEST(cx);
			offset = ftell(p->fp);            /* save current file position */
			fseek(p->fp, 0, SEEK_SET);
				case FILE_PROP_MD5_HEX:
				case FILE_PROP_MD5_B64:
					MD5_open(&md5_ctx);
					break;
				case FILE_PROP_SHA1_HEX:
				case FILE_PROP_SHA1_B64:
					SHA1Init(&sha1_ctx);
					break;
			while (!feof(p->fp)) {
				if ((rd = fread(block, 1, sizeof(block), p->fp)) < 1)
						for (i = 0; i < rd; i++)
							sum += block[i];
						for (i = 0; i < rd; i++)
							c16 = ucrc16(block[i], c16);
						for (i = 0; i < rd; i++)
							c32 = ucrc32(block[i], c32);
						break;
					case FILE_PROP_MD5_HEX:
					case FILE_PROP_MD5_B64:
						MD5_digest(&md5_ctx, block, rd);
					case FILE_PROP_SHA1_HEX:
					case FILE_PROP_SHA1_B64:
						SHA1Update(&sha1_ctx, block, rd);
					*vp = DOUBLE_TO_JSVAL((double)sum);
					*vp = UINT_TO_JSVAL(c16);
					*vp = UINT_TO_JSVAL(~c32);
					break;
				case FILE_PROP_MD5_HEX:
				case FILE_PROP_MD5_B64:
					MD5_close(&md5_ctx, digest);
					if (tiny == FILE_PROP_MD5_HEX)
						MD5_hex(str, digest);
						b64_encode(str, sizeof(str) - 1, (char *)digest, sizeof(digest));
					js_str = JS_NewStringCopyZ(cx, str);
					break;
				case FILE_PROP_SHA1_HEX:
				case FILE_PROP_SHA1_B64:
					SHA1Final(&sha1_ctx, digest);
					if (tiny == FILE_PROP_SHA1_HEX)
						SHA1_hex(str, digest);
						b64_encode(str, sizeof(str) - 1, (char *)digest, sizeof(digest));
					js_str = JS_NewStringCopyZ(cx, str);
			rc = JS_SUSPENDREQUEST(cx);
			fseeko(p->fp, offset, SEEK_SET);  /* restore saved file position */
		case FILE_INI_KEY_LEN:
			*vp = INT_TO_JSVAL(p->ini_style.key_len);
			break;
		case FILE_INI_KEY_PREFIX:
			s = p->ini_style.key_prefix;
			break;
		case FILE_INI_SECTION_SEPARATOR:
			s = p->ini_style.section_separator;
			break;
		case FILE_INI_VALUE_SEPARATOR:
			s = p->ini_style.value_separator;
			break;
		case FILE_INI_BIT_SEPARATOR:
			s = p->ini_style.bit_separator;
			break;
		case FILE_INI_LITERAL_SEPARATOR:
			s = p->ini_style.literal_separator;
	if (s != NULL) {
		if ((js_str = JS_NewStringCopyZ(cx, s)) == NULL)
			return JS_FALSE;
		*vp = STRING_TO_JSVAL(js_str);
#define FILE_PROP_FLAGS JSPROP_ENUMERATE | JSPROP_READONLY
static jsSyncPropertySpec js_file_properties[] = {
/*		 name				,tinyid					,flags,				ver	*/
	{   "name", FILE_PROP_NAME, FILE_PROP_FLAGS,   310},
	{   "mode", FILE_PROP_MODE, FILE_PROP_FLAGS,   310},
	{   "exists", FILE_PROP_EXISTS, FILE_PROP_FLAGS,   310},
	{   "is_open", FILE_PROP_IS_OPEN, FILE_PROP_FLAGS,   310},
	{   "eof", FILE_PROP_EOF, FILE_PROP_FLAGS,   310},
	{   "error", FILE_PROP_ERROR, FILE_PROP_FLAGS,   310},
	{   "descriptor", FILE_PROP_DESCRIPTOR, FILE_PROP_FLAGS,   310},
	{   "etx", FILE_PROP_ETX, JSPROP_ENUMERATE,  310},
	{   "debug", FILE_PROP_DEBUG, JSPROP_ENUMERATE,  310},
	{   "position", FILE_PROP_POSITION, JSPROP_ENUMERATE,  310},
	{   "date", FILE_PROP_DATE, JSPROP_ENUMERATE,  311},
	{   "length", FILE_PROP_LENGTH, JSPROP_ENUMERATE,  310},
	{   "attributes", FILE_PROP_ATTRIBUTES, JSPROP_ENUMERATE,  310},
	{   "network_byte_order", FILE_PROP_NETWORK_ORDER, JSPROP_ENUMERATE,  311},
	{   "rot13", FILE_PROP_ROT13, JSPROP_ENUMERATE,  311},
	{   "uue", FILE_PROP_UUENCODED, JSPROP_ENUMERATE,  311},
	{   "yenc", FILE_PROP_YENCODED, JSPROP_ENUMERATE,  311},
	{   "base64", FILE_PROP_B64ENCODED, JSPROP_ENUMERATE,  311},
	{   "crc16", FILE_PROP_CRC16, FILE_PROP_FLAGS,   311},
	{   "crc32", FILE_PROP_CRC32, FILE_PROP_FLAGS,   311},
	{   "chksum", FILE_PROP_CHKSUM, FILE_PROP_FLAGS,   311},
	{   "md5_hex", FILE_PROP_MD5_HEX, FILE_PROP_FLAGS,   311},
	{   "md5_base64", FILE_PROP_MD5_B64, FILE_PROP_FLAGS,   311},
	{   "sha1_hex", FILE_PROP_SHA1_HEX, FILE_PROP_FLAGS,   31900},
	{   "sha1_base64", FILE_PROP_SHA1_B64, FILE_PROP_FLAGS,   31900},
	/* ini style elements */
	{   "ini_key_len", FILE_INI_KEY_LEN, JSPROP_ENUMERATE,  317},
	{   "ini_key_prefix", FILE_INI_KEY_PREFIX, JSPROP_ENUMERATE,  317},
	{   "ini_section_separator", FILE_INI_SECTION_SEPARATOR, JSPROP_ENUMERATE,  317},
	{   "ini_value_separator", FILE_INI_VALUE_SEPARATOR, JSPROP_ENUMERATE,  317},
	{   "ini_bit_separator", FILE_INI_BIT_SEPARATOR, JSPROP_ENUMERATE,  317},
	{   "ini_literal_separator", FILE_INI_LITERAL_SEPARATOR, JSPROP_ENUMERATE,  317},
static const 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 is open or exists (case-insensitive) - <small>READ ONLY</small>"
	, "<i>true</i> if the file has been opened successfully - <small>READ ONLY</small>"
	, "<i>true</i> if the current file position is at the <i>end of file</i> - <small>READ ONLY</small>"
	, "The last occurred error value (use <i>clear_error</i> 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>"
	, "Set to <i>true</i> to enable debug log output"
	, "The current file position (offset in bytes), change value to seek within file"
	, "Last modified date/time (in time_t format)"
	, "The current length of the file (in bytes)"
	, "File type/mode flags (i.e. <tt>struct stat.st_mode</tt> value, compatible with <tt>file_chmod()</tt>)"
	, "Set to <i>true</i> if binary data is to be written and read in Network Byte Order (big end first)"
	, "Set to <i>true</i> to enable automatic ROT13 translation of text"
	, "Set to <i>true</i> to enable automatic Unix-to-Unix encode and decode on <tt>read</tt> and <tt>write</tt> calls"
	, "Set to <i>true</i> to enable automatic yEnc encode and decode on <tt>read</tt> and <tt>write</tt> calls"
	, "Set to <i>true</i> to enable automatic Base64 encode and decode on <tt>read</tt> and <tt>write</tt> calls"
	, "Calculated 16-bit CRC of file contents - <small>READ ONLY</small>"
	, "Calculated 32-bit CRC of file contents - <small>READ ONLY</small>"
	, "Calculated 32-bit checksum of file contents - <small>READ ONLY</small>"
	, "Calculated 128-bit MD5 digest of file contents as hexadecimal string - <small>READ ONLY</small>"
	, "Calculated 128-bit MD5 digest of file contents as base64-encoded string - <small>READ ONLY</small>"
	, "Calculated 160-bit SHA1 digest of file contents as hexadecimal string - <small>READ ONLY</small>"
	, "Calculated 160-bit SHA1 digest of file contents as base64-encoded string - <small>READ ONLY</small>"
	, "Ini style: minimum key length (for left-justified white-space padded keys)"
	, "Ini style: key prefix (e.g. '\\t', null = default prefix)"
	, "Ini style: section separator (e.g. '\\n', null = default separator)"
	, "Ini style: value separator (e.g. ' = ', null = default separator)"
	, "Ini style: bit separator (e.g. ' | ', null = default separator)"
	, "Ini style: literal separator (null = default separator)"
	, NULL
static jsSyncMethodSpec js_file_functions[] = {
	{"open",            js_open,            1,  JSTYPE_BOOLEAN, JSDOCSTR("[<i>string</i> mode=\"w+\"] [,<i>bool</i> shareable=false] [,<i>number</i> buffer_length]")
	 , JSDOCSTR("Open file, <i>shareable</i> defaults to <i>false</i>, <i>buffer_length</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 doesn't 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>"
		        "<tt>x&nbsp</tt> open a <i>non-shareable</i> file (that must not already exist) for <i>exclusive</i> access<br>"
		        "<br><b>Note:</b> When using the <tt>iniSet</tt> methods to modify a <tt>.ini</tt> file, "
		        "the file must be opened for both reading <b>and</b> writing.<br>"
		        "<br><b>Note:</b> To open an existing or create a new file for both reading and writing "
		        "(e.g. updating an <tt>.ini</tt> file) "
		        "use the <i>exists</i> property like so:<br>"
		        "<tt>file.open(file.exists ? 'r+':'w+');</tt><br>"
		        "<br><b>Note:</b> When <i>shareable</i> is <tt>false</tt>, uses the Synchronet <tt>nopen()</tt> function which will lock the file "
		        "and perform automatic retries.  The lock mode is as follows:<br>"
		        "<tt>r&nbsp</tt> DENYWRITE - Allows other scripts to open the file for reading, but not for writing.<br>"
		        "<tt>w&nbsp</tt> DENYALL - Does not allow other scripts to open the file when <i>shareable</i> is set to true<br>"
		        "<tt>a&nbsp</tt> DENYALL - Does not allow other scripts to open the file when <i>shareable</i> is set to true<br>"
		        "<tt>r+</tt> DENYALL - Does not allow other scripts to open the file when <i>shareable</i> is set to true<br>"
		        "<tt>w+</tt> DENYALL - Does not allow other scripts to open the file when <i>shareable</i> is set to true<br>"
		        "<tt>a+</tt> DENYALL - Does not allow other scripts to open the file when <i>shareable</i> is set to true<br>"
		        "<br>When <i>shareable</i> is <tt>true</tt> uses the standard C <tt>fopen()</tt> function, "
		        "and will only attempt to open the file once and will perform no locking.<br>"
		        "The behavior when one script has a file opened with <i>shareable</i> set to a different value than is used "
		        "with a new call is OS specific.  On Windows, the second open will always fail and on *nix, "
		        "the second open will always succeed.<br>"
		        )
	 , 310},
	{"popen",           js_popen,           1,  JSTYPE_BOOLEAN, JSDOCSTR("[<i>string</i> mode=\"r+\"] [,<i>number</i> buffer_length]")
	 , JSDOCSTR("Open pipe to command, <i>buffer_length</i> defaults to 2048 bytes, "
		        "mode (default: <tt>'r+'</tt>) specifies the type of access requested for the file, as follows:<br>"
		        "<tt>r&nbsp</tt> read the programs stdout;<br>"
		        "<tt>w&nbsp</tt> write to the programs stdin<br>"
		        "<tt>r+</tt> open for both reading stdout and writing stdin<br>"
		        "(<b>only functional on UNIX systems</b>)"
		        )
	 , 315},
	{"close",           js_close,           0,  JSTYPE_VOID,    JSDOCSTR("")
	 , JSDOCSTR("Close file")
	 , 310},
	{"remove",          js_delete,          0,  JSTYPE_BOOLEAN, JSDOCSTR("")
	 , JSDOCSTR("Remove the file from the disk")
	 , 310},
	{"clearError",      js_clear_error,     0,  JSTYPE_ALIAS },
	{"clear_error",     js_clear_error,     0,  JSTYPE_BOOLEAN, JSDOCSTR("")
	 , JSDOCSTR("Clears the current error value (AKA clearError)")
	 , 310},
	{"flush",           js_flush,           0,  JSTYPE_BOOLEAN, JSDOCSTR("")
	 , JSDOCSTR("Flush/commit buffers to disk")
	 , 310},
	{"rewind",          js_rewind,          0,  JSTYPE_BOOLEAN, JSDOCSTR("")
	 , JSDOCSTR("Repositions the file pointer (<i>position</i>) to the beginning of a file "
		        "and clears error and end-of-file indicators")
	 , 311},
	{"truncate",        js_truncate,        0,  JSTYPE_BOOLEAN, JSDOCSTR("[length=0]")
	 , JSDOCSTR("Changes the file <i>length</i> (default: 0) and repositions the file pointer "
		        "(<i>position</i>) to the new end-of-file")
	 , 314},
	{"lock",            js_lock,            2,  JSTYPE_BOOLEAN, JSDOCSTR("[offset=0] [,length=<i>file_length</i>-<i>offset</i>]")
	 , JSDOCSTR("Lock file record for exclusive access (file must be opened <i>shareable</i>)")
	 , 310},
	{"unlock",          js_unlock,          2,  JSTYPE_BOOLEAN, JSDOCSTR("[offset=0] [,length=<i>file_length</i>-<i>offset</i>]")
	 , JSDOCSTR("Unlock file record for exclusive access")
	 , 310},
	{"read",            js_read,            0,  JSTYPE_STRING,  JSDOCSTR("[maxlen=<i>file_length</i>-<i>file_position</i>]")
	 , JSDOCSTR("Read a string from file (optionally unix-to-unix or base64 encoding in the process), "
		        "<i>maxlen</i> defaults to the current length of the file minus the current file position")
	 , 310},
	{"readln",          js_readln,          0,  JSTYPE_STRING,  JSDOCSTR("[maxlen=512]")
	 , JSDOCSTR("Read a line-feed terminated string, <i>maxlen</i> defaults to 512 characters. "
		        "Returns <i>null</i> upon end of file.")
	 , 310},
	{"readBin",         js_readbin,         0,  JSTYPE_NUMBER,  JSDOCSTR("[bytes=4 [,count=1]]")
	 , JSDOCSTR("Read one or more binary integers from the file, default number of <i>bytes</i> is 4 (32-bits). "
		        "if count is not equal to 1, an array is returned (even if no integers were read)")
	 , 310},
	{"readAll",         js_readall,         0,  JSTYPE_ARRAY,   JSDOCSTR("[maxlen=512]")
	 , JSDOCSTR("Read all lines into an array of strings, <i>maxlen</i> defaults to 512 characters")
	 , 310},
	{"raw_read",        js_raw_read,        0,  JSTYPE_STRING,  JSDOCSTR("[maxlen=1]")
	 , JSDOCSTR("Read a string from underlying file descriptor. "
		        "Undefined results when mixed with any other read/write methods except raw_write, including indirect ones. "
		        "<i>maxlen</i> defaults to one")
	 , 317},
	{"raw_pollin",      js_raw_pollin,      0,  JSTYPE_BOOLEAN, JSDOCSTR("[timeout]")
	 , JSDOCSTR("Waits up to <i>timeout</i> milliseconds (or forever if timeout is not specified) for data to be available "
		        "via raw_read().")
	 , 317},
	{"write",           js_write,           1,  JSTYPE_BOOLEAN, JSDOCSTR("text [,length=<i>text_length</i>]")
	 , JSDOCSTR("Write a string to the file (optionally unix-to-unix or base64 decoding in the process). "
		        "If the specified <i>length</i> is longer than the <i>text</i>, the remaining length will be written as NUL bytes.")
	 , 310},
	{"writeln",         js_writeln,         0,  JSTYPE_BOOLEAN, JSDOCSTR("[text]")
	 , JSDOCSTR("Write a new-line terminated string (a line of text) to the file")
	 , 310},
	{"writeBin",        js_writebin,        1,  JSTYPE_BOOLEAN, JSDOCSTR("value(s) [,bytes=4]")
	 , JSDOCSTR("Write one or more binary integers to the file, default number of <i>bytes</i> is 4 (32-bits). "
		        "If value is an array, writes the entire array to the file.")
	 , 310},
	{"writeAll",        js_writeall,        0,  JSTYPE_BOOLEAN, JSDOCSTR("array lines")
	 , JSDOCSTR("Write an array of new-line terminated strings (lines of text) to the file")
	 , 310},
	{"raw_write",       js_raw_write,       1,  JSTYPE_BOOLEAN, JSDOCSTR("text")
	 , JSDOCSTR("Write a string to the underlying file descriptor. "
		        "Undefined results when mixed with any other read/write methods except raw_read, including indirect ones.")
	 , 317},
	{"printf",          js_fprintf,         0,  JSTYPE_NUMBER,  JSDOCSTR("format [,args]")
	 , JSDOCSTR("Write a C-style formatted string to the file (ala the standard C <tt>fprintf</tt> function)")
	 , 310},
	{"iniGetSections",  js_iniGetSections,  0,  JSTYPE_ARRAY,   JSDOCSTR("[prefix=<i>none</i>]")
	 , JSDOCSTR("Parse all section names from a <tt>.ini</tt> file (format = '<tt>[section]</tt>') "
		        "and return the section names as an <i>array of strings</i>, "
		        "optionally, only those section names that begin with the specified <i>prefix</i>")
	 , 311},
	{"iniGetKeys",      js_iniGetKeys,      1,  JSTYPE_ARRAY,   JSDOCSTR("[section=<i>root</i>]")
	 , JSDOCSTR("Parse all key names from the specified <i>section</i> in a <tt>.ini</tt> file "
		        "and return the key names as an <i>array of strings</i>. "
		        "if <i>section</i> is undefined, returns key names from the <i>root</i> section")
	 , 311},
	{"iniGetValue",     js_iniGetValue,     3,  JSTYPE_UNDEF,   JSDOCSTR("section, key [,default=<i>none</i>]")
	 , JSDOCSTR("Parse a key from a <tt>.ini</tt> file and return its value (format = '<tt>key = value</tt>'). "
		        "To parse a key from the <i>root</i> section, pass <i>null</i> for <i>section</i>. "
		        "Returns the specified <i>default</i> value if the key or value is missing or invalid.<br>"
		        "Returns a <i>bool</i>, <i>number</i>, <i>string</i>, or an <i>array of strings</i> "
		        "determined by the type of <i>default</i> value specified. "
		        "<br><b>Note:</b> To insure that any/all values are returned as a string (e.g. numeric passwords are <b>not</b> returned as a <i>number</i>), "
		        "pass an empty string ('') for the <i>default</i> value." )
	 , 311},
	{"iniSetValue",     js_iniSetValue,     3,  JSTYPE_BOOLEAN, JSDOCSTR("section, key, [value=<i>none</i>]")
	 , JSDOCSTR("Set the specified <i>key</i> to the specified <i>value</i> in the specified <i>section</i> "
		        "of a <tt>.ini</tt> file. "
		        "to set a key in the <i>root</i> section, pass <i>null</i> for <i>section</i>. ")
	 , 312},
	{"iniGetObject",    js_iniGetObject,    1,  JSTYPE_OBJECT,  JSDOCSTR("[section=<i>root</i>] [,<i>bool</i> lowercase=false] "
		                                                                 "[,<i>bool</i> blanks=false]")
	 , JSDOCSTR("Parse an entire section from a .ini file "
		        "and return all of its keys (optionally lowercased) and values as properties of an object.<br>"
		        "If <i>section</i> is <tt>null</tt> or <tt>undefined</tt>, returns keys and values from the <i>root</i> section.<br>"
		        "If <i>blanks</i> is <tt>true</tt> then empty string (instead of <tt>undefined</tt>) values may included in the returned object.<br>"
		        "Returns <i>null</i> if the specified <i>section</i> does not exist in the file or the file has not been opened.")
	 , 311},
	{"iniSetObject",    js_iniSetObject,    2,  JSTYPE_BOOLEAN, JSDOCSTR("section, <i>object</i> object")
	 , JSDOCSTR("Write all the properties of the specified <i>object</i> as separate <tt>key=value</tt> pairs "
		        "in the specified <i>section</i> of a <tt>.ini</tt> file.<br>"
		        "To write an object in the <i>root</i> section, pass <i>null</i> for <i>section</i>."
		        "<br><b>Note:</b> this method does not remove unreferenced keys from an existing section. "
		        "If your intention is to <i>replace</i> an existing section, use the <tt>iniRemoveSection</tt> function first." )
	 , 312},
	{"iniGetAllObjects", js_iniGetAllObjects, 1,  JSTYPE_ARRAY,   JSDOCSTR("[<i>string</i> name_property] [,<i>bool</i> prefix=<i>none</i>] [,<i>bool</i> lowercase=false] "
		                                                                   "[,blanks=false]")
	 , JSDOCSTR("Parse all sections from a .ini file and return all (non-<i>root</i>) sections "
		        "in an array of objects with each section's keys (optionally lowercased) as properties of each object.<br>"
		        "<i>name_property</i> is the name of the property to create to contain the section's name "
		        "(optionally lowercased, default is <tt>\"name\"</tt>), "
		        "the optional <i>prefix</i> has the same use as in the <tt>iniGetSections</tt> method.<br>"
		        "If a (String) <i>prefix</i> is specified, it is removed from each section's name.<br>"
		        "If <i>blanks</i> is <tt>true</tt> then empty string (instead of <tt>undefined</tt>) values may be included in the returned objects."
		        )
	 , 311},
	{"iniSetAllObjects", js_iniSetAllObjects, 1,  JSTYPE_BOOLEAN, JSDOCSTR("<i>object</i> array [,name_property=\"name\"]")
	 , JSDOCSTR("Write an array of objects to a .ini file, each object in its own section named "
		        "after the object's <i>name_property</i> (default: <tt>name</tt>)")
	 , 312},
	{"iniRemoveKey",    js_iniRemoveKey,    2,  JSTYPE_BOOLEAN, JSDOCSTR("section, key")
	 , JSDOCSTR("Remove specified <i>key</i> from specified <i>section</i> in <tt>.ini</tt> file.")
	 , 314},
	{"iniRemoveSection", js_iniRemoveSection, 1,  JSTYPE_BOOLEAN, JSDOCSTR("section")
	 , JSDOCSTR("Remove specified <i>section</i> from <tt>.ini</tt> file.")
	 , 314},
	{"iniRemoveSections", js_iniRemoveSections, 1, JSTYPE_BOOLEAN,    JSDOCSTR("[prefix]")
	 , JSDOCSTR("Remove all sections from <tt>.ini</tt> file, optionally only sections with the specified section name <i>prefix</i>.")
	 , 32000},
	{"iniReadAll",      js_iniReadAll,      0,  JSTYPE_ARRAY,   JSDOCSTR("")
	 , JSDOCSTR("Read entire <tt>.ini</tt> file into an array of strings (with <tt>!include</tt>ed files).")
	 , 31802},
/* File Destructor */

static void js_finalize_file(JSContext *cx, JSObject *obj)
{
	private_t* p;
	if ((p = (private_t*)JS_GetPrivate(cx, obj)) == NULL)
	dbprintf(FALSE, p, "finalized: %s", p->name);
	FREE_AND_NULL(p->ini_style.key_prefix);
	FREE_AND_NULL(p->ini_style.section_separator);
	FREE_AND_NULL(p->ini_style.value_separator);
	FREE_AND_NULL(p->ini_style.bit_separator);
	FREE_AND_NULL(p->ini_style.literal_separator);
static JSBool js_file_resolve(JSContext *cx, JSObject *obj, jsid id)
	char*  name = NULL;
	JSBool ret;
	if (id != JSID_VOID && id != JSID_EMPTY) {
deuce's avatar
deuce committed
		jsval idval;
deuce's avatar
deuce committed
		JS_IdToValue(cx, id, &idval);
		if (JSVAL_IS_STRING(idval))
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
deuce's avatar
deuce committed
	}
	ret = js_SyncResolve(cx, obj, name, js_file_properties, js_file_functions, NULL, 0);
	if (name)
		free(name);
	return ret;
}

static JSBool js_file_enumerate(JSContext *cx, JSObject *obj)
{
	return js_file_resolve(cx, obj, JSID_VOID);
	"File"                  /* name			*/
	, JSCLASS_HAS_PRIVATE    /* flags		*/
	, JS_PropertyStub        /* addProperty	*/
	, JS_PropertyStub        /* delProperty	*/
	, js_file_get            /* getProperty	*/
	, js_file_set            /* setProperty	*/
	, js_file_enumerate      /* enumerate	*/
	, js_file_resolve        /* resolve		*/
	, JS_ConvertStub         /* convert		*/
	, js_finalize_file       /* finalize		*/
};

/* File Constructor (creates file descriptor) */

static JSBool
js_file_constructor(JSContext *cx, uintN argc, jsval *arglist)
	JSObject * obj;
	jsval *    argv = JS_ARGV(cx, arglist);
	JSString*  str;
	private_t* p;
	obj = JS_NewObject(cx, &js_file_class, NULL, NULL);
	JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(obj));
	if (argc < 1 || (str = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_ReportError(cx, "No filename specified");
	if ((p = (private_t*)calloc(1, sizeof(private_t))) == NULL) {
		JS_ReportError(cx, "calloc failed");
	JSSTRING_TO_STRBUF(cx, str, p->name, sizeof(p->name), NULL);
	if (!JS_SetPrivate(cx, obj, p)) {
		dbprintf(TRUE, p, "JS_SetPrivate failed");
	js_DescribeSyncObject(cx, obj, "Class used for opening, creating, reading, or writing files on the local file system<p>"
	                      "Special features include:</h2><ol style=list-style-type:disc>"
	                      "<li>Exclusive-access files (default) or shared files<ol type=circle>"
	                      "<li>optional record-locking"
	                      "<li>buffered or non-buffered I/O"
	                      "</ol>"
	                      "<li>Support for binary files<ol style=list-style-type:circle>"
	                      "<li>native or network byte order (endian)"
	                      "<li>automatic Unix-to-Unix (<i>UUE</i>), yEncode (<i>yEnc</i>) or Base64 encoding/decoding"
	                      "</ol>"
	                      "<li>Support for ASCII text files<ol style=list-style-type:circle>"
	                      "<li>supports line-based I/O<ol style=list-style-type:square>"
	                      "<li>entire file may be read or written as an array of strings"
	                      "<li>individual lines may be read or written one line at a time"
	                      "</ol>"
	                      "<li>supports fixed-length records<ol style=list-style-type:square>"
	                      "<li>optional end-of-text (<i>etx</i>) character for automatic record padding/termination"
	                      "<li>Synchronet <tt>.dat</tt> files use an <i>etx</i> value of 3 (Ctrl-C)"
	                      "</ol>"
	                      "<li>supports <tt>.ini</tt> formatted configuration files"
	                      "</ol>"
	                      "<li>Dynamically-calculated industry standard checksums (e.g. CRC-16, CRC-32, MD5)"
	                      "</ol>"
	                      , 310
	                      );
	js_DescribeSyncConstructor(cx, obj, "To create a new File object: <tt>var f = new File(<i>filename</i>)</tt>");
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", file_prop_desc, JSPROP_READONLY);
#endif

	dbprintf(FALSE, p, "object constructed");
JSObject* js_CreateFileClass(JSContext* cx, JSObject* parent)
	obj = JS_InitClass(cx, parent, NULL
	                   , &js_file_class
	                   , js_file_constructor
	                   , 1 /* number of constructor args */
	                   , NULL /* props, set in constructor */
	                   , NULL /* funcs, set in constructor */
	                   , NULL, NULL);
JSObject* js_CreateFileObject(JSContext* cx, JSObject* parent, char *name, int fd, const char* mode)
	JSObject*  obj;
	private_t* p;
	int        newfd = dup(fd);
	FILE*      fp;
deuce's avatar
deuce committed
	if (newfd == -1)
deuce's avatar
deuce committed
	fp = fdopen(newfd, mode);
deuce's avatar
deuce committed
		close(newfd);
		return NULL;
	}

	obj = JS_DefineObject(cx, parent, name, &js_file_class, NULL
	                      , JSPROP_ENUMERATE | JSPROP_READONLY);
	if ((p = (private_t*)calloc(1, sizeof(private_t))) == NULL) {
deuce's avatar
deuce committed
		fclose(fp);
deuce's avatar
deuce committed
	}
	p->fp = fp;
	p->debug = JS_FALSE;
	if (!JS_SetPrivate(cx, obj, p)) {
		dbprintf(TRUE, p, "JS_SetPrivate failed");
	}
	dbprintf(FALSE, p, "object created");

#endif  /* JAVSCRIPT */