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
include_init(void)42 include_init (void)
43 {
44 dir_list = NULL;
45 dir_list_end = NULL;
46 dir_max_length = 0;
47 }
48
49 void
include_env_init(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
add_include_directory(const char * dir)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 *
m4_fopen(const char * 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 *
m4_path_search(const char * file,char ** result)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
include_dump(void)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 */
203