Added support for stdio File object.
authorMatthew Mondor <mmondor@pulsar-zone.net>
Mon, 25 Sep 2006 23:06:28 +0000 (23:06 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Mon, 25 Sep 2006 23:06:28 +0000 (23:06 +0000)
The file_new() function is exported for other modules to be able to create
File objects easily.

mmsoftware/js/classes/js_file.c [new file with mode: 0644]
mmsoftware/js/classes/js_file.h [new file with mode: 0644]
mmsoftware/js/js-sh/src/GNUmakefile
mmsoftware/js/js-sh/src/js-sh.c

diff --git a/mmsoftware/js/classes/js_file.c b/mmsoftware/js/classes/js_file.c
new file mode 100644 (file)
index 0000000..a2ed596
--- /dev/null
@@ -0,0 +1,1235 @@
+/* $Id: js_file.c,v 1.1 2006/09/25 23:06:23 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+/*
+ * Since many libraries support working with FILE * rather than file
+ * desdcriptors, it was necessary to provide stdio functionality.
+ * The file_new() function is exported so that other modules may create file
+ * handles.
+ *
+ * XXX
+ * - add path validity checking hook function
+ * - optionally make the class init function also initialize stdin, stdout,
+ *   stderr
+ * - implement formatted printing functions sprintf, fprintf, printf
+ */
+
+
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <jsapi.h>
+
+#include <js_file.h>
+
+
+
+#define QUEUE_EXCEPTION(s)     do {                                    \
+       JS_SetPendingException(cx,                                      \
+           STRING_TO_JSVAL(JS_NewStringCopyZ(cx, (s))));               \
+} while (/* CONSTCOND */0)
+
+#define SP(n) \
+    { #n, n }
+
+struct property_spec {
+       const char      *name;
+       int             value;
+};
+
+enum file_flags {
+       FF_NONE =       0,
+       FF_CLOSE =      (1 << 1),
+       FF_PCLOSE =     (1 << 2)
+};
+
+/*
+ * Although we could simply wrap around FILE * directly, we need to know if
+ * we should close the file or not upon finalization, and how to close it.
+ * We use the flags field for that, since stdio does not allow to associate
+ * user data pointers with FILE objects portably (which would have been better
+ * for performance).
+ */
+typedef struct file {
+       FILE    *fh;
+       int     flags;
+       char    *vbuf;
+       size_t  vbufsize;
+} file_t;
+
+
+
+/*
+ * Static prototypes
+ */
+
+static JSBool  file_constructor(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static void    file_finalize(JSContext *, JSObject *);
+
+static JSBool  file_sm_popen(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_sm_tmpfile(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_sm_remove(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_sm_strerror(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+
+static JSBool  file_m_close(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_reopen(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_m_seek(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_tell(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_rewind(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_m_flush(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_purge(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_setvbuf(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_m_clearerr(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_m_eof(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_error(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_fileno(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_m_read(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_write(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_gets(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_getc(JSContext *, JSObject *, uintN, jsval *, jsval *);
+static JSBool  file_m_ungetc(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  file_m_putc(JSContext *, JSObject *, uintN, jsval *, jsval *);
+
+
+
+/*
+ * Static globals
+ */
+
+/*
+ * File
+ */
+
+static JSClass file_class = {
+       "File", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub,
+       JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub,
+       JS_ConvertStub, file_finalize
+};
+
+/* Static properties */
+static struct property_spec    file_sprops[] = {
+       SP(SEEK_SET),
+       SP(SEEK_CUR),
+       SP(SEEK_END),
+       SP(_IONBF),
+       SP(_IOLBF),
+       SP(_IOFBF),
+       SP(STDIN_FILENO),
+       SP(STDOUT_FILENO),
+       SP(STDERR_FILENO)
+};
+
+/* Static methods */
+static JSFunctionSpec file_smethods[] = {
+       { "popen", file_sm_popen, 2, 0, 0 },
+       { "tmpfile", file_sm_tmpfile, 0, 0, 0 },
+       { "remove", file_sm_remove, 1, 0, 0 },
+       { "strerror", file_sm_strerror, 1, 0, 0 }
+};
+
+/* Methods */
+static JSFunctionSpec file_methods[] = {
+       { "close", file_m_close, 0, 0, 0 },
+       { "reopen", file_m_reopen, 2, 0, 0 },
+       { "seek", file_m_seek, 2, 0, 0 },
+       { "tell", file_m_tell, 0, 0, 0 },
+       { "rewind", file_m_rewind, 0, 0, 0 },
+       { "flush", file_m_flush, 0, 0, 0 },
+       { "purge", file_m_purge, 0, 0, 0 },
+       { "setvbuf", file_m_setvbuf, 2, 0, 0 },
+       { "clearerr", file_m_clearerr, 0, 0, 0 },
+       { "eof", file_m_eof, 0, 0, 0 },
+       { "error", file_m_error, 0, 0, 0 },
+       { "fileno", file_m_fileno, 0, 0, 0 },
+       { "read", file_m_read, 2, 0, 0 },
+       { "write", file_m_write, 1, 0, 0 },
+       { "gets", file_m_gets, 2, 0, 0 },
+       { "getc", file_m_getc, 1, 0, 0 },
+       { "ungetc", file_m_ungetc, 1, 0, 0 },
+       { "putc", file_m_putc, 1, 0, 0 }
+};
+
+/*
+ * Static globals.  These could cause reentrancy problems unless they were
+ * moved to be File specific, which would however require more RAM and reduce
+ * performance.
+ */
+static char    *read_charbuf = NULL;
+static size_t  read_charbuf_size = 0;
+
+
+
+/*
+ * Publically exported constructor function.  Returns the new File object on
+ * success or NULL on failure.
+ */
+JSObject *
+file_new(JSContext *cx, FILE *fh, int close)
+{
+       file_t          *f;
+       JSObject        *o;
+
+       assert(fh != NULL);
+
+       if ((f = JS_malloc(cx, sizeof(file_t))) != NULL) {
+               f->fh = fh;
+               f->flags = (close != 0 ? FF_CLOSE : 0);
+               f->vbuf = NULL;
+               f->vbufsize = 0;
+       }
+
+       if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL)
+               goto err;
+       if (!JS_SetPrivate(cx, o, f))
+               goto err;
+
+       return o;
+
+err:
+       if (f != NULL) {
+               if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL)
+                       (void) fclose(f->fh);
+               JS_free(cx, f);
+       }
+
+       return NULL;
+}
+
+
+/*
+ * File object control
+ */
+
+JSObject *
+js_InitFileClass(JSContext *cx, JSObject *obj)
+{
+       JSObject                *proto, *ctor;
+       struct property_spec    *sp;
+
+       if ((proto = JS_InitClass(cx, obj, NULL, &file_class, file_constructor,
+           0, NULL, file_methods, NULL, file_smethods)) == NULL) {
+               (void) fprintf(stderr, "Error initializing File class\n");
+               return NULL;
+       }
+
+       /* Create static properties. */
+       if ((ctor = JS_GetConstructor(cx, proto)) == NULL) {
+               (void) fprintf(stderr, "File: JS_GetConstructor == NULL\n");
+               return NULL;
+       }
+       for (sp = file_sprops; sp->name != NULL; sp++) {
+               if (JS_DefineProperty(cx, ctor, sp->name,
+                   INT_TO_JSVAL(sp->value), NULL, NULL,
+                   JSPROP_READONLY | JSPROP_PERMANENT) == JS_FALSE) {
+                       (void) fprintf(stderr,
+                           "File: Error defining property %s\n", sp->name);
+                       return NULL;
+               }
+       }
+
+       return proto;
+}
+
+/*
+ * We support a few forms:
+ * File(filename:String, mode:String); fopen(3)
+ * File(fdnum:Integer, mode:String);   fdopen(3)
+ */
+static JSBool
+file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t          *f = NULL;
+       JSObject        *o;
+
+       if (!JS_IsConstructing(cx)) {
+               QUEUE_EXCEPTION("Constructor called as a function");
+               goto err;
+       }
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               goto err;
+       }
+
+       if ((f = JS_malloc(cx, sizeof(file_t))) == NULL) {
+               QUEUE_EXCEPTION("Out of memory");
+               goto err;
+       }
+       f->flags = 0;
+       f->vbuf = NULL;
+       f->vbufsize = 0;
+
+       if (JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1])) {
+               char    *path, *mode;
+
+               /* File(filename:String, mode:String); */
+               if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0])))
+                   == NULL ||
+                   (mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1])))
+                   == NULL) {
+                       QUEUE_EXCEPTION("Internal error!");
+                       goto err;
+               }
+               f->flags = FF_CLOSE;
+               if ((f->fh = fopen(path, mode)) == NULL) {
+                       QUEUE_EXCEPTION(strerror(errno));
+                       goto err;
+               }
+       } else if (JSVAL_IS_INT(argv[0]) && JSVAL_IS_STRING(argv[1])) {
+               char    *mode;
+
+               /* File(fdnum:Integer, mode:String); */
+               if ((mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1])))
+                   == NULL) {
+                       QUEUE_EXCEPTION("Internal error!");
+                       goto err;
+               }
+               f->flags = FF_CLOSE;
+               if ((f->fh = fdopen(JSVAL_TO_INT(argv[0]), mode)) == NULL) {
+                       QUEUE_EXCEPTION(strerror(errno));
+                       goto err;
+               }
+       } else {
+               QUEUE_EXCEPTION("Unknown method (invalid argument types)");
+               goto err;
+       }
+
+       if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+       *rval = OBJECT_TO_JSVAL(o);
+
+       if (!JS_SetPrivate(cx, o, f)) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+
+       return JS_TRUE;
+
+err:
+       *rval = OBJECT_TO_JSVAL(NULL);
+       if (f != NULL) {
+               if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL)
+                       (void) fclose(f->fh);
+               JS_free(cx, f);
+       }
+
+       return JS_FALSE;
+}
+
+/* ARGSUSED */
+static void
+file_finalize(JSContext *cx, JSObject *obj)
+{
+       file_t  *f;
+
+       if ((f = JS_GetInstancePrivate(cx, obj, &file_class, NULL)) != NULL) {
+               if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL) {
+                       if ((f->flags & FF_PCLOSE) != 0)
+                               (void) pclose(f->fh);
+                       else
+                               (void) fclose(f->fh);
+                       if (f->vbuf != NULL)
+                               JS_free(cx, f->vbuf);
+               }
+               (void) JS_SetPrivate(cx, obj, NULL);
+               JS_free(cx, f);
+       }
+}
+
+
+/*
+ * File object static methods
+ */
+
+static JSBool
+file_sm_popen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t          *f = NULL;
+       JSObject        *o;
+       char            *cmd, *mode;
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               goto err;
+       }
+       if (!JSVAL_IS_STRING(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not a String");
+               goto err;
+       }
+       if (!JSVAL_IS_STRING(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not a String");
+               goto err;
+       }
+
+       if ((cmd = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL ||
+           (mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+
+       if ((f = JS_malloc(cx, sizeof(file_t))) == NULL) {
+               QUEUE_EXCEPTION("Out of memory");
+               goto err;
+       }
+       f->flags = FF_CLOSE | FF_PCLOSE;
+       f->vbuf = NULL;
+       f->vbufsize = 0;
+
+       if ((f->fh = popen(cmd, mode)) != 0) {
+               QUEUE_EXCEPTION(strerror(errno));
+               goto err;
+       }
+
+       if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+       *rval = OBJECT_TO_JSVAL(o);
+
+       if (!JS_SetPrivate(cx, o, f)) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+
+       return JS_TRUE;
+
+err:
+       *rval = OBJECT_TO_JSVAL(NULL);
+       if (f != NULL) {
+               if (f->fh != NULL)
+                       (void) pclose(f->fh);
+               JS_free(cx, f);
+       }
+
+       return JS_FALSE;
+}
+
+static JSBool
+file_sm_tmpfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t          *f = NULL;
+       JSObject        *o;
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               goto err;
+       }
+
+       if ((f = JS_malloc(cx, sizeof(file_t))) == NULL) {
+               QUEUE_EXCEPTION("Out of memory");
+               goto err;
+       }
+       f->flags = FF_CLOSE | FF_PCLOSE;
+       f->vbuf = NULL;
+       f->vbufsize = 0;
+
+       if ((f->fh = tmpfile()) == NULL) {
+               QUEUE_EXCEPTION(strerror(errno));
+               goto err;
+       }
+
+       if ((o = JS_NewObject(cx, &file_class, NULL, NULL)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+       *rval = OBJECT_TO_JSVAL(o);
+
+       if (!JS_SetPrivate(cx, o, f)) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+
+       return JS_TRUE;
+
+err:
+       *rval = OBJECT_TO_JSVAL(NULL);
+       if (f != NULL) {
+               if (f->fh != NULL)
+                       (void) fclose(f->fh);
+               JS_free(cx, f);
+       }
+
+       return JS_FALSE;
+}
+
+static JSBool
+file_sm_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       char    *path;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 1) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not a String");
+               return JS_FALSE;
+       }
+       if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+
+       if (remove(path) != 0) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+/*
+ * We allow two forms;  If no arguments are provided, we return the error
+ * message associated with errno.  If one is specified, we return the error
+ * message associated with the provided error number.
+ */
+static JSBool
+file_sm_strerror(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       int             error;
+       JSString        *str;
+
+       if (argc == 0)
+               error = errno;
+       else {
+               if (!JSVAL_IS_INT(argv[0])) {
+                       QUEUE_EXCEPTION("Argument 1 not an Integer");
+                       *rval = OBJECT_TO_JSVAL(NULL);
+                       return JS_FALSE;
+               }
+               error = JSVAL_TO_INT(argv[0]);
+       }
+
+       if ((str = JS_NewStringCopyZ(cx, strerror(error))) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               *rval = OBJECT_TO_JSVAL(NULL);
+               return JS_FALSE;
+       }
+       *rval = STRING_TO_JSVAL(str);
+
+       return JS_TRUE;
+}
+
+
+/*
+ * File object methods
+ */
+
+static JSBool
+file_m_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+       int     ret;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+
+       if ((f->flags & FF_CLOSE) != 0 && f->fh != NULL) {
+               if ((f->flags & FF_PCLOSE) != 0)
+                       ret = pclose(f->fh);
+               else
+                       ret = fclose(f->fh);
+               f->fh = NULL;
+       } else
+               ret = 0;
+       if (ret == -1) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_reopen(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+       char    *path, *mode;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       if (!JSVAL_IS_STRING(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not a String");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not a String");
+               return JS_FALSE;
+       }
+
+       if ((path = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]))) == NULL ||
+           (mode = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if ((f->fh = freopen(path, mode, f->fh)) == NULL) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t          *f;
+       jsdouble        v;
+       
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_NUMBER(argv[0]) || !JS_ValueToNumber(cx, argv[0], &v)) {
+               QUEUE_EXCEPTION("Argument 1 not a number");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Integer");
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if (fseeko(f->fh, (off_t)v, JSVAL_TO_INT(argv[1])) != 0) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_tell(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t          *f;
+       off_t           v;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if ((v = ftello(f->fh)) == -1) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       if (!JS_NewNumberValue(cx, (jsdouble)v, rval)) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_rewind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       rewind(f->fh);
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if (fflush(f->fh) != 0) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_purge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if (fpurge(f->fh) != 0) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+/*
+ * Unlike stdio setvbuf(3), requires two arguments.  One setting the buffering
+ * type, and the other the size of the wanted buffer, or 0 to use the default
+ * internal buffer.
+ * So our syntax is as follows:  file.setvbuf(type:Integer, bufsize:Integer);
+ */
+static JSBool
+file_m_setvbuf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+       size_t  size;
+       char    *vbuf = NULL;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Integer");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if ((size = JSVAL_TO_INT(argv[1])) > 0) {
+               if (f->vbuf == NULL) {
+                       if ((f->vbuf = JS_malloc(cx, size)) == NULL) {
+                               QUEUE_EXCEPTION("Out of memory");
+                               return JS_FALSE;
+                       }
+                       f->vbufsize = size;
+                       vbuf = f->vbuf;
+               } else if (size != f->vbufsize) {
+                       if ((vbuf = JS_realloc(cx, f->vbuf, size)) == NULL) {
+                               QUEUE_EXCEPTION("Out of memory");
+                               return JS_FALSE;
+                       }
+                       f->vbuf = vbuf;
+                       f->vbufsize = size;
+               }
+       }
+
+       if (setvbuf(f->fh, vbuf, JSVAL_TO_INT(argv[0]), size) != 0) {
+               QUEUE_EXCEPTION("setvbuf() == EOF");
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_clearerr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+       
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       clearerr(f->fh);
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_eof(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+       
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       *rval = JSVAL_TO_BOOLEAN(feof(f->fh));
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_error(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+       
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       *rval = JSVAL_TO_BOOLEAN(ferror(f->fh));
+
+       return JS_TRUE;
+}
+
+static JSBool
+file_m_fileno(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       *rval = JSVAL_TO_INT(fileno(f->fh));
+
+       return JS_TRUE;
+}
+
+/*
+ * Unlike fread(3), returns a string holding the read buffer (which could also
+ * be shorter than requested), or throws an exception on error.
+ */
+static JSBool
+file_m_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t          *f;
+       size_t          size, nmemb, tsize, rsize;
+       JSString        *str;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Integer");
+               return JS_FALSE;
+       }
+       size = JSVAL_TO_INT(argv[0]);
+       nmemb = JSVAL_TO_INT(argv[1]);
+       tsize = size * nmemb;
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       /*
+        * Ensure that read buffer is ready and large enough
+        */
+       if (read_charbuf_size < tsize) {
+               if (read_charbuf == NULL) {
+                       if ((read_charbuf = malloc(tsize)) == NULL) {
+                               QUEUE_EXCEPTION("Cannot allocate read buffer");
+                               return JS_FALSE;
+                       }
+               } else {
+                       char    *ptr;
+
+                       if ((ptr = realloc(read_charbuf, tsize)) == NULL) {
+                               QUEUE_EXCEPTION(
+                                   "Cannot reallocate read bufer");
+                               return JS_FALSE;
+                       }
+                       read_charbuf = ptr;
+               }
+               read_charbuf_size = tsize;
+       }
+
+       if ((rsize = fread(read_charbuf, size, nmemb, f->fh)) != nmemb) {
+               if (rsize == 0) {
+                       QUEUE_EXCEPTION(strerror(errno));
+                       return JS_FALSE;
+               }
+       }
+
+       if ((str = JS_NewStringCopyN(cx, read_charbuf, tsize)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+       *rval = STRING_TO_JSVAL(str);
+
+       return JS_TRUE;
+}
+
+/*
+ * Unlike fwrite(2), is simply provided the string to write rather than having
+ * to specify sizes.
+ */
+static JSBool
+file_m_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t          *f;
+       JSString        *str;
+       char            *bytes;
+       size_t          size;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 1) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not a String");
+               return JS_FALSE;
+       }
+       str = JSVAL_TO_STRING(argv[0]);
+       if ((bytes = JS_GetStringBytes(str)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+       size = JS_GetStringLength(str);
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if (fwrite(bytes, size, 1, f->fh) != 1) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+/*
+ * Like fgets(3), is provided a maximum buffer size (which although is
+ * internal).  Automatically strips the '\r', '\n' or '\r\n' if the second
+ * argument is false, or keep them otherwise.  On error, we throw an
+ * exception.
+ */
+static JSBool
+file_m_gets(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t          *f;
+       size_t          tsize;
+       JSBool          nostrip;
+       JSString        *str;
+       char            *rstr;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 2) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_BOOLEAN(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not a Boolean");
+               return JS_FALSE;
+       }
+       tsize = JSVAL_TO_INT(argv[0]);
+       nostrip = JSVAL_TO_BOOLEAN(argv[1]);
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       /*
+        * Ensure that read buffer is ready and large enough
+        */
+       if (read_charbuf_size < tsize) {
+               if (read_charbuf == NULL) {
+                       if ((read_charbuf = malloc(tsize)) == NULL) {
+                               QUEUE_EXCEPTION("Cannot allocate read buffer");
+                               return JS_FALSE;
+                       }
+               } else {
+                       char    *ptr;
+
+                       if ((ptr = realloc(read_charbuf, tsize)) == NULL) {
+                               QUEUE_EXCEPTION(
+                                   "Cannot reallocate read bufer");
+                               return JS_FALSE;
+                       }
+                       read_charbuf = ptr;
+               }
+               read_charbuf_size = tsize;
+       }
+
+       if ((rstr = fgets(read_charbuf, tsize, f->fh)) == NULL) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+       tsize = strlen(rstr);
+
+       if ((str = JS_NewStringCopyN(cx, rstr, tsize)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               return JS_FALSE;
+       }
+       *rval = STRING_TO_JSVAL(str);
+
+       return JS_TRUE;
+}
+
+/*
+ * Returns an int just like fgetc(3) but throws an exception on error.
+ */
+static JSBool
+file_m_getc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t  *f;
+       int     c;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 0) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if ((c = fgetc(f->fh)) == EOF) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+       *rval = INT_TO_JSVAL(c);
+
+       return JS_TRUE;
+}
+
+/*
+ * Like ungetc(3) but throws an exception on error.
+ */
+static JSBool
+file_m_ungetc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       file_t  *f;
+       int     c;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 1) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if ((c = ungetc(JSVAL_TO_INT(argv[0]), f->fh)) == EOF) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+       *rval = INT_TO_JSVAL(c);
+
+       return JS_TRUE;
+}
+
+/*
+ * Works with an int just like fputc(3).  Throws an exception on error.
+ */
+static JSBool
+file_m_putc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+       file_t  *f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 1) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               return JS_FALSE;
+       }
+
+       f = JS_GetInstancePrivate(cx, obj, &file_class, NULL);
+       assert(f != NULL);
+       if (f->fh == NULL) {
+               QUEUE_EXCEPTION("File already closed");
+               return JS_FALSE;
+       }
+
+       if (fputc(JSVAL_TO_INT(argv[0]), f->fh) != 0) {
+               QUEUE_EXCEPTION(strerror(errno));
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
diff --git a/mmsoftware/js/classes/js_file.h b/mmsoftware/js/classes/js_file.h
new file mode 100644 (file)
index 0000000..09a342d
--- /dev/null
@@ -0,0 +1,17 @@
+/* $Id: js_file.h,v 1.1 2006/09/25 23:06:23 mmondor Exp $ */
+
+/*
+ * Copyright (c) 2006, Matthew Mondor
+ * ALL RIGHTS RESERVED.
+ */
+
+#ifndef JSFILE_H
+#define JSFILE_H
+
+#include <js_file.h>
+
+extern JSObject        *js_InitFileClass(JSContext *, JSObject *);
+
+extern JSObject        *file_new(JSContext *, FILE *, int);
+
+#endif
index e076c17..65f84df 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: GNUmakefile,v 1.3 2006/09/15 21:03:49 mmondor Exp $
+# $Id: GNUmakefile,v 1.4 2006/09/25 23:06:28 mmondor Exp $
 
 #CFLAGS += -g
 CFLAGS += -Wall
@@ -10,21 +10,25 @@ PG_CFLAGS := $(shell pg_config --cppflags)
 PG_LDFLAGS := $(shell pg_config --ldflags)
 PG_LDFLAGS += -lpq
 
-OBJS := $(addprefix ../../classes/,js_fd.o js_errno.o js_signal.o js_pgsql.o \
-       js_dir.o js_fs.o)
+GD_CFLAGS := $(shell gdlib-config --cflags)
+GD_LDFLAGS := $(shell gdlib-config --ldflags --libs)
+GD_LDFLAGS += -lgd
+
+OBJS := $(addprefix ../../classes/,js_fd.o js_errno.o js_signal.o js_file.o \
+       js_pgsql.o js_dir.o js_fs.o js_gd.o)
 OBJS += js-sh.o
 
-CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) -I../../classes -Wall
-LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS)
+CFLAGS += $(JS_CFLAGS) $(PG_CFLAGS) $(GD_CFLAGS) -I../../classes -Wall
+LDFLAGS += $(JS_LDFLAGS) $(PG_LDFLAGS) $(GD_LDFLAGS)
 
 
 all: js-sh
 
 %.o: %.c
-       cc -c ${CFLAGS} -o $@ $<
+       cc -c $(CFLAGS) -o $@ $<
 
 js-sh: $(OBJS)
-       cc -o $@ -lc ${LDFLAGS} $(OBJS)
+       cc -o $@ -lc $(LDFLAGS) $(OBJS)
 
 clean:
        rm -f js-sh $(OBJS)
index d23cbc2..91d6271 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: js-sh.c,v 1.5 2006/09/08 12:50:47 mmondor Exp $ */
+/* $Id: js-sh.c,v 1.6 2006/09/25 23:06:28 mmondor Exp $ */
 
 /*
  * Copyright (c) 2004-2005, Matthew Mondor
 #include <js_fd.h>
 #include <js_errno.h>
 #include <js_signal.h>
+#include <js_file.h>
 #include <js_pgsql.h>
 #include <js_dir.h>
+#include <js_gd.h>
 
 
 
@@ -64,7 +66,7 @@ typedef struct {
        JSRuntime       *rt;
        JSContext       *ctx;
        JSObject        *global, *class_fd, *class_errno, *class_signal,
-                       *class_pgsql, *class_dir;
+                       *class_file, *class_pgsql, *class_dir, *class_gd;
 } js_context_t;
 
 /*
@@ -237,9 +239,13 @@ js_context_init(size_t gc_size, size_t stack_size)
                == NULL ||
            (cctx->class_signal = js_InitSignalClass(cctx->ctx, cctx->global))
                == NULL ||
+           (cctx->class_file = js_InitFileClass(cctx->ctx, cctx->global))
+               == NULL ||
            (cctx->class_pgsql = js_InitPGClass(cctx->ctx, cctx->global))
                == NULL  ||
            (cctx->class_dir = js_InitDirClass(cctx->ctx, cctx->global))
+               == NULL ||
+           (cctx->class_gd = js_InitGDClass(cctx->ctx, cctx->global))
                == NULL) {
                /* An error, free any partially allocated resources */
                if (cctx != NULL)