/* 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 "md5.h" #include "base64.h" #include "uucode.h" #include "yenc.h" #include "ini_file.h" #if !defined(__unix__) #include <conio.h> /* for kbhit() */ #endif #ifdef JAVASCRIPT #include "js_request.h" typedef struct { 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; char sbuf[1024]; if (p == NULL || (!p->debug && !error)) return; 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) { int flags = 0; if (strchr(mode, 'b')) flags |= O_BINARY; else flags |= O_TEXT; if (strchr(mode, 'x')) flags |= O_EXCL; 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; } /* File Object Methods */ extern JSClass js_file_class; static JSBool 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) { return JS_FALSE; } JS_SET_RVAL(cx, arglist, JSVAL_FALSE); if (p->fp != NULL) return JS_TRUE; 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) { JS_ReportError(cx, "Invalid mode specified: %s", str); return JS_TRUE; } 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)) return JS_FALSE; } } rc = JS_SUSPENDREQUEST(cx); if (shareable) p->fp = fopen(p->name, p->mode); else { 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)); } } free(fdomode); } } 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 */ else { #ifdef _WIN32 if (bufsize < 2) bufsize = 2; #endif setvbuf(p->fp, NULL, _IOFBF, bufsize); } } else if (file >= 0) close(file); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp != NULL) return JS_TRUE; 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) { JS_ReportError(cx, "Invalid mode specified: %s", str); return JS_TRUE; } 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)) return JS_FALSE; } } 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 */ else setvbuf(p->fp, NULL, _IOFBF, bufsize); } JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } JS_SET_RVAL(cx, arglist, JSVAL_VOID); if (p->fp == NULL) return JS_TRUE; rc = JS_SUSPENDREQUEST(cx); #ifdef __unix__ if (p->pipe) pclose(p->fp); else #endif fclose(p->fp); p->fp = NULL; dbprintf(FALSE, p, "closed: %s", p->name); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc && !JSVAL_NULL_OR_VOID(argv[0])) { if (!JS_ValueToInt32(cx, argv[0], &timeout)) return JS_FALSE; } JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(FALSE)); rc = JS_SUSPENDREQUEST(cx); #ifdef __unix__ /* * 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 while (timeout) { if (isatty(fileno(p->fp))) { if (kbhit()) { JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE)); rc = JS_SUSPENDREQUEST(cx); break; } SLEEP(1); if (timeout > 0) timeout--; } else { if (!eof(fileno(p->fp))) { JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(TRUE)); rc = JS_SUSPENDREQUEST(cx); break; } SLEEP(1); if (timeout > 0) timeout--; } } #endif JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc && !JSVAL_NULL_OR_VOID(argv[0])) { if (!JS_ValueToInt32(cx, argv[0], &len)) return JS_FALSE; } else len = 1; if (len < 0) len = 1; if ((buf = malloc(len)) == NULL) return JS_TRUE; rc = JS_SUSPENDREQUEST(cx); // 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); if (pos < 0) 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); if (str == NULL) return JS_FALSE; JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str)); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc && !JSVAL_NULL_OR_VOID(argv[0])) { if (!JS_ValueToInt32(cx, argv[0], &len)) return JS_FALSE; } else { rc = JS_SUSPENDREQUEST(cx); len = (long)filelength(fileno(p->fp)); offset = (long)ftell(p->fp); if (offset > 0) len -= offset; JS_RESUMEREQUEST(cx, rc); } if (len < 0) len = 512; if ((buf = malloc(len + 1)) == NULL) return JS_TRUE; 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 (cp) *cp = 0; len = strlen(buf); } if (p->rot13) rot13(buf); if (p->uuencoded || p->b64encoded || p->yencoded) { uulen = len * 2; if ((uubuf = malloc(uulen)) == NULL) { free(buf); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } if (p->uuencoded) uulen = uuencode(uubuf, uulen, buf, len); else if (p->yencoded) uulen = yencode(uubuf, uulen, buf, len); else uulen = b64_encode(uubuf, uulen, buf, len); if (uulen >= 0) { free(buf); buf = uubuf; len = uulen; } else free(uubuf); } JS_RESUMEREQUEST(cx, rc); str = JS_NewStringCopyN(cx, buf, len); free(buf); if (str == NULL) return JS_FALSE; JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str)); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc && !JSVAL_NULL_OR_VOID(argv[0])) { if (!JS_ValueToInt32(cx, argv[0], &len)) return JS_FALSE; } if ((buf = malloc(len + 1)) == NULL) return JS_FALSE; 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')) len--; buf[len] = 0; if (p->etx) { cp = strchr(buf, p->etx); if (cp) *cp = 0; } if (p->rot13) rot13(buf); JS_RESUMEREQUEST(cx, rc); if ((js_str = JS_NewStringCopyZ(cx, buf)) != NULL) /* exception here Feb-12-2005 */ JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str)); } else { JS_RESUMEREQUEST(cx, rc); } free(buf); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc && !JSVAL_NULL_OR_VOID(argv[0])) { if (!JS_ValueToInt32(cx, argv[0], &size)) return JS_FALSE; if (argc > 1 && !JSVAL_NULL_OR_VOID(argv[1])) { if (!JS_ValueToInt32(cx, argv[1], &count)) return JS_FALSE; } } rc = JS_SUSPENDREQUEST(cx); if (size != sizeof(BYTE) && size != sizeof(WORD) && size != sizeof(DWORD) && size != sizeof(uint64_t)) { /* unknown size */ dbprintf(TRUE, p, "unsupported binary read size: %d", size); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } buffer = malloc(size * count); if (buffer == NULL) { dbprintf(TRUE, p, "malloc failure of %u bytes", size * count); JS_RESUMEREQUEST(cx, rc); return JS_FALSE; } b = buffer; w = buffer; l = buffer; q = buffer; retlen = fread(buffer, size, count, p->fp); if (count == 1) { if (retlen == 1) { switch (size) { case sizeof(BYTE): JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(*b)); break; case sizeof(WORD): 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)); break; case sizeof(uint64_t): if (p->network_byte_order) *q = BE_INT64(*q); else *q = LE_INT64(*q); JS_SET_RVAL(cx, arglist, DOUBLE_TO_JSVAL((double)*q)); break; } } } else { JS_RESUMEREQUEST(cx, rc); 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++)); break; case sizeof(uint64_t): if (p->network_byte_order) *q = BE_INT64(*q); else *q = LE_INT64(*q); v = DOUBLE_TO_JSVAL((double)*(q++)); break; } if (!JS_SetElement(cx, array, i, &v)) { rc = JS_SUSPENDREQUEST(cx); goto end; } } JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); } end: free(buffer); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; array = JS_NewArrayObject(cx, 0, NULL); while (!feof(p->fp)) { js_readln(cx, argc, arglist); if (JS_RVAL(cx, arglist) == JSVAL_NULL) break; if (!JS_SetElement(cx, array, len++, &JS_RVAL(cx, arglist))) break; } JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); return JS_TRUE; } static jsval get_value(JSContext *cx, char* value, bool blanks) { char* p; BOOL f = FALSE; jsval val; if (value == NULL || (*value == 0 && !blanks)) return JSVAL_VOID; /* integer or float? */ for (p = value; *p; p++) { if (*p == '.' && !f) f = TRUE; else if (!IS_DIGIT(*p)) break; } if (p != value && *p == 0) { if (f) val = DOUBLE_TO_JSVAL(atof(value)); else val = UINT_TO_JSVAL(strtoul(value, NULL, 10)); return val; } /* hexadecimal number? */ if (!strncmp(value, "0x", 2)) { for (p = value + 2; *p; p++) if (!isxdigit((uchar) * p)) break; if (*p == 0) { val = UINT_TO_JSVAL(strtoul(value, NULL, 0)); return val; } } /* Boolean? */ if (!stricmp(value, "true")) return JSVAL_TRUE; if (!stricmp(value, "false")) return JSVAL_FALSE; /* String */ return STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value)); } static double js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) { jsval rval; if (!JS_CallFunctionName(cx, obj, "getTime", 0, NULL, &rval)) { return ((double)time(NULL)) * 1000; } return JSVAL_TO_DOUBLE(rval); } static JSBool 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) { return JS_FALSE; } 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); return JS_FALSE; } /* * Although section can be NULL (ie: root), a NULL key will cause a * segfault. */ if (key == NULL) { JS_ReportError(cx, "Invalid NULL key specified"); FREE_AND_NULL(section); return JS_FALSE; } 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_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, get_value(cx, cstr, /* blanks */ false)); return JS_TRUE; } if (JSVAL_IS_BOOLEAN(dflt)) { JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL( iniGetBool(ini, section, key, JSVAL_TO_BOOLEAN(dflt)))); } 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); dbl *= 1000; JS_RESUMEREQUEST(cx, rc); date_obj = JS_NewDateObjectMsec(cx, dbl); if (date_obj != NULL) { JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(date_obj)); } } 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); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); list = iniGetStringList(ini, section, key, ",", cstr); FREE_AND_NULL(cstr); JS_RESUMEREQUEST(cx, rc); for (i = 0; list && list[i]; i++) { val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, list[i])); if (!JS_SetElement(cx, array, i, &val)) break; } rc = JS_SUSPENDREQUEST(cx); iniFreeStringList(list); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); } } else if (JSVAL_IS_DOUBLE(dflt)) { rc = JS_SUSPENDREQUEST(cx); dbl = iniGetFloat(ini, section, key, JSVAL_TO_DOUBLE(dflt)); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, DOUBLE_TO_JSVAL(dbl)); } else if (JSVAL_IS_NUMBER(dflt)) { if (!JS_ValueToInt32(cx, dflt, &i)) { FREE_AND_NULL(section); FREE_AND_NULL(key); strListFree(&ini); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); i = iniGetInteger(ini, section, key, i); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(i)); } else { 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); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); cstr2 = iniGetString(ini, section, key, cstr, buf); 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); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; 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); return JS_FALSE; } if (value == JSVAL_VOID) { /* unspecified value */ rc = JS_SUSPENDREQUEST(cx); result = iniSetString(list, section, key, "", &p->ini_style); JS_RESUMEREQUEST(cx, rc); } else if (JSVAL_IS_BOOLEAN(value)) { result = iniSetBool(list, section, key, JSVAL_TO_BOOLEAN(value), &p->ini_style); } else if (JSVAL_IS_DOUBLE(value)) { result = iniSetFloat(list, section, key, JSVAL_TO_DOUBLE(value), &p->ini_style); } else if (JSVAL_IS_NUMBER(value)) { if (!JS_ValueToInt32(cx, value, &i)) { FREE_AND_NULL(section); FREE_AND_NULL(key); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); result = iniSetInteger(list, section, key, i, &p->ini_style); 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); JS_RESUMEREQUEST(cx, rc); } else { cstr = NULL; JSVALUE_TO_MSTRING(cx, value, cstr, NULL); if (JS_IsExceptionPending(cx)) { FREE_AND_NULL(cstr); FREE_AND_NULL(section); FREE_AND_NULL(key); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); result = iniSetString(list, section, key, cstr, &p->ini_style); FREE_AND_NULL(cstr); JS_RESUMEREQUEST(cx, rc); } FREE_AND_NULL(section); FREE_AND_NULL(key); return result != NULL; } 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; rc = JS_SUSPENDREQUEST(cx); if ((list = iniReadFile(p->fp)) != NULL) { if (js_iniSetValue_internal(cx, obj, argc, argv, &list)) rval = BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list)); } strListFree(&list); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, rval); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc && argv[0] != JSVAL_VOID && argv[0] != JSVAL_NULL) { JSVALUE_TO_MSTRING(cx, argv[0], section, NULL); HANDLE_PENDING(cx, section); } JSVALUE_TO_MSTRING(cx, argv[1], key, NULL); if (JS_IsExceptionPending(cx)) { FREE_AND_NULL(key); FREE_AND_NULL(section); return JS_FALSE; } if (key == NULL) { JS_ReportError(cx, "Invalid NULL key specified"); FREE_AND_NULL(section); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); if ((list = iniReadFile(p->fp)) == NULL) { JS_RESUMEREQUEST(cx, rc); FREE_AND_NULL(section); FREE_AND_NULL(key); return JS_TRUE; } if (iniRemoveKey(&list, section, key)) JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list))); FREE_AND_NULL(section); FREE_AND_NULL(key); strListFree(&list); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; 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) { JS_RESUMEREQUEST(cx, rc); FREE_AND_NULL(section); return JS_TRUE; } if (iniRemoveSection(&list, section)) JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(iniWriteFile(p->fp, list))); FREE_AND_NULL(section); strListFree(&list); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } 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 (p->fp == NULL) return JS_TRUE; 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; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (argc) { JSVALUE_TO_MSTRING(cx, argv[0], prefix, NULL); HANDLE_PENDING(cx, prefix); } array = JS_NewArrayObject(cx, 0, NULL); rc = JS_SUSPENDREQUEST(cx); list = iniReadSectionList(p->fp, prefix); FREE_AND_NULL(prefix); JS_RESUMEREQUEST(cx, rc); for (i = 0; list && list[i]; i++) { val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, list[i])); if (!JS_SetElement(cx, array, i, &val)) break; } rc = JS_SUSPENDREQUEST(cx); iniFreeStringList(list); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; 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); FREE_AND_NULL(section); JS_RESUMEREQUEST(cx, rc); for (i = 0; list && list[i]; i++) { val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, list[i])); if (!JS_SetElement(cx, array, i, &val)) break; } rc = JS_SUSPENDREQUEST(cx); iniFreeStringList(list); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); return JS_TRUE; } static JSBool 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; named_string_t** list; 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; uintN argn = 0; 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); 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++; } rc = JS_SUSPENDREQUEST(cx); str_list_t ini = iniReadFile(p->fp); list = iniGetNamedStringList(ini, section); strListFree(&ini); FREE_AND_NULL(section); JS_RESUMEREQUEST(cx, rc); if (list == NULL) return JS_TRUE; object = JS_NewObject(cx, NULL, NULL, obj); for (i = 0; list && list[i]; i++) { if (lowercase) strlwr(list[i]->name); JS_DefineProperty(cx, object, list[i]->name , get_value(cx, list[i]->value, blanks) , NULL, NULL, JSPROP_ENUMERATE); } rc = JS_SUSPENDREQUEST(cx); iniFreeNamedStringList(list); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(object)); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; set_argv[0] = argv[0]; /* section */ if (!JSVAL_IS_OBJECT(argv[1]) || argv[1] == JSVAL_NULL) return JS_TRUE; object = JSVAL_TO_OBJECT(argv[1]); if (object == NULL || (id_array = JS_Enumerate(cx, object)) == NULL) return JS_TRUE; rc = JS_SUSPENDREQUEST(cx); if ((list = iniReadFile(p->fp)) == NULL) { JS_RESUMEREQUEST(cx, rc); JS_DestroyIdArray(cx, id_array); return JS_TRUE; } JS_RESUMEREQUEST(cx, rc); rval = JSVAL_TRUE; for (i = 0; i < id_array->length; i++) { /* property */ JS_IdToValue(cx, id_array->vector[i], &set_argv[1]); /* value */ cp = NULL; 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)) { FREE_AND_NULL(cp); JS_DestroyIdArray(cx, id_array); return JS_FALSE; } (void)JS_GetProperty(cx, object, cp, &set_argv[2]); FREE_AND_NULL(cp); if (!js_iniSetValue_internal(cx, obj, 3, set_argv, &list)) { rval = JSVAL_FALSE; break; } } 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); JS_DestroyIdArray(cx, id_array); return JS_TRUE; } static JSBool 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; named_string_t** key_list; 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; uintN argn = 0; if (argc > argn && JSVAL_IS_STRING(argv[argn])) { JSVALUE_TO_MSTRING(cx, argv[argn], name, NULL); HANDLE_PENDING(cx, name); if (name == NULL) { 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)) { FREE_AND_NULL(prefix); 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); JS_RESUMEREQUEST(cx, rc); 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) strlwr(sec_name); 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]); JS_RESUMEREQUEST(cx, rc); 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); JS_RESUMEREQUEST(cx, rc); val = OBJECT_TO_JSVAL(object); if (!JS_SetElement(cx, array, i, &val)) break; } rc = JS_SUSPENDREQUEST(cx); FREE_AND_NULL(prefix); if (name != name_def) free(name); iniFreeStringList(sec_list); iniFreeStringList(ini); JS_RESUMEREQUEST(cx, rc); JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(array)); return JS_TRUE; } static JSBool 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])) return JS_TRUE; array = JSVAL_TO_OBJECT(argv[0]); if (array == NULL || !JS_IsArrayObject(cx, array)) return JS_TRUE; if (!JS_GetArrayLength(cx, array, &count)) return JS_TRUE; if (argc > 1) { JSVALUE_TO_MSTRING(cx, argv[1], name, NULL); HANDLE_PENDING(cx, name); if (name == NULL) { 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); return JS_FALSE; } if (p->fp == NULL) { if (name != name_def) free(name); return JS_TRUE; } rc = JS_SUSPENDREQUEST(cx); if ((list = iniReadFile(p->fp)) == NULL) { JS_RESUMEREQUEST(cx, rc); if (name != name_def) free(name); return JS_TRUE; } JS_RESUMEREQUEST(cx, rc); /* enumerate the array */ rval = JSVAL_TRUE; for (i = 0; i < count && rval == JSVAL_TRUE; i++) { if (!JS_GetElement(cx, array, i, &oval)) break; if (!JSVAL_IS_OBJECT(oval)) /* must be an array of objects */ break; object = JSVAL_TO_OBJECT(oval); if (object == NULL || !JS_GetProperty(cx, object, name, &set_argv[0])) continue; if ((id_array = JS_Enumerate(cx, object)) == NULL) { if (name != name_def) free(name); return JS_TRUE; } for (j = 0; j < id_array->length; j++) { /* property */ JS_IdToValue(cx, id_array->vector[j], &set_argv[1]); /* check if not name */ JSVALUE_TO_MSTRING(cx, set_argv[1], cp, NULL); if (JS_IsExceptionPending(cx)) { FREE_AND_NULL(cp); JS_DestroyIdArray(cx, id_array); if (name != name_def) free(name); return JS_FALSE; } if (cp == NULL) continue; if (strcmp(cp, name) == 0) { FREE_AND_NULL(cp); continue; } /* value */ 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)) { rval = JSVAL_FALSE; break; } } JS_DestroyIdArray(cx, id_array); } if (name != name_def) free(name); 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); return JS_TRUE; } 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) return JS_FALSE; if (p->fp == NULL) return JS_TRUE; JSObject* array = JS_NewArrayObject(cx, 0, NULL); if (array == NULL) return JS_FALSE; 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) break; 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)); return JS_TRUE; } 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; JS_SET_RVAL(cx, arglist, JSVAL_FALSE); if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if ((str = JS_ValueToString(cx, argv[0])) == NULL) return JS_FALSE; JSSTRING_TO_MSTRING(cx, str, cp, &len); HANDLE_PENDING(cx, cp); if (cp == NULL) return JS_TRUE; rc = JS_SUSPENDREQUEST(cx); if (write(fileno(p->fp), cp, len) == (size_t)len) { free(cp); 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_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if ((str = JS_ValueToString(cx, argv[0])) == NULL) return JS_FALSE; JSSTRING_TO_MSTRING(cx, str, cp, &len); HANDLE_PENDING(cx, cp); if (cp == NULL) return JS_TRUE; 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); else decoded_len = b64_decode(uubuf, len, cp, len); if (decoded_len < 0) { free(uubuf); free(cp); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } free(cp); cp = uubuf; len = decoded_len; } if (p->rot13) rot13(cp); JS_RESUMEREQUEST(cx, rc); tlen = len; if (argc > 1 && !JSVAL_NULL_OR_VOID(argv[1])) { if (!JS_ValueToInt32(cx, argv[1], &i)) { free(cp); return JS_FALSE; } tlen = i; if (len > tlen) len = tlen; } rc = JS_SUSPENDREQUEST(cx); if (fwrite(cp, 1, len, p->fp) == (size_t)len) { free(cp); if (tlen > len) { len = tlen - len; if ((cp = malloc(len)) == NULL) { JS_RESUMEREQUEST(cx, rc); JS_ReportError(cx, "malloc failure of %u bytes", len); return JS_FALSE; } memset(cp, p->etx, len); if (fwrite(cp, 1, len, p->fp) < len) { free(cp); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } free(cp); } dbprintf(FALSE, p, "wrote %lu bytes", (ulong)tlen); JS_SET_RVAL(cx, arglist, JSVAL_TRUE); } else { free(cp); dbprintf(TRUE, p, "write of %lu bytes failed", (ulong)len); } JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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; *rval = JSVAL_FALSE; if ((p = (private_t*)js_GetClassPrivate(cx, obj, &js_file_class)) == NULL) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (arg) { if ((str = JS_ValueToString(cx, *arg)) == NULL) { JS_ReportError(cx, "JS_ValueToString failed"); return JS_FALSE; } 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) rot13(cp); if (fprintf(p->fp, "%s\n", cp) != 0) *rval = JSVAL_TRUE; if (cp != cp_def) free(cp); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } 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; if (argc) { ret = js_writeln_internal(cx, obj, &argv[0], &rval); } else { ret = js_writeln_internal(cx, obj, NULL, &rval); } JS_SET_RVAL(cx, arglist, rval); return ret; } static JSBool js_writebin(JSContext *cx, uintN argc, jsval *arglist) { JSObject *obj = JS_THIS_OBJECT(cx, arglist); jsval * argv = JS_ARGV(cx, arglist); union { uint8_t *b; uint16_t *w; uint32_t *l; uint64_t *q; int8_t *sb; int16_t *sw; int32_t *sl; int64_t *sq; } o; 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; 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)) return JS_TRUE; } else array = NULL; } if (array == NULL) { if (!JS_ValueToNumber(cx, argv[0], &val)) return JS_FALSE; } if (argc > 1 && !JSVAL_NULL_OR_VOID(argv[1])) { if (!JS_ValueToInt32(cx, argv[1], &size)) return JS_FALSE; } 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); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } buffer = calloc(size, count); if (buffer == NULL) { rc = JS_SUSPENDREQUEST(cx); dbprintf(TRUE, p, "malloc failure of %u bytes", size * count); JS_RESUMEREQUEST(cx, rc); return JS_FALSE; } o.b = buffer; if (array == NULL) { switch (size) { case sizeof(int8_t): if (val < 0) *o.sb = (int8_t)val; else *o.b = (uint8_t)val; break; case sizeof(int16_t): if (val < 0) *o.sw = (int16_t)val; else *o.w = (uint16_t)val; if (p->network_byte_order) *o.w = BE_SHORT(*o.w); else *o.w = LE_SHORT(*o.w); break; case sizeof(int32_t): if (val < 0) *o.sl = (int32_t)val; else *o.l = (uint32_t)val; if (p->network_byte_order) *o.l = BE_LONG(*o.l); else *o.l = LE_LONG(*o.l); break; case sizeof(int64_t): if (val < 0) *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); break; } } else { for (wr = 0; wr < count; wr++) { if (!JS_GetElement(cx, array, wr, &elemval)) goto end; if (!JS_ValueToNumber(cx, elemval, &val)) goto end; switch (size) { case sizeof(int8_t): if (val < 0) *o.sb = (int8_t)val; else *o.b = (uint8_t)val; o.b++; break; case sizeof(int16_t): if (val < 0) *o.sw = (int16_t)val; else *o.w = (uint16_t)val; if (p->network_byte_order) *o.w = BE_SHORT(*o.w); else *o.w = LE_SHORT(*o.w); o.w++; break; case sizeof(int32_t): if (val < 0) *o.sl = (int32_t)val; else *o.l = (uint32_t)val; if (p->network_byte_order) *o.l = BE_LONG(*o.l); else *o.l = LE_LONG(*o.l); o.l++; break; case sizeof(int64_t): if (val < 0) *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); o.q++; break; } } } rc = JS_SUSPENDREQUEST(cx); wr = fwrite(buffer, size, count, p->fp); JS_RESUMEREQUEST(cx, rc); if (wr == count) JS_SET_RVAL(cx, arglist, JSVAL_TRUE); end: free(buffer); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if (JSVAL_IS_NULL(argv[0]) || !JSVAL_IS_OBJECT(argv[0])) return JS_TRUE; array = JSVAL_TO_OBJECT(argv[0]); if (array == NULL || !JS_IsArrayObject(cx, array)) return JS_TRUE; if (!JS_GetArrayLength(cx, array, &limit)) return JS_FALSE; JS_SET_RVAL(cx, arglist, JSVAL_TRUE); for (i = 0; i < limit; i++) { jsval rval; if (!JS_GetElement(cx, array, i, &elemval)) break; js_writeln_internal(cx, obj, &elemval, &rval); JS_SET_RVAL(cx, arglist, rval); if (rval != JSVAL_TRUE) break; } return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; /* offset */ if (argc) { if (!JS_ValueToNumber(cx, argv[0], &val)) return JS_FALSE; offset = (off_t)val; } /* length */ if (argc > 1) { if (!JS_ValueToNumber(cx, argv[1], &val)) return JS_FALSE; len = (off_t)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_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; /* offset */ if (argc) { if (!JS_ValueToNumber(cx, argv[0], &val)) return JS_FALSE; offset = (off_t)val; } /* length */ if (argc > 1) { if (!JS_ValueToNumber(cx, argv[1], &val)) return JS_FALSE; len = (off_t)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); JS_RESUMEREQUEST(cx, rc); return JS_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) { return JS_FALSE; } if (p->fp != NULL) { /* close it if it's open */ fclose(p->fp); p->fp = NULL; } rc = JS_SUSPENDREQUEST(cx); JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(remove(p->name) == 0)); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); if (p->fp == NULL) JS_SET_RVAL(cx, arglist, JSVAL_FALSE); else JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(fflush(p->fp) == 0)); JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); if (p->fp == NULL) JS_SET_RVAL(cx, arglist, JSVAL_FALSE); else { JS_SET_RVAL(cx, arglist, JSVAL_TRUE); rewind(p->fp); } JS_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (argc && !JSVAL_NULL_OR_VOID(argv[0])) { if (!JS_ValueToInt32(cx, argv[0], &len)) return JS_FALSE; } 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_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } 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_RESUMEREQUEST(cx, rc); return JS_TRUE; } static JSBool 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) { return JS_FALSE; } if (p->fp == NULL) return JS_TRUE; if ((cp = js_sprintf(cx, 0, argc, argv)) == NULL) { JS_ReportError(cx, "js_sprintf failed"); return JS_FALSE; } rc = JS_SUSPENDREQUEST(cx); JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(fwrite(cp, 1, strlen(cp), p->fp))); JS_RESUMEREQUEST(cx, rc); js_sprintf_free(cp); return JS_TRUE; } /* File Object Properties */ enum { 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 /* dynamically calculated */ , 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 /* ini style */ , 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) { return JS_TRUE; } JS_IdToValue(cx, id, &idval); tiny = JSVAL_TO_INT(idval); rc = JS_SUSPENDREQUEST(cx); dbprintf(FALSE, p, "setting property %d", tiny); JS_RESUMEREQUEST(cx, rc); switch (tiny) { case FILE_PROP_DEBUG: JS_ValueToBoolean(cx, *vp, &(p->debug)); break; case FILE_PROP_YENCODED: JS_ValueToBoolean(cx, *vp, &(p->yencoded)); break; case FILE_PROP_UUENCODED: JS_ValueToBoolean(cx, *vp, &(p->uuencoded)); break; case FILE_PROP_B64ENCODED: JS_ValueToBoolean(cx, *vp, &(p->b64encoded)); break; case FILE_PROP_ROT13: JS_ValueToBoolean(cx, *vp, &(p->rot13)); break; case FILE_PROP_NETWORK_ORDER: JS_ValueToBoolean(cx, *vp, &(p->network_byte_order)); break; case FILE_PROP_POSITION: if (p->fp != NULL) { if (!JS_ValueToECMAUint32(cx, *vp, &u)) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); fseek(p->fp, u, SEEK_SET); JS_RESUMEREQUEST(cx, rc); } break; case FILE_PROP_DATE: if (!JS_ValueToECMAUint32(cx, *vp, &u)) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); setfdate(p->name, u); JS_RESUMEREQUEST(cx, rc); break; case FILE_PROP_LENGTH: if (p->fp != NULL) { if (!JS_ValueToECMAUint32(cx, *vp, &u)) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); result = chsize(fileno(p->fp), u); JS_RESUMEREQUEST(cx, rc); if (result != 0) { JS_ReportError(cx, "Error %d changing file size", errno); return JS_FALSE; } } break; case FILE_PROP_ATTRIBUTES: if (!JS_ValueToInt32(cx, *vp, &i)) return JS_FALSE; rc = JS_SUSPENDREQUEST(cx); (void)CHMOD(p->name, i); JS_RESUMEREQUEST(cx, rc); break; case FILE_PROP_ETX: if (!JS_ValueToInt32(cx, *vp, &i)) return JS_FALSE; p->etx = (uchar)i; break; case FILE_INI_KEY_LEN: if (!JS_ValueToInt32(cx, *vp, &i)) return JS_FALSE; 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; } break; } return JS_TRUE; } 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) return JS_TRUE; JS_IdToValue(cx, id, &idval); tiny = JSVAL_TO_INT(idval); switch (tiny) { case FILE_PROP_NAME: s = p->name; break; case FILE_PROP_MODE: s = p->mode; break; case FILE_PROP_EXISTS: if (p->fp) /* open? */ *vp = JSVAL_TRUE; else { rc = JS_SUSPENDREQUEST(cx); *vp = BOOLEAN_TO_JSVAL(fexistcase(p->name)); JS_RESUMEREQUEST(cx, rc); } break; case FILE_PROP_DATE: rc = JS_SUSPENDREQUEST(cx); tt = fdate(p->name); JS_RESUMEREQUEST(cx, rc); *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); JS_RESUMEREQUEST(cx, rc); } else *vp = JSVAL_TRUE; break; case FILE_PROP_ERROR: if (p->fp) { rc = JS_SUSPENDREQUEST(cx); *vp = INT_TO_JSVAL(ferror(p->fp)); JS_RESUMEREQUEST(cx, rc); } else *vp = INT_TO_JSVAL(errno); break; case FILE_PROP_POSITION: if (p->fp) { rc = JS_SUSPENDREQUEST(cx); lng = ftell(p->fp); JS_RESUMEREQUEST(cx, rc); *vp = DOUBLE_TO_JSVAL((double)lng); } else *vp = INT_TO_JSVAL(-1); break; case FILE_PROP_LENGTH: rc = JS_SUSPENDREQUEST(cx); if (p->fp) /* open? */ lng = filelength(fileno(p->fp)); else lng = flength(p->name); JS_RESUMEREQUEST(cx, rc); *vp = DOUBLE_TO_JSVAL((double)lng); break; case FILE_PROP_ATTRIBUTES: rc = JS_SUSPENDREQUEST(cx); in = getfmode(p->name); JS_RESUMEREQUEST(cx, rc); *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); break; 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: if (p->fp) *vp = INT_TO_JSVAL(fileno(p->fp)); else *vp = INT_TO_JSVAL(-1); break; 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; if (p->fp == NULL) break; /* fall-through */ case FILE_PROP_MD5_HEX: case FILE_PROP_MD5_B64: case FILE_PROP_SHA1_HEX: case FILE_PROP_SHA1_B64: *vp = JSVAL_VOID; if (p->fp == NULL) break; rc = JS_SUSPENDREQUEST(cx); offset = ftell(p->fp); /* save current file position */ fseek(p->fp, 0, SEEK_SET); /* Initialization */ switch (tiny) { 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; } /* calculate */ while (!feof(p->fp)) { if ((rd = fread(block, 1, sizeof(block), p->fp)) < 1) break; switch (tiny) { case FILE_PROP_CHKSUM: for (i = 0; i < rd; i++) sum += block[i]; break; case FILE_PROP_CRC16: for (i = 0; i < rd; i++) c16 = ucrc16(block[i], c16); break; case FILE_PROP_CRC32: 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); break; case FILE_PROP_SHA1_HEX: case FILE_PROP_SHA1_B64: SHA1Update(&sha1_ctx, block, rd); break; } } JS_RESUMEREQUEST(cx, rc); /* finalize */ switch (tiny) { case FILE_PROP_CHKSUM: *vp = DOUBLE_TO_JSVAL((double)sum); break; case FILE_PROP_CRC16: *vp = UINT_TO_JSVAL(c16); break; case FILE_PROP_CRC32: *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); else 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); else b64_encode(str, sizeof(str) - 1, (char *)digest, sizeof(digest)); js_str = JS_NewStringCopyZ(cx, str); break; } rc = JS_SUSPENDREQUEST(cx); fseeko(p->fp, offset, SEEK_SET); /* restore saved file position */ JS_RESUMEREQUEST(cx, rc); if (js_str != NULL) *vp = STRING_TO_JSVAL(js_str); break; 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; if (s == NULL) *vp = JSVAL_NULL; break; case FILE_INI_SECTION_SEPARATOR: s = p->ini_style.section_separator; if (s == NULL) *vp = JSVAL_NULL; break; case FILE_INI_VALUE_SEPARATOR: s = p->ini_style.value_separator; if (s == NULL) *vp = JSVAL_NULL; break; case FILE_INI_BIT_SEPARATOR: s = p->ini_style.bit_separator; if (s == NULL) *vp = JSVAL_NULL; break; case FILE_INI_LITERAL_SEPARATOR: s = p->ini_style.literal_separator; if (s == NULL) *vp = JSVAL_NULL; break; } if (s != NULL) { if ((js_str = JS_NewStringCopyZ(cx, s)) == NULL) return JS_FALSE; *vp = STRING_TO_JSVAL(js_str); } return JS_TRUE; } #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}, /* writeable */ { "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}, /* dynamically calculated */ { "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}, {0} }; #ifdef BUILD_JSDOCS 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 }; #endif 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 </tt> open for reading; if the file does not exist or cannot be found, the open call fails<br>" "<tt>w </tt> open an empty file for writing; if the given file exists, its contents are destroyed<br>" "<tt>a </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 </tt> open in binary (untranslated) mode; translations involving carriage-return and linefeed characters are suppressed (e.g. <tt>r+b</tt>)<br>" "<tt>x </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 </tt> DENYWRITE - Allows other scripts to open the file for reading, but not for writing.<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>" "<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 </tt> read the programs stdout;<br>" "<tt>w </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}, {0} }; /* 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->fp != NULL) fclose(p->fp); 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); free(p); JS_SetPrivate(cx, obj, NULL); } static JSBool js_file_resolve(JSContext *cx, JSObject *obj, jsid id) { char* name = NULL; JSBool ret; if (id != JSID_VOID && id != JSID_EMPTY) { jsval idval; JS_IdToValue(cx, id, &idval); if (JSVAL_IS_STRING(idval)) JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL); } 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); } 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_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 = JS_THIS_OBJECT(cx, arglist); 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"); return JS_FALSE; } if ((p = (private_t*)calloc(1, sizeof(private_t))) == NULL) { JS_ReportError(cx, "calloc failed"); return JS_FALSE; } JSSTRING_TO_STRBUF(cx, str, p->name, sizeof(p->name), NULL); if (!JS_SetPrivate(cx, obj, p)) { dbprintf(TRUE, p, "JS_SetPrivate failed"); return JS_FALSE; } #ifdef BUILD_JSDOCS 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"); return JS_TRUE; } JSObject* js_CreateFileClass(JSContext* cx, JSObject* parent) { JSObject* obj; 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); return obj; } 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; if (newfd == -1) return NULL; fp = fdopen(newfd, mode); if (fp == NULL) { close(newfd); return NULL; } obj = JS_DefineObject(cx, parent, name, &js_file_class, NULL , JSPROP_ENUMERATE | JSPROP_READONLY); if (obj == NULL) { fclose(fp); return NULL; } if ((p = (private_t*)calloc(1, sizeof(private_t))) == NULL) { fclose(fp); return NULL; } p->fp = fp; p->debug = JS_FALSE; if (!JS_SetPrivate(cx, obj, p)) { fclose(fp); dbprintf(TRUE, p, "JS_SetPrivate failed"); return NULL; } dbprintf(FALSE, p, "object created"); return obj; } #endif /* JAVSCRIPT */