blob: 75c3bf4d31050d608c0c788e6868c3ba298fe9fb [file] [log] [blame]
/* GNU m4 -- A simple macro processor
Copyright (C) 1989, 1990, 1991, 1992, 1993, 2004, 2006, 2007, 2008
Free Software Foundation, Inc.
This file is part of GNU M4.
GNU M4 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU M4 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Handling of path search of included files via the builtins "include"
and "sinclude". */
#include "m4.h"
struct includes
{
struct includes *next; /* next directory to search */
const char *dir; /* directory */
int len;
};
typedef struct includes includes;
static includes *dir_list; /* the list of path directories */
static includes *dir_list_end; /* the end of same */
static int dir_max_length; /* length of longest directory name */
void
include_init (void)
{
dir_list = NULL;
dir_list_end = NULL;
dir_max_length = 0;
}
void
include_env_init (void)
{
char *path;
char *path_end;
char *env_path;
if (no_gnu_extensions)
return;
env_path = getenv ("M4PATH");
if (env_path == NULL)
return;
env_path = xstrdup (env_path);
path = env_path;
do
{
path_end = strchr (path, ':');
if (path_end)
*path_end = '\0';
add_include_directory (path);
path = path_end + 1;
}
while (path_end);
free (env_path);
}
void
add_include_directory (const char *dir)
{
includes *incl;
if (no_gnu_extensions)
return;
if (*dir == '\0')
dir = ".";
incl = (includes *) xmalloc (sizeof (struct includes));
incl->next = NULL;
incl->len = strlen (dir);
incl->dir = xstrdup (dir);
if (incl->len > dir_max_length) /* remember len of longest directory */
dir_max_length = incl->len;
if (dir_list_end == NULL)
dir_list = incl;
else
dir_list_end->next = incl;
dir_list_end = incl;
#ifdef DEBUG_INCL
xfprintf (stderr, "add_include_directory (%s);\n", dir);
#endif
}
/* Search for FILE, first in `.', then according to -I options. If
successful, return the open file, and if RESULT is not NULL, set
*RESULT to a malloc'd string that represents the file found with
respect to the current working directory. */
FILE *
m4_path_search (const char *file, char **result)
{
FILE *fp;
includes *incl;
char *name; /* buffer for constructed name */
int e;
if (result)
*result = NULL;
/* Reject empty file. */
if (!*file)
{
errno = ENOENT;
return NULL;
}
/* Look in current working directory first. */
fp = fopen (file, "r");
if (fp != NULL)
{
if (set_cloexec_flag (fileno (fp), true) != 0)
m4_warn (errno, NULL, _("cannot protect input file across forks"));
if (result)
*result = xstrdup (file);
return fp;
}
/* If file not found, and filename absolute, fail. */
if (*file == '/' || no_gnu_extensions)
return NULL;
e = errno;
name = (char *) xmalloc (dir_max_length + 1 + strlen (file) + 1);
for (incl = dir_list; incl != NULL; incl = incl->next)
{
strncpy (name, incl->dir, incl->len);
name[incl->len] = '/';
strcpy (name + incl->len + 1, file);
#ifdef DEBUG_INCL
xfprintf (stderr, "m4_path_search (%s) -- trying %s\n", file, name);
#endif
fp = fopen (name, "r");
if (fp != NULL)
{
if (debug_level & DEBUG_TRACE_PATH)
debug_message ("path search for %s found %s",
quotearg_style (locale_quoting_style, file),
quotearg_n_style (1, locale_quoting_style, name));
if (set_cloexec_flag (fileno (fp), true) != 0)
m4_warn (errno, NULL, _("cannot protect input file across forks"));
if (result)
*result = name;
else
free (name);
errno = e;
return fp;
}
}
free (name);
errno = e;
return fp;
}
#ifdef DEBUG_INCL
static void M4_GNUC_UNUSED
include_dump (void)
{
includes *incl;
xfprintf (stderr, "include_dump:\n");
for (incl = dir_list; incl != NULL; incl = incl->next)
xfprintf (stderr, "\t%s\n", incl->dir);
}
#endif /* DEBUG_INCL */