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