1 |
|
|
/* $OpenBSD: path.c,v 1.18 2015/12/14 13:59:42 tb Exp $ */ |
2 |
|
|
|
3 |
|
|
#include <sys/stat.h> |
4 |
|
|
|
5 |
|
|
#include <errno.h> |
6 |
|
|
#include <string.h> |
7 |
|
|
#include <unistd.h> |
8 |
|
|
|
9 |
|
|
#include "sh.h" |
10 |
|
|
|
11 |
|
|
/* |
12 |
|
|
* Contains a routine to search a : separated list of |
13 |
|
|
* paths (a la CDPATH) and make appropriate file names. |
14 |
|
|
* Also contains a routine to simplify .'s and ..'s out of |
15 |
|
|
* a path name. |
16 |
|
|
* |
17 |
|
|
* Larry Bouzane (larry@cs.mun.ca) |
18 |
|
|
*/ |
19 |
|
|
|
20 |
|
|
static char *do_phys_path(XString *, char *, const char *); |
21 |
|
|
|
22 |
|
|
/* |
23 |
|
|
* Makes a filename into result using the following algorithm. |
24 |
|
|
* - make result NULL |
25 |
|
|
* - if file starts with '/', append file to result & set cdpathp to NULL |
26 |
|
|
* - if file starts with ./ or ../ append cwd and file to result |
27 |
|
|
* and set cdpathp to NULL |
28 |
|
|
* - if the first element of cdpathp doesnt start with a '/' xx or '.' xx |
29 |
|
|
* then cwd is appended to result. |
30 |
|
|
* - the first element of cdpathp is appended to result |
31 |
|
|
* - file is appended to result |
32 |
|
|
* - cdpathp is set to the start of the next element in cdpathp (or NULL |
33 |
|
|
* if there are no more elements. |
34 |
|
|
* The return value indicates whether a non-null element from cdpathp |
35 |
|
|
* was appended to result. |
36 |
|
|
*/ |
37 |
|
|
int |
38 |
|
|
make_path(const char *cwd, const char *file, |
39 |
|
|
char **cdpathp, /* & of : separated list */ |
40 |
|
|
XString *xsp, |
41 |
|
|
int *phys_pathp) |
42 |
|
426 |
{ |
43 |
|
426 |
int rval = 0; |
44 |
|
426 |
int use_cdpath = 1; |
45 |
|
|
char *plist; |
46 |
|
|
int len; |
47 |
|
426 |
int plen = 0; |
48 |
|
426 |
char *xp = Xstring(*xsp, xp); |
49 |
|
|
|
50 |
✗✓ |
426 |
if (!file) |
51 |
|
|
file = null; |
52 |
|
|
|
53 |
✓✓ |
426 |
if (file[0] == '/') { |
54 |
|
379 |
*phys_pathp = 0; |
55 |
|
379 |
use_cdpath = 0; |
56 |
|
|
} else { |
57 |
✓✓ |
47 |
if (file[0] == '.') { |
58 |
|
4 |
char c = file[1]; |
59 |
|
|
|
60 |
✓✗ |
4 |
if (c == '.') |
61 |
|
4 |
c = file[2]; |
62 |
✓✗ |
4 |
if (c == '/' || c == '\0') |
63 |
|
4 |
use_cdpath = 0; |
64 |
|
|
} |
65 |
|
|
|
66 |
|
47 |
plist = *cdpathp; |
67 |
✗✓ |
47 |
if (!plist) |
68 |
|
|
use_cdpath = 0; |
69 |
✓✓ |
47 |
else if (use_cdpath) { |
70 |
|
|
char *pend; |
71 |
|
|
|
72 |
✗✓ |
43 |
for (pend = plist; *pend && *pend != ':'; pend++) |
73 |
|
|
; |
74 |
|
43 |
plen = pend - plist; |
75 |
✗✓ |
43 |
*cdpathp = *pend ? ++pend : NULL; |
76 |
|
|
} |
77 |
|
|
|
78 |
✗✓✗✗ ✓✗✓✗
|
47 |
if ((use_cdpath == 0 || !plen || plist[0] != '/') && |
79 |
|
|
(cwd && *cwd)) { |
80 |
|
47 |
len = strlen(cwd); |
81 |
✗✓ |
47 |
XcheckN(*xsp, xp, len); |
82 |
|
47 |
memcpy(xp, cwd, len); |
83 |
|
47 |
xp += len; |
84 |
✓✗ |
47 |
if (cwd[len - 1] != '/') |
85 |
|
47 |
Xput(*xsp, xp, '/'); |
86 |
|
|
} |
87 |
|
47 |
*phys_pathp = Xlength(*xsp, xp); |
88 |
✗✓ |
47 |
if (use_cdpath && plen) { |
89 |
|
|
XcheckN(*xsp, xp, plen); |
90 |
|
|
memcpy(xp, plist, plen); |
91 |
|
|
xp += plen; |
92 |
|
|
if (plist[plen - 1] != '/') |
93 |
|
|
Xput(*xsp, xp, '/'); |
94 |
|
|
rval = 1; |
95 |
|
|
} |
96 |
|
|
} |
97 |
|
|
|
98 |
|
426 |
len = strlen(file) + 1; |
99 |
✗✓ |
426 |
XcheckN(*xsp, xp, len); |
100 |
|
426 |
memcpy(xp, file, len); |
101 |
|
|
|
102 |
✓✓ |
426 |
if (!use_cdpath) |
103 |
|
383 |
*cdpathp = NULL; |
104 |
|
|
|
105 |
|
426 |
return rval; |
106 |
|
|
} |
107 |
|
|
|
108 |
|
|
/* |
109 |
|
|
* Simplify pathnames containing "." and ".." entries. |
110 |
|
|
* ie, simplify_path("/a/b/c/./../d/..") returns "/a/b" |
111 |
|
|
*/ |
112 |
|
|
void |
113 |
|
|
simplify_path(char *path) |
114 |
|
3951 |
{ |
115 |
|
|
char *cur; |
116 |
|
|
char *t; |
117 |
|
|
int isrooted; |
118 |
|
3951 |
char *very_start = path; |
119 |
|
|
char *start; |
120 |
|
|
|
121 |
✓✗ |
3951 |
if (!*path) |
122 |
|
|
return; |
123 |
|
|
|
124 |
✓✗ |
3951 |
if ((isrooted = (path[0] == '/'))) |
125 |
|
3951 |
very_start++; |
126 |
|
|
|
127 |
|
|
/* Before After |
128 |
|
|
* /foo/ /foo |
129 |
|
|
* /foo/../../bar /bar |
130 |
|
|
* /foo/./blah/.. /foo |
131 |
|
|
* . . |
132 |
|
|
* .. .. |
133 |
|
|
* ./foo foo |
134 |
|
|
* foo/../../../bar ../../bar |
135 |
|
|
*/ |
136 |
|
|
|
137 |
|
3951 |
for (cur = t = start = very_start; ; ) { |
138 |
|
|
/* treat multiple '/'s as one '/' */ |
139 |
✓✓ |
37133 |
while (*t == '/') |
140 |
|
14616 |
t++; |
141 |
|
|
|
142 |
✓✓ |
22517 |
if (*t == '\0') { |
143 |
✗✓ |
3951 |
if (cur == path) |
144 |
|
|
/* convert empty path to dot */ |
145 |
|
|
*cur++ = '.'; |
146 |
|
3951 |
*cur = '\0'; |
147 |
|
3951 |
break; |
148 |
|
|
} |
149 |
|
|
|
150 |
✓✓ |
18566 |
if (t[0] == '.') { |
151 |
✗✓ |
4 |
if (!t[1] || t[1] == '/') { |
152 |
|
|
t += 1; |
153 |
|
|
continue; |
154 |
✓✗✓✗
|
4 |
} else if (t[1] == '.' && (!t[2] || t[2] == '/')) { |
155 |
✗✓ |
4 |
if (!isrooted && cur == start) { |
156 |
|
|
if (cur != very_start) |
157 |
|
|
*cur++ = '/'; |
158 |
|
|
*cur++ = '.'; |
159 |
|
|
*cur++ = '.'; |
160 |
|
|
start = cur; |
161 |
✓✗ |
4 |
} else if (cur != start) |
162 |
✓✗✓✓
|
92 |
while (--cur > start && *cur != '/') |
163 |
|
|
; |
164 |
|
4 |
t += 2; |
165 |
|
4 |
continue; |
166 |
|
|
} |
167 |
|
|
} |
168 |
|
|
|
169 |
✓✓ |
18562 |
if (cur != very_start) |
170 |
|
14611 |
*cur++ = '/'; |
171 |
|
|
|
172 |
|
|
/* find/copy next component of pathname */ |
173 |
✓✓ |
102974 |
while (*t && *t != '/') |
174 |
|
84412 |
*cur++ = *t++; |
175 |
|
|
} |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
|
179 |
|
|
void |
180 |
|
|
set_current_wd(char *path) |
181 |
|
3951 |
{ |
182 |
|
|
int len; |
183 |
|
3951 |
char *p = path; |
184 |
|
|
|
185 |
✓✓✗✓
|
3951 |
if (!p && !(p = ksh_get_wd(NULL, 0))) |
186 |
|
|
p = null; |
187 |
|
|
|
188 |
|
3951 |
len = strlen(p) + 1; |
189 |
|
|
|
190 |
✓✓ |
3951 |
if (len > current_wd_size) |
191 |
|
3570 |
current_wd = aresize(current_wd, current_wd_size = len, APERM); |
192 |
|
3951 |
memcpy(current_wd, p, len); |
193 |
✓✓✓✗
|
3951 |
if (p != path && p != null) |
194 |
|
464 |
afree(p, ATEMP); |
195 |
|
3951 |
} |
196 |
|
|
|
197 |
|
|
char * |
198 |
|
|
get_phys_path(const char *path) |
199 |
|
|
{ |
200 |
|
|
XString xs; |
201 |
|
|
char *xp; |
202 |
|
|
|
203 |
|
|
Xinit(xs, xp, strlen(path) + 1, ATEMP); |
204 |
|
|
|
205 |
|
|
xp = do_phys_path(&xs, xp, path); |
206 |
|
|
|
207 |
|
|
if (!xp) |
208 |
|
|
return NULL; |
209 |
|
|
|
210 |
|
|
if (Xlength(xs, xp) == 0) |
211 |
|
|
Xput(xs, xp, '/'); |
212 |
|
|
Xput(xs, xp, '\0'); |
213 |
|
|
|
214 |
|
|
return Xclose(xs, xp); |
215 |
|
|
} |
216 |
|
|
|
217 |
|
|
static char * |
218 |
|
|
do_phys_path(XString *xsp, char *xp, const char *path) |
219 |
|
|
{ |
220 |
|
|
const char *p, *q; |
221 |
|
|
int len, llen; |
222 |
|
|
int savepos; |
223 |
|
|
char lbuf[PATH]; |
224 |
|
|
|
225 |
|
|
Xcheck(*xsp, xp); |
226 |
|
|
for (p = path; p; p = q) { |
227 |
|
|
while (*p == '/') |
228 |
|
|
p++; |
229 |
|
|
if (!*p) |
230 |
|
|
break; |
231 |
|
|
len = (q = strchr(p, '/')) ? q - p : strlen(p); |
232 |
|
|
if (len == 1 && p[0] == '.') |
233 |
|
|
continue; |
234 |
|
|
if (len == 2 && p[0] == '.' && p[1] == '.') { |
235 |
|
|
while (xp > Xstring(*xsp, xp)) { |
236 |
|
|
xp--; |
237 |
|
|
if (*xp == '/') |
238 |
|
|
break; |
239 |
|
|
} |
240 |
|
|
continue; |
241 |
|
|
} |
242 |
|
|
|
243 |
|
|
savepos = Xsavepos(*xsp, xp); |
244 |
|
|
Xput(*xsp, xp, '/'); |
245 |
|
|
XcheckN(*xsp, xp, len + 1); |
246 |
|
|
memcpy(xp, p, len); |
247 |
|
|
xp += len; |
248 |
|
|
*xp = '\0'; |
249 |
|
|
|
250 |
|
|
llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1); |
251 |
|
|
if (llen < 0) { |
252 |
|
|
/* EINVAL means it wasn't a symlink... */ |
253 |
|
|
if (errno != EINVAL) |
254 |
|
|
return NULL; |
255 |
|
|
continue; |
256 |
|
|
} |
257 |
|
|
lbuf[llen] = '\0'; |
258 |
|
|
|
259 |
|
|
/* If absolute path, start from scratch.. */ |
260 |
|
|
xp = lbuf[0] == '/' ? Xstring(*xsp, xp) : |
261 |
|
|
Xrestpos(*xsp, xp, savepos); |
262 |
|
|
if (!(xp = do_phys_path(xsp, xp, lbuf))) |
263 |
|
|
return NULL; |
264 |
|
|
} |
265 |
|
|
return xp; |
266 |
|
|
} |