[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] Add doc command
From: |
Jose E. Marchesi |
Subject: |
Re: [PATCH] Add doc command |
Date: |
Sat, 29 Feb 2020 17:24:13 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux) |
Hi John.
This is nice, but I think it would be better to call the command .info
instead of .doc. WDYT?
---
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;