(root)/
texinfo-7.1/
tp/
Texinfo/
XS/
parsetexi/
indices.c
       1  /* Copyright 2010-2023 Free Software Foundation, Inc.
       2  
       3     This program is free software: you can redistribute it and/or modify
       4     it under the terms of the GNU General Public License as published by
       5     the Free Software Foundation, either version 3 of the License, or
       6     (at your option) any later version.
       7  
       8     This program is distributed in the hope that it will be useful,
       9     but WITHOUT ANY WARRANTY; without even the implied warranty of
      10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      11     GNU General Public License for more details.
      12  
      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  
      16  #include <config.h>
      17  #include <string.h>
      18  #include <stdio.h>
      19  
      20  #include "parser.h"
      21  #include "indices.h"
      22  
      23  INDEX **index_names = 0;
      24  int number_of_indices = 0;
      25  int space_for_indices = 0;
      26  
      27  typedef struct {
      28      enum command_id cmd;
      29      INDEX *idx;
      30  } CMD_TO_IDX;
      31  
      32  /* Array mapping Texinfo commands to index data structures. */
      33  static CMD_TO_IDX *cmd_to_idx = 0;
      34  static size_t num_index_commands = 0;
      35  static size_t cmd_to_idx_space = 0;
      36  
      37  static void
      38  associate_command_to_index (enum command_id cmd, INDEX *idx)
      39  {
      40    if (num_index_commands == cmd_to_idx_space)
      41      {
      42        cmd_to_idx = realloc (cmd_to_idx,
      43                              sizeof (CMD_TO_IDX) * (cmd_to_idx_space += 10));
      44        if (!cmd_to_idx)
      45          fatal ("no index for command");
      46      }
      47  
      48    cmd_to_idx[num_index_commands].cmd = cmd;
      49    cmd_to_idx[num_index_commands++].idx = idx;
      50  }
      51  
      52  /* Get the index associated with CMD. */
      53  INDEX *
      54  index_of_command (enum command_id cmd)
      55  {
      56    int i;
      57  
      58    for (i = 0; i < num_index_commands; i++)
      59      {
      60        if (cmd_to_idx[i].cmd == cmd)
      61          return cmd_to_idx[i].idx;
      62      }
      63    return 0;
      64  }
      65  
      66  
      67  /* Save a new Texinfo command with the name CMDNAME and record that it
      68     creates index entries in IDX. */
      69  static void
      70  add_index_command (char *cmdname, INDEX *idx)
      71  {
      72    enum command_id new = add_texinfo_command (cmdname);
      73    user_defined_command_data[new & ~USER_COMMAND_BIT].flags
      74      |= CF_line | CF_index_entry_command | CF_contain_basic_inline
      75      /*  | CF_close_paragraph */
      76        | CF_no_paragraph;
      77    user_defined_command_data[new & ~USER_COMMAND_BIT].data = LINE_line;
      78    associate_command_to_index (new, idx);
      79  }
      80  
      81  static INDEX *
      82  add_index_internal (char *name, int in_code)
      83  {
      84    INDEX *idx = malloc (sizeof (INDEX));
      85  
      86    memset (idx, 0, sizeof *idx);
      87    idx->name = name;
      88    idx->prefix = name;
      89    idx->in_code = in_code;
      90    if (number_of_indices == space_for_indices)
      91      {
      92        space_for_indices += 5;
      93        index_names = realloc (index_names, (space_for_indices + 1)
      94                               * sizeof (INDEX *));
      95      }
      96    index_names[number_of_indices++] = idx;
      97    index_names[number_of_indices] = 0;
      98    return idx;
      99  }
     100  
     101  /* NAME is the name of an index, e.g. "cp" */
     102  INDEX *
     103  index_by_name (char *name)
     104  {
     105    int i;
     106  
     107    for (i = 0; i < number_of_indices; i++)
     108      {
     109        if (!strcmp (index_names[i]->name, name))
     110          return index_names[i];
     111      }
     112    return 0;
     113  }
     114  
     115  
     116  /* Add a user defined index with the name NAME */
     117  void
     118  add_index (char *name, int in_code)
     119  {
     120    INDEX *idx = index_by_name (name);
     121    char *cmdname;
     122  
     123    if (!idx)
     124      idx = add_index_internal (strdup (name), in_code);
     125  
     126    /* For example, "rq" -> "rqindex". */
     127    xasprintf (&cmdname, "%s%s", name, "index");
     128    add_index_command (cmdname, idx);
     129    free (cmdname);
     130  }
     131  
     132  static void
     133  wipe_index (INDEX *idx)
     134  {
     135    free (idx->name);
     136    free (idx->index_entries);
     137  }
     138  
     139  void
     140  wipe_indices (void)
     141  {
     142    int i;
     143    for (i = 0; i < number_of_indices; i++)
     144      {
     145        wipe_index (index_names[i]);
     146        free (index_names[i]);
     147      }
     148    number_of_indices = 0;
     149    return;
     150  }
     151  
     152  void
     153  init_index_commands (void)
     154  {
     155    INDEX *idx;
     156  
     157    struct def { char *name; int in_code; }
     158    *p, default_indices[] = {
     159      "cp", 0, /* concepts */
     160      "fn", 1, /* functions */
     161      "vr", 1, /* variables */
     162      "ky", 1, /* keystrokes */
     163      "pg", 1, /* programs */
     164      "tp", 1, /* types */
     165      0, 0
     166    };
     167    int i, j;
     168  
     169    char name[] = "?index";
     170    char name2[] = "??index";
     171  
     172  #define MAX (10 * 2)
     173  
     174  #define X(command) CM_##command, CM_##command##x
     175    struct def_cmds { char *name; enum command_id id[MAX]; }
     176      def_command_indices[] = {
     177        "fn",
     178  
     179        {X(deffn),
     180         X(deftypefn),
     181         X(deftypeop),
     182         X(defop),
     183         X(defun),
     184         X(defmac),
     185         X(defspec),
     186         X(deftypefun),
     187         X(defmethod),
     188         X(deftypemethod),
     189        },
     190  
     191        "vr",
     192       
     193        {X(defvr),
     194         X(deftypevr),
     195         X(defcv),
     196         X(deftypecv),
     197         X(defvar),
     198         X(defivar),
     199         X(defopt),
     200         X(deftypevar),
     201         X(deftypeivar),
     202        },
     203  
     204        "tp",
     205       
     206        {X(deftp),}
     207      };
     208  #undef X
     209  
     210    number_of_indices = 0;
     211    num_index_commands = 0;
     212  
     213    for (p = default_indices; p->name; p++)
     214      {
     215        /* Both @cindex and @cpindex are added. */
     216        idx = add_index_internal (strdup (p->name), p->in_code);
     217  
     218        *name = p->name[0];
     219        add_index_command (name, idx); /* @cindex */
     220  
     221        name2[0] = p->name[0];
     222        name2[1] = p->name[1];
     223        add_index_command (name2, idx); /* @cpindex */
     224      }
     225  
     226    associate_command_to_index (CM_vtable, index_by_name ("vr"));
     227    associate_command_to_index (CM_ftable, index_by_name ("fn"));
     228  
     229    for (i = 0;
     230         i < sizeof (def_command_indices) / sizeof (def_command_indices[0]);
     231         i++)
     232      {
     233        enum command_id cmd;
     234        idx = index_by_name (def_command_indices[i].name);
     235        if (idx)
     236          {
     237            for (j = 0; j < MAX; j++)
     238              {
     239                cmd = def_command_indices[i].id[j];
     240                if (cmd)
     241                  associate_command_to_index (cmd, idx);
     242              }
     243          }
     244      }
     245  #undef MAX
     246  }
     247  
     248  
     249  /* A reference to an index entry, in the "index_entry" extra key of
     250     an element.  index->index_entries[entry] is the referred-to index
     251     entry.  Not actually used in api.c (element_to_perl_hash). */
     252  typedef struct {
     253      INDEX *index;
     254      int entry;
     255  } INDEX_ENTRY_REF;
     256  
     257  
     258  /* INDEX_TYPE_CMD is used to determine which index to enter the entry in.
     259     index entry.  ELEMENT is the element in the main body of the manual that
     260     the index entry refers/belongs to.
     261  */
     262  void
     263  enter_index_entry (enum command_id index_type_cmd,
     264                     ELEMENT *element)
     265  {
     266    INDEX *idx;
     267    INDEX_ENTRY *entry;
     268    TEXT ignored_chars;
     269  
     270    idx = index_of_command (index_type_cmd);
     271    if (idx->index_number == idx->index_space)
     272      {
     273        idx->index_entries = realloc (idx->index_entries,
     274                               sizeof (INDEX_ENTRY) * (idx->index_space += 20));
     275        if (!idx->index_entries)
     276          fatal ("realloc failed");
     277      }
     278    entry = &idx->index_entries[idx->index_number++];
     279    memset (entry, 0, sizeof (INDEX_ENTRY));
     280  
     281    entry->index_name = idx->name;
     282    /* not needed, the position in the index is directly used
     283    entry->number = idx->index_number;
     284    */
     285    entry->command = element;
     286  
     287    /* Create ignored_chars string. */
     288    text_init (&ignored_chars);
     289    if (global_info.ignored_chars.backslash)
     290      text_append (&ignored_chars, "\\");
     291    if (global_info.ignored_chars.hyphen)
     292      text_append (&ignored_chars, "-");
     293    if (global_info.ignored_chars.lessthan)
     294      text_append (&ignored_chars, "<");
     295    if (global_info.ignored_chars.atsign)
     296      text_append (&ignored_chars, "@");
     297    if (ignored_chars.end > 0)
     298      {
     299        add_extra_string_dup (element, "index_ignore_chars", ignored_chars.text);
     300        free (ignored_chars.text);
     301      }
     302  
     303    /* index_entry is an array with two elements.  Use
     304       extra_misc_args to pass that information as an array */
     305    {
     306      ELEMENT *index_entry = new_element (ET_NONE);
     307      ELEMENT *e = new_element (ET_NONE);
     308      text_append (&e->text, idx->name);
     309      add_to_element_contents (index_entry, e);
     310      e = new_element (ET_NONE);
     311      add_extra_integer (e, "integer", idx->index_number);
     312      add_to_element_contents (index_entry, e);
     313      add_extra_misc_args (element, "index_entry", index_entry);
     314    }
     315  
     316    if (nesting_context.regions_stack.top > 0)
     317      {
     318        enum command_id region = top_command (&nesting_context.regions_stack);
     319        add_extra_string_dup (element, "element_region", command_name (region));
     320      }
     321    else if (current_node)
     322      add_extra_element (element, "element_node", current_node);
     323  
     324    if (nesting_context.regions_stack.top == 0
     325        && !current_node && !current_section)
     326      line_warn ("entry for index `%s' outside of any node", idx->name);
     327  }
     328  
     329  /* turn spaces that are ignored before @-commands like @sortas{} and
     330     @seeentry{} back to regular spaces if there is content after the @-command
     331   */
     332  void
     333  set_non_ignored_space_in_index_before_command (ELEMENT *content)
     334  {
     335    ELEMENT *e;
     336    ELEMENT *pending_spaces_element = 0;
     337    int i;
     338    for (i = 0; i < content->contents.number; i++)
     339      {
     340        /* could also be, but it does not seems to be needed here:
     341           e = contents_child_by_index (content, i); */
     342        e = content->contents.list[i];
     343        if (e->type == ET_internal_spaces_before_brace_in_index)
     344          {
     345            pending_spaces_element = e;
     346            /* set to "spaces_at_end" in case there are only spaces after */
     347            e->type = ET_spaces_at_end;
     348          }
     349        else if (pending_spaces_element
     350                  && ! (e->cmd == CM_sortas
     351                         || e->cmd == CM_seeentry
     352                         || e->cmd == CM_seealso
     353                         || e->type == ET_spaces_after_close_brace)
     354                  && (! check_space_element(e)))
     355          {
     356            pending_spaces_element->type = ET_NONE;
     357            pending_spaces_element = 0;
     358          }
     359      }
     360  }
     361  
     362  
     363  
     364  INDEX *
     365  ultimate_index (INDEX *index)
     366  {
     367    while (index->merged_in)
     368      index = index->merged_in;
     369    return index;
     370  }