(root)/
texinfo-7.1/
info/
infopath.c
       1  /* infopath.c -- INFOPATH handling.
       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  
      19  #include "info.h"
      20  #include "scan.h"
      21  #include "util.h"
      22  #include "session.h"
      23  #include "filesys.h"
      24  
      25  typedef struct {
      26    char *name;    /* Path to directory to be searched. */
      27    dev_t device;  /* Storage device this directory is on. */
      28    ino_t inode;   /* Inode number, used to detect duplicates. */
      29  } INFO_DIR;
      30  
      31  INFO_DIR **infodirs = 0;
      32  size_t infodirs_index = 0;
      33  size_t infodirs_slots = 0;
      34  
      35  /* Exclude default file search directories. */
      36  int infopath_no_defaults_p;
      37  
      38  static void infopath_add_dir (char *path);
      39  char *extract_colon_unit (char *string, int *idx);
      40  
      41  void
      42  infopath_init ()
      43  {
      44    /* Initialize INFOPATH.
      45       Highest priority is the environment variable, if set
      46       Then comes the user's INFODIR from the Makefile.
      47       The hardwired default settings (filesys.h) are the lowest priority. */
      48    char *path_from_env = getenv ("INFOPATH");
      49  
      50    if (path_from_env)
      51      {
      52        infopath_add (path_from_env);
      53      }
      54  
      55    if (!infopath_no_defaults_p)
      56      {
      57  #ifdef INFODIR /* $infodir, set by configure script in Makefile */
      58        infopath_add (INFODIR);
      59  #ifdef INFODIR2 /* $datadir/info, which could be different. */
      60        if (!STREQ (INFODIR, INFODIR2))
      61          infopath_add (INFODIR2);
      62  #endif /* INFODIR2 */
      63  #endif /* INFODIR */
      64      }
      65  
      66    if (!path_from_env)
      67      {
      68        infopath_add (DEFAULT_INFOPATH);
      69      }
      70    else
      71      { 
      72        /* Only insert default path if there is a trailing : on INFOPATH. */
      73  
      74        unsigned len = strlen (path_from_env);
      75        if (len && path_from_env[len - 1] == PATH_SEP[0])
      76  	{
      77  	  path_from_env[len - 1] = 0;
      78  	  infopath_add (DEFAULT_INFOPATH);
      79  	}
      80      }
      81  }
      82  
      83  /* Return value to be freed by caller. */
      84  char *
      85  infopath_string ()
      86  {
      87    struct text_buffer path;
      88    int dir_idx;
      89    char *this_dir;
      90  
      91    this_dir = infopath_first (&dir_idx);
      92    if (!this_dir)
      93      return "";
      94  
      95    text_buffer_init (&path);
      96  
      97    while (1)
      98      {
      99        text_buffer_printf (&path, "%s", this_dir);
     100        this_dir = infopath_next (&dir_idx);
     101        if (!this_dir)
     102          break;
     103        text_buffer_add_char (&path, ':');
     104      }
     105    return text_buffer_base (&path); 
     106  }
     107  
     108  /* For each path element PREFIX/DIR in PATH substitute either
     109     PREFIX/share/info or PREFIX/info if that directory exists. */
     110  static void
     111  build_infopath_from_path (void)
     112  {
     113    char *path_from_env, *temp_dirname;
     114    int dirname_index = 0;
     115    struct stat finfo;
     116  
     117    path_from_env = getenv ("PATH");
     118  
     119    while ((temp_dirname = extract_colon_unit (path_from_env, &dirname_index)))
     120      {
     121        unsigned int i, dir = 0;
     122  
     123        /* Find end of DIRNAME/ (but ignore "/") */
     124        for (i = 0; temp_dirname[i]; i++)
     125          if (i && IS_SLASH (temp_dirname[i]))
     126            dir = i + 1;
     127  
     128        /* Discard path elements ending with "/", "/.", or "/.." */
     129        if (!temp_dirname[dir] || STREQ (temp_dirname + dir, ".") || STREQ (temp_dirname + dir, "."))
     130          dir = 0;
     131        
     132        if (dir)
     133          {
     134            temp_dirname = xrealloc (temp_dirname, dir + strlen ("share/info") +1);
     135  
     136            /* first try DIRNAME/share/info */
     137            strcpy (temp_dirname + dir, "share/info");
     138            if (stat (temp_dirname, &finfo) != 0 || !S_ISDIR (finfo.st_mode))
     139              {
     140                /* then try DIRNAME/info */
     141                strcpy (temp_dirname + dir, "info");
     142                if (stat (temp_dirname, &finfo) != 0 || !S_ISDIR (finfo.st_mode))
     143                  dir = 0;
     144              }
     145          }
     146  
     147        if (dir)
     148          infopath_add_dir (temp_dirname);
     149        else
     150          free (temp_dirname);
     151      }
     152  }
     153  
     154  /* Add directory at PATH to Info search path.  A reference to PATH is retained,
     155     or PATH is freed. */
     156  static void
     157  infopath_add_dir (char *path)
     158  {
     159    struct stat dirinfo;
     160    INFO_DIR *entry;
     161    int i;
     162  
     163    if (stat (path, &dirinfo) == -1)
     164      {
     165        debug (2, ("inaccessible directory %s not added to INFOPATH", path));
     166        free (path);
     167        return; /* Doesn't exist, or not accessible. */
     168      }
     169  
     170    for (i = 0; i < infodirs_index; i++)
     171      {
     172        if (   dirinfo.st_ino == infodirs[i]->inode
     173            && dirinfo.st_dev == infodirs[i]->device
     174            /* On MS-Windows, `stat' returns zero as the inode, so we
     175               use file-name comparison instead for that OS.  */
     176            && (infodirs[i]->inode != 0 || fncmp (path, infodirs[i]->name) == 0))
     177          {
     178            debug (2, ("duplicate directory %s not added to INFOPATH", path));
     179            free (path);
     180            return; /* We have it already. */
     181          }
     182      }
     183  
     184    debug (2, ("adding %s to INFOPATH", path));
     185    entry = xmalloc (sizeof (INFO_DIR));
     186    entry->name = path;
     187    entry->inode = dirinfo.st_ino;
     188    entry->device = dirinfo.st_dev;
     189    add_pointer_to_array (entry, infodirs_index, infodirs, infodirs_slots, 8);
     190  }
     191  
     192  /* Add PATH to the list of paths found in INFOPATH.  PATH should be allocated
     193     on the heap and not referenced by the caller after calling this function.
     194     If PATH is "PATH", add a sequence of path elements derived from the
     195     environment variable PATH. */
     196  void
     197  infopath_add (char *path)
     198  {
     199    int idx = 0;
     200    char *dirname;
     201  
     202    while ((dirname = extract_colon_unit (path, &idx)))
     203      {
     204        if (!strcmp ("PATH", dirname))
     205          {
     206            free (dirname);
     207            build_infopath_from_path ();
     208          }
     209        else
     210          infopath_add_dir (dirname);
     211      }
     212  }
     213  
     214  /* Used to iterate over INFOPATH.  Return value should not be freed
     215     by caller. */
     216  char *
     217  infopath_next (int *idx)
     218  {
     219    INFO_DIR *entry;
     220   
     221    if (!infodirs)
     222     return 0;
     223    entry = infodirs[(*idx)++];
     224    if (!entry)
     225      return 0;
     226    return entry->name;
     227  }
     228  
     229  char *
     230  infopath_first (int *idx)
     231  {
     232    *idx = 0;
     233    return infopath_next (idx);
     234  }
     235  
     236  /* Given a string containing units of information separated by the
     237     PATH_SEP character, return the next one after IDX, or NULL if there
     238     are no more.  Advance IDX to the character after the colon. */
     239  char *
     240  extract_colon_unit (char *string, int *idx)
     241  {
     242    unsigned int i = (unsigned int) *idx;
     243    unsigned int start = i;
     244  
     245    if (!string || i >= strlen (string))
     246      return NULL;
     247  
     248    if (!string[i]) /* end of string */
     249      return NULL;
     250  
     251    /* Advance to next PATH_SEP.  */
     252    while (string[i] && string[i] != PATH_SEP[0])
     253      i++;
     254  
     255    {
     256      char *value = xmalloc ((i - start) + 1);
     257      strncpy (value, &string[start], (i - start));
     258      value[i - start] = 0;
     259  
     260      i++; /* move past PATH_SEP */
     261      *idx = i;
     262      return value;
     263    }
     264  }