You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
9.4 KiB
365 lines
9.4 KiB
#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;
|
|
}
|
|
|