loksh-noxz

[fork] a Linux port of OpenBSD's ksh
git clone https://noxz.tech/git/loksh-noxz.git
Log | Files | README

path.c
1/*	$OpenBSD: path.c,v 1.23 2019/06/28 13:34:59 deraadt Exp $	*/
2
3#include <sys/stat.h>
4
5#include <errno.h>
6#include <string.h>
7#include <unistd.h>
8
9#include "sh.h"
10
11/*
12 *	Contains a routine to search a : separated list of
13 *	paths (a la CDPATH) and make appropriate file names.
14 *	Also contains a routine to simplify .'s and ..'s out of
15 *	a path name.
16 *
17 *	Larry Bouzane (larry@cs.mun.ca)
18 */
19
20static char	*do_phys_path(XString *, char *, const char *);
21
22/*
23 *	Makes a filename into result using the following algorithm.
24 *	- make result NULL
25 *	- if file starts with '/', append file to result & set cdpathp to NULL
26 *	- if file starts with ./ or ../ append cwd and file to result
27 *	  and set cdpathp to NULL
28 *	- if the first element of cdpathp doesnt start with a '/' xx or '.' xx
29 *	  then cwd is appended to result.
30 *	- the first element of cdpathp is appended to result
31 *	- file is appended to result
32 *	- cdpathp is set to the start of the next element in cdpathp (or NULL
33 *	  if there are no more elements.
34 *	The return value indicates whether a non-null element from cdpathp
35 *	was appended to result.
36 */
37int
38make_path(const char *cwd, const char *file,
39    char **cdpathp,		/* & of : separated list */
40    XString *xsp,
41    int *phys_pathp)
42{
43	int	rval = 0;
44	int	use_cdpath = 1;
45	char	*plist;
46	int	len;
47	int	plen = 0;
48	char	*xp = Xstring(*xsp, xp);
49
50	if (!file)
51		file = null;
52
53	if (file[0] == '/') {
54		*phys_pathp = 0;
55		use_cdpath = 0;
56	} else {
57		if (file[0] == '.') {
58			char c = file[1];
59
60			if (c == '.')
61				c = file[2];
62			if (c == '/' || c == '\0')
63				use_cdpath = 0;
64		}
65
66		plist = *cdpathp;
67		if (!plist)
68			use_cdpath = 0;
69		else if (use_cdpath) {
70			char *pend;
71
72			for (pend = plist; *pend && *pend != ':'; pend++)
73				;
74			plen = pend - plist;
75			*cdpathp = *pend ? ++pend : NULL;
76		}
77
78		if ((use_cdpath == 0 || !plen || plist[0] != '/') &&
79		    (cwd && *cwd)) {
80			len = strlen(cwd);
81			XcheckN(*xsp, xp, len);
82			memcpy(xp, cwd, len);
83			xp += len;
84			if (cwd[len - 1] != '/')
85				Xput(*xsp, xp, '/');
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 (plist[plen - 1] != '/')
93				Xput(*xsp, xp, '/');
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 = NULL;
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 */
112void
113simplify_path(char *path)
114{
115	char	*cur;
116	char	*t;
117	int	isrooted;
118	char	*very_start = path;
119	char	*start;
120
121	if (!*path)
122		return;
123
124	if ((isrooted = (path[0] == '/')))
125		very_start++;
126
127	/* Before			After
128	 *  /foo/			/foo
129	 *  /foo/../../bar		/bar
130	 *  /foo/./blah/..		/foo
131	 *  .				.
132	 *  ..				..
133	 *  ./foo			foo
134	 *  foo/../../../bar		../../bar
135	 */
136
137	for (cur = t = start = very_start; ; ) {
138		/* treat multiple '/'s as one '/' */
139		while (*t == '/')
140			t++;
141
142		if (*t == '\0') {
143			if (cur == path)
144				/* convert empty path to dot */
145				*cur++ = '.';
146			*cur = '\0';
147			break;
148		}
149
150		if (t[0] == '.') {
151			if (!t[1] || t[1] == '/') {
152				t += 1;
153				continue;
154			} else if (t[1] == '.' && (!t[2] || t[2] == '/')) {
155				if (!isrooted && cur == start) {
156					if (cur != very_start)
157						*cur++ = '/';
158					*cur++ = '.';
159					*cur++ = '.';
160					start = cur;
161				} else if (cur != start)
162					while (--cur > start && *cur != '/')
163						;
164				t += 2;
165				continue;
166			}
167		}
168
169		if (cur != very_start)
170			*cur++ = '/';
171
172		/* find/copy next component of pathname */
173		while (*t && *t != '/')
174			*cur++ = *t++;
175	}
176}
177
178
179void
180set_current_wd(char *path)
181{
182	int len;
183	char *p = path;
184
185	if (!p && !(p = ksh_get_wd(NULL, 0)))
186		p = null;
187
188	len = strlen(p) + 1;
189
190	if (len > current_wd_size)
191		current_wd = aresize(current_wd, current_wd_size = len, APERM);
192	memcpy(current_wd, p, len);
193	if (p != path && p != null)
194		afree(p, ATEMP);
195}
196
197char *
198get_phys_path(const char *path)
199{
200	XString xs;
201	char *xp;
202
203	Xinit(xs, xp, strlen(path) + 1, ATEMP);
204
205	xp = do_phys_path(&xs, xp, path);
206
207	if (!xp)
208		return NULL;
209
210	if (Xlength(xs, xp) == 0)
211		Xput(xs, xp, '/');
212	Xput(xs, xp, '\0');
213
214	return Xclose(xs, xp);
215}
216
217static char *
218do_phys_path(XString *xsp, char *xp, const char *path)
219{
220	const char *p, *q;
221	int len, llen;
222	int savepos;
223	char lbuf[PATH_MAX];
224
225	Xcheck(*xsp, xp);
226	for (p = path; p; p = q) {
227		while (*p == '/')
228			p++;
229		if (!*p)
230			break;
231		len = (q = strchr(p, '/')) ? (size_t)(q - p) : strlen(p);
232		if (len == 1 && p[0] == '.')
233			continue;
234		if (len == 2 && p[0] == '.' && p[1] == '.') {
235			while (xp > Xstring(*xsp, xp)) {
236				xp--;
237				if (*xp == '/')
238					break;
239			}
240			continue;
241		}
242
243		savepos = Xsavepos(*xsp, xp);
244		Xput(*xsp, xp, '/');
245		XcheckN(*xsp, xp, len + 1);
246		memcpy(xp, p, len);
247		xp += len;
248		*xp = '\0';
249
250		llen = readlink(Xstring(*xsp, xp), lbuf, sizeof(lbuf) - 1);
251		if (llen == -1) {
252			/* EINVAL means it wasn't a symlink... */
253			if (errno != EINVAL)
254				return NULL;
255			continue;
256		}
257		lbuf[llen] = '\0';
258
259		/* If absolute path, start from scratch.. */
260		xp = lbuf[0] == '/' ? Xstring(*xsp, xp) :
261		    Xrestpos(*xsp, xp, savepos);
262		if (!(xp = do_phys_path(xsp, xp, lbuf)))
263			return NULL;
264	}
265	return xp;
266}