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