Line data Source code
1 : /* Copyright 1989-2024 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include <config.h>
21 : #endif
22 :
23 : #include <assert.h>
24 : #include <errno.h>
25 : #include <stdlib.h>
26 :
27 : // for stat(2)
28 : #include <sys/types.h>
29 : #include <sys/stat.h>
30 : #include <unistd.h>
31 :
32 : #include "lib.h"
33 :
34 : #include "searchpath.h"
35 : #include "nonposix.h"
36 :
37 : #ifdef _WIN32
38 : # include "relocate.h"
39 : #else
40 : # define relocate(path) strsave(path)
41 : #endif
42 :
43 56720 : static bool is_directory(const char *name)
44 : {
45 : struct stat statbuf;
46 : // If stat() fails, a later fopen() will fail anyway (he said
47 : // TOCTTOUishly).
48 56720 : if ((stat(name, &statbuf) == 0)
49 56720 : && ((statbuf.st_mode & S_IFMT) == S_IFDIR))
50 0 : return true;
51 56720 : return false;
52 : }
53 :
54 10013 : search_path::search_path(const char *envvar, const char *standard,
55 10013 : int add_home, int add_current)
56 : {
57 10013 : char *home = 0 /* nullptr */;
58 10013 : if (add_home)
59 2994 : home = getenv("HOME");
60 10013 : char *e = 0 /* nullptr */;
61 10013 : if (envvar != 0 /* nullptr */)
62 8456 : e = getenv(envvar);
63 10013 : dirs = new char[((e && *e) ? strlen(e) + 1 : 0)
64 10013 : + (add_current ? 1 + 1 : 0)
65 10013 : + ((home && *home) ? strlen(home) + 1 : 0)
66 10013 : + ((standard && *standard) ? strlen(standard) : 0)
67 10013 : + 1];
68 10013 : *dirs = '\0';
69 10013 : if (e && *e) {
70 8208 : strcat(dirs, e);
71 8208 : strcat(dirs, PATH_SEP);
72 : }
73 10013 : if (add_current) {
74 3054 : strcat(dirs, ".");
75 3054 : strcat(dirs, PATH_SEP);
76 : }
77 10013 : if (home && *home) {
78 2994 : strcat(dirs, home);
79 2994 : strcat(dirs, PATH_SEP);
80 : }
81 10013 : if (standard && *standard)
82 8456 : strcat(dirs, standard);
83 10013 : init_len = strlen(dirs);
84 10013 : }
85 :
86 20026 : search_path::~search_path()
87 : {
88 : // dirs is always allocated
89 10013 : delete[] dirs;
90 10013 : }
91 :
92 852 : void search_path::command_line_dir(const char *s)
93 : {
94 852 : char *old = dirs;
95 852 : unsigned old_len = strlen(old);
96 852 : unsigned slen = strlen(s);
97 852 : dirs = new char[old_len + 1 + slen + 1];
98 852 : memcpy(dirs, old, old_len - init_len);
99 852 : char *p = dirs;
100 852 : p += old_len - init_len;
101 852 : if (init_len == 0)
102 0 : *p++ = PATH_SEP_CHAR;
103 852 : memcpy(p, s, slen);
104 852 : p += slen;
105 852 : if (init_len > 0) {
106 852 : *p++ = PATH_SEP_CHAR;
107 852 : memcpy(p, old + old_len - init_len, init_len);
108 852 : p += init_len;
109 : }
110 852 : *p++ = '\0';
111 852 : delete[] old;
112 852 : }
113 :
114 43190 : FILE *search_path::open_file(const char *name, char **pathp)
115 : {
116 43190 : assert(name != 0 /* nullptr */);
117 43190 : if (IS_ABSOLUTE(name) || *dirs == '\0') {
118 0 : if (is_directory(name)) {
119 0 : errno = EISDIR;
120 0 : return 0 /* nullptr */;
121 : }
122 0 : FILE *fp = fopen(name, "r");
123 0 : if (fp != 0 /* nullptr */) {
124 0 : if (pathp != 0 /* nullptr */)
125 0 : *pathp = strsave(name);
126 0 : return fp;
127 : }
128 : else
129 0 : return 0 /* nullptr */;
130 : }
131 43190 : unsigned namelen = strlen(name);
132 43190 : char *p = dirs;
133 : for (;;) {
134 56628 : char *end = strchr(p, PATH_SEP_CHAR);
135 56628 : if (0 /* nullptr */ == end)
136 69 : end = strchr(p, '\0');
137 56628 : int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
138 56628 : char *origpath = new char[(end - p) + need_slash + namelen + 1];
139 56628 : memcpy(origpath, p, end - p);
140 56628 : if (need_slash)
141 56628 : origpath[end - p] = '/';
142 56628 : strcpy(origpath + (end - p) + need_slash, name);
143 : #if 0
144 : fprintf(stderr, "origpath '%s'\n", origpath);
145 : #endif
146 56628 : char *path = relocate(origpath);
147 56628 : delete[] origpath;
148 : #if 0
149 : fprintf(stderr, "trying '%s'\n", path);
150 : #endif
151 56628 : if (is_directory(name)) {
152 0 : errno = EISDIR;
153 0 : return 0 /* nullptr */;
154 : }
155 56628 : FILE *fp = fopen(path, "r");
156 56628 : int err = errno;
157 56628 : if (fp != 0 /* nullptr */) {
158 43121 : if (pathp != 0 /* nullptr */)
159 43121 : *pathp = path;
160 : else {
161 0 : free(path);
162 0 : errno = err;
163 : }
164 43121 : return fp;
165 : }
166 13507 : free(path);
167 13507 : errno = err;
168 13507 : if (*end == '\0')
169 69 : break;
170 13438 : p = end + 1;
171 13438 : }
172 69 : return 0 /* nullptr */;
173 : }
174 :
175 130 : FILE *search_path::open_file_cautiously(const char *name, char **pathp,
176 : const char *mode)
177 : {
178 130 : if (0 /* nullptr */ == mode)
179 125 : mode = "r";
180 130 : bool reading = (strchr(mode, 'r') != 0 /* nullptr */);
181 130 : if (0 /* nullptr */ == name || strcmp(name, "-") == 0) {
182 63 : if (pathp != 0)
183 63 : *pathp = strsave(reading ? "stdin" : "stdout");
184 63 : return (reading ? stdin : stdout);
185 : }
186 67 : if (!reading || IS_ABSOLUTE(name) || *dirs == '\0') {
187 4 : if (is_directory(name)) {
188 0 : errno = EISDIR;
189 0 : return 0 /* nullptr */;
190 : }
191 4 : FILE *fp = fopen(name, mode);
192 4 : if (fp != 0 /* nullptr */) {
193 4 : if (pathp != 0 /* nullptr */)
194 3 : *pathp = strsave(name);
195 4 : return fp;
196 : }
197 : else
198 0 : return 0 /* nullptr */;
199 : }
200 63 : unsigned namelen = strlen(name);
201 63 : char *p = dirs;
202 : for (;;) {
203 88 : char *end = strchr(p, PATH_SEP_CHAR);
204 88 : if (0 /* nullptr */ == end)
205 1 : end = strchr(p, '\0');
206 88 : int need_slash = (end > p
207 88 : && strchr(DIR_SEPS, end[-1]) == 0 /* nullptr */);
208 88 : char *origpath = new char[(end - p) + need_slash + namelen + 1];
209 88 : memcpy(origpath, p, end - p);
210 88 : if (need_slash)
211 87 : origpath[end - p] = '/';
212 88 : strcpy(origpath + (end - p) + need_slash, name);
213 : #if 0
214 : fprintf(stderr, "origpath '%s'\n", origpath);
215 : #endif
216 88 : char *path = relocate(origpath);
217 88 : delete[] origpath;
218 : #if 0
219 : fprintf(stderr, "trying '%s'\n", path);
220 : #endif
221 88 : if (is_directory(name)) {
222 0 : errno = EISDIR;
223 0 : return 0 /* nullptr */;
224 : }
225 88 : FILE *fp = fopen(path, mode);
226 88 : int err = errno;
227 88 : if (fp != 0 /* nullptr */) {
228 62 : if (pathp != 0 /* nullptr */)
229 12 : *pathp = path;
230 : else {
231 50 : free(path);
232 50 : errno = err;
233 : }
234 62 : return fp;
235 : }
236 26 : free(path);
237 26 : errno = err;
238 26 : if (err != ENOENT)
239 0 : return 0 /* nullptr */;
240 26 : if (*end == '\0')
241 1 : break;
242 25 : p = end + 1;
243 25 : }
244 1 : errno = ENOENT;
245 1 : return 0 /* nullptr */;
246 : }
247 :
248 : // Local Variables:
249 : // fill-column: 72
250 : // mode: C++
251 : // End:
252 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|