libm4: spell TRUNCATE_FILENAME consistently.
[m4/ericb.git] / m4 / path.c
blobef743a3cb00b4df78a917a794e944190cc21a4a5
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 #endif
169 /* Functions for normal input path search */
171 void
172 m4_add_include_directory (m4 *context, const char *dir, bool prepend)
174 if (m4_get_posixly_correct_opt (context))
175 return;
177 search_path_add (m4__get_search_path (context), dir, prepend);
179 #ifdef DEBUG_INCL
180 xfprintf (stderr, "add_include_directory (%s) %s;\n", dir,
181 prepend ? "prepend" : "append");
182 #endif
186 /* Search for FILENAME according to -B options, `.', -I options, then
187 M4PATH environment. If successful, return the open file, and if
188 RESULT is not NULL, set *RESULT to a malloc'd string that
189 represents the file found with respect to the current working
190 directory. Otherwise, return NULL, and errno reflects the failure
191 from searching `.' (regardless of what else was searched). */
192 char *
193 m4_path_search (m4 *context, const char *filename, const char **suffixes)
195 m4__search_path *incl;
196 char *filepath; /* buffer for constructed name */
197 size_t max_suffix_len = 0;
198 int i, e = 0;
200 /* Reject empty file. */
201 if (*filename == '\0')
203 errno = ENOENT;
204 return NULL;
207 /* Use no suffixes by default. */
208 if (suffixes == NULL)
209 suffixes = NO_SUFFIXES;
211 /* Find the longest suffix, so that we will always allocate enough
212 memory for a filename with suffix. */
213 for (i = 0; suffixes && suffixes[i]; ++i)
215 size_t len = strlen (suffixes[i]);
216 if (len > max_suffix_len)
217 max_suffix_len = len;
220 /* If file is absolute, or if we are not searching a path, a single
221 lookup will do the trick. */
222 if (IS_ABSOLUTE_FILE_NAME (filename))
224 size_t mem = strlen (filename);
226 /* Try appending each of the suffixes we were given. */
227 filepath = strncpy (xmalloc (mem + max_suffix_len +1), filename, mem +1);
228 #if TRUNCATE_FILENAME
229 filepath = path_truncate (filepath);
230 mem = strlen (filepath); /* recalculate length after truncation */
231 #endif
232 for (i = 0; suffixes && suffixes[i]; ++i)
234 strcpy (filepath + mem, suffixes[i]);
235 if (access (filepath, R_OK) == 0)
236 return filepath;
238 /* If search fails, we'll use the error we got from the first
239 access (usually with no suffix). */
240 if (i == 0)
241 e = errno;
243 free (filepath);
245 /* No such file. */
246 errno = e;
247 return NULL;
250 for (incl = m4__get_search_path (context)->list;
251 incl != NULL; incl = incl->next)
253 char *pathname = file_name_concat (incl->dir, filename, NULL);
254 size_t mem = strlen (pathname);
256 #ifdef DEBUG_INCL
257 xfprintf (stderr, "path_search (%s) -- trying %s\n", filename, pathname);
258 #endif
260 if (access (pathname, R_OK) == 0)
262 m4_debug_message (context, M4_DEBUG_TRACE_PATH,
263 _("path search for %s found %s"),
264 quotearg_style (locale_quoting_style, filename),
265 quotearg_n_style (1, locale_quoting_style, pathname));
266 return pathname;
268 else if (!incl->len)
269 /* Capture errno only when searching `.'. */
270 e = errno;
272 filepath = strncpy (xmalloc (mem + max_suffix_len +1), pathname, mem +1);
273 free (pathname);
274 #if TRUNCATE_FILENAME
275 filepath = path_truncate (filepath);
276 mem = strlen (filepath); /* recalculate length after truncation */
277 #endif
279 for (i = 0; suffixes && suffixes[i]; ++i)
281 strcpy (filepath + mem, suffixes[i]);
282 if (access (filepath, R_OK) == 0)
283 return filepath;
285 free (filepath);
288 errno = e;
289 return NULL;
293 /* Attempt to open FILE; if it opens, verify that it is not a
294 directory, and ensure it does not leak across execs. */
295 FILE *
296 m4_fopen (m4 *context, const char *file, const char *mode)
298 FILE *fp = NULL;
300 if (file)
302 struct stat st;
303 int fd;
305 fp = fopen (file, mode);
306 fd = fileno (fp);
308 if (fstat (fd, &st) == 0 && S_ISDIR (st.st_mode))
310 fclose (fp);
311 errno = EISDIR;
312 return NULL;
314 if (set_cloexec_flag (fileno (fp), true) != 0)
315 m4_error (context, 0, errno, NULL,
316 _("cannot protect input file across forks"));
318 return fp;
322 /* Generic load function. Push the input file or load the module named
323 FILENAME, if it can be found in the search path. Complain
324 about inaccesible files iff SILENT is false. */
325 bool
326 m4_load_filename (m4 *context, const m4_call_info *caller,
327 const char *filename, m4_obstack *obs, bool silent)
329 char *filepath = NULL;
330 char *suffix = NULL;
331 bool new_input = false;
333 if (m4_get_posixly_correct_opt (context))
335 if (access (filename, R_OK) == 0)
336 filepath = xstrdup (filename);
338 else
339 filepath = m4_path_search (context, filename, FILE_SUFFIXES);
341 if (filepath)
342 suffix = strrchr (filepath, '.');
344 if (!m4_get_posixly_correct_opt (context)
345 && suffix
346 && STREQ (suffix, LT_MODULE_EXT))
348 m4_module_load (context, filename, obs);
350 else
352 FILE *fp = NULL;
354 if (filepath)
355 fp = m4_fopen (context, filepath, "r");
357 if (fp == NULL)
359 if (!silent)
360 m4_error (context, 0, errno, caller, _("cannot open file '%s'"),
361 filename);
362 free (filepath);
363 return false;
366 m4_push_file (context, fp, filepath, true);
367 new_input = true;
369 free (filepath);
371 return new_input;
375 void
376 m4__include_init (m4 *context)
378 include_env_init (context);
381 m4__search_path_info *info = m4__get_search_path (context);
383 /* If M4PATH was not set, then search just the current directory by
384 default. */
385 assert (info);
386 if (info->list_end == NULL)
387 search_path_add (info, "", false);
389 /* Non-core modules installation directory. */
390 search_path_add (info, PKGLIBDIR, false);
393 #ifdef DEBUG_INCL
394 fputs ("initial include search path...\n", stderr);
395 include_dump (context);
396 #endif
401 #ifdef DEBUG_INCL
403 static void
404 include_dump (m4 *context)
406 m4__search_path *incl;
408 fputs ("include_dump:\n", stderr);
409 for (incl = m4__get_search_path (context)->list;
410 incl != NULL; incl = incl->next)
411 xfprintf (stderr, "\t'%s'\n", incl->dir);
414 #endif /* DEBUG_INCL */