Bump to m4 1.4.19
[platform/upstream/m4.git] / src / path.c
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 \f
41 void
42 include_init (void)
43 {
44   dir_list = NULL;
45   dir_list_end = NULL;
46   dir_max_length = 0;
47 }
48
49 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
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 *
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 *
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
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 */