#include #include #include #include #include #include #include #include #include "js_util_functions.h" #include "stream.h" #include "directorystream.h" static void DirectoryStream_finalize(JSContext *cx, JSObject *obj); static JSBool DirectoryStream_ctor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool DirectoryStream_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool DirectoryStream_getBytesAvailable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool DirectoryStream_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool DirectoryStream_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool directorystream_open(DirectoryStreamInformation *info); static JSBool directorystream_close(DirectoryStreamInformation *info); static char *errnostr(int err); static JSObject *DirectoryStreamPrototype = NULL; static JSClass js_DirectoryStream_class = { "DirectoryStream", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, DirectoryStream_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSFunctionSpec DirectoryStream_methods[] = { {"open", DirectoryStream_open, 0, 0 ,0}, {"getBytesAvailable", DirectoryStream_getBytesAvailable, 0, 0 ,0}, {"read", DirectoryStream_read, 0, 0 ,0}, {"close", DirectoryStream_close, 0, 0 ,0}, {NULL} }; JSBool register_class_DirectoryStream(JSContext *cx){ JSObject *globalObj = JS_GetGlobalObject(cx); JSObject *proto = Stream_getPrototypeObject(); /* Define the file object. */ DirectoryStreamPrototype = JS_InitClass(cx, globalObj, proto, &js_DirectoryStream_class, DirectoryStream_ctor, 1, NULL, DirectoryStream_methods, NULL, NULL); if (!DirectoryStreamPrototype){ return JS_FALSE; } return JS_TRUE; } JSObject *DirectoryStream_getPrototypeObject(){ return DirectoryStreamPrototype; } static char *errnostr(int e){ char *msg = NULL; switch (e){ case ENOENT: msg = strdup("File does not exist."); break; case EACCES: msg = strdup("Permission denied to check file."); break; case EBADF: msg = strdup("Bad file descriptor"); break; case ELOOP: msg = strdup("Symbolic link loop encountered."); break; case ENAMETOOLONG: msg = strdup("File name too long, not supported."); break; case ENOMEM: msg = strdup("Out of memory."); break; case ENOTDIR: msg = strdup("Path component not a directory."); break; default: msg = strdup("Unknown error occured."); } return msg; } static void DirectoryStream_finalize(JSContext *cx, JSObject *obj){ DirectoryStreamInformation *info = (DirectoryStreamInformation *)JS_GetPrivate(cx, obj); if (info != NULL){ directorystream_close(info); free(info->path); if (info->streamInfo.buffer != NULL){ free(info->streamInfo.buffer); } JS_free(cx, info); } } static JSBool DirectoryStream_ctor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ if (argc < 1){ JS_SetPendingException(cx, printf_exception(cx, "Incorrect number of parameters to DirectoryStream constructor.")); return JS_FALSE; } else { char *path=NULL; DirectoryStreamInformation *info=NULL; info = JS_malloc(cx, sizeof(*info)); info->streamInfo.handle = -1; info->streamInfo.buffer = NULL; info->streamInfo.bufferLength = 0; info->streamInfo.eofFlag = JS_FALSE; info->path = NULL; info->handle = NULL; JSString_to_CString(JS_ValueToString(cx, argv[0]), &path); info->path = strdup(path); JS_SetPrivate(cx, obj, info); return JS_TRUE; } } static JSBool DirectoryStream_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ DirectoryStreamInformation *info=(DirectoryStreamInformation*)JS_GetPrivate(cx, obj); JSBool result = directorystream_open(info); if (result == JS_FALSE){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to open directory stream. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); } return result; } static JSBool DirectoryStream_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ DirectoryStreamInformation *info=(DirectoryStreamInformation*)JS_GetPrivate(cx, obj); JSBool result = directorystream_close(info); if (result == JS_FALSE){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to close directory stream. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); } return result; } static JSBool DirectoryStream_getBytesAvailable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ DirectoryStreamInformation *info=(DirectoryStreamInformation*)JS_GetPrivate(cx, obj); off_t curPos = 0; unsigned int length = 0; curPos = telldir(info->handle); if (curPos == (off_t)-1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to determine number of files available. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } errno = 0; while (readdir(info->handle) != NULL){ length++; } if (errno != 0){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to determine number of files available. (%d) %s", errno, msg); free(msg); seekdir(info->handle, curPos); JS_SetPendingException(cx, exception); return JS_FALSE; } else { seekdir(info->handle, curPos); *rval = INT_TO_JSVAL(length); return JS_TRUE; } } /*This function serves multiple purposes depending on the number of arguments passed in (argc)*/ static JSBool DirectoryStream_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ DirectoryStreamInformation *info=(DirectoryStreamInformation*)JS_GetPrivate(cx, obj); struct dirent *entry; if (argc == 0){ errno = 0; entry = readdir(info->handle); if (entry != NULL){ JSString *fileName = NULL; int len = strlen(entry->d_name); char *copy = JS_malloc(cx, len+1); if (copy == NULL){ JS_SetPendingException(cx, printf_exception(cx, "Unable to read directory entry.")); return JS_FALSE; } memset(copy, 0, len+1); strncpy(copy, entry->d_name, len); fileName = JS_NewString(cx, copy, len); *rval = STRING_TO_JSVAL(fileName); return JS_TRUE; } else if (errno == 0){ *rval = BOOLEAN_TO_JSVAL(JS_FALSE); info->streamInfo.eofFlag=JS_TRUE; return JS_TRUE; } else { char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to read directory stream. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } } else { int numberToRead = 0; if (JS_ValueToInt32(cx, argv[0], &numberToRead) == JS_TRUE){ jsval *fileNameArray = NULL; int totalNamesRead = 0; errno=0; while ((totalNamesRead < numberToRead) && (entry = readdir(info->handle)) != NULL){ JSString *fileName = NULL; int len = strlen(entry->d_name); char *copy = JS_malloc(cx, len+1); jsval *reallocPtr = NULL; if (copy == NULL){ JS_SetPendingException(cx, printf_exception(cx, "Unable to read directory entry.")); return JS_FALSE; } memset(copy, 0, len+1); strncpy(copy, entry->d_name, len); fileName = JS_NewString(cx, copy, len); totalNamesRead++; reallocPtr = JS_realloc(cx, fileNameArray, sizeof(*fileNameArray)*totalNamesRead); if (reallocPtr == NULL){ JS_SetPendingException(cx, printf_exception(cx, "Unable to read directory entry.")); return JS_FALSE; } else { fileNameArray = reallocPtr; fileNameArray[totalNamesRead-1] = STRING_TO_JSVAL(fileName); } errno = 0; } if (totalNamesRead < numberToRead){ if (errno == 0){ if (totalNamesRead == 0){ *rval = BOOLEAN_TO_JSVAL(JS_FALSE); info->streamInfo.eofFlag=JS_TRUE; return JS_TRUE; } else { *rval = OBJECT_TO_JSVAL(JS_NewArrayObject(cx, totalNamesRead, fileNameArray)); return JS_TRUE; } } else { char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to read directory stream. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } } else { *rval = OBJECT_TO_JSVAL(JS_NewArrayObject(cx, totalNamesRead, fileNameArray)); return JS_TRUE; } } else { JS_SetPendingException(cx, printf_exception(cx, "Unable to understand length parameter.")); return JS_FALSE; } } } static JSBool directorystream_open(DirectoryStreamInformation *info){ if (info->handle == NULL && info->path != NULL){ info->handle = opendir(info->path); info->streamInfo.eofFlag = JS_FALSE; if (info->handle == NULL){ return JS_FALSE; } info->streamInfo.handle = dirfd(info->handle); return JS_TRUE; } else { return JS_FALSE; } } static JSBool directorystream_close(DirectoryStreamInformation *info){ info->streamInfo.eofFlag = JS_FALSE; if (info->handle != NULL){ if (closedir(info->handle) == 0){ info->streamInfo.handle = -1; info->handle = NULL; return JS_TRUE; } return JS_FALSE; } else { return JS_TRUE; } }