summaryrefslogtreecommitdiff
path: root/macros/texinfo/texinfo/info/nodemenu.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/texinfo/texinfo/info/nodemenu.c
Initial commit
Diffstat (limited to 'macros/texinfo/texinfo/info/nodemenu.c')
-rw-r--r--macros/texinfo/texinfo/info/nodemenu.c283
1 files changed, 283 insertions, 0 deletions
diff --git a/macros/texinfo/texinfo/info/nodemenu.c b/macros/texinfo/texinfo/info/nodemenu.c
new file mode 100644
index 0000000000..a870c07642
--- /dev/null
+++ b/macros/texinfo/texinfo/info/nodemenu.c
@@ -0,0 +1,283 @@
+/* nodemenu.c -- produce a menu of all visited nodes.
+
+ Copyright 1993-2019 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Originally written by Brian Fox. */
+
+#include "info.h"
+#include "session.h"
+#include "echo-area.h"
+#include "variables.h"
+
+static NODE *get_visited_nodes (void);
+
+/* Return a line describing the format of a node information line. */
+static const char *
+nodemenu_format_info (void)
+{
+ /* TRANSLATORS: The "\n* Menu:\n\n" part of this should not be translated, as
+ it is part of the Info syntax. */
+ return _("\n* Menu:\n\n\
+ (File)Node Lines Size Containing File\n\
+ ---------- ----- ---- ---------------");
+}
+
+/* Produce a formatted line of information about NODE. Here is what we want
+ the output listing to look like:
+
+* Menu:
+ (File)Node Lines Size Containing File
+ ---------- ----- ---- ---------------
+* (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1
+* (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1
+* (dir)Top:: 40 589 /usr/gnu/info/dir
+*/
+static char *
+format_node_info (NODE *node)
+{
+ register int i;
+ char *containing_file;
+ static struct text_buffer line_buffer = { 0 };
+
+ if (!text_buffer_base (&line_buffer))
+ text_buffer_init (&line_buffer);
+ else
+ text_buffer_reset (&line_buffer);
+
+ if (node->subfile)
+ containing_file = node->subfile;
+ else
+ containing_file = node->fullpath;
+
+ if (!containing_file || !*containing_file)
+ text_buffer_printf (&line_buffer, "* %s::", node->nodename);
+ else
+ text_buffer_printf (&line_buffer, "* (%s)%s::",
+ filename_non_directory (node->fullpath),
+ node->nodename);
+
+ for (i = text_buffer_off (&line_buffer); i < 36; i++)
+ text_buffer_add_char (&line_buffer, ' ');
+
+ {
+ int lines = 1;
+
+ for (i = 0; i < node->nodelen; i++)
+ if (node->contents[i] == '\n')
+ lines++;
+
+ text_buffer_printf (&line_buffer, "%d", lines);
+ }
+
+ text_buffer_add_char (&line_buffer, ' ');
+ for (i = text_buffer_off (&line_buffer); i < 44; i++)
+ text_buffer_add_char (&line_buffer, ' ');
+ text_buffer_printf (&line_buffer, "%ld", node->nodelen);
+
+ if (containing_file)
+ {
+ for (i = text_buffer_off (&line_buffer); i < 51; i++)
+ text_buffer_add_char (&line_buffer, ' ');
+ text_buffer_printf (&line_buffer, containing_file);
+ }
+
+ return xstrdup (text_buffer_base (&line_buffer));
+}
+
+/* Little string comparison routine for qsort (). */
+static int
+compare_strings (const void *entry1, const void *entry2)
+{
+ char **e1 = (char **) entry1;
+ char **e2 = (char **) entry2;
+
+ return mbscasecmp (*e1, *e2);
+}
+
+/* The name of the nodemenu node. */
+static char *nodemenu_nodename = "*Node Menu*";
+
+/* Produce an informative listing of all the visited nodes, and return it
+ in a newly allocated node. */
+static NODE *
+get_visited_nodes (void)
+{
+ register int i;
+ WINDOW *info_win;
+ NODE *node;
+ char **lines = NULL;
+ size_t lines_index = 0, lines_slots = 0;
+ struct text_buffer message;
+
+ for (info_win = windows; info_win; info_win = info_win->next)
+ {
+ for (i = 0; i < info_win->hist_index; i++)
+ {
+ NODE *history_node = info_win->hist[i]->node;
+
+ /* We skip mentioning "*Node Menu*" nodes. */
+ if (strcmp (history_node->nodename, nodemenu_nodename) == 0)
+ continue;
+
+ if (history_node)
+ {
+ char *line;
+
+ line = format_node_info (history_node);
+ add_pointer_to_array (line, lines_index, lines, lines_slots, 20);
+ }
+ }
+ }
+
+ /* Sort the array of information lines, if there are any. */
+ if (lines)
+ {
+ register int j, newlen;
+ char **temp;
+
+ qsort (lines, lines_index, sizeof (char *), compare_strings);
+
+ /* Delete duplicates. */
+ for (i = 0, newlen = 1; i < lines_index - 1; i++)
+ {
+ /* Use FILENAME_CMP here, since the most important piece
+ of info in each line is the file name of the node. */
+ if (FILENAME_CMP (lines[i], lines[i + 1]) == 0)
+ {
+ free (lines[i]);
+ lines[i] = NULL;
+ }
+ else
+ newlen++;
+ }
+
+ /* We have free ()'d and marked all of the duplicate slots.
+ Copy the live slots rather than pruning the dead slots. */
+ temp = xmalloc ((1 + newlen) * sizeof (char *));
+ for (i = 0, j = 0; i < lines_index; i++)
+ if (lines[i])
+ temp[j++] = lines[i];
+
+ temp[j] = NULL;
+ free (lines);
+ lines = temp;
+ lines_index = newlen;
+ }
+
+ text_buffer_init (&message);
+
+ text_buffer_printf (&message, "\n");
+ text_buffer_printf (&message,
+ "%s", replace_in_documentation
+ (_("Here is the menu of nodes you have recently visited.\n\
+Select one from this menu, or use '\\[history-node]' in another window.\n"), 0));
+
+ text_buffer_printf (&message, "%s\n", nodemenu_format_info ());
+
+ for (i = 0; (lines != NULL) && (i < lines_index); i++)
+ {
+ text_buffer_printf (&message, "%s\n", lines[i]);
+ free (lines[i]);
+ }
+
+ if (lines)
+ free (lines);
+
+ node = text_buffer_to_node (&message);
+ scan_node_contents (node, 0, 0);
+
+ return node;
+}
+
+DECLARE_INFO_COMMAND (list_visited_nodes,
+ _("Make a window containing a menu of all of the currently visited nodes"))
+{
+ WINDOW *new;
+ NODE *node;
+
+ /* If a window is visible and showing the buffer list already, re-use it. */
+ for (new = windows; new; new = new->next)
+ {
+ node = new->node;
+
+ if (internal_info_node_p (node) &&
+ (strcmp (node->nodename, nodemenu_nodename) == 0))
+ break;
+ }
+
+ /* If we couldn't find an existing window, try to use the next window
+ in the chain. */
+ if (!new)
+ {
+ if (window->next)
+ new = window->next;
+ /* If there is more than one window, wrap around. */
+ else if (window != windows)
+ new = windows;
+ }
+
+ /* If we still don't have a window, make a new one to contain the list. */
+ if (!new)
+ new = window_make_window ();
+
+ /* If we couldn't make a new window, use this one. */
+ if (!new)
+ new = window;
+
+ /* Lines do not wrap in this window. */
+ new->flags |= W_NoWrap;
+ node = get_visited_nodes ();
+ name_internal_node (node, xstrdup (nodemenu_nodename));
+ node->flags |= N_WasRewritten;
+
+ info_set_node_of_window (new, node);
+ active_window = new;
+}
+
+DECLARE_INFO_COMMAND (select_visited_node,
+ _("Select a node which has been previously visited in a visible window"))
+{
+ char *line;
+ NODE *node;
+
+ node = get_visited_nodes ();
+
+ line = info_read_completing_in_echo_area (_("Select visited node: "),
+ node->references);
+
+ window = active_window;
+
+ if (!line)
+ /* User aborts, just quit. */
+ info_abort_key (window, 0);
+ else if (*line)
+ {
+ REFERENCE *entry;
+
+ /* Find the selected label in the references. */
+ entry = info_get_menu_entry_by_label (node, line, 0);
+
+ if (!entry)
+ /* This shouldn't happen, because LINE was in the completion list
+ built from the list of references. */
+ info_error (_("The reference disappeared! (%s)."), line);
+ else
+ info_select_reference (window, entry);
+ }
+
+ free (line);
+ free (node);
+}