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