1 /* $NetBSD: path.c,v 1.8 2009/04/25 05:11:37 lukem Exp $ */
2 #include <sys/cdefs.h>
3
4 #ifndef lint
5 __RCSID("$NetBSD: path.c,v 1.8 2009/04/25 05:11:37 lukem Exp $");
6 #endif
7
8
9 #include "sh.h"
10 #include "ksh_stat.h"
11
12 /*
13 * Contains a routine to search a : separated list of
14 * paths (a la CDPATH) and make appropriate file names.
15 * Also contains a routine to simplify .'s and ..'s out of
16 * a path name.
17 *
18 * Larry Bouzane (larry@cs.mun.ca)
19 */
20
21 #ifdef S_ISLNK
22 static char *do_phys_path ARGS((XString *, char *, const char *));
23 #endif /* S_ISLNK */
24
25 /*
26 * Makes a filename into result using the following algorithm.
27 * - make result NULL
28 * - if file starts with '/', append file to result & set cdpathp to NULL
29 * - if file starts with ./ or ../ append cwd and file to result
30 * and set cdpathp to NULL
31 * - if the first element of cdpathp doesnt start with a '/' xx or '.' xx
32 * then cwd is appended to result.
33 * - the first element of cdpathp is appended to result
34 * - file is appended to result
35 * - cdpathp is set to the start of the next element in cdpathp (or NULL
36 * if there are no more elements.
37 * The return value indicates whether a non-null element from cdpathp
38 * was appended to result.
39 */
40 int
make_path(cwd,file,cdpathp,xsp,phys_pathp)41 make_path(cwd, file, cdpathp, xsp, phys_pathp)
42 const char *cwd;
43 const char *file;
44 char **cdpathp; /* & of : separated list */
45 XString *xsp;
46 int *phys_pathp;
47 {
48 int rval = 0;
49 int use_cdpath = 1;
50 char *plist;
51 int len;
52 int plen = 0;
53 char *xp = Xstring(*xsp, xp);
54
55 if (!file)
56 file = null;
57
58 if (!ISRELPATH(file)) {
59 *phys_pathp = 0;
60 use_cdpath = 0;
61 } else {
62 if (file[0] == '.') {
63 char c = file[1];
64
65 if (c == '.')
66 c = file[2];
67 if (ISDIRSEP(c) || c == '\0')
68 use_cdpath = 0;
69 }
70
71 plist = *cdpathp;
72 if (!plist)
73 use_cdpath = 0;
74 else if (use_cdpath) {
75 char *pend;
76
77 for (pend = plist; *pend && *pend != PATHSEP; pend++)
78 ;
79 plen = pend - plist;
80 *cdpathp = *pend ? ++pend : (char *) 0;
81 }
82
83 if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
84 && (cwd && *cwd))
85 {
86 len = strlen(cwd);
87 XcheckN(*xsp, xp, len);
88 memcpy(xp, cwd, len);
89 xp += len;
90 if (!ISDIRSEP(cwd[len - 1]))
91 Xput(*xsp, xp, DIRSEP);
92 }
93 *phys_pathp = Xlength(*xsp, xp);
94 if (use_cdpath && plen) {
95 XcheckN(*xsp, xp, plen);
96 memcpy(xp, plist, plen);
97 xp += plen;
98 if (!ISDIRSEP(plist[plen - 1]))
99 Xput(*xsp, xp, DIRSEP);
100 rval = 1;
101 }
102 }
103
104 len = strlen(file) + 1;
105 XcheckN(*xsp, xp, len);
106 memcpy(xp, file, len);
107
108 if (!use_cdpath)
109 *cdpathp = (char *) 0;
110
111 return rval;
112 }
113
114 /*
115 * Simplify pathnames containing "." and ".." entries.
116 * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
117 */
118 void
simplify_path(pathx)119 simplify_path(pathx)
120 char *pathx;
121 {
122 char *cur;
123 char *t;
124 int isrooted;
125 char *very_start = pathx;
126 char *start;
127
128 if (!*pathx)
129 return;
130
131 if ((isrooted = ISROOTEDPATH(pathx)))
132 very_start++;
133 #if defined (OS2) || defined (__CYGWIN__)
134 if (pathx[0] && pathx[1] == ':') /* skip a: */
135 very_start += 2;
136 #endif /* OS2 || __CYGWIN__ */
137
138 /* Before After
139 * /foo/ /foo
140 * /foo/../../bar /bar
141 * /foo/./blah/.. /foo
142 * . .
143 * .. ..
144 * ./foo foo
145 * foo/../../../bar ../../bar
146 * OS2 and CYGWIN:
147 * a:/foo/../.. a:/
148 * a:. a:
149 * a:.. a:..
150 * a:foo/../../blah a:../blah
151 */
152
153 #ifdef __CYGWIN__
154 /* preserve leading double-slash on pathnames (for UNC paths) */
155 if (pathx[0] && ISDIRSEP(pathx[0]) && pathx[1] && ISDIRSEP(pathx[1]))
156 very_start++;
157 #endif /* __CYGWIN__ */
158
159 for (cur = t = start = very_start; ; ) {
160 /* treat multiple '/'s as one '/' */
161 while (ISDIRSEP(*t))
162 t++;
163
164 if (*t == '\0') {
165 if (cur == pathx)
166 /* convert empty path to dot */
167 *cur++ = '.';
168 *cur = '\0';
169 break;
170 }
171
172 if (t[0] == '.') {
173 if (!t[1] || ISDIRSEP(t[1])) {
174 t += 1;
175 continue;
176 } else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
177 if (!isrooted && cur == start) {
178 if (cur != very_start)
179 *cur++ = DIRSEP;
180 *cur++ = '.';
181 *cur++ = '.';
182 start = cur;
183 } else if (cur != start)
184 while (--cur > start && !ISDIRSEP(*cur))
185 ;
186 t += 2;
187 continue;
188 }
189 }
190
191 if (cur != very_start)
192 *cur++ = DIRSEP;
193
194 /* find/copy next component of pathname */
195 while (*t && !ISDIRSEP(*t))
196 *cur++ = *t++;
197 }
198 }
199
200
201 void
set_current_wd(pathx)202 set_current_wd(pathx)
203 char *pathx;
204 {
205 int len;
206 char *p = pathx;
207
208 if (!p && !(p = ksh_get_wd((char *) 0, 0)))
209 p = null;
210
211 len = strlen(p) + 1;
212
213 if (len > current_wd_size)
214 current_wd = aresize(current_wd, current_wd_size = len, APERM);
215 memcpy(current_wd, p, len);
216 if (p != pathx && p != null)
217 afree(p, ATEMP);
218 }
219
220 #ifdef S_ISLNK
221 char *
get_phys_path(pathx)222 get_phys_path(pathx)
223 const char *pathx;
224 {
225 XString xs;
226 char *xp;
227
228 Xinit(xs, xp, strlen(pathx) + 1, ATEMP);
229
230 xp = do_phys_path(&xs, xp, pathx);
231
232 if (!xp)
233 return (char *) 0;
234
235 if (Xlength(xs, xp) == 0)
236 Xput(xs, xp, DIRSEP);
237 Xput(xs, xp, '\0');
238
239 return Xclose(xs, xp);
240 }
241
242 static char *
do_phys_path(xsp,xp,pathx)243 do_phys_path(xsp, xp, pathx)
244 XString *xsp;
245 char *xp;
246 const char *pathx;
247 {
248 const char *p, *q;
249 int len, llen;
250 int savepos;
251 char lbuf[PATH];
252
253 Xcheck(*xsp, xp);
254 for (p = pathx; p; p = q) {
255 while (ISDIRSEP(*p))
256 p++;
257 if (!*p)
258 break;
259 len = (q = ksh_strchr_dirsep(p)) ? q - p : (int)strlen(p);
260 if (len == 1 && p[0] == '.')
261 continue;
262 if (len == 2 && p[0] == '.' && p[1] == '.') {
263 while (xp > Xstring(*xsp, xp)) {
264 xp--;
265 if (ISDIRSEP(*xp))
266 break;
267 }
268 continue;
269 }
270
271 savepos = Xsavepos(*xsp, xp);
272 Xput(*xsp, xp, DIRSEP);
273 XcheckN(*xsp, xp, len + 1);
274 memcpy(xp, p, len);
275 xp += len;
276 *xp = '\0';
277
278 llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
279 if (llen < 0) {
280 /* EINVAL means it wasn't a symlink... */
281 if (errno != EINVAL)
282 return (char *) 0;
283 continue;
284 }
285 lbuf[llen] = '\0';
286
287 /* If absolute path, start from scratch.. */
288 xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
289 : Xrestpos(*xsp, xp, savepos);
290 if (!(xp = do_phys_path(xsp, xp, lbuf)))
291 return (char *) 0;
292 }
293 return xp;
294 }
295 #endif /* S_ISLNK */
296
297 #ifdef TEST
298
main(argc,argv)299 main(argc, argv)
300 {
301 int rv;
302 char *cp, cdpath[256], pwd[256], file[256], result[256];
303
304 printf("enter CDPATH: "); gets(cdpath);
305 printf("enter PWD: "); gets(pwd);
306 while (1) {
307 if (printf("Enter file: "), gets(file) == 0)
308 return 0;
309 cp = cdpath;
310 do {
311 rv = make_path(pwd, file, &cp, result, sizeof(result));
312 printf("make_path returns (%d), \"%s\" ", rv, result);
313 simplify_path(result);
314 printf("(simpifies to \"%s\")\n", result);
315 } while (cp);
316 }
317 }
318 #endif /* TEST */
319