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