poke-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[COMMITTED] poke: new internal terminal pager


From: Jose E. Marchesi
Subject: [COMMITTED] poke: new internal terminal pager
Date: Sat, 29 Jan 2022 15:25:55 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

This patch introduces basic paging support in the poke
terminal (pk-term).  A new poke setting `pager' has been introduced to
control whether paging takes effect.  It's default value is `no'.  You
can change this setting (for example in your ~/.pokerc) like this:

  .set pager yes

Note that I am applying this in `master' but not in maint/poke-2.  The
pager works, but I think it needs to be used a little and improved
before being released.  An example of a thing to improve is to
remove (erase) the "--More--" lines when continuing.

2022-01-29  Jose E. Marchesi  <jemarch@gnu.org>

        * poke/pk-cmd-set.c (pk_cmd_set_pager): New function.
        (set_pager_cmd): New variable.
        (pk_cmd_set_init): Add set_pager command.
        * poke/pk-repl.c (pk_repl): Use pager (conditionally) when
        executing commands in the repl.
        * poke/pk-settings.pk: Setting for `pager'.
        * poke/pk-term.c (screen_lines): New variable.
        (screen_cols): Likewise.
        (pager_active_p): Likewise.
        (nlines): Likewise.
        (pk_term_init): Initialize terminal dimensions.
        (pk_term_start_pager): New function.
        (pk_term_stop_pager): Likewise.
        (pk_puts_paged): Likewise.
        (pk_puts): Call pk_puts_paged when necessary.
        (pk_printf): Output via pk_puts.
        * poke/poke.c: New global poke_pager_p.
---
 ChangeLog           |  20 +++++++++
 poke/pk-cmd-set.c   |  57 ++++++++++++++++++++++++-
 poke/pk-repl.c      |   3 ++
 poke/pk-settings.pk |  15 +++++++
 poke/pk-term.c      | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 poke/pk-term.h      |   4 ++
 poke/poke.c         |   5 +++
 poke/poke.h         |   1 +
 8 files changed, 222 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 81315c27..92888a76 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,25 @@
 2022-01-29  Jose E. Marchesi  <jemarch@gnu.org>
 
+       * poke/pk-cmd-set.c (pk_cmd_set_pager): New function.
+       (set_pager_cmd): New variable.
+       (pk_cmd_set_init): Add set_pager command.
+       * poke/pk-repl.c (pk_repl): Use pager (conditionally) when
+       executing commands in the repl.
+       * poke/pk-settings.pk: Setting for `pager'.
+       * poke/pk-term.c (screen_lines): New variable.
+       (screen_cols): Likewise.
+       (pager_active_p): Likewise.
+       (nlines): Likewise.
+       (pk_term_init): Initialize terminal dimensions.
+       (pk_term_start_pager): New function.
+       (pk_term_stop_pager): Likewise.
+       (pk_puts_paged): Likewise.
+       (pk_puts): Call pk_puts_paged when necessary.
+       (pk_printf): Output via pk_puts.
+       * poke/poke.c: New global poke_pager_p.
+
+2022-01-29  Jose E. Marchesi  <jemarch@gnu.org>
+
        * poke/poke.c (parse_args_2): Really restore proper behavior with
        -l and unhandled exceptions.
 
diff --git a/poke/pk-cmd-set.c b/poke/pk-cmd-set.c
index 99fbd36d..4b10eb7d 100644
--- a/poke/pk-cmd-set.c
+++ b/poke/pk-cmd-set.c
@@ -182,8 +182,56 @@ pk_cmd_set_error_on_warning (int argc, struct pk_cmd_arg 
argv[],
 
   return 1;
 }
+
+static int
+pk_cmd_set_pager (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
+{
+  /* set error-on-warning {yes,no} */
+
+  const char *arg;
+
+  /* Note that it is not possible to distinguish between no argument
+     and an empty unique string argument.  Therefore, argc should be
+     always 1 here, and we determine when no value was specified by
+     checking whether the passed string is empty or not.  */
+
+  if (argc != 2)
+    assert (0);
+
+  arg = PK_CMD_ARG_STR (argv[1]);
+
+  if (*arg == '\0')
+    {
+      if (poke_pager_p)
+        pk_puts ("yes\n");
+      else
+        pk_puts ("no\n");
+    }
+  else
+    {
+      if (STREQ (arg, "yes"))
+        poke_pager_p = 1;
+      else if (STREQ (arg, "no"))
+        poke_pager_p = 0;
+      else
+        {
+          pk_term_class ("error");
+          pk_puts (_("error: "));
+          pk_term_end_class ("error");
+          pk_puts (_("pager should be one of `yes' or `no'\n"));
+          return 0;
+        }
+    }
+
+  return 1;
+}
+
 extern struct pk_cmd null_cmd; /* pk-cmd.c  */
 
+const struct pk_cmd set_pager_cmd =
+  {"pager", "s?", "", 0, NULL, NULL, pk_cmd_set_pager,
+   "set pager (yes|no)", NULL};
+
 const struct pk_cmd set_error_on_warning_cmd =
   {"error-on-warning", "s?", "", 0, NULL, NULL, pk_cmd_set_error_on_warning,
    "set error-on-warning (yes|no)", NULL};
@@ -224,7 +272,7 @@ pk_cmd_set_init ()
   nsettings = pk_array_nelem (registry_settings);
 
   set_cmds = xmalloc (sizeof (struct pk_cmd *)
-                      * (pk_int_value (nsettings) + 2));
+                      * (pk_int_value (nsettings) + 3));
 
   for (i = 0; i < pk_int_value (nsettings); ++i)
     {
@@ -265,6 +313,13 @@ pk_cmd_set_init ()
   /* Add error-on-warning. */
   set_cmds[i++] = &set_error_on_warning_cmd;
 
+  /* Add set-pager.  */
+  set_cmds[i++] = &set_pager_cmd;
+
+  /* NOTE: if you add more C-handled commands here like
+     `error-on-warning' and `set-pager', please do not forget to
+     update the xmalloc count above.  */
+
   /* Finish set_cmds with the null command.  */
   set_cmds[i] = &null_cmd;
 }
diff --git a/poke/pk-repl.c b/poke/pk-repl.c
index b256163c..76c9df6f 100644
--- a/poke/pk-repl.c
+++ b/poke/pk-repl.c
@@ -383,7 +383,10 @@ pk_repl (void)
           add_history (line);
 #endif
 
+          if (poke_pager_p)
+            pk_term_start_pager ();
           pk_cmd_exec (line);
+          pk_term_stop_pager ();
         }
       free (line);
     }
diff --git a/poke/pk-settings.pk b/poke/pk-settings.pk
index 99796bf0..63d8ee75 100644
--- a/poke/pk-settings.pk
+++ b/poke/pk-settings.pk
@@ -535,6 +535,21 @@ turn warnings into errors.
 This setting is `no' by default.",
     };
 
+/* Likewise for `pager'.  */
+
+pk_help_add_topic
+  :entry Poke_HelpEntry {
+           category = "settings",
+           topic = "pager",
+           summary = "whether to use the terminal pager",
+           synopsis = ".set pager {yes,no}",
+           description = "\
+This setting determines whether the output of poke commands will
+be paged or not.  Each page will have the height of the terminal.
+
+This setting is `no' by default.",
+   };
+
 /* Dump current settings.  */
 
 fun pk_settings_dump = void:
diff --git a/poke/pk-term.c b/poke/pk-term.c
index d191f6fd..ef908301 100644
--- a/poke/pk-term.c
+++ b/poke/pk-term.c
@@ -18,17 +18,29 @@
 
 #include <config.h>
 
-#include <stdlib.h> /* For exit.  */
+#include <stdlib.h> /* For exit and getenv.  */
 #include <assert.h> /* For assert. */
 #include <string.h>
 #include <unistd.h> /* For isatty */
 #include <textstyle.h>
 #include <assert.h>
 #include <xalloc.h>
+#include <xstrndup.h>
+#include <stdio.h>
+#include <termios.h>
+#include <termcap.h>
 
 #include "poke.h"
 #include "pk-utils.h"
 
+/* Several variables related to the pager.  */
+
+static int screen_lines;
+static int screen_cols;
+
+static int pager_active_p;
+static int nlines = 1;
+
 /* Default style to use when the user doesn't specify a style file.
    We provide two defaults: one for dark backgrounds (the default) and
    another for bright backgrounds.  */
@@ -244,6 +256,28 @@ pk_term_init (int argc, char *argv[])
       register_color (default_bgcolor, -1, -1, -1);
     }
 #endif
+
+  /* Get the terminal dimensions using termcap.  */
+  {
+    char *termtype = getenv ("TERM");
+    static char term_buffer[2048];
+
+    if (termtype == NULL)
+      {
+        /* Stupid defaults.  */
+        screen_cols = 80;
+        screen_lines = 25;
+      }
+
+    if (tgetent (term_buffer, termtype) < 0)
+      fputs ("error: could not access the termcap database; ignoring TERM.\n",
+             stderr);
+    else
+      {
+        screen_cols = tgetnum ("co");
+        screen_lines = tgetnum ("li");
+      }
+  }
 }
 
 void
@@ -261,10 +295,91 @@ pk_term_flush ()
   ostream_flush (pk_ostream, FLUSH_THIS_STREAM);
 }
 
+
+void
+pk_term_start_pager (void)
+{
+  pager_active_p = 1;
+  nlines = 1;
+}
+
+void
+pk_term_stop_pager (void)
+{
+  pager_active_p = 0;
+}
+
+static void
+pk_puts_paged (const char *lines)
+{
+  char *start, *end;
+
+
+  start = (char *) lines;
+
+  do
+  {
+    char *line;
+
+    end = strchrnul (start, '\n');
+    line = xstrndup (start, end - start + 1);
+
+    ostream_write_str (pk_ostream, line);
+    free (line);
+    start = end + 1;
+    if (*end != '\0')
+      nlines++;
+
+    if (nlines >= screen_lines)
+      {
+        struct termios old_termios;
+        struct termios new_termios;
+
+        ostream_write_str (pk_ostream, "--More--");
+        ostream_flush (pk_ostream, FLUSH_THIS_STREAM);
+
+        /* Set stdin in non-buffered mode.  */
+        tcgetattr (0, &old_termios);
+        memcpy (&new_termios, &old_termios, sizeof (struct termios));
+        new_termios.c_lflag &= ~(ICANON | ECHO);
+        new_termios.c_cc[VTIME] = 0;
+        new_termios.c_cc[VMIN] = 1;
+        tcsetattr (0, TCSANOW, &new_termios);
+
+        /* Wait for a key and process it.  */
+        while (1)
+          {
+            int c = fgetc (stdin);
+
+            if (c == '\n')
+              {
+                nlines--;
+                break;
+              }
+            if (c == ' ')
+              {
+                nlines = 1;
+                break;
+              }
+
+            /* Ding! 8-) */
+            fprintf (stderr, "\007");
+          }
+
+        /* Restore stdin to buffered-mode.  */
+        tcsetattr (0, TCSANOW, &old_termios);
+        ostream_write_str (pk_ostream, "\n");
+     }
+  } while (*end != '\0');
+}
+
 void
 pk_puts (const char *str)
 {
-  ostream_write_str (pk_ostream, str);
+  if (pager_active_p)
+    pk_puts_paged (str);
+  else
+    ostream_write_str (pk_ostream, str);
 }
 
 __attribute__ ((__format__ (__printf__, 1, 2)))
@@ -280,7 +395,7 @@ pk_printf (const char *format, ...)
   assert (r != -1);
   va_end (ap);
 
-  ostream_write_str (pk_ostream, str);
+  pk_puts (str);
   free (str);
 }
 
diff --git a/poke/pk-term.h b/poke/pk-term.h
index 15a5db01..e1ce4a3a 100644
--- a/poke/pk-term.h
+++ b/poke/pk-term.h
@@ -60,4 +60,8 @@ extern struct pk_color pk_term_get_bgcolor (void);
 extern void pk_term_set_color (struct pk_color color);
 extern void pk_term_set_bgcolor (struct pk_color color);
 
+/* Paging.  */
+extern void pk_term_start_pager (void);
+extern void pk_term_stop_pager (void);
+
 #endif /* PK_TERM_H */
diff --git a/poke/poke.c b/poke/poke.c
index 76ff9a69..b589729d 100644
--- a/poke/poke.c
+++ b/poke/poke.c
@@ -128,6 +128,11 @@ pk_compiler poke_compiler;
 
 int poke_load_init_file = 1;
 
+/* The following global indicates whether to use the terminal
+   pager.  It defaults to 0.  */
+
+int poke_pager_p;
+
 /* Command line options management.  */
 
 enum
diff --git a/poke/poke.h b/poke/poke.h
index c09bf005..8508dd68 100644
--- a/poke/poke.h
+++ b/poke/poke.h
@@ -31,6 +31,7 @@
 extern int poke_interactive_p;
 extern int poke_quiet_p;
 extern int poke_exit_p;
+extern int poke_pager_p;
 #if HAVE_HSERVER
 extern int poke_hserver_p;
 #endif
-- 
2.11.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]