(root)/
m4-1.4.19/
src/
path.c
       1  /* GNU m4 -- A simple macro processor
       2  
       3     Copyright (C) 1989-1993, 2004, 2006-2014, 2016-2017, 2020-2021 Free
       4     Software Foundation, Inc.
       5  
       6     This file is part of GNU M4.
       7  
       8     GNU M4 is free software: you can redistribute it and/or modify
       9     it under the terms of the GNU General Public License as published by
      10     the Free Software Foundation, either version 3 of the License, or
      11     (at your option) any later version.
      12  
      13     GNU M4 is distributed in the hope that it will be useful,
      14     but WITHOUT ANY WARRANTY; without even the implied warranty of
      15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16     GNU General Public License for more details.
      17  
      18     You should have received a copy of the GNU General Public License
      19     along with this program.  If not, see <https://www.gnu.org/licenses/>.
      20  */
      21  
      22  /* Handling of path search of included files via the builtins "include"
      23     and "sinclude".  */
      24  
      25  #include "m4.h"
      26  
      27  struct includes
      28  {
      29    struct includes *next;        /* next directory to search */
      30    const char *dir;              /* directory */
      31    int len;
      32  };
      33  
      34  typedef struct includes includes;
      35  
      36  static includes *dir_list;              /* the list of path directories */
      37  static includes *dir_list_end;          /* the end of same */
      38  static int dir_max_length;              /* length of longest directory name */
      39  
      40  
      41  void
      42  include_init (void)
      43  {
      44    dir_list = NULL;
      45    dir_list_end = NULL;
      46    dir_max_length = 0;
      47  }
      48  
      49  void
      50  include_env_init (void)
      51  {
      52    char *path;
      53    char *path_end;
      54    char *env_path;
      55  
      56    if (no_gnu_extensions)
      57      return;
      58  
      59    env_path = getenv ("M4PATH");
      60    if (env_path == NULL)
      61      return;
      62  
      63    env_path = xstrdup (env_path);
      64    path = env_path;
      65  
      66    do
      67      {
      68        path_end = strchr (path, ':');
      69        if (path_end)
      70          *path_end = '\0';
      71        add_include_directory (path);
      72        path = path_end + 1;
      73      }
      74    while (path_end);
      75    free (env_path);
      76  }
      77  
      78  void
      79  add_include_directory (const char *dir)
      80  {
      81    includes *incl;
      82  
      83    if (no_gnu_extensions)
      84      return;
      85  
      86    if (*dir == '\0')
      87      dir = ".";
      88  
      89    incl = (includes *) xmalloc (sizeof (struct includes));
      90    incl->next = NULL;
      91    incl->len = strlen (dir);
      92    incl->dir = xstrdup (dir);
      93  
      94    if (incl->len > dir_max_length) /* remember len of longest directory */
      95      dir_max_length = incl->len;
      96  
      97    if (dir_list_end == NULL)
      98      dir_list = incl;
      99    else
     100      dir_list_end->next = incl;
     101    dir_list_end = incl;
     102  
     103  #ifdef DEBUG_INCL
     104    xfprintf (stderr, "add_include_directory (%s);\n", dir);
     105  #endif
     106  }
     107  
     108  /* Attempt to open FILE; if it opens, verify that it is not a
     109     directory, and ensure it does not leak across execs.  */
     110  static FILE *
     111  m4_fopen (const char *file)
     112  {
     113    FILE *fp = fopen (file, "re");
     114    if (fp)
     115      {
     116        struct stat st;
     117        int fd = fileno (fp);
     118        if (fstat (fd, &st) == 0 && S_ISDIR (st.st_mode))
     119          {
     120            fclose (fp);
     121            errno = EISDIR;
     122            return NULL;
     123          }
     124      }
     125    return fp;
     126  }
     127  
     128  /* Search for FILE, first in `.', then according to -I options.  If
     129     successful, return the open file, and if RESULT is not NULL, set
     130     *RESULT to a malloc'd string that represents the file found with
     131     respect to the current working directory.  */
     132  
     133  FILE *
     134  m4_path_search (const char *file, char **result)
     135  {
     136    FILE *fp;
     137    includes *incl;
     138    char *name;                   /* buffer for constructed name */
     139    int e;
     140  
     141    if (result)
     142      *result = NULL;
     143  
     144    /* Reject empty file.  */
     145    if (!*file)
     146      {
     147        errno = ENOENT;
     148        return NULL;
     149      }
     150  
     151    /* Look in current working directory first.  */
     152    fp = m4_fopen (file);
     153    if (fp != NULL)
     154      {
     155        if (result)
     156          *result = xstrdup (file);
     157        return fp;
     158      }
     159  
     160    /* If file not found, and filename absolute, fail.  */
     161    if (IS_ABSOLUTE_FILE_NAME (file) || no_gnu_extensions)
     162      return NULL;
     163    e = errno;
     164  
     165    for (incl = dir_list; incl != NULL; incl = incl->next)
     166      {
     167        name = file_name_concat (incl->dir, file, NULL);
     168  
     169  #ifdef DEBUG_INCL
     170        xfprintf (stderr, "m4_path_search (%s) -- trying %s\n", file, name);
     171  #endif
     172  
     173        fp = m4_fopen (name);
     174        if (fp != NULL)
     175          {
     176            if (debug_level & DEBUG_TRACE_PATH)
     177              DEBUG_MESSAGE2 ("path search for `%s' found `%s'", file, name);
     178            if (result)
     179              *result = name;
     180            else
     181              free (name);
     182            return fp;
     183          }
     184        free (name);
     185      }
     186    errno = e;
     187    return fp;
     188  }
     189  
     190  #ifdef DEBUG_INCL
     191  
     192  static void MAYBE_UNUSED
     193  include_dump (void)
     194  {
     195    includes *incl;
     196  
     197    xfprintf (stderr, "include_dump:\n");
     198    for (incl = dir_list; incl != NULL; incl = incl->next)
     199      xfprintf (stderr, "\t%s\n", incl->dir);
     200  }
     201  
     202  #endif /* DEBUG_INCL */