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