1 /* $OpenBSD: path.c,v 1.22 2018/01/06 16:28:58 millert Exp $ */
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
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.
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.
38 make_path(const char *cwd
, const char *file
,
39 char **cdpathp
, /* & of : separated list */
48 char *xp
= Xstring(*xsp
, xp
);
62 if (c
== '/' || c
== '\0')
69 else if (use_cdpath
) {
72 for (pend
= plist
; *pend
&& *pend
!= ':'; pend
++)
75 *cdpathp
= *pend
? ++pend
: NULL
;
78 if ((use_cdpath
== 0 || !plen
|| plist
[0] != '/') &&
81 XcheckN(*xsp
, xp
, len
);
84 if (cwd
[len
- 1] != '/')
87 *phys_pathp
= Xlength(*xsp
, xp
);
88 if (use_cdpath
&& plen
) {
89 XcheckN(*xsp
, xp
, plen
);
90 memcpy(xp
, plist
, plen
);
92 if (plist
[plen
- 1] != '/')
98 len
= strlen(file
) + 1;
99 XcheckN(*xsp
, xp
, len
);
100 memcpy(xp
, file
, len
);
109 * Simplify pathnames containing "." and ".." entries.
110 * ie, simplify_path("/a/b/c/./../d/..") returns "/a/b"
113 simplify_path(char *path
)
118 char *very_start
= path
;
124 if ((isrooted
= (path
[0] == '/')))
129 * /foo/../../bar /bar
130 * /foo/./blah/.. /foo
134 * foo/../../../bar ../../bar
137 for (cur
= t
= start
= very_start
; ; ) {
138 /* treat multiple '/'s as one '/' */
144 /* convert empty path to dot */
151 if (!t
[1] || t
[1] == '/') {
154 } else if (t
[1] == '.' && (!t
[2] || t
[2] == '/')) {
155 if (!isrooted
&& cur
== start
) {
156 if (cur
!= very_start
)
161 } else if (cur
!= start
)
162 while (--cur
> start
&& *cur
!= '/')
169 if (cur
!= very_start
)
172 /* find/copy next component of pathname */
173 while (*t
&& *t
!= '/')
180 set_current_wd(char *path
)
185 if (!p
&& !(p
= ksh_get_wd(NULL
, 0)))
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
)
198 get_phys_path(const char *path
)
203 Xinit(xs
, xp
, strlen(path
) + 1, ATEMP
);
205 xp
= do_phys_path(&xs
, xp
, path
);
210 if (Xlength(xs
, xp
) == 0)
214 return Xclose(xs
, xp
);
218 do_phys_path(XString
*xsp
, char *xp
, const char *path
)
226 for (p
= path
; p
; p
= q
) {
231 len
= (q
= strchr(p
, '/')) ? (size_t)(q
- p
) : strlen(p
);
232 if (len
== 1 && p
[0] == '.')
234 if (len
== 2 && p
[0] == '.' && p
[1] == '.') {
235 while (xp
> Xstring(*xsp
, xp
)) {
243 savepos
= Xsavepos(*xsp
, xp
);
245 XcheckN(*xsp
, xp
, len
+ 1);
250 llen
= readlink(Xstring(*xsp
, xp
), lbuf
, sizeof(lbuf
) - 1);
252 /* EINVAL means it wasn't a symlink... */
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
)))