#include #include #include #include #include #include #include #include #include "js_util_functions.h" #include "stream.h" #include "filestream.h" #define CHUNK_SIZE 1024 static void FileStream_finalize(JSContext *cx, JSObject *obj); static JSBool FileStream_ctor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool FileStream_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool FileStream_getBytesAvailable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool FileStream_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool FileStream_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool FileStream_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool FileStream_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); static JSBool filestream_open(FileStreamInformation *info); static JSBool filestream_close(FileStreamInformation *info); static JSBool filestream_fill_buffer(FileStreamInformation *info); static char *errnostr(int err); static JSObject *FileStreamPrototype = NULL; static JSClass js_FileStream_class = { "FileStream", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, FileStream_finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; static JSFunctionSpec FileStream_methods[] = { {"open", FileStream_open, 0, 0 ,0}, {"getBytesAvailable", FileStream_getBytesAvailable, 0, 0 ,0}, {"read", FileStream_read, 0, 0 ,0}, {"write", FileStream_write, 0, 0, 0}, {"close", FileStream_close, 0, 0 ,0}, {"seek", FileStream_seek, 2, 0, 0}, {NULL} }; JSBool register_class_FileStream(JSContext *cx){ JSObject *globalObj = JS_GetGlobalObject(cx); JSObject *proto = Stream_getPrototypeObject(); /* Define the file object. */ FileStreamPrototype = JS_InitClass(cx, globalObj, proto, &js_FileStream_class, FileStream_ctor, 1, NULL, FileStream_methods, NULL, NULL); if (!FileStreamPrototype){ return JS_FALSE; } return JS_TRUE; } JSObject *FileStream_getPrototypeObject(){ return FileStreamPrototype; } 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 FileStream_finalize(JSContext *cx, JSObject *obj){ FileStreamInformation *info = (FileStreamInformation *)JS_GetPrivate(cx, obj); if (info != NULL){ filestream_close(info); free(info->path); if (info->streamInfo.buffer != NULL){ free(info->streamInfo.buffer); } JS_free(cx, info); } } static JSBool FileStream_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 FileStream constructor.")); return JS_FALSE; } else { char *path=NULL; FileStreamInformation *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; JSString_to_CString(JS_ValueToString(cx, argv[0]), &path); info->path = strdup(path); JS_SetPrivate(cx, obj, info); return JS_TRUE; } } static JSBool FileStream_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ FileStreamInformation *info=(FileStreamInformation*)JS_GetPrivate(cx, obj); JSBool result = filestream_open(info); if (result == JS_FALSE){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to open stream. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); } return result; } static JSBool FileStream_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ FileStreamInformation *info=(FileStreamInformation*)JS_GetPrivate(cx, obj); JSBool result = filestream_close(info); if (result == JS_FALSE){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to close stream. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); } return result; } static JSBool FileStream_getBytesAvailable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ FileStreamInformation *info=(FileStreamInformation*)JS_GetPrivate(cx, obj); off_t curPos = 0; off_t endPos = 0; unsigned int length = 0; curPos = lseek(info->streamInfo.handle, 0, SEEK_CUR); if (curPos == (off_t)-1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to determine available bytes. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } endPos = lseek(info->streamInfo.handle, 0, SEEK_END); if (endPos == (off_t)-1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to determine available bytes. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } length = endPos - curPos; curPos = lseek(info->streamInfo.handle, curPos, SEEK_SET); if (curPos == (off_t)-1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Stream has become corrupt. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } length += info->streamInfo.bufferLength; *rval = INT_TO_JSVAL(length); return JS_TRUE; } /*This function serves multiple purposes depending on the number of arguments passed in (argc)*/ static JSBool FileStream_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ FileStreamInformation *info=(FileStreamInformation*)JS_GetPrivate(cx, obj); char *buffer=NULL; char *bufferPtr = NULL; char *bufferDst = NULL; char *bufferReallocPtr = NULL; int bufferFillLen=0; int bufferSize=0; JSString *returnValue=NULL; JSBool foundEndOfLine=JS_FALSE; if (argc == 0){ while (foundEndOfLine == JS_FALSE){ if (info->streamInfo.bufferLength == 0){ if (filestream_fill_buffer(info) == JS_FALSE){ JS_SetPendingException(cx, printf_exception(cx, "Unable to read from file.")); JS_free(cx, buffer); return JS_FALSE; } else { if (info->streamInfo.bufferLength == 0){ goto complete; } } } bufferPtr = info->streamInfo.buffer; bufferSize = 0; while (bufferSize < info->streamInfo.bufferLength && (*bufferPtr != '\n' && *bufferPtr != '\r')){ bufferPtr++; bufferSize++; } if (bufferSize < info->streamInfo.bufferLength){ if (*bufferPtr == '\r' && *(bufferPtr+1) == '\n'){ bufferPtr++; bufferSize++; } if (*bufferPtr == '\r' || *bufferPtr == '\n'){ foundEndOfLine = JS_TRUE; bufferSize++; } } bufferReallocPtr = JS_realloc(cx, buffer, sizeof(*buffer)*(bufferSize+bufferFillLen+1)); if (bufferReallocPtr == NULL){ JS_SetPendingException(cx, printf_exception(cx, "Unable to allocate memory.")); if (buffer != NULL){ JS_free(cx, buffer); } return JS_FALSE; } else { buffer = bufferReallocPtr; } memcpy(buffer+bufferFillLen, info->streamInfo.buffer, bufferSize); bufferFillLen += bufferSize; buffer[bufferFillLen] = '\0'; if (bufferSize < info->streamInfo.bufferLength){ info->streamInfo.bufferLength = info->streamInfo.bufferLength - bufferSize; bufferPtr = info->streamInfo.buffer+bufferSize; bufferDst = info->streamInfo.buffer; while (bufferDst < (info->streamInfo.buffer+info->streamInfo.bufferLength)){ *bufferDst = *bufferPtr; bufferDst++; bufferPtr++; } bufferReallocPtr = realloc(info->streamInfo.buffer, sizeof(*(info->streamInfo.buffer))*info->streamInfo.bufferLength); if (bufferReallocPtr == NULL){ JS_SetPendingException(cx, printf_exception(cx, "Unable to allocate memory for read buffer.")); JS_free(cx, buffer); return JS_FALSE; } else { info->streamInfo.buffer = bufferReallocPtr; } } else { free(info->streamInfo.buffer); info->streamInfo.buffer = NULL; info->streamInfo.bufferLength = 0; } } } else { if (JS_ValueToInt32(cx, argv[0], &bufferSize) == JS_TRUE){ buffer = JS_malloc(cx, bufferSize+1); memset(buffer, 0, bufferSize+1); while (bufferFillLen < bufferSize){ if (info->streamInfo.bufferLength == 0){ if (filestream_fill_buffer(info) == JS_FALSE){ JS_SetPendingException(cx, printf_exception(cx, "Unable to read from file.")); JS_free(cx, buffer); return JS_FALSE; } else { if (info->streamInfo.bufferLength == 0){ goto complete; } } } if (bufferFillLen+info->streamInfo.bufferLength > bufferSize){ int diff = bufferSize - bufferFillLen; memcpy(buffer+bufferFillLen, info->streamInfo.buffer, diff); bufferFillLen += diff; info->streamInfo.bufferLength = info->streamInfo.bufferLength - diff; bufferPtr = info->streamInfo.buffer+diff; bufferDst = info->streamInfo.buffer; while (bufferDst < (info->streamInfo.buffer+diff)){ *bufferDst = *bufferPtr; bufferDst++; bufferPtr++; } bufferReallocPtr = realloc(info->streamInfo.buffer, sizeof(*(info->streamInfo.buffer))*info->streamInfo.bufferLength); if (bufferReallocPtr == NULL){ JS_SetPendingException(cx, printf_exception(cx, "Unable to allocate memory for read buffer.")); JS_free(cx, buffer); return JS_FALSE; } else { info->streamInfo.buffer = bufferReallocPtr; } } else { memcpy(buffer+bufferFillLen, info->streamInfo.buffer, info->streamInfo.bufferLength); bufferFillLen += info->streamInfo.bufferLength; free(info->streamInfo.buffer); info->streamInfo.buffer = NULL; info->streamInfo.bufferLength = 0; } } } else { JS_SetPendingException(cx, printf_exception(cx, "Bad parameter passed to FileStream.read()")); return JS_FALSE; } } complete: if (buffer != NULL){ returnValue = JS_NewString(cx, buffer, bufferFillLen); *rval = STRING_TO_JSVAL(returnValue); } else { buffer = JS_malloc(cx, 1); buffer[0] = '\0'; returnValue = JS_NewString(cx, buffer, 0); *rval = STRING_TO_JSVAL(returnValue); } return JS_TRUE; } static JSBool filestream_open(FileStreamInformation *info){ if (info->streamInfo.handle == -1 && info->path != NULL){ info->streamInfo.handle = open(info->path, O_RDWR); info->streamInfo.eofFlag = JS_FALSE; if (info->streamInfo.handle == -1){ return JS_FALSE; } return JS_TRUE; } else { return JS_FALSE; } } static JSBool filestream_close(FileStreamInformation *info){ if (info->streamInfo.buffer != NULL){ free(info->streamInfo.buffer); info->streamInfo.buffer = NULL; } info->streamInfo.bufferLength = 0; info->streamInfo.eofFlag = JS_FALSE; if (info->streamInfo.handle != -1){ if (close(info->streamInfo.handle) == 0){ info->streamInfo.handle = -1; return JS_TRUE; } return JS_FALSE; } else { return JS_TRUE; } } static JSBool filestream_fill_buffer(FileStreamInformation *info){ int newLen = info->streamInfo.bufferLength + CHUNK_SIZE; char *buff = realloc(info->streamInfo.buffer, newLen); if (buff == NULL){ return JS_FALSE; } else { info->streamInfo.buffer = buff; info->streamInfo.bufferLength += read(info->streamInfo.handle, (info->streamInfo.buffer+info->streamInfo.bufferLength), CHUNK_SIZE); if (info->streamInfo.bufferLength == 0){ free(buff); info->streamInfo.buffer=NULL; info->streamInfo.eofFlag=JS_TRUE; } return JS_TRUE; } } static JSBool FileStream_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ FileStreamInformation *info=(FileStreamInformation*)JS_GetPrivate(cx, obj); if (argc == 1){ char *stringToWrite = NULL; int stringLength=0; size_t amountWritten=0; stringLength = JSString_to_CString(JS_ValueToString(cx, argv[0]), &stringToWrite); amountWritten = write(info->streamInfo.handle, stringToWrite, stringLength); if (amountWritten == -1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to write data to file. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } else { *rval = INT_TO_JSVAL(amountWritten); return JS_TRUE; } } else if (argc == 2){ char *stringToWrite = NULL; int stringLength = 0; size_t amountWritten=0; JSString_to_CString(JS_ValueToString(cx, argv[0]), &stringToWrite); if (JS_ValueToInt32(cx, argv[1], &stringLength) == JS_FALSE){ JS_SetPendingException(cx, printf_exception(cx, "Cannot understand length argument.")); return JS_FALSE; } amountWritten = write(info->streamInfo.handle, stringToWrite, stringLength); if (amountWritten == -1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Unable to write data to file. (%d) %s", errno, msg); free(msg); JS_SetPendingException(cx, exception); return JS_FALSE; } else { *rval = INT_TO_JSVAL(amountWritten); return JS_TRUE; } } else { JS_SetPendingException(cx, printf_exception(cx, "Incorrect number of arguments.")); return JS_FALSE; } } static JSBool FileStream_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ FileStreamInformation *info=(FileStreamInformation*)JS_GetPrivate(cx, obj); int offset=-1; int base_point=-1; off_t newoffset=0; if (JS_ValueToInt32(cx, argv[0], &offset) == JS_FALSE){ JS_SetPendingException(cx, printf_exception(cx, "Cannot understand offset number.")); return JS_FALSE; } if (JS_ValueToInt32(cx, argv[1], &base_point) == JS_FALSE){ JS_SetPendingException(cx, printf_exception(cx, "base_point argument must be one of Stream.SEEK_SET, Stream.SEEK_CURRENT, Stream.SEEK_END")); return JS_FALSE; } if (base_point != SEEK_SET && base_point != SEEK_CUR && base_point != SEEK_END){ JS_SetPendingException(cx, printf_exception(cx, "base_point argument must be one of Stream.SEEK_SET, Stream.SEEK_CURRENT, Stream.SEEK_END")); return JS_FALSE; } newoffset = lseek(info->streamInfo.handle, offset, base_point); if (newoffset == (off_t)-1){ char *msg = errnostr(errno); jsval exception = printf_exception(cx, "Seek failed. (%d) %s", errno, msg); JS_SetPendingException(cx, exception); return JS_FALSE; } else { *rval = INT_TO_JSVAL(newoffset); return JS_TRUE; } }