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