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