import openbsd kill(1)
[unleashed.git] / bin / ksh / path.c
blob5da57a3f8290c989e576256c0ba6fa76c6c026ae
1 /* $OpenBSD: path.c,v 1.22 2018/01/06 16:28:58 millert Exp $ */
3 #include <sys/stat.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <unistd.h>
9 #include "sh.h"
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.
17 * Larry Bouzane (larry@cs.mun.ca)
20 static char *do_phys_path(XString *, char *, const char *);
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.
37 int
38 make_path(const char *cwd, const char *file,
39 char **cdpathp, /* & of : separated list */
40 XString *xsp,
41 int *phys_pathp)
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);
50 if (!file)
51 file = null;
53 if (file[0] == '/') {
54 *phys_pathp = 0;
55 use_cdpath = 0;
56 } else {
57 if (file[0] == '.') {
58 char c = file[1];
60 if (c == '.')
61 c = file[2];
62 if (c == '/' || c == '\0')
63 use_cdpath = 0;
66 plist = *cdpathp;
67 if (!plist)
68 use_cdpath = 0;
69 else if (use_cdpath) {
70 char *pend;
72 for (pend = plist; *pend && *pend != ':'; pend++)
74 plen = pend - plist;
75 *cdpathp = *pend ? ++pend : NULL;
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, '/');
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;
98 len = strlen(file) + 1;
99 XcheckN(*xsp, xp, len);
100 memcpy(xp, file, len);
102 if (!use_cdpath)
103 *cdpathp = NULL;
105 return rval;
109 * Simplify pathnames containing "." and ".." entries.
110 * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
112 void
113 simplify_path(char *path)
115 char *cur;
116 char *t;
117 int isrooted;
118 char *very_start = path;
119 char *start;
121 if (!*path)
122 return;
124 if ((isrooted = (path[0] == '/')))
125 very_start++;
127 /* Before After
128 * /foo/ /foo
129 * /foo/../../bar /bar
130 * /foo/./blah/.. /foo
131 * . .
132 * .. ..
133 * ./foo foo
134 * foo/../../../bar ../../bar
137 for (cur = t = start = very_start; ; ) {
138 /* treat multiple '/'s as one '/' */
139 while (*t == '/')
140 t++;
142 if (*t == '\0') {
143 if (cur == path)
144 /* convert empty path to dot */
145 *cur++ = '.';
146 *cur = '\0';
147 break;
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 != '/')
164 t += 2;
165 continue;
169 if (cur != very_start)
170 *cur++ = '/';
172 /* find/copy next component of pathname */
173 while (*t && *t != '/')
174 *cur++ = *t++;
179 void
180 set_current_wd(char *path)
182 int len;
183 char *p = path;
185 if (!p && !(p = ksh_get_wd(NULL, 0)))
186 p = null;
188 len = strlen(p) + 1;
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);
197 char *
198 get_phys_path(const char *path)
200 XString xs;
201 char *xp;
203 Xinit(xs, xp, strlen(path) + 1, ATEMP);
205 xp = do_phys_path(&xs, xp, path);
207 if (!xp)
208 return NULL;
210 if (Xlength(xs, xp) == 0)
211 Xput(xs, xp, '/');
212 Xput(xs, xp, '\0');
214 return Xclose(xs, xp);
217 static char *
218 do_phys_path(XString *xsp, char *xp, const char *path)
220 const char *p, *q;
221 int len, llen;
222 int savepos;
223 char lbuf[PATH_MAX];
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;
240 continue;
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';
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;
257 lbuf[llen] = '\0';
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;
265 return xp;