]> git.ipfire.org Git - thirdparty/bash.git/blame - lib/sh/pathphys.c
bash-4.3-beta overlay
[thirdparty/bash.git] / lib / sh / pathphys.c
CommitLineData
2e4498b3 1/* pathphys.c -- return pathname with all symlinks expanded. */
28ef6c31
JA
2
3/* Copyright (C) 2000 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
2e4498b3
CR
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
28ef6c31 11
2e4498b3
CR
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
28ef6c31 16
2e4498b3
CR
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19*/
28ef6c31 20
f73dda09 21#include <config.h>
28ef6c31 22
f73dda09 23#include <bashtypes.h>
fd58d46e 24#if defined (HAVE_SYS_PARAM_H)
28ef6c31
JA
25# include <sys/param.h>
26#endif
f73dda09 27#include <posixstat.h>
28ef6c31
JA
28
29#if defined (HAVE_UNISTD_H)
30# include <unistd.h>
31#endif
32
f73dda09
JA
33#include <filecntl.h>
34#include <bashansi.h>
28ef6c31 35#include <stdio.h>
f73dda09 36#include <chartypes.h>
28ef6c31
JA
37#include <errno.h>
38
39#include "shell.h"
40
28ef6c31
JA
41#if !defined (MAXSYMLINKS)
42# define MAXSYMLINKS 32
43#endif
44
45#if !defined (errno)
46extern int errno;
47#endif /* !errno */
48
49extern char *get_working_directory __P((char *));
50
51static int
52_path_readlink (path, buf, bufsiz)
53 char *path;
54 char *buf;
55 int bufsiz;
56{
57#ifdef HAVE_READLINK
58 return readlink (path, buf, bufsiz);
59#else
60 errno = EINVAL;
61 return -1;
62#endif
63}
64
65/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
66
67#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
68
69/*
70 * Return PATH with all symlinks expanded in newly-allocated memory.
d3a24ed2 71 * This always gets an absolute pathname.
28ef6c31
JA
72 */
73
74char *
75sh_physpath (path, flags)
76 char *path;
77 int flags;
78{
79 char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1];
80 char *result, *p, *q, *qsave, *qbase, *workpath;
81 int double_slash_path, linklen, nlink;
82
d3a24ed2
CR
83 linklen = strlen (path);
84
85#if 0
5e13499c 86 /* First sanity check -- punt immediately if the name is too long. */
d3a24ed2
CR
87 if (linklen >= PATH_MAX)
88 return (savestring (path));
89#endif
90
28ef6c31 91 nlink = 0;
f73dda09 92 q = result = (char *)xmalloc (PATH_MAX + 1);
28ef6c31 93
d3a24ed2
CR
94 /* Even if we get something longer than PATH_MAX, we might be able to
95 shorten it, so we try. */
96 if (linklen >= PATH_MAX)
97 workpath = savestring (path);
98 else
99 {
100 workpath = (char *)xmalloc (PATH_MAX + 1);
101 strcpy (workpath, path);
102 }
28ef6c31
JA
103
104 /* This always gets an absolute pathname. */
105
106 /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any
107 leading `x:' (dos drive name). */
108#if defined (__CYGWIN__)
f73dda09 109 qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
28ef6c31
JA
110#else
111 qbase = workpath + 1;
112#endif
113 double_slash_path = DOUBLE_SLASH (workpath);
114 qbase += double_slash_path;
115
116 for (p = workpath; p < qbase; )
117 *q++ = *p++;
118 qbase = q;
119
120 /*
121 * invariants:
122 * qbase points to the portion of the result path we want to modify
123 * p points at beginning of path element we're considering.
124 * q points just past the last path element we wrote (no slash).
125 *
126 * XXX -- need to fix error checking for too-long pathnames
127 */
128
129 while (*p)
130 {
131 if (ISDIRSEP(p[0])) /* null element */
132 p++;
133 else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
134 p += 1; /* don't count the separator in case it is nul */
135 else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
136 {
137 p += 2; /* skip `..' */
138 if (q > qbase)
139 {
140 while (--q > qbase && ISDIRSEP(*q) == 0)
141 ;
142 }
143 }
144 else /* real path element */
145 {
146 /* add separator if not at start of work portion of result */
147 qsave = q;
148 if (q != qbase)
149 *q++ = DIRSEP;
150 while (*p && (ISDIRSEP(*p) == 0))
d3a24ed2
CR
151 {
152 if (q - result >= PATH_MAX)
153 {
154#ifdef ENAMETOOLONG
155 errno = ENAMETOOLONG;
156#else
157 errno = EINVAL;
158#endif
159 goto error;
160 }
161
162 *q++ = *p++;
163 }
28ef6c31
JA
164
165 *q = '\0';
166
167 linklen = _path_readlink (result, linkbuf, PATH_MAX);
168 if (linklen < 0) /* if errno == EINVAL, it's not a symlink */
169 {
170 if (errno != EINVAL)
171 goto error;
172 continue;
173 }
174
175 /* It's a symlink, and the value is in LINKBUF. */
176 nlink++;
177 if (nlink > MAXSYMLINKS)
178 {
179#ifdef ELOOP
180 errno = ELOOP;
5e13499c
CR
181#else
182 errno = EINVAL;
28ef6c31
JA
183#endif
184error:
185 free (result);
186 free (workpath);
187 return ((char *)NULL);
188 }
189
190 linkbuf[linklen] = '\0';
191
d3a24ed2
CR
192 /* If the new path length would overrun PATH_MAX, punt now. */
193 if ((strlen (p) + linklen + 2) >= PATH_MAX)
194 {
195#ifdef ENAMETOOLONG
196 errno = ENAMETOOLONG;
197#else
198 errno = EINVAL;
199#endif
200 goto error;
201 }
202
28ef6c31
JA
203 /* Form the new pathname by copying the link value to a temporary
204 buffer and appending the rest of `workpath'. Reset p to point
205 to the start of the rest of the path. If the link value is an
206 absolute pathname, reset p, q, and qbase. If not, reset p
207 and q. */
208 strcpy (tbuf, linkbuf);
209 tbuf[linklen] = '/';
210 strcpy (tbuf + linklen, p);
211 strcpy (workpath, tbuf);
212
213 if (ABSPATH(linkbuf))
214 {
215 q = result;
216 /* Duplicating some code here... */
217#if defined (__CYGWIN__)
f73dda09 218 qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
28ef6c31
JA
219#else
220 qbase = workpath + 1;
221#endif
222 double_slash_path = DOUBLE_SLASH (workpath);
223 qbase += double_slash_path;
224
225 for (p = workpath; p < qbase; )
226 *q++ = *p++;
227 qbase = q;
228 }
229 else
230 {
231 p = workpath;
232 q = qsave;
233 }
234 }
235 }
236
237 *q = '\0';
238 free (workpath);
239
240 /* If the result starts with `//', but the original path does not, we
241 can turn the // into /. Because of how we set `qbase', this should never
242 be true, but it's a sanity check. */
243 if (DOUBLE_SLASH(result) && double_slash_path == 0)
244 {
245 if (result[2] == '\0') /* short-circuit for bare `//' */
246 result[1] = '\0';
247 else
248 strcpy (result, result + 1);
249 }
250
251 return (result);
252}
253
254char *
255sh_realpath (pathname, resolved)
256 const char *pathname;
257 char *resolved;
258{
259 char *tdir, *wd;
260
261 if (pathname == 0 || *pathname == '\0')
262 {
263 errno = (pathname == 0) ? EINVAL : ENOENT;
264 return ((char *)NULL);
265 }
266
267 if (ABSPATH (pathname) == 0)
268 {
269 wd = get_working_directory ("sh_realpath");
270 if (wd == 0)
271 return ((char *)NULL);
1442f67c 272 tdir = sh_makepath (wd, (char *)pathname, 0);
28ef6c31
JA
273 free (wd);
274 }
275 else
276 tdir = savestring (pathname);
277
278 wd = sh_physpath (tdir, 0);
279 free (tdir);
280
281 if (resolved == 0)
282 return (wd);
283
284 if (wd)
285 {
286 strncpy (resolved, wd, PATH_MAX - 1);
287 resolved[PATH_MAX - 1] = '\0';
ff247e74 288 free (wd);
28ef6c31
JA
289 return resolved;
290 }
291 else
292 {
293 resolved[0] = '\0';
294 return wd;
295 }
296}