nodemenu.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* nodemenu.c -- produce a menu of all visited nodes.
  2. $Id$
  3. Copyright 1993, 1997, 1998, 2002, 2003, 2004, 2007, 2008, 2011,
  4. 2013, 2014, 2015, 2016 Free Software Foundation, Inc.
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. Originally written by Brian Fox. */
  16. #include "info.h"
  17. #include "session.h"
  18. #include "echo-area.h"
  19. #include "variables.h"
  20. static NODE *get_visited_nodes (void);
  21. /* Return a line describing the format of a node information line. */
  22. static const char *
  23. nodemenu_format_info (void)
  24. {
  25. /* TRANSLATORS: The "\n* Menu:\n\n" part of this should not be translated, as
  26. it is part of the Info syntax. */
  27. return _("\n* Menu:\n\n\
  28. (File)Node Lines Size Containing File\n\
  29. ---------- ----- ---- ---------------");
  30. }
  31. /* Produce a formatted line of information about NODE. Here is what we want
  32. the output listing to look like:
  33. * Menu:
  34. (File)Node Lines Size Containing File
  35. ---------- ----- ---- ---------------
  36. * (emacs)Buffers:: 48 2230 /usr/gnu/info/emacs/emacs-1
  37. * (autoconf)Writing configure.in:: 123 58789 /usr/gnu/info/autoconf/autoconf-1
  38. * (dir)Top:: 40 589 /usr/gnu/info/dir
  39. */
  40. static char *
  41. format_node_info (NODE *node)
  42. {
  43. register int i;
  44. char *containing_file;
  45. static struct text_buffer line_buffer = { 0 };
  46. if (!text_buffer_base (&line_buffer))
  47. text_buffer_init (&line_buffer);
  48. else
  49. text_buffer_reset (&line_buffer);
  50. if (node->subfile)
  51. containing_file = node->subfile;
  52. else
  53. containing_file = node->fullpath;
  54. if (!containing_file || !*containing_file)
  55. text_buffer_printf (&line_buffer, "* %s::", node->nodename);
  56. else
  57. text_buffer_printf (&line_buffer, "* (%s)%s::",
  58. filename_non_directory (node->fullpath),
  59. node->nodename);
  60. for (i = text_buffer_off (&line_buffer); i < 36; i++)
  61. text_buffer_add_char (&line_buffer, ' ');
  62. {
  63. int lines = 1;
  64. for (i = 0; i < node->nodelen; i++)
  65. if (node->contents[i] == '\n')
  66. lines++;
  67. text_buffer_printf (&line_buffer, "%d", lines);
  68. }
  69. text_buffer_add_char (&line_buffer, ' ');
  70. for (i = text_buffer_off (&line_buffer); i < 44; i++)
  71. text_buffer_add_char (&line_buffer, ' ');
  72. text_buffer_printf (&line_buffer, "%ld", node->nodelen);
  73. if (containing_file)
  74. {
  75. for (i = text_buffer_off (&line_buffer); i < 51; i++)
  76. text_buffer_add_char (&line_buffer, ' ');
  77. text_buffer_printf (&line_buffer, containing_file);
  78. }
  79. return xstrdup (text_buffer_base (&line_buffer));
  80. }
  81. /* Little string comparison routine for qsort (). */
  82. static int
  83. compare_strings (const void *entry1, const void *entry2)
  84. {
  85. char **e1 = (char **) entry1;
  86. char **e2 = (char **) entry2;
  87. return mbscasecmp (*e1, *e2);
  88. }
  89. /* The name of the nodemenu node. */
  90. static char *nodemenu_nodename = "*Node Menu*";
  91. /* Produce an informative listing of all the visited nodes, and return it
  92. in a newly allocated node. */
  93. static NODE *
  94. get_visited_nodes (void)
  95. {
  96. register int i;
  97. WINDOW *info_win;
  98. NODE *node;
  99. char **lines = NULL;
  100. size_t lines_index = 0, lines_slots = 0;
  101. struct text_buffer message;
  102. for (info_win = windows; info_win; info_win = info_win->next)
  103. {
  104. for (i = 0; i < info_win->hist_index; i++)
  105. {
  106. NODE *history_node = info_win->hist[i]->node;
  107. /* We skip mentioning "*Node Menu*" nodes. */
  108. if (strcmp (history_node->nodename, nodemenu_nodename) == 0)
  109. continue;
  110. if (history_node)
  111. {
  112. char *line;
  113. line = format_node_info (history_node);
  114. add_pointer_to_array (line, lines_index, lines, lines_slots, 20);
  115. }
  116. }
  117. }
  118. /* Sort the array of information lines, if there are any. */
  119. if (lines)
  120. {
  121. register int j, newlen;
  122. char **temp;
  123. qsort (lines, lines_index, sizeof (char *), compare_strings);
  124. /* Delete duplicates. */
  125. for (i = 0, newlen = 1; i < lines_index - 1; i++)
  126. {
  127. /* Use FILENAME_CMP here, since the most important piece
  128. of info in each line is the file name of the node. */
  129. if (FILENAME_CMP (lines[i], lines[i + 1]) == 0)
  130. {
  131. free (lines[i]);
  132. lines[i] = NULL;
  133. }
  134. else
  135. newlen++;
  136. }
  137. /* We have free ()'d and marked all of the duplicate slots.
  138. Copy the live slots rather than pruning the dead slots. */
  139. temp = xmalloc ((1 + newlen) * sizeof (char *));
  140. for (i = 0, j = 0; i < lines_index; i++)
  141. if (lines[i])
  142. temp[j++] = lines[i];
  143. temp[j] = NULL;
  144. free (lines);
  145. lines = temp;
  146. lines_index = newlen;
  147. }
  148. text_buffer_init (&message);
  149. text_buffer_printf (&message, "\n");
  150. text_buffer_printf (&message,
  151. "%s", replace_in_documentation
  152. (_("Here is the menu of nodes you have recently visited.\n\
  153. Select one from this menu, or use '\\[history-node]' in another window.\n"), 0));
  154. text_buffer_printf (&message, "%s\n", nodemenu_format_info ());
  155. for (i = 0; (lines != NULL) && (i < lines_index); i++)
  156. {
  157. text_buffer_printf (&message, "%s\n", lines[i]);
  158. free (lines[i]);
  159. }
  160. if (lines)
  161. free (lines);
  162. node = text_buffer_to_node (&message);
  163. scan_node_contents (node, 0, 0);
  164. return node;
  165. }
  166. DECLARE_INFO_COMMAND (list_visited_nodes,
  167. _("Make a window containing a menu of all of the currently visited nodes"))
  168. {
  169. WINDOW *new;
  170. NODE *node;
  171. /* If a window is visible and showing the buffer list already, re-use it. */
  172. for (new = windows; new; new = new->next)
  173. {
  174. node = new->node;
  175. if (internal_info_node_p (node) &&
  176. (strcmp (node->nodename, nodemenu_nodename) == 0))
  177. break;
  178. }
  179. /* If we couldn't find an existing window, try to use the next window
  180. in the chain. */
  181. if (!new)
  182. {
  183. if (window->next)
  184. new = window->next;
  185. /* If there is more than one window, wrap around. */
  186. else if (window != windows)
  187. new = windows;
  188. }
  189. /* If we still don't have a window, make a new one to contain the list. */
  190. if (!new)
  191. new = window_make_window ();
  192. /* If we couldn't make a new window, use this one. */
  193. if (!new)
  194. new = window;
  195. /* Lines do not wrap in this window. */
  196. new->flags |= W_NoWrap;
  197. node = get_visited_nodes ();
  198. name_internal_node (node, xstrdup (nodemenu_nodename));
  199. node->flags |= N_WasRewritten;
  200. info_set_node_of_window (new, node);
  201. active_window = new;
  202. }
  203. DECLARE_INFO_COMMAND (select_visited_node,
  204. _("Select a node which has been previously visited in a visible window"))
  205. {
  206. char *line;
  207. NODE *node;
  208. node = get_visited_nodes ();
  209. line = info_read_completing_in_echo_area (_("Select visited node: "),
  210. node->references);
  211. window = active_window;
  212. if (!line)
  213. /* User aborts, just quit. */
  214. info_abort_key (window, 0);
  215. else if (*line)
  216. {
  217. REFERENCE *entry;
  218. /* Find the selected label in the references. */
  219. entry = info_get_menu_entry_by_label (node, line, 0);
  220. if (!entry)
  221. /* This shouldn't happen, because LINE was in the completion list
  222. built from the list of references. */
  223. info_error (_("The reference disappeared! (%s)."), line);
  224. else
  225. info_select_reference (window, entry);
  226. }
  227. free (line);
  228. free (node);
  229. }