xref: /plan9/sys/src/ape/cmd/pdksh/path.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include "sh.h"
2 #include "ksh_stat.h"
3 
4 /*
5  *	Contains a routine to search a : separated 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.2  1994/05/19  18:32:40  michael
16  * Merge complete, stdio replaced, various fixes. (pre autoconf)
17  *
18  * Revision 1.1  1994/04/06  13:14:03  michael
19  * Initial revision
20  *
21  * Revision 4.2  1990/12/06  18:05:24  larry
22  * Updated test code to reflect parameter change.
23  * Fixed problem with /a/./.dir being simplified to /a and not /a/.dir due
24  * to *(cur+2) == *f test instead of the correct cur+2 == f
25  *
26  * Revision 4.1  90/10/29  14:42:19  larry
27  * base MUN version
28  *
29  * Revision 3.1.0.4  89/02/16  20:28:36  larry
30  * Forgot to set *pathlist to NULL when last changed make_path().
31  *
32  * Revision 3.1.0.3  89/02/13  20:29:55  larry
33  * Fixed up cd so that it knew when a node from CDPATH was used and would
34  * print a message only when really necessary.
35  *
36  * Revision 3.1.0.2  89/02/13  17:51:22  larry
37  * Merged with Eric Gisin's version.
38  *
39  * Revision 3.1.0.1  89/02/13  17:50:58  larry
40  * *** empty log message ***
41  *
42  * Revision 3.1  89/02/13  17:49:28  larry
43  * *** empty log message ***
44  *
45  */
46 
47 #ifdef S_ISLNK
48 static char	*do_phys_path ARGS((XString *xsp, char *xp, const char *path));
49 #endif /* S_ISLNK */
50 
51 /*
52  *	Makes a filename into result using the following algorithm.
53  *	- make result NULL
54  *	- if file starts with '/', append file to result & set cdpathp to NULL
55  *	- if file starts with ./ or ../ append cwd and file to result
56  *	  and set cdpathp to NULL
57  *	- if the first element of cdpathp doesnt start with a '/' xx or '.' xx
58  *	  then cwd is appended to result.
59  *	- the first element of cdpathp is appended to result
60  *	- file is appended to result
61  *	- cdpathp is set to the start of the next element in cdpathp (or NULL
62  *	  if there are no more elements.
63  *	The return value indicates whether a non-null element from cdpathp
64  *	was appened to result.
65  */
66 int
make_path(cwd,file,cdpathp,xsp,phys_pathp)67 make_path(cwd, file, cdpathp, xsp, phys_pathp)
68 	const char *cwd;
69 	const char *file;
70 	char	**cdpathp;	/* & of : separated list */
71 	XString	*xsp;
72 	int	*phys_pathp;
73 {
74 	int	rval = 0;
75 	int	use_cdpath = 1;
76 	char	*plist;
77 	int	len;
78 	int	plen = 0;
79 	char	*xp = Xstring(*xsp, xp);
80 
81 	if (!file)
82 		file = null;
83 
84 	if (!ISRELPATH(file)) {
85 		*phys_pathp = 0;
86 		use_cdpath = 0;
87 	} else {
88 		if (file[0] == '.') {
89 			char c = file[1];
90 
91 			if (c == '.')
92 				c = file[2];
93 			if (ISDIRSEP(c) || c == '\0')
94 				use_cdpath = 0;
95 		}
96 
97 		plist = *cdpathp;
98 		if (!plist)
99 			use_cdpath = 0;
100 		else if (use_cdpath) {
101 			char *pend;
102 
103 			for (pend = plist; *pend && *pend != PATHSEP; pend++)
104 				;
105 			plen = pend - plist;
106 			*cdpathp = *pend ? ++pend : (char *) 0;
107 		}
108 
109 		if ((use_cdpath == 0 || !plen || ISRELPATH(plist))
110 		    && (cwd && *cwd))
111 		{
112 			len = strlen(cwd);
113 			XcheckN(*xsp, xp, len);
114 			memcpy(xp, cwd, len);
115 			xp += len;
116 			if (!ISDIRSEP(cwd[len - 1]))
117 				Xput(*xsp, xp, DIRSEP);
118 		}
119 		*phys_pathp = Xlength(*xsp, xp);
120 		if (use_cdpath && plen) {
121 			XcheckN(*xsp, xp, plen);
122 			memcpy(xp, plist, plen);
123 			xp += plen;
124 			if (!ISDIRSEP(plist[plen - 1]))
125 				Xput(*xsp, xp, DIRSEP);
126 			rval = 1;
127 		}
128 	}
129 
130 	len = strlen(file) + 1;
131 	XcheckN(*xsp, xp, len);
132 	memcpy(xp, file, len);
133 
134 	if (!use_cdpath)
135 		*cdpathp = (char *) 0;
136 
137 	return rval;
138 }
139 
140 /*
141  * Simplify pathnames containing "." and ".." entries.
142  * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
143  */
144 void
simplify_path(path)145 simplify_path(path)
146 	char	*path;
147 {
148 	char	*cur;
149 	char	*t;
150 	int	isrooted;
151 	char	*very_start = path;
152 	char	*start;
153 
154 	if (!*path)
155 		return;
156 
157 	if ((isrooted = ISROOTEDPATH(path)))
158 		very_start++;
159 #if defined (OS2) || defined (__CYGWIN__)
160 	if (path[0] && path[1] == ':')	/* skip a: */
161 		very_start += 2;
162 #endif /* OS2 || __CYGWIN__ */
163 
164 	/* Before			After
165 	 *  /foo/			/foo
166 	 *  /foo/../../bar		/bar
167 	 *  /foo/./blah/..		/foo
168 	 *  .				.
169 	 *  ..				..
170 	 *  ./foo			foo
171 	 *  foo/../../../bar		../../bar
172 	 * OS2 and CYGWIN:
173 	 *  a:/foo/../..		a:/
174 	 *  a:.				a:
175 	 *  a:..			a:..
176 	 *  a:foo/../../blah		a:../blah
177 	 */
178 
179 #ifdef __CYGWIN__
180        /* preserve leading double-slash on pathnames (for UNC paths) */
181        if (path[0] && ISDIRSEP(path[0]) && path[1] && ISDIRSEP(path[1]))
182                very_start++;
183 #endif /* __CYGWIN__ */
184 
185 	for (cur = t = start = very_start; ; ) {
186 		/* treat multiple '/'s as one '/' */
187 		while (ISDIRSEP(*t))
188 			t++;
189 
190 		if (*t == '\0') {
191 			if (cur == path)
192 				/* convert empty path to dot */
193 				*cur++ = '.';
194 			*cur = '\0';
195 			break;
196 		}
197 
198 		if (t[0] == '.') {
199 			if (!t[1] || ISDIRSEP(t[1])) {
200 				t += 1;
201 				continue;
202 			} else if (t[1] == '.' && (!t[2] || ISDIRSEP(t[2]))) {
203 				if (!isrooted && cur == start) {
204 					if (cur != very_start)
205 						*cur++ = DIRSEP;
206 					*cur++ = '.';
207 					*cur++ = '.';
208 					start = cur;
209 				} else if (cur != start)
210 					while (--cur > start && !ISDIRSEP(*cur))
211 						;
212 				t += 2;
213 				continue;
214 			}
215 		}
216 
217 		if (cur != very_start)
218 			*cur++ = DIRSEP;
219 
220 		/* find/copy next component of pathname */
221 		while (*t && !ISDIRSEP(*t))
222 			*cur++ = *t++;
223 	}
224 }
225 
226 
227 void
set_current_wd(path)228 set_current_wd(path)
229 	char *path;
230 {
231 	int len;
232 	char *p = path;
233 
234 	if (!p && !(p = ksh_get_wd((char *) 0, 0)))
235 		p = null;
236 
237 	len = strlen(p) + 1;
238 
239 	if (len > current_wd_size)
240 		current_wd = aresize(current_wd, current_wd_size = len, APERM);
241 	memcpy(current_wd, p, len);
242 	if (p != path && p != null)
243 		afree(p, ATEMP);
244 }
245 
246 #ifdef S_ISLNK
247 char *
get_phys_path(path)248 get_phys_path(path)
249 	const char *path;
250 {
251 	XString xs;
252 	char *xp;
253 
254 	Xinit(xs, xp, strlen(path) + 1, ATEMP);
255 
256 	xp = do_phys_path(&xs, xp, path);
257 
258 	if (!xp)
259 		return (char *) 0;
260 
261 	if (Xlength(xs, xp) == 0)
262 		Xput(xs, xp, DIRSEP);
263 	Xput(xs, xp, '\0');
264 
265 	return Xclose(xs, xp);
266 }
267 
268 static char *
do_phys_path(xsp,xp,path)269 do_phys_path(xsp, xp, path)
270 	XString *xsp;
271 	char *xp;
272 	const char *path;
273 {
274 	const char *p, *q;
275 	int len, llen;
276 	int savepos;
277 	char lbuf[PATH];
278 
279 	Xcheck(*xsp, xp);
280 	for (p = path; p; p = q) {
281 		while (ISDIRSEP(*p))
282 			p++;
283 		if (!*p)
284 			break;
285 		len = (q = ksh_strchr_dirsep(p)) ? q - p : strlen(p);
286 		if (len == 1 && p[0] == '.')
287 			continue;
288 		if (len == 2 && p[0] == '.' && p[1] == '.') {
289 			while (xp > Xstring(*xsp, xp)) {
290 				xp--;
291 				if (ISDIRSEP(*xp))
292 					break;
293 			}
294 			continue;
295 		}
296 
297 		savepos = Xsavepos(*xsp, xp);
298 		Xput(*xsp, xp, DIRSEP);
299 		XcheckN(*xsp, xp, len + 1);
300 		memcpy(xp, p, len);
301 		xp += len;
302 		*xp = '\0';
303 
304 		llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
305 		if (llen < 0) {
306 			/* EINVAL means it wasn't a symlink... */
307 			if (errno != EINVAL)
308 				return (char *) 0;
309 			continue;
310 		}
311 		lbuf[llen] = '\0';
312 
313 		/* If absolute path, start from scratch.. */
314 		xp = ISABSPATH(lbuf) ? Xstring(*xsp, xp)
315 				     : Xrestpos(*xsp, xp, savepos);
316 		if (!(xp = do_phys_path(xsp, xp, lbuf)))
317 			return (char *) 0;
318 	}
319 	return xp;
320 }
321 #endif /* S_ISLNK */
322 
323 #ifdef	TEST
324 
main(argc,argv)325 main(argc, argv)
326 {
327 	int	rv;
328 	char	*cp, cdpath[256], pwd[256], file[256], result[256];
329 
330 	printf("enter CDPATH: "); gets(cdpath);
331 	printf("enter PWD: "); gets(pwd);
332 	while (1) {
333 		if (printf("Enter file: "), gets(file) == 0)
334 			return 0;
335 		cp = cdpath;
336 		do {
337 			rv = make_path(pwd, file, &cp, result, sizeof(result));
338 			printf("make_path returns (%d), \"%s\" ", rv, result);
339 			simplify_path(result);
340 			printf("(simpifies to \"%s\")\n", result);
341 		} while (cp);
342 	}
343 }
344 #endif	/* TEST */
345