GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/ksh/path.c Lines: 65 115 56.5 %
Date: 2017-11-07 Branches: 57 126 45.2 %

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
92460
	char	*xp = Xstring(*xsp, xp);
49
50
46230
	if (!file)
51
		file = null;
52
53
46230
	if (file[0] == '/') {
54
46042
		*phys_pathp = 0;
55
		use_cdpath = 0;
56
46042
	} else {
57
188
		if (file[0] == '.') {
58
53
			char c = file[1];
59
60
53
			if (c == '.')
61
51
				c = file[2];
62

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

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

376
		if ((use_cdpath == 0 || !plen || plist[0] != '/') &&
79
376
		    (cwd && *cwd)) {
80
188
			len = strlen(cwd);
81
188
			XcheckN(*xsp, xp, len);
82
188
			memcpy(xp, cwd, len);
83
188
			xp += len;
84
188
			if (cwd[len - 1] != '/')
85
188
				Xput(*xsp, xp, '/');
86
		}
87
188
		*phys_pathp = Xlength(*xsp, xp);
88
188
		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
46230
	len = strlen(file) + 1;
99
46230
	XcheckN(*xsp, xp, len);
100
46230
	memcpy(xp, file, len);
101
102
46230
	if (!use_cdpath)
103
46095
		*cdpathp = NULL;
104
105
46230
	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
528000
	if (!*path)
122
		return;
123
124
264000
	if ((isrooted = (path[0] == '/')))
125
264000
		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
264000
	for (cur = t = start = very_start; ; ) {
138
		/* treat multiple '/'s as one '/' */
139
5579422
		while (*t == '/')
140
1130870
			t++;
141
142
1658841
		if (*t == '\0') {
143
264000
			if (cur == path)
144
				/* convert empty path to dot */
145
				*cur++ = '.';
146
264000
			*cur = '\0';
147
			break;
148
		}
149
150
1394841
		if (t[0] == '.') {
151

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

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

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

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

22784389
		while (*t && *t != '/')
174
6753351
			*cur++ = *t++;
175
	}
176
528000
}
177
178
179
void
180
set_current_wd(char *path)
181
{
182
	int len;
183
	char *p = path;
184
185
536724
	if (!p && !(p = ksh_get_wd(NULL, 0)))
186
		p = null;
187
188
263999
	len = strlen(p) + 1;
189
190
263999
	if (len > current_wd_size)
191
218099
		current_wd = aresize(current_wd, current_wd_size = len, APERM);
192
263999
	memcpy(current_wd, p, len);
193
263999
	if (p != path && p != null)
194
8726
		afree(p, ATEMP);
195
263999
}
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
}