(root)/
texinfo-7.1/
info/
variables.c
       1  /* variables.c -- how to manipulate user visible variables in Info.
       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 "echo-area.h"
      23  #include "variables.h"
      24  #include "terminal.h"
      25  #include "display.h"
      26  
      27  static VARIABLE_ALIST *read_variable_name (char *prompt, WINDOW *window);
      28  
      29  /* **************************************************************** */
      30  /*                                                                  */
      31  /*                  User Visible Variables in Info                  */
      32  /*                                                                  */
      33  /* **************************************************************** */
      34  
      35  /* Choices used by the completer when reading a zero/non-zero value for
      36     a variable. */
      37  static char *on_off_choices[] = { "Off", "On", NULL };
      38  
      39  static char *mouse_choices[] = { "Off", "normal-tracking", NULL };
      40  
      41  static char *follow_strategy_choices[] = { "remain", "path", NULL };
      42  
      43  static char *nodeline_choices[] = { "no", "print", "pointers", NULL };
      44  
      45  /* Choices used by the completer when reading a value for the user-visible
      46     variable "scroll-behaviour". */
      47  static char *info_scroll_choices[] = { "Continuous", "Next Only",
      48      "Page Only", NULL };
      49  
      50  /* Choices for the scroll-last-node variable */
      51  static char *scroll_last_node_choices[] = { "Stop", "Top", NULL };
      52  
      53  /* Choices for, and indicator of, rendition. */
      54  static char *rendition_choices[] = { "black", "red", "green", "yellow", "blue",
      55      "magenta", "cyan", "white", "nocolour", "bgblack", "bgred", "bggreen",
      56      "bgyellow", "bgblue", "bgmagenta", "bgcyan", "bgwhite", "nobgcolour",
      57      "underline", "nounderline", "standout", "nostandout", "bold", "regular",
      58      "blink", "noblink", NULL };
      59  
      60  /* Address of this indicates the 'highlight-searches' variable. */
      61  static int *highlight_searches;
      62  
      63  /* Note that the 'where_set' field of each element in the array is
      64     not given and defaults to 0. */
      65  VARIABLE_ALIST info_variables[] = {
      66    { "automatic-footnotes",
      67        N_("When \"On\", footnotes appear and disappear automatically"),
      68        &auto_footnotes_p, (char **)on_off_choices },
      69  
      70    { "automatic-tiling",
      71        N_("When \"On\", creating or deleting a window resizes other windows"),
      72        &auto_tiling_p, (char **)on_off_choices },
      73  
      74    { "visible-bell",
      75        N_("When \"On\", flash the screen instead of ringing the bell"),
      76        &terminal_use_visible_bell_p, (char **)on_off_choices },
      77  
      78    { "errors-ring-bell",
      79        N_("When \"On\", errors cause the bell to ring"),
      80        &info_error_rings_bell_p, (char **)on_off_choices },
      81  
      82    { "gc-compressed-files",
      83        N_("When \"On\", Info garbage collects files which had to be uncompressed"),
      84        &gc_compressed_files, (char **)on_off_choices },
      85    { "show-index-match",
      86        N_("When \"On\", the portion of the matched search string is highlighted"),
      87        &show_index_match, (char **)on_off_choices },
      88  
      89    { "scroll-behaviour",
      90        N_("Controls what happens when scrolling is requested at the end of a node"),
      91        &info_scroll_behaviour, (char **)info_scroll_choices },
      92  
      93    /* Alternate spelling */
      94    { "scroll-behavior",
      95        N_("Same as scroll-behaviour"),
      96        &info_scroll_behaviour, (char **)info_scroll_choices },
      97  
      98    { "scroll-step",
      99        N_("The number lines to scroll when the cursor moves out of the window"),
     100        &window_scroll_step, NULL },
     101  
     102    { "cursor-movement-scrolls",
     103      N_("Controls whether scroll-behavior affects cursor movement commands"),
     104      &cursor_movement_scrolls_p, (char **)on_off_choices },
     105    
     106    { "ISO-Latin",
     107        N_("When \"On\", Info accepts and displays ISO Latin characters"),
     108        &ISO_Latin_p, (char **)on_off_choices },
     109  
     110    { "scroll-last-node",
     111      N_("What to do when a scrolling command is issued at the end of the "
     112         "last node"),
     113      &scroll_last_node, (char**)scroll_last_node_choices },
     114    
     115    { "min-search-length",
     116      N_("Minimal length of a search string"),
     117      &min_search_length, NULL },
     118  
     119    { "search-skip-screen",
     120        N_("Skip current window when searching"),
     121      &search_skip_screen_p, (char **)on_off_choices },
     122  
     123    { "infopath-no-defaults",
     124        N_("Exclude default directories from file search path"),
     125      &infopath_no_defaults_p, (char **)on_off_choices },
     126  
     127    { "hide-note-references",
     128        N_("Hide some Info file syntax in the text of nodes"),
     129      &preprocess_nodes_p, (char **)on_off_choices },
     130  
     131    { "key-time",
     132        N_("Length of time in milliseconds to wait for the next byte in a sequence indicating that a key has been pressed"),
     133      &key_time, NULL },
     134  
     135    { "mouse",
     136        N_("Method to use to track mouse events"),
     137      &mouse_protocol, (char **)mouse_choices },
     138  
     139    { "follow-strategy",
     140        N_("How to follow a cross-reference"),
     141      &follow_strategy, (char **)follow_strategy_choices },
     142  
     143    { "highlight-searches",
     144        N_("Highlight search matches"),
     145      &highlight_searches, (char **)on_off_choices },
     146  
     147    { "link-style",
     148        N_("Styles for links"),
     149      &ref_rendition, (char **)rendition_choices },
     150  
     151    { "active-link-style",
     152        N_("Styles for active links"),
     153      &hl_ref_rendition, (char **)rendition_choices },
     154  
     155    { "match-style",
     156        N_("Styles for search matches"),
     157      &match_rendition, (char **)rendition_choices },
     158  
     159    { "nodeline",
     160        N_("How to print the information line at the start of a node"),
     161      &nodeline_print, (char **)nodeline_choices },
     162  
     163    { NULL }
     164  };
     165  
     166  static char *
     167  rendition_to_string (RENDITION *rendition)
     168  {
     169    static char string[8 /* nocolour */ + 1 /* comma */
     170                       + 10 /* nobgcolour */ + 1
     171                       + 11 /* nounderline */ + 1
     172                       + 10 /* nostandout */ + 1
     173                       + 7 /* regular */ + 1
     174                       + 7 /* noblink */ + 1];
     175    unsigned long style;
     176    static const char* fg[] = { "black", "red", "green", "yellow", "blue",
     177        "magenta", "cyan", "white" };
     178    static const char* bg[] = { "bgblack", "bgred", "bggreen", "bgyellow",
     179        "bgblue", "bgmagenta", "bgcyan", "bgwhite" };
     180  
     181    *string = '\0';
     182  
     183    if (rendition->mask & BLINK_MASK)
     184      strcat (string, rendition->value & BLINK_MASK ? "blink" : "noblink");
     185    if (rendition->mask & BOLD_MASK)
     186      {
     187        if (*string != '\0')
     188          strcat (string, ",");
     189        strcat (string, rendition->value & BOLD_MASK ? "bold" : "nobold");
     190      }
     191    if (rendition->mask & STANDOUT_MASK)
     192      {
     193        if (*string != '\0')
     194          strcat (string, ",");
     195        strcat (string, rendition->value & STANDOUT_MASK ? "standout" : "nostandout");
     196      }
     197    if (rendition->mask & UNDERLINE_MASK)
     198      {
     199        if (*string != '\0')
     200          strcat (string, ",");
     201        strcat (string, rendition->value & UNDERLINE_MASK ? "underline" : "nounderline");
     202      }
     203    if (rendition->mask & COLOUR_MASK)
     204      {
     205        if (*string != '\0')
     206          strcat (string, ",");
     207        style = rendition->value & COLOUR_MASK;
     208        strcat (string, style >= 8 ? fg[style - 8] : "nocolour");
     209      }
     210    if (rendition->mask & BGCOLOUR_MASK)
     211      {
     212        if (*string != '\0')
     213          strcat (string, ",");
     214        style = (rendition->value & BGCOLOUR_MASK) >> 9;
     215        strcat (string, style >= 8 ? bg[style - 8] : "nobgcolour");
     216      }
     217  
     218    return string;
     219  }
     220  
     221  DECLARE_INFO_COMMAND (describe_variable, _("Explain the use of a variable"))
     222  {
     223    VARIABLE_ALIST *var;
     224    char *description;
     225  
     226    /* Get the variable's name. */
     227    var = read_variable_name (_("Describe variable: "), window);
     228    if (!var)
     229      return;
     230  
     231    if (var->choices)
     232      xasprintf (&description, "%s (%s): %s.",
     233               var->name,
     234               var->value == &highlight_searches
     235               ? on_off_choices[match_rendition.mask != 0]
     236               : var->choices == (char **) &rendition_choices
     237               ? rendition_to_string (var->value)
     238               : var->choices[*(int *)var->value], _(var->doc));
     239    else
     240      xasprintf (&description, "%s (%d): %s.",
     241               var->name, *(int *)var->value, _(var->doc));
     242  
     243    window_message_in_echo_area ("%s", description);
     244    free (description);
     245  }
     246  
     247  DECLARE_INFO_COMMAND (set_variable, _("Set the value of an Info variable"))
     248  {
     249    VARIABLE_ALIST *var;
     250    char *line;
     251    char prompt[100];
     252  
     253    /* Get the variable's name and value. */
     254    var = read_variable_name (_("Set variable: "), window);
     255  
     256    if (!var)
     257      return;
     258  
     259    /* Read a new value for this variable. */
     260  
     261    if (!var->choices)
     262      {
     263        int potential_value;
     264  
     265        if (info_explicit_arg || count != 1)
     266          potential_value = count;
     267        else
     268          potential_value = *(int *)(var->value);
     269  
     270        sprintf (prompt, _("Set %s to value (%d): "),
     271                 var->name, potential_value);
     272        line = info_read_in_echo_area (prompt);
     273  
     274        /* User aborted? */
     275        if (!line)
     276          return;
     277  
     278        /* If the user specified a value, get that, otherwise, we are done. */
     279        canonicalize_whitespace (line);
     280  
     281        set_variable_to_value (var, line, SET_IN_SESSION);
     282  
     283        free (line);
     284      }
     285    else
     286      {
     287        register int i;
     288        REFERENCE **array = NULL;
     289        size_t array_index = 0;
     290        size_t array_slots = 0;
     291  
     292        for (i = 0; var->choices[i]; i++)
     293          {
     294            REFERENCE *entry;
     295  
     296            entry = xmalloc (sizeof (REFERENCE));
     297            entry->label = xstrdup (var->choices[i]);
     298            entry->nodename = NULL;
     299            entry->filename = NULL;
     300  
     301            add_pointer_to_array (entry, array_index, array, array_slots, 10);
     302          }
     303  
     304        sprintf (prompt, _("Set %s to value (%s): "),
     305                 var->name,
     306                 var->value == &highlight_searches
     307                 ? on_off_choices[match_rendition.mask != 0]
     308                 : var->choices == (char **) &rendition_choices
     309                 ? rendition_to_string (var->value)
     310                 : var->choices[*(int *)(var->value)]);
     311  
     312        /* Ask the completer to read a variable value for us. */
     313        if (var->choices == (char **) &rendition_choices)
     314          line = info_read_maybe_completing (prompt, array);
     315        else
     316          line = info_read_completing_in_echo_area (prompt, array);
     317  
     318        info_free_references (array);
     319  
     320        /* User aborted? */
     321        if (!line)
     322          {
     323            info_abort_key (active_window, 0);
     324            return;
     325          }
     326  
     327        /* User accepted default choice?  If so, no change. */
     328        if (!*line)
     329          {
     330            free (line);
     331            return;
     332          }
     333  
     334        set_variable_to_value (var, line, SET_IN_SESSION);
     335      }
     336  }
     337  
     338  VARIABLE_ALIST *
     339  variable_by_name (char *name)
     340  {
     341    int i;
     342  
     343    /* Find the variable in our list of variables. */
     344    for (i = 0; info_variables[i].name; i++)
     345      if (strcmp (info_variables[i].name, name) == 0)
     346        break;
     347  
     348    if (!info_variables[i].name)
     349      return NULL;
     350    else
     351      return &info_variables[i];
     352  }
     353  
     354  /* Read the name of an Info variable in the echo area and return the
     355     address of a VARIABLE_ALIST member.  A return value of NULL indicates
     356     that no variable could be read. */
     357  static VARIABLE_ALIST *
     358  read_variable_name (char *prompt, WINDOW *window)
     359  {
     360    char *line;
     361    REFERENCE **variables;
     362  
     363    /* Get the completion array of variable names. */
     364    variables = make_variable_completions_array ();
     365  
     366    /* Ask the completer to read a variable for us. */
     367    line = info_read_completing_in_echo_area (prompt, variables);
     368  
     369    info_free_references (variables);
     370  
     371    /* User aborted? */
     372    if (!line)
     373      {
     374        info_abort_key (active_window, 0);
     375        return NULL;
     376      }
     377  
     378    /* User accepted "default"?  (There is none.) */
     379    if (!*line)
     380      {
     381        free (line);
     382        return NULL;
     383      }
     384  
     385    return variable_by_name (line);
     386  }
     387  
     388  /* Make an array of REFERENCE which actually contains the names of the
     389     variables available in Info. */
     390  REFERENCE **
     391  make_variable_completions_array (void)
     392  {
     393    register int i;
     394    REFERENCE **array = NULL;
     395    size_t array_index = 0, array_slots = 0;
     396  
     397    for (i = 0; info_variables[i].name; i++)
     398      {
     399        REFERENCE *entry;
     400  
     401        entry = xmalloc (sizeof (REFERENCE));
     402        entry->label = xstrdup (info_variables[i].name);
     403        entry->nodename = NULL;
     404        entry->filename = NULL;
     405  
     406        add_pointer_to_array (entry, array_index, array, array_slots, 200);
     407      }
     408  
     409    return array;
     410  }
     411  
     412  /* VALUE is a string that is the value of the variable specified
     413     by the user.  Update our internal data structure VAR using this
     414     information. */
     415  int
     416  set_variable_to_value (VARIABLE_ALIST *var, char *value, int where)
     417  {
     418    /* If variable was set elsewhere with a higher priority, don't do
     419       anything, but don't indicate an error. */
     420    if (var->where_set > where)
     421      return 1;
     422  
     423    if (var->choices)
     424      {
     425        register int j;
     426  
     427        /* "highlight-searches=On" is equivalent to
     428           "match-rendition=standout". */
     429        if (var->value == &highlight_searches)
     430          {
     431            if (strcmp (on_off_choices[0], value) == 0)
     432              {
     433                match_rendition.mask = 0;
     434                match_rendition.value = 0;
     435              }
     436            else
     437              {
     438                match_rendition.mask = STANDOUT_MASK;
     439                match_rendition.value = STANDOUT_MASK;
     440              }
     441          }
     442        else if (var->choices != (char **) &rendition_choices)
     443          {
     444            /* Find the choice in our list of choices. */
     445            for (j = 0; var->choices[j]; j++)
     446              if (strcmp (var->choices[j], value) == 0)
     447                {
     448                  *(int *)var->value = j;
     449                  var->where_set = where;
     450                  return 1;
     451                }
     452          }
     453        else
     454          {
     455            static struct {
     456                unsigned long mask;
     457                unsigned long value;
     458                char *name;
     459            } styles[] = {
     460                COLOUR_MASK, COLOUR_BLACK,   "black",
     461                COLOUR_MASK, COLOUR_RED,     "red",
     462                COLOUR_MASK, COLOUR_GREEN,   "green",
     463                COLOUR_MASK, COLOUR_YELLOW,  "yellow",
     464                COLOUR_MASK, COLOUR_BLUE,    "blue",
     465                COLOUR_MASK, COLOUR_MAGENTA, "magenta",
     466                COLOUR_MASK, COLOUR_CYAN,    "cyan",
     467                COLOUR_MASK, COLOUR_WHITE,   "white",
     468                COLOUR_MASK, 0,           "nocolour",
     469                COLOUR_MASK, 0,           "nocolor",
     470                BGCOLOUR_MASK, BGCOLOUR_BLACK,   "bgblack",
     471                BGCOLOUR_MASK, BGCOLOUR_RED,     "bgred",
     472                BGCOLOUR_MASK, BGCOLOUR_GREEN,   "bggreen",
     473                BGCOLOUR_MASK, BGCOLOUR_YELLOW,  "bgyellow",
     474                BGCOLOUR_MASK, BGCOLOUR_BLUE,    "bgblue",
     475                BGCOLOUR_MASK, BGCOLOUR_MAGENTA, "bgmagenta",
     476                BGCOLOUR_MASK, BGCOLOUR_CYAN,    "bgcyan",
     477                BGCOLOUR_MASK, BGCOLOUR_WHITE,   "bgwhite",
     478                BGCOLOUR_MASK, 0,           "nobgcolour",
     479                BGCOLOUR_MASK, 0,           "nobgcolor",
     480                UNDERLINE_MASK, UNDERLINE_MASK, "underline",
     481                UNDERLINE_MASK, 0,              "nounderline",
     482                STANDOUT_MASK, STANDOUT_MASK, "standout",
     483                STANDOUT_MASK, 0,             "nostandout",
     484                BOLD_MASK, BOLD_MASK,         "bold",
     485                BOLD_MASK, 0,                 "regular",
     486                BOLD_MASK, 0,                 "nobold",
     487                BLINK_MASK, BLINK_MASK,       "blink",
     488                BLINK_MASK, 0,                "noblink",
     489            };
     490            int i;
     491            char *component;
     492            unsigned long rendition_mask = 0;
     493            unsigned long rendition_value = 0;
     494  
     495            component = strtok (value, ",");
     496            while (component)
     497              {
     498                for (i = 0; (styles[i].name); i++)
     499                  {
     500                    if (!strcmp (styles[i].name, component))
     501                      break;
     502                  }
     503                if (styles[i].name)
     504                  {
     505                    rendition_mask |= styles[i].mask;
     506                    rendition_value &= ~styles[i].mask;
     507                    rendition_value |= styles[i].value;
     508                  }
     509                /* If not found, silently ignore, in case more options are
     510                   added in the future. */
     511  
     512                component = strtok (0, ",");
     513              }
     514  
     515            /* Now all the specified styles are recorded in rendition_value. */
     516            ((RENDITION *)var->value)->mask = rendition_mask;
     517            ((RENDITION *)var->value)->value = rendition_value;
     518          }
     519        return 1;
     520      }
     521    else
     522      {
     523        char *p;
     524        long n = strtol (value, &p, 10);
     525        if (*p == 0 && INT_MIN <= n && n <= INT_MAX)
     526  	{
     527            *(int *)var->value = n;
     528  	  return 1;
     529  	}
     530      }
     531  
     532    return 0;
     533  }
     534