modules: Use LT_MODULE_EXT for host module file extension.
[m4/ericb.git] / m4 / path.c
blobca5e261cd588a0f560182a05ba26bcbf5697d8c3
1 /* GNU m4 -- A simple macro processor
2 Copyright (C) 1989-1993, 1998, 2004, 2006-2010, 2013-2014 Free
3 Software Foundation, Inc.
5 This file is part of GNU M4.
7 GNU M4 is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU M4 is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 /* Handling of path search of included files via the builtins "include"
22 and "sinclude". */
24 #include <config.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
31 #include "m4private.h"
33 #include "configmake.h"
34 #include "dirname.h"
35 #include "filenamecat.h"
37 #if OS2 /* Any others? */
38 # define TRUNCATE_FILENAME 1
39 #endif
41 /* Define this to see runtime debug info. Implied by DEBUG. */
42 /*#define DEBUG_INCL */
44 static const char *FILE_SUFFIXES[] = {
45 "",
46 ".m4f",
47 ".m4",
48 LT_MODULE_EXT,
49 NULL
52 static const char *NO_SUFFIXES[] = { "", NULL };
54 static void search_path_add (m4__search_path_info *, const char *, bool);
55 static void search_path_env_init (m4__search_path_info *, char *, bool);
56 static void include_env_init (m4 *context);
58 #ifdef DEBUG_INCL
59 static void include_dump (m4 *context);
60 #endif
64 * General functions for search paths
67 static void
68 search_path_add (m4__search_path_info *info, const char *dir, bool prepend)
70 m4__search_path *path = (m4__search_path *) xmalloc (sizeof *path);
72 path->len = strlen (dir);
73 path->dir = xstrdup (dir);
75 if (path->len > info->max_length) /* remember len of longest directory */
76 info->max_length = path->len;
78 if (prepend)
80 path->next = info->list;
81 info->list = path;
82 if (info->list_end == NULL)
83 info->list_end = path;
85 else
87 path->next = NULL;
89 if (info->list_end == NULL)
90 info->list = path;
91 else
92 info->list_end->next = path;
93 info->list_end = path;
97 static void
98 search_path_env_init (m4__search_path_info *info, char *path, bool isabs)
100 char *path_end;
102 if (info == NULL || path == NULL)
103 return;
107 path_end = strchr (path, PATH_SEPARATOR);
108 if (path_end)
109 *path_end = '\0';
110 if (!isabs || *path == '/')
111 search_path_add (info, path, false);
112 path = path_end + 1;
114 while (path_end);
117 static void
118 include_env_init (m4 *context)
120 char *m4path;
122 if (m4_get_posixly_correct_opt (context))
123 return;
125 m4path = getenv ("M4PATH");
126 if (m4path)
127 m4path = xstrdup (m4path);
128 search_path_env_init (m4__get_search_path (context), m4path, false);
129 free (m4path);
133 #if TRUNCATE_FILENAME
134 /* Destructively modify PATH to contain no more than 8 non-`.'
135 characters, optionally followed by a `.' and a filenname extension
136 of 3 characters or fewer. */
137 static char *
138 path_truncate (char *path)
140 char *p, *beg = path; /* following final '/' */
141 for (p = path; *p != '\0'; ++p)
143 if (ISSLASH (*p))
144 beg = 1+ p;
147 char *end = strchr (beg, '.'); /* first period */
148 char *ext = strrchr (beg, '.'); /* last period */
150 size_t len = (size_t) (end - beg); /* length of filename element */
151 if (len > 8)
152 end = beg + 8;
154 if (ext == NULL)
156 *end = '\0';
158 else if (ext != end)
160 stpncpy (end, ext, 4);
163 return path;
165 #else
166 # define path_truncate(path) (path)
167 #endif
171 /* Functions for normal input path search */
173 void
174 m4_add_include_directory (m4 *context, const char *dir, bool prepend)
176 if (m4_get_posixly_correct_opt (context))
177 return;
179 search_path_add (m4__get_search_path (context), dir, prepend);
181 #ifdef DEBUG_INCL
182 xfprintf (stderr, "add_include_directory (%s) %s;\n", dir,
183 prepend ? "prepend" : "append");
184 #endif
188 /* Search for FILENAME according to -B options, `.', -I options, then
189 M4PATH environment. If successful, return the open file, and if
190 RESULT is not NULL, set *RESULT to a malloc'd string that
191 represents the file found with respect to the current working
192 directory. Otherwise, return NULL, and errno reflects the failure
193 from searching `.' (regardless of what else was searched). */
194 char *
195 m4_path_search (m4 *context, const char *filename, const char **suffixes)
197 m4__search_path *incl;
198 char *filepath; /* buffer for constructed name */
199 size_t max_suffix_len = 0;
200 int i, e = 0;
202 /* Reject empty file. */
203 if (*filename == '\0')
205 errno = ENOENT;
206 return NULL;
209 /* Use no suffixes by default. */
210 if (suffixes == NULL)
211 suffixes = NO_SUFFIXES;
213 /* Find the longest suffix, so that we will always allocate enough
214 memory for a filename with suffix. */
215 for (i = 0; suffixes && suffixes[i]; ++i)
217 size_t len = strlen (suffixes[i]);
218 if (len > max_suffix_len)
219 max_suffix_len = len;
222 /* If file is absolute, or if we are not searching a path, a single
223 lookup will do the trick. */
224 if (IS_ABSOLUTE_FILE_NAME (filename))
226 size_t mem = strlen (filename);
228 /* Try appending each of the suffixes we were given. */
229 filepath = path_truncate (strncpy (xmalloc (mem + max_suffix_len +1), filename, mem));
230 for (i = 0; suffixes && suffixes[i]; ++i)
232 strcpy (filepath + mem, suffixes[i]);
233 if (access (filepath, R_OK) == 0)
234 return filepath;
236 /* If search fails, we'll use the error we got from the first
237 access (usually with no suffix). */
238 if (i == 0)
239 e = errno;
241 free (filepath);
243 /* No such file. */
244 errno = e;
245 return NULL;
248 for (incl = m4__get_search_path (context)->list;
249 incl != NULL; incl = incl->next)
251 char *pathname = file_name_concat (incl->dir, filename, NULL);
252 size_t mem = strlen (pathname);
254 #ifdef DEBUG_INCL
255 xfprintf (stderr, "path_search (%s) -- trying %s\n", filename, pathname);
256 #endif
258 if (access (pathname, R_OK) == 0)
260 m4_debug_message (context, M4_DEBUG_TRACE_PATH,
261 _("path search for %s found %s"),
262 quotearg_style (locale_quoting_style, filename),
263 quotearg_n_style (1, locale_quoting_style, pathname));
264 return pathname;
266 else if (!incl->len)
267 /* Capture errno only when searching `.'. */
268 e = errno;
270 filepath = path_truncate (strncpy (xmalloc (mem + max_suffix_len +1), pathname, mem));
271 free (pathname);
273 for (i = 0; suffixes && suffixes[i]; ++i)
275 strcpy (filepath + mem, suffixes[i]);
276 if (access (filepath, R_OK) == 0)
277 return filepath;
279 free (filepath);
282 errno = e;
283 return NULL;
287 /* Attempt to open FILE; if it opens, verify that it is not a
288 directory, and ensure it does not leak across execs. */
289 FILE *
290 m4_fopen (m4 *context, const char *file, const char *mode)
292 FILE *fp = NULL;
294 if (file)
296 struct stat st;
297 int fd;
299 fp = fopen (file, mode);
300 fd = fileno (fp);
302 if (fstat (fd, &st) == 0 && S_ISDIR (st.st_mode))
304 fclose (fp);
305 errno = EISDIR;
306 return NULL;
308 if (set_cloexec_flag (fileno (fp), true) != 0)
309 m4_error (context, 0, errno, NULL,
310 _("cannot protect input file across forks"));
312 return fp;
316 /* Generic load function. Push the input file or load the module named
317 FILENAME, if it can be found in the search path. Complain
318 about inaccesible files iff SILENT is false. */
319 bool
320 m4_load_filename (m4 *context, const m4_call_info *caller,
321 const char *filename, m4_obstack *obs, bool silent)
323 char *filepath = NULL;
324 char *suffix = NULL;
325 bool new_input = false;
327 if (m4_get_posixly_correct_opt (context))
329 if (access (filename, R_OK) == 0)
330 filepath = xstrdup (filename);
332 else
333 filepath = m4_path_search (context, filename, FILE_SUFFIXES);
335 if (filepath)
336 suffix = strrchr (filepath, '.');
338 if (!m4_get_posixly_correct_opt (context)
339 && suffix
340 && STREQ (suffix, ".so"))
342 m4_module_load (context, filename, obs);
344 else
346 FILE *fp = NULL;
348 if (filepath)
349 fp = m4_fopen (context, filepath, "r");
351 if (fp == NULL)
353 if (!silent)
354 m4_error (context, 0, errno, caller, _("cannot open file '%s'"),
355 filename);
356 free (filepath);
357 return false;
360 m4_push_file (context, fp, filepath, true);
361 new_input = true;
363 free (filepath);
365 return new_input;
369 void
370 m4__include_init (m4 *context)
372 include_env_init (context);
375 m4__search_path_info *info = m4__get_search_path (context);
377 /* If M4PATH was not set, then search just the current directory by
378 default. */
379 assert (info);
380 if (info->list_end == NULL)
381 search_path_add (info, "", false);
383 /* Non-core modules installation directory. */
384 search_path_add (info, PKGLIBDIR, false);
387 #ifdef DEBUG_INCL
388 fputs ("initial include search path...\n", stderr);
389 include_dump (context);
390 #endif
395 #ifdef DEBUG_INCL
397 static void
398 include_dump (m4 *context)
400 m4__search_path *incl;
402 fputs ("include_dump:\n", stderr);
403 for (incl = m4__get_search_path (context)->list;
404 incl != NULL; incl = incl->next)
405 xfprintf (stderr, "\t'%s'\n", incl->dir);
408 #endif /* DEBUG_INCL */