(root)/
texinfo-7.1/
info/
dir.c
       1  /* dir.c -- how to build a special "dir" node from "localdir" files.
       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 "scan.h"
      22  #include "filesys.h"
      23  #include "tilde.h"
      24  
      25  static void add_menu_to_node (char *contents, size_t size, NODE *node);
      26  static void insert_text_into_node (NODE *node, long start,
      27      char *text, int textlen);
      28  
      29  static NODE *dir_node = 0;
      30  
      31  static NODE *build_dir_node (void);
      32  
      33  /* Return composite directory node.  Return value should be freed by caller,
      34     but none of its fields should be. */
      35  NODE *
      36  get_dir_node (void)
      37  {
      38    NODE *node;
      39  
      40    if (!dir_node)
      41      dir_node = build_dir_node ();
      42  
      43    node = xmalloc (sizeof (NODE));
      44    *node = *dir_node;
      45  
      46    return node;
      47  }
      48  
      49  static char *dir_contents;
      50  
      51  static NODE *
      52  build_dir_node (void)
      53  {
      54    char *this_dir;
      55    int path_index = 0;
      56  
      57    NODE *node;
      58  
      59    node = info_create_node ();
      60    node->nodename = xstrdup ("Top");
      61    node->fullpath = xstrdup ("dir");
      62    node->contents = xstrdup (
      63  
      64  "File: dir,	Node: Top,	This is the top of the INFO tree.\n"
      65  "\n"
      66  "This is the Info main menu (aka directory node).\n"
      67  "A few useful Info commands:\n"
      68  "\n"
      69  "  'q' quits;\n"
      70  "  'H' lists all Info commands;\n"
      71  "  'h' starts the Info tutorial;\n"
      72  "  'mTexinfo RET' visits the Texinfo manual, etc.\n"
      73  
      74    );
      75  
      76    node->nodelen = strlen (node->contents);
      77  
      78   for (this_dir = infopath_first (&path_index); this_dir; 
      79          this_dir = infopath_next (&path_index))
      80     {
      81       char *result;
      82       char *fullpath;
      83       int len;
      84       size_t filesize;
      85       struct stat finfo;
      86       int compressed;
      87       char *contents;
      88  
      89  /* Space for an appended compressed file extension, like ".gz". */
      90  #define PADDING "XXXXXXXXX"
      91  
      92       len = xasprintf (&fullpath, "%s/dir%s", this_dir, PADDING);
      93       fullpath[len - strlen(PADDING)] = '\0';
      94  
      95       result = info_check_compressed (fullpath, &finfo);
      96       if (!result)
      97         {
      98           free (fullpath);
      99           continue;
     100         }
     101  
     102        contents = filesys_read_info_file (fullpath, &filesize,
     103                                           &finfo, &compressed);
     104        if (contents)
     105          {
     106            add_menu_to_node (contents, filesize, node);
     107            free (contents);
     108          }
     109  
     110        free (fullpath);
     111      }
     112  
     113    node->flags |= N_IsDir;
     114    dir_contents = node->contents;
     115    scan_node_contents (node, 0, 0);
     116    return node;
     117  }
     118  
     119  /* Given CONTENTS and NODE, add the menu found in CONTENTS to the menu
     120     found in NODE->contents.  SIZE is the total size of CONTENTS. */
     121  static void
     122  add_menu_to_node (char *contents, size_t size, NODE *node)
     123  {
     124    SEARCH_BINDING contents_binding, fb_binding;
     125    long contents_offset, fb_offset;
     126  
     127    contents_binding.buffer = contents;
     128    contents_binding.start = 0;
     129    contents_binding.end = size;
     130    contents_binding.flags = S_FoldCase | S_SkipDest;
     131  
     132    fb_binding.buffer = node->contents;
     133    fb_binding.start = 0;
     134    fb_binding.end = node->nodelen;
     135    fb_binding.flags = S_FoldCase | S_SkipDest;
     136  
     137    /* Move to the start of the menus in CONTENTS and NODE. */
     138    if (search_forward (INFO_MENU_LABEL, &contents_binding, &contents_offset)
     139        != search_success)
     140      /* If there is no menu in CONTENTS, quit now. */
     141      return;
     142  
     143    /* There is a menu in CONTENTS, and contents_offset points to the first
     144       character following the menu starter string.  Skip all whitespace
     145       and newline characters. */
     146    contents_offset += skip_whitespace_and_newlines (contents + contents_offset);
     147  
     148    /* If there is no menu in NODE, make one. */
     149    if (search_forward (INFO_MENU_LABEL, &fb_binding, &fb_offset)
     150        != search_success)
     151      {
     152        fb_binding.start = node->nodelen;
     153  
     154        insert_text_into_node
     155          (node, fb_binding.start, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL));
     156  
     157        fb_binding.buffer = node->contents;
     158        fb_binding.start = 0;
     159        fb_binding.end = node->nodelen;
     160        if (search_forward (INFO_MENU_LABEL, &fb_binding, &fb_offset)
     161  	  != search_success)
     162          abort ();
     163      }
     164  
     165    /* CONTENTS_OFFSET and FB_OFFSET point to the starts of the menus that
     166       appear in their respective buffers.  Add the remainder of CONTENTS
     167       to the end of NODE's menu. */
     168    fb_binding.start = fb_offset;
     169    fb_offset = find_node_separator (&fb_binding);
     170    if (fb_offset != -1)
     171      fb_binding.start = fb_offset;
     172    else
     173      fb_binding.start = fb_binding.end;
     174  
     175    /* Leave exactly one blank line between directory entries. */
     176    {
     177      int num_found = 0;
     178  
     179      while ((fb_binding.start > 0) &&
     180             (whitespace_or_newline (fb_binding.buffer[fb_binding.start - 1])))
     181        {
     182          num_found++;
     183          fb_binding.start--;
     184        }
     185  
     186      /* Optimize if possible. */
     187      if (num_found >= 2)
     188        {
     189          fb_binding.buffer[fb_binding.start++] = '\n';
     190          fb_binding.buffer[fb_binding.start++] = '\n';
     191        }
     192      else
     193        {
     194          /* Do it the hard way. */
     195          insert_text_into_node (node, fb_binding.start, "\n\n", 2);
     196          fb_binding.start += 2;
     197        }
     198    }
     199  
     200    /* Insert the new menu. */
     201    insert_text_into_node
     202      (node, fb_binding.start, contents + contents_offset, size - contents_offset);
     203  }
     204  
     205  static void
     206  insert_text_into_node (NODE *node, long start, char *text, int textlen)
     207  {
     208    char *contents;
     209    long end;
     210  
     211    end = node->nodelen;
     212  
     213    contents = xmalloc (node->nodelen + textlen + 1);
     214    memcpy (contents, node->contents, start);
     215    memcpy (contents + start, text, textlen);
     216    memcpy (contents + start + textlen, node->contents + start, end - start + 1);
     217    free (node->contents);
     218    node->contents = contents;
     219    node->nodelen += textlen;
     220  }
     221  
     222  /* Return directory entry.  Return value should not be freed or modified. */
     223  REFERENCE *
     224  lookup_dir_entry (char *label, int sloppy)
     225  {
     226    REFERENCE *entry;
     227  
     228    if (!dir_node)
     229      dir_node = build_dir_node ();
     230  
     231    entry = info_get_menu_entry_by_label (dir_node, label, sloppy);
     232  
     233    return entry;
     234  }
     235  
     236  /* Look up entry in "dir" in search directory.  Return
     237     value is a pointer to a newly allocated REFERENCE. */
     238  REFERENCE *
     239  dir_entry_of_infodir (char *label, char *searchdir)
     240  {
     241    char *dir_fullpath;
     242    int len;
     243    char *result;
     244  
     245    struct stat dummy;
     246    char *entry_fullpath;
     247  
     248    NODE *dir_node;
     249    REFERENCE *entry;
     250  
     251    if (IS_ABSOLUTE(searchdir))
     252      len = xasprintf (&dir_fullpath, "%s/dir%s", searchdir, PADDING);
     253    else
     254      len = xasprintf (&dir_fullpath, "./%s/dir%s", searchdir, PADDING);
     255    dir_fullpath[len - strlen(PADDING)] = '\0';
     256  
     257    result = info_check_compressed (dir_fullpath, &dummy);
     258    if (!result)
     259      {
     260        free (dir_fullpath);
     261        return 0;
     262      }
     263  
     264    dir_node = info_get_node (dir_fullpath, "Top");
     265    free (dir_fullpath);
     266    entry = info_get_menu_entry_by_label (dir_node, label, 1);
     267    if (!entry || !entry->filename)
     268      {
     269        free_history_node (dir_node);
     270        return 0;
     271        /* A dir entry with no filename is unlikely, but not impossible. */
     272      }
     273  
     274    entry = info_copy_reference (entry);
     275    entry_fullpath = info_add_extension (searchdir, entry->filename, &dummy);
     276    if (entry_fullpath)
     277      {
     278        free (entry->filename);
     279        entry->filename = entry_fullpath;
     280      }
     281  
     282    free_history_node (dir_node);
     283    return entry;
     284  }
     285