xref: /netbsd/bin/ksh/path.c (revision bf9ec67e)
1 /*	$NetBSD: path.c,v 1.4 1999/10/20 15:10:00 hubertf Exp $	*/
2 
3 #include "sh.h"
4 #include "ksh_stat.h"
5 
6 /*
7  *	Contains a routine to search a : separated 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 #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 : separated 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 #if defined (OS2) || defined (__CYGWIN__)
128 	if (path[0] && path[1] == ':')	/* skip a: */
129 		very_start += 2;
130 #endif /* OS2 || __CYGWIN__ */
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 and CYGWIN:
141 	 *  a:/foo/../..		a:/
142 	 *  a:.				a:
143 	 *  a:..			a:..
144 	 *  a:foo/../../blah		a:../blah
145 	 */
146 
147 #ifdef __CYGWIN__
148        /* preserve leading double-slash on pathnames (for UNC paths) */
149        if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1]))
150                very_start++;
151 #endif /* __CYGWIN__ */
152 
153 	for (cur = t = start = very_start; ; ) {
154 		/* treat multiple '/'s as one '/' */
155 		while (ISDIRSEP(*t))
156 			t++;
157 
158 		if (*t == '\0') {
159 			if (cur == path)
160 				/* convert empty path to dot */
161 				*cur++ = '.';
162 			*cur = '\0';
163 			break;
164 		}
165 
166 		if (t[0] == '.') {
167 			if (!t[1] || ISDIRSEP(t[1])) {
168 				t += 1;
169 				continue;
170 			} else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
171 				if (!isrooted && cur == start) {
172 					if (cur != very_start)
173 						*cur++ = DIRSEP;
174 					*cur++ = '.';
175 					*cur++ = '.';
176 					start = cur;
177 				} else if (cur != start)
178 					while (--cur > start && !ISDIRSEP(*cur))
179 						;
180 				t += 2;
181 				continue;
182 			}
183 		}
184 
185 		if (cur != very_start)
186 			*cur++ = DIRSEP;
187 
188 		/* find/copy next component of pathname */
189 		while (*t && !ISDIRSEP(*t))
190 			*cur++ = *t++;
191 	}
192 }
193 
194 
195 void
196 set_current_wd(path)
197 	char *path;
198 {
199 	int len;
200 	char *p = path;
201 
202 	if (!p && !(p = ksh_get_wd((char *) 0, 0)))
203 		p = null;
204 
205 	len = strlen(p) + 1;
206 
207 	if (len > current_wd_size)
208 		current_wd = aresize(current_wd, current_wd_size = len, APERM);
209 	memcpy(current_wd, p, len);
210 	if (p != path && p != null)
211 		afree(p, ATEMP);
212 }
213 
214 #ifdef S_ISLNK
215 char *
216 get_phys_path(path)
217 	const char *path;
218 {
219 	XString xs;
220 	char *xp;
221 
222 	Xinit(xs, xp, strlen(path) + 1, ATEMP);
223 
224 	xp = do_phys_path(&xs, xp, path);
225 
226 	if (!xp)
227 		return (char *) 0;
228 
229 	if (Xlength(xs, xp) == 0)
230 		Xput(xs, xp, DIRSEP);
231 	Xput(xs, xp, '\0');
232 
233 	return Xclose(xs, xp);
234 }
235 
236 static char *
237 do_phys_path(xsp, xp, path)
238 	XString *xsp;
239 	char *xp;
240 	const char *path;
241 {
242 	const char *p, *q;
243 	int len, llen;
244 	int savepos;
245 	char lbuf[PATH];
246 
247 	Xcheck(*xsp, xp);
248 	for (p = path; p; p = q) {
249 		while (ISDIRSEP(*p))
250 			p++;
251 		if (!*p)
252 			break;
253 		len = (q = ksh_strchr_dirsep(p)) ? q - p : strlen(p);
254 		if (len == 1 && p[0] == '.')
255 			continue;
256 		if (len == 2 && p[0] == '.' && p[1] == '.') {
257 			while (xp > Xstring(*xsp, xp)) {
258 				xp--;
259 				if (ISDIRSEP(*xp))
260 					break;
261 			}
262 			continue;
263 		}
264 
265 		savepos = Xsavepos(*xsp, xp);
266 		Xput(*xsp, xp, DIRSEP);
267 		XcheckN(*xsp, xp, len + 1);
268 		memcpy(xp, p, len);
269 		xp += len;
270 		*xp = '\0';
271 
272 		llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
273 		if (llen < 0) {
274 			/* EINVAL means it wasn't a symlink... */
275 			if (errno != EINVAL)
276 				return (char *) 0;
277 			continue;
278 		}
279 		lbuf[llen] = '\0';
280 
281 		/* If absolute path, start from scratch.. */
282 		xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
283 				     : Xrestpos(*xsp, xp, savepos);
284 		if (!(xp = do_phys_path(xsp, xp, lbuf)))
285 			return (char *) 0;
286 	}
287 	return xp;
288 }
289 #endif /* S_ISLNK */
290 
291 #ifdef	TEST
292 
293 main(argc, argv)
294 {
295 	int	rv;
296 	char	*cp, cdpath[256], pwd[256], file[256], result[256];
297 
298 	printf("enter CDPATH: "); gets(cdpath);
299 	printf("enter PWD: "); gets(pwd);
300 	while (1) {
301 		if (printf("Enter file: "), gets(file) == 0)
302 			return 0;
303 		cp = cdpath;
304 		do {
305 			rv = make_path(pwd, file, &cp, result, sizeof(result));
306 			printf("make_path returns (%d), \"%s\" ", rv, result);
307 			simplify_path(result);
308 			printf("(simpifies to \"%s\")\n", result);
309 		} while (cp);
310 	}
311 }
312 #endif	/* TEST */
313