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