GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/path.c Lines: 64 115 55.7 %
Date: 2017-11-13 Branches: 54 126 42.9 %

Line Branch Exec Source
1
/*	$OpenBSD: path.c,v 1.19 2017/09/03 11:52:01 jca 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
20
static 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
 */
37
int
38
make_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
46462
	char	*xp = Xstring(*xsp, xp);
49
50
23231
	if (!file)
51
		file = null;
52
53
23231
	if (file[0] == '/') {
54
23203
		*phys_pathp = 0;
55
		use_cdpath = 0;
56
23203
	} else {
57
28
		if (file[0] == '.') {
58
11
			char c = file[1];
59
60
11
			if (c == '.')
61
11
				c = file[2];
62

22
			if (c == '/' || c == '\0')
63
11
				use_cdpath = 0;
64
11
		}
65
66
28
		plist = *cdpathp;
67
28
		if (!plist)
68
			use_cdpath = 0;
69
28
		else if (use_cdpath) {
70
			char *pend;
71
72

34
			for (pend = plist; *pend && *pend != ':'; pend++)
73
				;
74
17
			plen = pend - plist;
75
34
			*cdpathp = *pend ? ++pend : NULL;
76
17
		}
77
78

56
		if ((use_cdpath == 0 || !plen || plist[0] != '/') &&
79
56
		    (cwd && *cwd)) {
80
28
			len = strlen(cwd);
81
28
			XcheckN(*xsp, xp, len);
82
28
			memcpy(xp, cwd, len);
83
28
			xp += len;
84
28
			if (cwd[len - 1] != '/')
85
28
				Xput(*xsp, xp, '/');
86
		}
87
28
		*phys_pathp = Xlength(*xsp, xp);
88
28
		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
23231
	len = strlen(file) + 1;
99
23231
	XcheckN(*xsp, xp, len);
100
23231
	memcpy(xp, file, len);
101
102
23231
	if (!use_cdpath)
103
23214
		*cdpathp = NULL;
104
105
23231
	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(char *path)
114
{
115
	char	*cur;
116
	char	*t;
117
	int	isrooted;
118
	char	*very_start = path;
119
	char	*start;
120
121
255988
	if (!*path)
122
		return;
123
124
127994
	if ((isrooted = (path[0] == '/')))
125
127994
		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
807502
	for (cur = t = start = very_start; ; ) {
138
		/* treat multiple '/'s as one '/' */
139
2718786
		while (*t == '/')
140
551704
			t++;
141
142
807689
		if (*t == '\0') {
143
127994
			if (cur == path)
144
				/* convert empty path to dot */
145
				*cur++ = '.';
146
127994
			*cur = '\0';
147
			break;
148
		}
149
150
679695
		if (t[0] == '.') {
151

374
			if (!t[1] || t[1] == '/') {
152
				t += 1;
153
				continue;
154

550
			} else if (t[1] == '.' && (!t[2] || t[2] == '/')) {
155

187
				if (!isrooted && cur == start) {
156
					if (cur != very_start)
157
						*cur++ = '/';
158
					*cur++ = '.';
159
					*cur++ = '.';
160
					start = cur;
161
187
				} else if (cur != start)
162

3636
					while (--cur > start && *cur != '/')
163
						;
164
				t += 2;
165
187
				continue;
166
			}
167
		}
168
169
679508
		if (cur != very_start)
170
551514
			*cur++ = '/';
171
172
		/* find/copy next component of pathname */
173

11859492
		while (*t && *t != '/')
174
3316316
			*cur++ = *t++;
175
	}
176
255988
}
177
178
179
void
180
set_current_wd(char *path)
181
{
182
	int len;
183
	char *p = path;
184
185
257994
	if (!p && !(p = ksh_get_wd(NULL, 0)))
186
		p = null;
187
188
127994
	len = strlen(p) + 1;
189
190
127994
	if (len > current_wd_size)
191
104836
		current_wd = aresize(current_wd, current_wd_size = len, APERM);
192
127994
	memcpy(current_wd, p, len);
193
127994
	if (p != path && p != null)
194
2006
		afree(p, ATEMP);
195
127994
}
196
197
char *
198
get_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
217
static char *
218
do_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, '/')) ? 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 < 0) {
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
}