(root)/
texinfo-7.1/
tp/
Texinfo/
XS/
parsetexi/
labels.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  
      19  #include "parser.h"
      20  #include "convert.h"
      21  #include "source_marks.h"
      22  #include "labels.h"
      23  
      24  /* Array of recorded elements with labels. */
      25  ELEMENT **target_elements_list = 0;
      26  size_t labels_number = 0;
      27  size_t labels_space = 0;
      28  
      29  /* Register a target element associated to a label that may be the target of
      30     a reference and must be unique in the document.  Corresponds to @node,
      31     @anchor, and @float (float label corresponds to the second argument). */
      32  void
      33  register_label (ELEMENT *target_element)
      34  {
      35    /* register the element in the list. */
      36    if (labels_number == labels_space)
      37      {
      38        labels_space += 1;
      39        labels_space *= 1.5;
      40        target_elements_list = realloc (target_elements_list,
      41                                        labels_space * sizeof (ELEMENT *));
      42        if (!target_elements_list)
      43          fatal ("realloc failed");
      44      }
      45    target_elements_list[labels_number++] = target_element;
      46  }
      47  
      48  void
      49  reset_labels (void)
      50  {
      51    labels_number = 0;
      52  }
      53  
      54  void
      55  check_register_target_element_label (ELEMENT *label_element,
      56                                       ELEMENT *target_element)
      57  {
      58    if (label_element)
      59      {
      60        /* check that the label used as an anchor for link target has no
      61           external manual part */
      62        NODE_SPEC_EXTRA *label_info = parse_node_manual (label_element, 0);
      63        if (label_info && label_info->manual_content)
      64          {
      65            /* show contents only to avoid leading/trailing spaces */
      66            char *texi = convert_contents_to_texinfo (label_element);
      67            line_error ("syntax for an external node used for `%s'", texi);
      68            free (texi);
      69          }
      70        destroy_node_spec (label_info);
      71      }
      72    register_label (target_element);
      73  }
      74  
      75  
      76  
      77  /* NODE->contents is the Texinfo for the specification of a node.  This
      78     function sets two fields on the returned object:
      79  
      80       manual_content - Texinfo tree for a manual name extracted from the
      81                        node specification.
      82       node_content - Texinfo tree for the node name on its own
      83  
      84     Objects returned from this function are used as an 'extra' key in
      85     the element for elements linking to nodes (such as @*ref,
      86     menu_entry_node or node direction arguments).  In that case
      87     modify_node is set to 1 and the node contents are modified in-place to
      88     hold the same elements as the returned objects.
      89  
      90     This function is also used for elements that are targets of links (@node and
      91     @anchor first argument, float second argument) mainly to check that
      92     the syntax for an external node is not used.  In that case modify_node
      93     is set to 0 and the node is not modified, and added elements are
      94     collected in a third field of the returned object,
      95       out_of_tree_elements - elements collected in manual_content or
      96                              node_content and not in the node
      97   */
      98  
      99  NODE_SPEC_EXTRA *
     100  parse_node_manual (ELEMENT *node, int modify_node)
     101  {
     102    NODE_SPEC_EXTRA *result;
     103    ELEMENT *node_content = 0;
     104    int idx = 0; /* index into node->contents */
     105  
     106    result = malloc (sizeof (NODE_SPEC_EXTRA));
     107    result->manual_content = result->node_content = 0;
     108    /* if not modifying the tree, and there is a manual name, the elements
     109       added for the manual name and for the node content that are based
     110       on texts from tree elements are not anywhere in the tree.
     111       They are collected in result->out_of_tree_element to be freed later.
     112       These elements correspond to the text after the first manual name
     113       opening brace and text before and after the closing manual name brace */
     114    result->out_of_tree_elements = 0;
     115  
     116    /* If the content starts with a '(', try to get a manual name. */
     117    if (node->contents.number > 0 && node->contents.list[0]->text.end > 0
     118        && node->contents.list[0]->text.text[0] == '(')
     119      {
     120        ELEMENT *manual, *first;
     121        ELEMENT *new_first = 0;
     122        ELEMENT *opening_brace = 0;
     123        char *opening_bracket, *closing_bracket;
     124  
     125        /* Handle nested parentheses in the manual name, for whatever reason. */
     126        int bracket_count = 1; /* Number of ( seen minus number of ) seen. */
     127  
     128        manual = new_element (ET_NONE);
     129  
     130        /* If the first contents element is "(" followed by more text, split
     131           the leading "(" into its own element. */
     132        first = node->contents.list[0];
     133        if (first->text.end > 1)
     134          {
     135            if (modify_node)
     136              {
     137                opening_brace = new_element (0);
     138                text_append_n (&opening_brace->text, "(", 1);
     139              }
     140            new_first = new_element (0);
     141            text_append_n (&new_first->text, first->text.text +1, first->text.end -1);
     142          }
     143        else
     144          {
     145            /* first element is "(", keep it */
     146            idx++;
     147          }
     148  
     149        for (; idx < node->contents.number; idx++)
     150          {
     151            ELEMENT *e;
     152            char *p, *q;
     153  
     154            if (idx == 0)
     155              e = new_first;
     156            else
     157              e = node->contents.list[idx];
     158  
     159            if (e->text.end == 0)
     160              {
     161                /* Put this element in the manual contents. */
     162                add_to_contents_as_array (manual, e);
     163                continue;
     164              }
     165            p = e->text.text;
     166            while (p < e->text.text + e->text.end
     167                   && bracket_count > 0)
     168              {
     169                opening_bracket = strchr (p, '(');
     170                closing_bracket = strchr (p, ')');
     171                if (!opening_bracket && !closing_bracket)
     172                  {
     173                    break;
     174                  }
     175                else if (opening_bracket && !closing_bracket)
     176                  {
     177                    bracket_count++;
     178                    p = opening_bracket + 1;
     179                  }
     180                else if (!opening_bracket && closing_bracket)
     181                  {
     182                    bracket_count--;
     183                    p = closing_bracket + 1;
     184                  }
     185                else if (opening_bracket < closing_bracket)
     186                  {
     187                    bracket_count++;
     188                    p = opening_bracket + 1;
     189                  }
     190                else if (opening_bracket > closing_bracket)
     191                  {
     192                    bracket_count--;
     193                    p = closing_bracket + 1;
     194                  }
     195              }
     196  
     197            if (bracket_count > 0)
     198              add_to_contents_as_array (manual, e);
     199            else /* end of filename component */
     200              {
     201                size_t current_position = 0;
     202                /* At this point, we are sure that there is a manual part,
     203                   so the pending removal/addition of elements at the beginning
     204                   of the manual can proceed (if modify_node). */
     205                /* Also, split the element in two, putting the part before the ")"
     206                   in the manual name, leaving the part afterwards for the
     207                   node name. */
     208                if (modify_node)
     209                  {
     210                    if (opening_brace)
     211                      {
     212                        /* remove the original first element and prepend the
     213                           split "(" and text elements */
     214                        remove_from_contents (node, 0); /* remove first element */
     215                        insert_into_contents (node, new_first, 0);
     216                        insert_into_contents (node, opening_brace, 0);
     217                        idx++;
     218                        if (first->source_mark_list.number > 0)
     219                          {
     220                            size_t current_position
     221                              = relocate_source_marks (&(first->source_mark_list),
     222                                                       opening_brace, 0,
     223                                     count_convert_u8 (opening_brace->text.text));
     224                            relocate_source_marks (&(first->source_mark_list),
     225                                                   new_first, current_position,
     226                                         count_convert_u8 (new_first->text.text));
     227                          }
     228                        destroy_element (first);
     229                      }
     230                    remove_from_contents (node, idx); /* Remove current element e
     231                                                         with closing brace from the tree. */
     232                  }
     233                else
     234                  {
     235                    /* collect elements out of tree */
     236                    result->out_of_tree_elements = calloc (3, sizeof (ELEMENT *));
     237                    if (new_first)
     238                      result->out_of_tree_elements[0] = new_first;
     239                  }
     240                p--; /* point at ) */
     241                if (p > e->text.text)
     242                  {
     243                    /* text before ), part of the manual name */
     244                    ELEMENT *last_manual_element = new_element (ET_NONE);
     245                    text_append_n (&last_manual_element->text, e->text.text,
     246                                   p - e->text.text);
     247                    add_to_contents_as_array (manual, last_manual_element);
     248                    if (modify_node)
     249                      {
     250                        insert_into_contents (node, last_manual_element, idx++);
     251                        current_position
     252                          = relocate_source_marks (&(e->source_mark_list),
     253                                                   last_manual_element,
     254                                                   current_position,
     255                              count_convert_u8 (last_manual_element->text.text));
     256                      }
     257                    else
     258                      result->out_of_tree_elements[1] = last_manual_element;
     259                  }
     260  
     261                if (modify_node)
     262                  {
     263                    ELEMENT *closing_brace = new_element (0);
     264                    text_append_n (&closing_brace->text, ")", 1);
     265                    insert_into_contents (node, closing_brace, idx++);
     266                    current_position
     267                      = relocate_source_marks (&(e->source_mark_list),
     268                                               closing_brace,
     269                                               current_position,
     270                          count_convert_u8 (closing_brace->text.text));
     271                  }
     272  
     273                /* Skip ')' and any following whitespace.
     274                   Note that we don't manage to skip any multibyte
     275                   UTF-8 space characters here. */
     276                p++;
     277                q = p + strspn (p, whitespace_chars);
     278                if (q > p && modify_node)
     279                  {
     280                    ELEMENT *spaces_element = new_element (0);
     281                    text_append_n (&spaces_element->text, p, q - p);
     282                    insert_into_contents (node, spaces_element, idx++);
     283                    current_position
     284                      = relocate_source_marks (&(e->source_mark_list),
     285                                               spaces_element,
     286                                               current_position,
     287                          count_convert_u8 (spaces_element->text.text));
     288                  }
     289  
     290                p = q;
     291                if (*p)
     292                  {
     293                    /* text after ), part of the node name. */
     294                    ELEMENT *leading_node_content = new_element (ET_NONE);
     295                    text_append_n (&leading_node_content->text, p,
     296                                   e->text.text + e->text.end - p);
     297                    /* start node_content */
     298                    node_content = new_element (0);
     299                    add_to_contents_as_array (node_content, leading_node_content);
     300                    if (modify_node)
     301                      {
     302                        insert_into_contents (node, leading_node_content, idx);
     303                        current_position
     304                          = relocate_source_marks (&(e->source_mark_list),
     305                                                   leading_node_content,
     306                                                   current_position,
     307                              count_convert_u8 (leading_node_content->text.text));
     308                      }
     309                    else
     310                      result->out_of_tree_elements[2] = leading_node_content;
     311                    idx++;
     312                  }
     313                if (modify_node)
     314                  destroy_element (e);
     315                break;
     316              }
     317          } /* for */
     318  
     319        if (bracket_count == 0)
     320          result->manual_content = manual;
     321        else /* Unbalanced parentheses, consider that there is no manual
     322                afterall.  So far the node has not been modified, so the
     323                only thing that needs to be done is to remove the manual
     324                element and the elements allocated for the beginning of
     325                the manual, and start over */
     326          {
     327            destroy_element (manual);
     328            if (new_first)
     329              destroy_element (new_first);
     330            if (opening_brace)
     331              destroy_element (opening_brace);
     332            idx = 0; /* Back to the start, and consider the whole thing
     333                        as a node name. */
     334          }
     335      }
     336  
     337    /* If anything left, it is part of the node name. */
     338    if (idx < node->contents.number)
     339      {
     340        if (!node_content)
     341          node_content = new_element (0);
     342        insert_slice_into_contents (node_content, node_content->contents.number,
     343                                    node, idx, node->contents.number);
     344      }
     345  
     346    if (node_content)
     347      result->node_content = node_content;
     348  
     349    return result;
     350  }
     351  
     352  
     353  
     354  ELEMENT **internal_xref_list = 0;
     355  size_t internal_xref_number = 0;
     356  size_t internal_xref_space = 0;
     357  
     358  void
     359  remember_internal_xref (ELEMENT *element)
     360  {
     361    if (internal_xref_number == internal_xref_space)
     362      {
     363        internal_xref_list = realloc (internal_xref_list,
     364                               (internal_xref_space += 2)
     365                               * sizeof (*internal_xref_list));
     366      }
     367    internal_xref_list[internal_xref_number++] = element;
     368  }
     369  
     370  void
     371  reset_internal_xrefs (void)
     372  {
     373    internal_xref_number = 0;
     374  }