poke-devel
[Top][All Lists]
Advanced

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

[PATCH] Add doc command


From: John Darrington
Subject: [PATCH] Add doc command
Date: Sat, 29 Feb 2020 16:42:00 +0100

---
 doc/Makefile.am |  11 +++++
 doc/poke.texi   |  21 +++++++++
 run.in          |   3 +-
 src/Makefile.am |   1 +
 src/pk-cmd.c    |   2 +
 src/pk-misc.c   | 116 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/pk-repl.c   |  71 ++++++++++++++++++++++++++++-
 src/poke.c      |   9 ++++
 src/poke.h      |   1 +
 9 files changed, 233 insertions(+), 2 deletions(-)

diff --git a/doc/Makefile.am b/doc/Makefile.am
index ddf62f7b..58d88148 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -16,3 +16,14 @@
 
 info_TEXINFOS = poke.texi
 poke_TEXINFOS = fdl.texi
+
+
+# This rule generates a list of the node names from the manual.
+# It then substitutes spaces with '/' which is a kludge used to
+# work around some limitations of readline.
+# Look for the macro SPACE_SUBSTITUTE in pk-repl.c to see how this
+# is used.
+$(srcdir)/nodelist: $(srcdir)/poke.info
+       $(SED) -n -e 's/^\* \([^:]*\)::.*/\1/p' $< | $(SED) -e 's| |/|g' > $@
+
+INFO_DEPS = $(srcdir)/nodelist $(srcdir)/poke.info
diff --git a/doc/poke.texi b/doc/poke.texi
index 69548d0b..a7e270b0 100644
--- a/doc/poke.texi
+++ b/doc/poke.texi
@@ -70,6 +70,7 @@ Dot-Commands
 * mem command::                        Opening and selecting memory IO spaces.
 * ios command::                        Switching between IO spaces.
 * close command::              Closing IO spaces.
+* doc command::                 Online manual.
 * editor command::             Using an external editor for input.
 * info command::               Getting information about open files, @i{etc}.
 * set command::                        Querying and setting global options.
@@ -582,6 +583,26 @@ is:
 
 Where @var{#tag} is a tag identifying an open IO stream.
 
+@node doc command
+@chapter @code{.doc}
+@cindex @code{.doc}
+@cindex doc
+The @command{.doc} command is used to display this manual in poke's REPL.
+The syntax is:
+
+@example
+.doc [@var{node}]
+@end example
+
+@noindent
+where @var{node} is an optional parameter which indicates the chapter
+or section at which the manual should be opened.
+This command uses the info program (@ref{Top,,, info, The GNU Texinfo Manual})
+to interactively present the manual.
+Hence, it will fail if info is not installed.
+If poke is not running interactively then @command{.doc} does nothing.
+
+
 @node editor command
 @chapter @code{.editor}
 @cindex @code{.editor}
diff --git a/run.in b/run.in
index 7254412a..7c0c031d 100644
--- a/run.in
+++ b/run.in
@@ -31,9 +31,10 @@ b=$(cd @abs_builddir@ && pwd)
 # setup to run uninstalled poke
 PATH=$b/src:$PATH
 POKEDATADIR=$s/src
+POKEINFODIR=$s/doc
 POKEPICKLESDIR=$s/pickles
 POKESTYLESDIR=$s/etc
-export PATH POKEDATADIR POKEPICKLESDIR POKESTYLESDIR
+export PATH POKEDATADIR POKEPICKLESDIR POKESTYLESDIR POKEINFODIR
 
 # Cheap way to find some use-after-free and uninit read problems with glibc
 MALLOC_CHECK_=1
diff --git a/src/Makefile.am b/src/Makefile.am
index 07db253b..21475612 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -73,6 +73,7 @@ AM_LFLAGS = -d
 poke_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib \
                 -DPKL_DEBUG \
                 -DPKGDATADIR=\"$(pkgdatadir)\" \
+                -DPKGINFODIR=\"$(infodir)\" \
                 -DJITTER_VERSION=\"$(JITTER_VERSION)\" \
                 -DLOCALEDIR=\"$(localedir)\"
 poke_CFLAGS = -Wall $(BDW_GC_CFLAGS)
diff --git a/src/pk-cmd.c b/src/pk-cmd.c
index 547674fb..e920f5a9 100644
--- a/src/pk-cmd.c
+++ b/src/pk-cmd.c
@@ -49,6 +49,7 @@ extern struct pk_cmd load_cmd; /* pk-file.c */
 extern struct pk_cmd info_cmd; /* pk-info.c  */
 extern struct pk_cmd exit_cmd; /* pk-misc.c  */
 extern struct pk_cmd version_cmd; /* pk-misc.c */
+extern struct pk_cmd doc_cmd; /* pk-misc.c */
 extern struct pk_cmd jmd_cmd; /* pk-misc.c */
 extern struct pk_cmd help_cmd; /* pk-help.c */
 extern struct pk_cmd vm_cmd; /* pk-vm.c  */
@@ -63,6 +64,7 @@ static struct pk_cmd *dot_cmds[] =
     &file_cmd,
     &exit_cmd,
     &version_cmd,
+    &doc_cmd,
     &jmd_cmd,
     &info_cmd,
     &close_cmd,
diff --git a/src/pk-misc.c b/src/pk-misc.c
index cd0fc6c3..44717e6b 100644
--- a/src/pk-misc.c
+++ b/src/pk-misc.c
@@ -20,6 +20,8 @@
 #include <assert.h>
 #include <time.h>
 
+#include "findprog.h"
+#include "readline.h"
 #include "poke.h"
 #include "pk-cmd.h"
 
@@ -54,6 +56,59 @@ pk_cmd_version (int argc, struct pk_cmd_arg argv[], uint64_t 
uflags)
   return 1;
 }
 
+/* Call the info command for the poke documentation, using
+   the requested node.  */
+static int
+pk_cmd_doc (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
+{
+  int ret = 1;
+
+  /* This command is inherently interactive.  So if we're not
+     supposed to be in interactive mode, then do nothing.  */
+  if (poke_interactive_p)
+  {
+    int size = 0;
+    char *cmd = NULL;
+    int bytes = 64;
+
+    const char info_prog_name[] = "info";
+    const char *ip = find_in_path (info_prog_name);
+    if (strcmp (ip, info_prog_name) == 0)
+      {
+       pk_term_class ("error");
+       pk_puts ("error: ");
+       pk_term_end_class ("error");
+       pk_puts ("the \"info\" program is not installed.\n");
+       return 0;
+      }
+
+    do
+      {
+       size = bytes + 1;
+       cmd = xrealloc (cmd, size);
+       bytes = snprintf (cmd, size, "info -f \"%s/poke.info\"",
+                         poke_infodir);
+      }
+    while (bytes >= size);
+
+    if (argv[0].type == PK_CMD_ARG_STR)
+      {
+       const char *node = argv[0].val.str;
+       cmd = xrealloc (cmd, bytes + 7 + strlen (node));
+       strcat (cmd, " -n \"");
+       strcat (cmd, node);
+       strcat (cmd, "\"");
+      }
+
+    /* Open the documentation at the requested page.  */
+    ret = (0 == system (cmd));
+
+    free (cmd);
+  }
+
+  return ret;
+}
+
 static int
 pk_cmd_jmd (int argc, struct pk_cmd_arg argv[], uint64_t uflags)
 {
@@ -94,6 +149,64 @@ pk_cmd_jmd (int argc, struct pk_cmd_arg argv[], uint64_t 
uflags)
   return 1;
 }
 
+/* A completer to provide the node names of the info
+   documentation.  */
+char *
+doc_completion_function (const char *x, int state)
+{
+  static char **nodelist = NULL;
+  if (nodelist == NULL)
+    {
+      int n_nodes = 0;
+      char nlfile[256];
+      snprintf (nlfile, 256, "%s/nodelist", poke_infodir);
+      FILE *fp = fopen (nlfile, "r");
+      if (fp == NULL)
+       return NULL;
+      char *lineptr = NULL;
+      size_t size = 0;
+      while (!feof (fp))
+       {
+         int x = getline (&lineptr, &size, fp);
+         if (x != -1)
+           {
+             nodelist = xrealloc (nodelist, ++n_nodes * sizeof (*nodelist));
+             lineptr [strlen (lineptr) - 1] = '\0';
+             nodelist[n_nodes - 1] = strdup (lineptr);
+           }
+       }
+      fclose (fp);
+      free (lineptr);
+      nodelist = xrealloc (nodelist, ++n_nodes * sizeof (*nodelist));
+      nodelist[n_nodes - 1] = NULL;
+    }
+
+  static int idx = 0;
+  if (state == 0)
+    idx = 0;
+  else
+    ++idx;
+
+  int len = strlen (x);
+  while (1)
+    {
+      const char *name = nodelist[idx];
+      if (name == NULL)
+       break;
+
+      int match = strncmp (name, x, len);
+      if (match != 0)
+       {
+         idx++;
+         continue;
+       }
+      return strdup (name);
+    }
+
+  return NULL;
+}
+
+
 struct pk_cmd exit_cmd =
   {"exit", "?i", "", 0, NULL, pk_cmd_exit, "exit [CODE]", NULL};
 
@@ -102,3 +215,6 @@ struct pk_cmd version_cmd =
 
 struct pk_cmd jmd_cmd =
   {"jmd", "", "", 0, NULL, pk_cmd_jmd, "jmd", NULL};
+
+struct pk_cmd doc_cmd =
+  {"doc", "?s", "", 0, NULL, pk_cmd_doc, "doc [section]", 
doc_completion_function};
diff --git a/src/pk-repl.c b/src/pk-repl.c
index 3e7215ae..a8ae15f0 100644
--- a/src/pk-repl.c
+++ b/src/pk-repl.c
@@ -152,6 +152,49 @@ null_completion_function (const char *x, int state)
   return NULL;
 }
 
+char * doc_completion_function (const char *x, int state);
+
+#define SPACE_SUBSTITUTE  '/'
+
+/* Display the list of matches, replacing SPACE_SUBSTITUTE with
+   a space.  */
+static void
+space_substitute_display_matches (char **matches, int num_matches,
+                               int max_length)
+{
+  for (int i = 0; i < num_matches + 1; ++i)
+    {
+      for (char *m = matches[i]; *m; ++m)
+       {
+         if (*m == SPACE_SUBSTITUTE)
+           *m = ' ';
+       }
+    }
+
+  rl_display_match_list (matches, num_matches, max_length);
+}
+
+/* Display the rl_line_buffer substituting
+SPACE_SUBSTITUTE with a space.  */
+static void
+space_substitute_redisplay (void)
+{
+  /* Take a copy of the line_buffer.  */
+  char *olb = strdup (rl_line_buffer);
+
+  for (char *x = rl_line_buffer; *x ; x++)
+    {
+      if (*x == SPACE_SUBSTITUTE)
+        *x = ' ';
+    }
+
+  rl_redisplay ();
+
+  /* restore the line_buffer to its original state.  */
+  strcpy (rl_line_buffer, olb);
+  free (olb);
+}
+
 /* Readline's getc callback.
    Use this function to update the completer which
    should be used.
@@ -181,7 +224,27 @@ poke_getc (FILE *stream)
      }
   free (line_to_point);
 
-  return rl_getc (stream);
+  int c =  rl_getc (stream);
+
+  /* Due to readline's apparent inability to change the word break
+     character in the middle of a line, we have to do some underhand
+     skullduggery here.  Spaces are substituted with SPACE_SUBSTITUTE,
+     and then substituted back again in various callback functions.  */
+  if (rl_completion_entry_function == doc_completion_function)
+    {
+      rl_completion_display_matches_hook = space_substitute_display_matches;
+      rl_redisplay_function = space_substitute_redisplay;
+
+      if (c == ' ')
+       c = SPACE_SUBSTITUTE;
+    }
+  else
+    {
+      rl_completion_display_matches_hook = NULL;
+      rl_redisplay_function = rl_redisplay;
+    }
+
+  return c;
 }
 
 
@@ -299,6 +362,12 @@ pk_repl (void)
           break;
         }
 
+      for (char *s = line; *s; ++s)
+       {
+         if (*s == SPACE_SUBSTITUTE)
+           *s = ' ';
+       }
+
       /* Ignore empty lines.  */
       if (*line == '\0')
         continue;
diff --git a/src/poke.c b/src/poke.c
index 48f5055c..cabc8a36 100644
--- a/src/poke.c
+++ b/src/poke.c
@@ -56,6 +56,11 @@ int poke_quiet_p;
 
 char *poke_datadir;
 
+/* The following global contains the directory holding the program's
+   info file(s).  */
+
+char *poke_infodir;
+
 /* The following global contains the directory holding pickles shipped
    with poke.  In an installed program, this is the same than
    poke_datadir, but the POKE_PICKLESDIR environment variable can be
@@ -366,6 +371,10 @@ initialize (int argc, char *argv[])
   if (poke_picklesdir == NULL)
     poke_picklesdir = poke_datadir;
 
+  poke_infodir = getenv ("POKEINFODIR");
+  if (poke_infodir == NULL)
+    poke_infodir = PKGINFODIR;
+
   /* Initialize the terminal output.  */
   pk_term_init (argc, argv);
 
diff --git a/src/poke.h b/src/poke.h
index 1ee798c9..c8027de6 100644
--- a/src/poke.h
+++ b/src/poke.h
@@ -28,6 +28,7 @@ extern int poke_exit_code;
 extern pkl_compiler poke_compiler;
 extern pvm poke_vm;
 extern char *poke_datadir;
+extern char *poke_infodir;
 extern char *poke_picklesdir;
 extern int poke_obase;
 
-- 
2.20.1




reply via email to

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