xref: /netbsd-src/bin/ksh/path.c (revision d0fed6c87ddc40a8bffa6f99e7433ddfc864dd83)
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@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