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