#include "forth_internal.h" struct file_input_spec { struct fh_input_spec_s spec; char saved_buffer[INPUT_BUFFER_SIZE]; FILE *file; }; struct string_input_spec { struct fh_input_spec_s spec; const char *str; size_t len; }; enum fh_error fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput) { LOG("--- Push input spec ---"); if (newinput == NULL) { LOGE("push input with NULL"); return FH_ERR_INTERNAL; // TODO IO } struct fh_input_spec_s *oldinput = fh->input; fh->input = NULL; if (NULL != oldinput) { oldinput->push_prepare(fh, oldinput); // memcpy(&oldinput->saved_buffer[0], &fh->heap[INPUTBUF_ADDR], INPUT_BUFFER_SIZE); oldinput->saved_inputlen = fh->inputlen; oldinput->saved_inputptr = fh->inputptr; oldinput->saved_execptr = fh->execptr; oldinput->saved_inputaddr = fh->inputaddr; newinput->previous = oldinput; } fh->execptr = MAGICADDR_EXEC_INTERACTIVE; fh->inputlen = 0; fh->inputptr = 0; // fh_setstate(fh, FH_STATE_INTERPRET, 0); fh->input = newinput; if (newinput->cwd) { LOG("CD to %s", newinput->cwd); chdir(newinput->cwd); } return FH_OK; } enum fh_error fh_pop_input(struct fh_thread_s *fh) { LOG("--- Pop input spec ---"); struct fh_input_spec_s *discarded = fh->input; fh->input = NULL; struct fh_input_spec_s *restored = discarded->previous; if (!restored) { return FH_OK; // topmost } fh->input = restored; // this can be NULL, that must be checked by caller. restored->pop_restore(fh, restored); // memcpy(&fh->heap[INPUTBUF_ADDR], &restored->saved_buffer[0], INPUT_BUFFER_SIZE); fh->inputlen = restored->saved_inputlen; fh->inputptr = restored->saved_inputptr; fh->execptr = restored->saved_execptr; fh->inputaddr = restored->saved_inputaddr; // fh_setstate(fh, restored->saved_state, 0); if (discarded->free_self) { discarded->free_self(discarded); } if (restored->cwd) { LOG("CD to %s", restored->cwd); chdir(restored->cwd); } return FH_OK; } static enum fh_error file_save_input(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { enum fh_error rv; struct file_input_spec *fspec = (void*) spec; if (isatty(fileno(fspec->file))) { TRY(ds_push(fh, 0)); return FH_OK; } uint32_t pos = (uint32_t) ftell(fspec->file); TRY(ds_push(fh, spec->linenum)); TRY(ds_push(fh, fh->inputptr)); TRY(ds_push(fh, pos - fh->inputlen)); TRY(ds_push(fh, 3)); return FH_OK; } static enum fh_error file_restore_input(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { enum fh_error rv; struct file_input_spec *fspec = (void*) spec; uint32_t n; TRY(ds_pop(fh, &n)); if (n == 3) { uint32_t filepos; TRY(ds_pop(fh, &filepos)); fseek(fspec->file, filepos, SEEK_SET); TRY(ds_pop(fh, &fh->inputptr)); TRY(ds_pop(fh, &spec->linenum)); LOG("file input, restore filepos %d, inptr %d, linenum %d", filepos, fh->inputptr, spec->linenum); fgets(&fh->heap[INPUTBUF_ADDR], INPUT_BUFFER_SIZE, fspec->file); TRY(ds_push(fh, 0)); } else { LOG("file input, restore nothing, bad N!"); TRY(ds_push(fh, TOBOOL(1))); } return FH_OK; } static enum fh_error str_save_input(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { enum fh_error rv; TRY(ds_push(fh, spec->linenum)); TRY(ds_push(fh, fh->inputptr)); TRY(ds_push(fh, 2)); return FH_OK; } static enum fh_error str_restore_input(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { enum fh_error rv; uint32_t n; TRY(ds_pop(fh, &n)); if (n == 2) { TRY(ds_pop(fh, &fh->inputptr)); TRY(ds_pop(fh, &spec->linenum)); LOG("str input, inptr %d, linenum %d", fh->inputptr, spec->linenum); TRY(ds_push(fh, 0)); } else { LOG("str input, restore nothing, bad N!"); TRY(ds_push(fh, TOBOOL(1))); } return FH_OK; } static void file_push_prepare(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { struct file_input_spec *fspec = (void*) spec; memcpy(&fspec->saved_buffer[0], &fh->heap[INPUTBUF_ADDR], INPUT_BUFFER_SIZE); } static void file_pop_restore(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { struct file_input_spec *fspec = (void*) spec; memcpy(&fh->heap[INPUTBUF_ADDR], &fspec->saved_buffer[0], INPUT_BUFFER_SIZE); } static void str_push_prepare(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { } static void str_pop_restore(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { } static inline uint8_t *inputbuf_at(struct fh_thread_s *fh, size_t pos) { return &fh->heap[fh->inputaddr + pos]; } void fh_input_memmove_leftovers(struct fh_thread_s *fh) { if (fh->inputptr < fh->inputlen) { // something is left uint32_t remains = fh->inputlen - fh->inputptr; if (remains > 0) { memmove(inputbuf_at(fh, 0), inputbuf_at(fh, fh->inputptr), remains); fh->inputptr = 0; fh->inputlen = remains; } else { fh->inputptr = 0; fh->inputlen = 0; } } else { fh->inputptr = 0; fh->inputlen = 0; } } static void file_start(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { fh->inputaddr = INPUTBUF_ADDR; // } static void str_start(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { struct string_input_spec *strspec = (struct string_input_spec *) spec; fh->inputaddr = (void*)strspec->str - (void*)&fh->heap[0]; // XXX this will explode if string is not in the internal heap! fh->inputlen = strspec->len; fh->inputptr = 0; } static bool file_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { struct file_input_spec *fis = (struct file_input_spec *) spec; fh_input_memmove_leftovers(fh); uint32_t space_left = INPUT_BUFFER_SIZE - fh->inputlen - 1; char *wp = (char *) inputbuf_at(fh, fh->inputptr); //LOG("spec %p, fgets %d", spec, space_left); if (fgets(wp, (int) space_left, fis->file)) { spec->linenum++; fh->inputlen = strnlen(wp, INPUT_BUFFER_SIZE); wp[fh->inputlen] = 0; //LOG("read %d bytes from file", fh->inputlen); return true; } else { return fh->inputptr > fh->inputlen; // return false only if there is nothing left } } static bool str_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec) { // no refilling, the string exists in its entirety on heap return fh->inputptr < fh->inputlen; } static void free_filespec(void *p) { struct file_input_spec *fis = (struct file_input_spec *) p; if (fis->file != stdin) { fclose(fis->file); fis->file = NULL; } if (fis->spec.cwd) { free(fis->spec.cwd); fis->spec.cwd = NULL; } free(fis); } static void free_strspec(void *p) { struct string_input_spec *sis = (struct string_input_spec *) p; if (sis->spec.cwd) { free(sis->spec.cwd); sis->spec.cwd = NULL; } free(sis); } struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f, const char *cwd) { struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1); if (!spec) { LOGE("Err alloc input spec struct"); return NULL; } LOG("Input for FILE, cwd: %s", cwd); spec->spec.free_self = free_filespec; spec->spec.start = file_start; spec->spec.push_prepare = file_push_prepare; spec->spec.pop_restore = file_pop_restore; spec->spec.refill_input_buffer = file_refill; spec->spec.save_input = file_save_input; spec->spec.restore_input = file_restore_input; spec->file = f; spec->spec.source_id = fileno(f); // stdin is 0 if (cwd) { spec->spec.cwd = strdup(cwd); } return (struct fh_input_spec_s*) spec; } struct fh_input_spec_s *fh_create_input_from_filename(char *path) { struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1); if (!spec) { LOGE("Err alloc input spec struct"); return NULL; } FILE *f = fopen(path, "r"); if (!f) { LOGE("Err open file \"%s\"", path); free(spec); return NULL; } spec->spec.free_self = free_filespec; spec->spec.start = file_start; spec->spec.push_prepare = file_push_prepare; spec->spec.pop_restore = file_pop_restore; spec->spec.refill_input_buffer = file_refill; spec->spec.save_input = file_save_input; spec->spec.restore_input = file_restore_input; spec->file = f; spec->spec.source_id = fileno(f); // stdin is 0 char pwd[PATH_MAX+1]; realpath(path, pwd); char *end = strrchr(pwd, '/'); if (end) { // add terminator *end = 0; } spec->spec.cwd = strdup(pwd); LOG("Input for file %s, path %s\n", path, pwd); return (struct fh_input_spec_s*) spec; } struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len, const char *cwd) { struct string_input_spec *spec = calloc(sizeof(struct string_input_spec), 1); if (!spec) { return NULL; } LOG("Evaluating string: \"%.*s\", len %d", (int)len, str, len); spec->spec.free_self = free_strspec; spec->spec.refill_input_buffer = str_refill; spec->spec.push_prepare = str_push_prepare; spec->spec.pop_restore = str_pop_restore; spec->spec.start = str_start; spec->spec.save_input = str_save_input; spec->spec.restore_input = str_restore_input; spec->str = str; spec->len = len; spec->spec.cwd = cwd ? strdup(cwd) : NULL; spec->spec.source_id = -1; return (struct fh_input_spec_s*) spec; } void fh_input_teardown(struct fh_thread_s *fh) { struct fh_input_spec_s *s = fh->input; if (!s) return; fh->input = NULL; while (s) { struct fh_input_spec_s *prev = s->previous; if (s->free_self) { s->free_self(s); } s = prev; } fh->inputptr = 0; fh->inputlen = 0; fh->inputaddr = INPUTBUF_ADDR; }