From 8d66238f4711ba7d094049b7e083bcc25a946899 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= <ondra@ondrovo.com>
Date: Sat, 27 Nov 2021 01:37:16 +0100
Subject: [PATCH] implement saving and restoring input, at least for string
 (file not tested)

---
 include/fh_input.h      |  12 ++--
 src/fh_builtins_meta.c  |  20 ++++++-
 src/fh_input.c          | 119 ++++++++++++++++++++++++++++++----------
 src/fh_parse.c          |   2 +-
 testfiles/saverestore.f |  19 +++++++
 5 files changed, 136 insertions(+), 36 deletions(-)
 create mode 100644 testfiles/saverestore.f

diff --git a/include/fh_input.h b/include/fh_input.h
index f50a58d..c420bb6 100644
--- a/include/fh_input.h
+++ b/include/fh_input.h
@@ -15,17 +15,19 @@ typedef bool (*fh_input_refill_t)(struct fh_thread_s *fh, struct fh_input_spec_s
 /** Spec free func */
 typedef void (*fh_input_free_t)(void *spec);
 
-typedef void (*fh_input_start_t)(struct fh_thread_s *fh, struct fh_input_spec_s *spec);
+typedef void (*fh_input_void_method_t)(struct fh_thread_s *fh, struct fh_input_spec_s *spec);
 
-typedef void (*fh_input_pushpop_t)(struct fh_thread_s *fh, struct fh_input_spec_s *spec);
+typedef enum fh_error (*fh_input_error_method_t)(struct fh_thread_s *fh, struct fh_input_spec_s *spec);
 
 struct fh_input_spec_s {
   struct fh_input_spec_s *previous;
   fh_input_refill_t refill_input_buffer;
-  fh_input_start_t start;
+  fh_input_void_method_t start;
   fh_input_free_t free_self;
-  fh_input_pushpop_t push_prepare;
-  fh_input_pushpop_t pop_restore;
+  fh_input_void_method_t push_prepare;
+  fh_input_void_method_t pop_restore;
+  fh_input_error_method_t save_input;
+  fh_input_error_method_t restore_input;
   uint32_t linenum;
   char *cwd; // CWD of the input, used for relative includes. malloc'd (strdup)
 
diff --git a/src/fh_builtins_meta.c b/src/fh_builtins_meta.c
index f538720..d63472b 100644
--- a/src/fh_builtins_meta.c
+++ b/src/fh_builtins_meta.c
@@ -603,6 +603,22 @@ static enum fh_error w_evaluate(struct fh_thread_s *fh, const struct fh_word_s *
   return FH_OK;
 }
 
+static enum fh_error w_save_input(struct fh_thread_s *fh, const struct fh_word_s *w)
+{
+  (void) w;
+  enum fh_error rv;
+  fh->input->save_input(fh, fh->input);
+  return FH_OK;
+}
+
+static enum fh_error w_restore_input(struct fh_thread_s *fh, const struct fh_word_s *w)
+{
+  (void) w;
+  enum fh_error rv;
+  fh->input->restore_input(fh, fh->input);
+  return FH_OK;
+}
+
 static enum fh_error w_included(struct fh_thread_s *fh, const struct fh_word_s *w)
 {
   (void) w;
@@ -738,11 +754,13 @@ const struct name_and_handler fh_builtins_meta[] = {
     {"'",            wp_tick,         1, 0},
     {"[']",          wp_tick,         1, 1},
     {"execute",      w_execute,       0, 0},
+    {"save-input",   w_save_input,    0, 0},
+    {"restore-input",w_restore_input, 0, 0},
     {"environment?", w_env_query,     0, 0},
     {"marker",       w_marker,        0, 0},
     {"compile,",     w_compile_comma, 0, 0},
     {"evaluate",     w_evaluate,      0, 0},
     {"included",     w_included,      0, 0},
-    {"include",     w_include,        0, 0},
+    {"include",      w_include,       0, 0},
     { /* end marker */ }
 };
diff --git a/src/fh_input.c b/src/fh_input.c
index f66f43c..e9f82c4 100644
--- a/src/fh_input.c
+++ b/src/fh_input.c
@@ -1,5 +1,17 @@
 #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 ---");
@@ -70,17 +82,78 @@ enum fh_error fh_pop_input(struct fh_thread_s *fh)
   return FH_OK;
 }
 
-struct file_input_spec {
-  struct fh_input_spec_s spec;
-  char saved_buffer[INPUT_BUFFER_SIZE];
-  FILE *file;
-};
+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;
+}
 
-struct string_input_spec {
-  struct fh_input_spec_s spec;
-  const char *str;
-  size_t len;
-};
+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)
 {
@@ -160,26 +233,8 @@ static bool file_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec)
 
 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;
-//  struct string_input_spec *fis = (struct string_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);
-//
-//  uint32_t chars_remaining_in_string = fis->len - fis->readpos;
-//  if (chars_remaining_in_string > 0) {
-//    if (chars_remaining_in_string < space_left) {
-//      space_left = chars_remaining_in_string;
-//    }
-//
-//    memcpy(wp, &fis->str[fis->readpos], space_left);
-//    *(wp + space_left) = 0;
-//    fis->readpos += space_left;
-//    fh->inputlen += space_left;
-//    return true;
-//  } else {
-//    return false;
-//  }
 }
 
 static void free_filespec(void *p)
@@ -220,6 +275,8 @@ struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f, const char *cwd
   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;
   if (cwd) {
     spec->spec.cwd = strdup(cwd);
@@ -247,6 +304,8 @@ struct fh_input_spec_s *fh_create_input_from_filename(char *path)
   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;
 
   char pwd[PATH_MAX+1];
@@ -275,6 +334,8 @@ struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len, const
   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;
diff --git a/src/fh_parse.c b/src/fh_parse.c
index b6ddf03..15247f2 100644
--- a/src/fh_parse.c
+++ b/src/fh_parse.c
@@ -386,7 +386,7 @@ enum fh_error fh_process_line(struct fh_thread_s *fh)
           }
         } else if (fh->parse_if_level == 0) {
           /* eval a word */
-          LOG("Handle \"%.*s\", len %d", (int) length, rp, length);
+          LOG("Handle \"%.*s\"", (int) length, rp);
           TRY(fh_handle_ascii_word(fh, rp, length));
         } else {
           if (EQ(rp, "\\", length)) {
diff --git a/testfiles/saverestore.f b/testfiles/saverestore.f
new file mode 100644
index 0000000..6eb4438
--- /dev/null
+++ b/testfiles/saverestore.f
@@ -0,0 +1,19 @@
+\ 1 debug
+
+VARIABLE SI_INC 0 SI_INC !
+
+: SI1
+   SI_INC @ >IN +!
+   15 SI_INC !
+;
+
+: S$ S" SAVE-INPUT SI1 RESTORE-INPUT 12345" ;
+
+S$ EVALUATE SI_INC @
+
+. . .
+
+\ 0 2345 15
+
+bye
+