#include #include #include #include #include #include #include #include #include GroundStruct fileStruct = {}; GroundValue fileStructConstructor(GroundScope* scope, List args) { GroundValue value = groundCreateValue(CUSTOM, &fileStruct); char* filePath = args.values[0].data.stringVal; // do a check to make sure the file is actually a file first struct stat st; if (0 != lstat(filePath, &st)) { // lstat failed, file probably doesn't exist char buffer[256]; sprintf(buffer, "The file \"%s\" does not exist!", filePath); ERROR(buffer, "FileNotFound"); } if (S_IFREG != (st.st_mode & S_IFMT)) { switch (st.st_mode & S_IFMT) { case S_IFSOCK: ERROR("Attempt to open socket as file!", "NotAFile"); case S_IFBLK: ERROR("Attempt to open block device as file!", "NotAFile"); case S_IFDIR: ERROR("Attempt to open directory as file!", "NotAFile"); case S_IFCHR: ERROR("Attempt to open character device as file!", "NotAFile"); case S_IFIFO: ERROR("Attempt to open FIFO (pipe) as file!", "NotAFile"); default: break; } } // opens the given file in read and write mode, the file must exist or openning the file will fail FILE* file = fopen(filePath, "r+"); if (file == NULL) { ERROR("Unkown error while openning file!", "GenericFileError"); } // seek to end of file to get size fseek(file, 0, SEEK_END); int64_t size = ftell(file); rewind(file); GroundObjectField *handleField = groundFindField(*value.data.customVal, "fileHandle"); GroundObjectField *sizeField = groundFindField(*value.data.customVal, "size"); GroundObjectField *filePathField = groundFindField(*value.data.customVal, "filePath"); handleField->value.data.intVal = (int64_t)file; sizeField->value.data.intVal = size; filePathField->value.data.stringVal = filePath; value.type = CUSTOM; return value; } GroundValue fileStructRead(GroundScope* scope, List args) { GroundVariable* handleField = groundFindVariable(scope, "fileHandle"); if (handleField == NULL) { ERROR("A field called \"fileHandle\" was not found", "FieldNotFound"); } FILE* file = (FILE*)handleField->value.data.intVal; GroundVariable* sizeField = groundFindVariable(scope, "size"); if (sizeField == NULL) { ERROR("A field called \"size\" was not found", "FieldNotFound"); } int64_t size = sizeField->value.data.intVal; // allocate buffer to read file into char* buffer = malloc(size + 1); if (buffer == NULL) { ERROR("Failed to allocate memory for buffer while reading file!", "AllocFail"); } // read file size_t readSize = fread(buffer, 1, size, file); buffer[readSize] = 0; // null terminate buffer return groundCreateValue(STRING, buffer); } GroundValue fileStructWrite(GroundScope* scope, List args) { char* buffer = args.values[0].data.stringVal; GroundVariable* handleField = groundFindVariable(scope, "fileHandle"); if (handleField == NULL) { ERROR("A field called \"fileHandle\" was not found", "FieldNotFound"); } FILE* file = (FILE*)handleField->value.data.intVal; GroundVariable* sizeField = groundFindVariable(scope, "size"); if (sizeField == NULL) { ERROR("A field called \"size\" was not found", "FieldNotFound"); } GroundVariable* tellField = groundFindVariable(scope, "tell"); if (tellField == NULL) { ERROR("A field called \"tell\" was not found", "FieldNotFound"); } ftruncate(fileno(file), 0); rewind(file); fprintf(file, "%s", buffer); sizeField->value.data.intVal = strlen(buffer); tellField->value.data.intVal = strlen(buffer); return groundCreateValue(INT, 0); } GroundValue fileStructAppend(GroundScope* scope, List args) { char* buffer = args.values[0].data.stringVal; GroundVariable* handleField = groundFindVariable(scope, "fileHandle"); if (handleField == NULL) { ERROR("A field called \"fileHandle\" was not found", "FieldNotFound"); } FILE* file = (FILE*)handleField->value.data.intVal; GroundVariable* sizeField = groundFindVariable(scope, "size"); if (sizeField == NULL) { ERROR("A field called \"size\" was not found", "FieldNotFound"); } int64_t size = sizeField->value.data.intVal; GroundVariable* tellField = groundFindVariable(scope, "tell"); if (tellField == NULL) { ERROR("A field called \"tell\" was not found", "FieldNotFound"); } fprintf(file, "%s", buffer); sizeField->value.data.intVal = size + strlen(buffer); tellField->value.data.intVal = tellField->value.data.intVal + strlen(buffer); return groundCreateValue(INT, 0); } GroundValue fileStructFlush(GroundScope* scope, List args) { GroundVariable* handleField = groundFindVariable(scope, "fileHandle"); if (handleField == NULL) { ERROR("A field called \"fileHandle\" was not found", "FieldNotFound"); } FILE* file = (FILE*)handleField->value.data.intVal; fflush(file); return groundCreateValue(INT, 0); } GroundValue fileStructSeek(GroundScope* scope, List args) { int64_t offset = args.values[0].data.intVal; if (offset < 0) { ERROR("Can't seek to negative offset!", "OutOfBounds"); } GroundVariable* handleField = groundFindVariable(scope, "fileHandle"); if (handleField == NULL) { ERROR("A field called \"fileHandle\" was not found", "FieldNotFound"); } FILE* file = (FILE*)handleField->value.data.intVal; GroundVariable* tellField = groundFindVariable(scope, "tell"); if (tellField == NULL) { ERROR("A field called \"tell\" was not found", "FieldNotFound"); } GroundVariable* sizeField = groundFindVariable(scope, "size"); if (sizeField == NULL) { ERROR("A field called \"size\" was not found", "FieldNotFound"); } int64_t size = sizeField->value.data.intVal; if (offset >= size) { ERROR("Attempt to seek past file size!", "OutOfBounds"); } int result = fseek(file, offset, SEEK_SET); if (result != 0) { char buffer[100]; sprintf(buffer, "fseek failed with error code: \"%d\"", result); ERROR(buffer, "SeekError"); } tellField->value.data.intVal = offset; return groundCreateValue(INT, 0); } GroundValue fileStructSeekEnd(GroundScope* scope, List args) { int64_t offset = args.values[0].data.intVal; if (offset < 0) { ERROR("Can't seek to negative offset!", "OutOfBounds"); } GroundVariable* handleField = groundFindVariable(scope, "fileHandle"); if (handleField == NULL) { ERROR("A field called \"fileHandle\" was not found", "FieldNotFound"); } FILE* file = (FILE*)handleField->value.data.intVal; GroundVariable* tellField = groundFindVariable(scope, "tell"); if (tellField == NULL) { ERROR("A field called \"tell\" was not found", "FieldNotFound"); } GroundVariable* sizeField = groundFindVariable(scope, "size"); if (sizeField == NULL) { ERROR("A field called \"size\" was not found", "FieldNotFound"); } int64_t size = sizeField->value.data.intVal; if (offset >= size) { ERROR("Attempt to seek past file size!", "OutOfBounds"); } int result = fseek(file, offset, SEEK_END); if (result != 0) { char buffer[100]; sprintf(buffer, "fseek failed with error code: \"%d\"", result); ERROR(buffer, "SeekError"); } tellField->value.data.intVal = size - offset; return groundCreateValue(INT, 0); } void ground_init(GroundScope* scope) { fileStruct = groundCreateStruct(); groundAddFieldToStruct(&fileStruct, "fileHandle", groundCreateValue(INT, 0)); groundAddFieldToStruct(&fileStruct, "filePath", groundCreateValue(STRING, "")); groundAddFieldToStruct(&fileStruct, "size", groundCreateValue(INT, 0)); groundAddFieldToStruct(&fileStruct, "tell", groundCreateValue(INT, 0)); groundAddFunctionToStruct(&fileStruct, "read", fileStructRead, STRING, 0); // reads the file and returns the contents as a string // readLines function implemented in solstice groundAddFunctionToStruct(&fileStruct, "write", fileStructWrite, INT, 1, STRING, "buffer"); // wipes the file before writing the buffer to the file groundAddFunctionToStruct(&fileStruct, "append", fileStructAppend, INT, 1, STRING, "buffer"); // appends the buffer to end of the contents in the file groundAddFunctionToStruct(&fileStruct, "flush", fileStructFlush, INT, 0); // flush file buffer (not THAT useful tbh) groundAddFunctionToStruct(&fileStruct, "seek", fileStructSeek, INT, 1, INT, "offset"); // jump to offset relative to start of file groundAddFunctionToStruct(&fileStruct, "seekEnd", fileStructSeekEnd, INT, 1, INT, "offset"); // jump to offset relative to end of file groundAddNativeFunction(scope, "openFile", fileStructConstructor, CUSTOM, 1, STRING, "filePath"); groundAddNativeFunction(scope, "File_SOLS_CONSTRUCTOR", fileStructConstructor, CUSTOM, 1, STRING, "filePath"); }