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