]>
git.ipfire.org Git - thirdparty/bash.git/blob - lib/sh/pathphys.c
1 /* pathphys.c -- return pathname with all symlinks expanded. */
3 /* Copyright (C) 2000 Free Software Foundation, Inc.
5 This file is part of GNU Bash, the Bourne Again SHell.
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.
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.
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/>.
23 #include <bashtypes.h>
24 #if defined (HAVE_SYS_PARAM_H)
25 # include <sys/param.h>
27 #include <posixstat.h>
29 #if defined (HAVE_UNISTD_H)
36 #include <chartypes.h>
41 #if !defined (MAXSYMLINKS)
42 # define MAXSYMLINKS 32
49 extern char *get_working_directory
__P((char *));
52 _path_readlink (path
, buf
, bufsiz
)
58 return readlink (path
, buf
, bufsiz
);
65 /* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
67 #define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
70 * Return PATH with all symlinks expanded in newly-allocated memory.
71 * This always gets an absolute pathname.
75 sh_physpath (path
, flags
)
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
;
83 linklen
= strlen (path
);
86 /* First sanity check -- punt immediately if the name is too long. */
87 if (linklen
>= PATH_MAX
)
88 return (savestring (path
));
92 q
= result
= (char *)xmalloc (PATH_MAX
+ 1);
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
);
100 workpath
= (char *)xmalloc (PATH_MAX
+ 1);
101 strcpy (workpath
, path
);
104 /* This always gets an absolute pathname. */
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__)
109 qbase
= (ISALPHA((unsigned char)workpath
[0]) && workpath
[1] == ':') ? workpath
+ 3 : workpath
+ 1;
111 qbase
= workpath
+ 1;
113 double_slash_path
= DOUBLE_SLASH (workpath
);
114 qbase
+= double_slash_path
;
116 for (p
= workpath
; p
< qbase
; )
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).
126 * XXX -- need to fix error checking for too-long pathnames
131 if (ISDIRSEP(p
[0])) /* null element */
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 ../ */
137 p
+= 2; /* skip `..' */
140 while (--q
> qbase
&& ISDIRSEP(*q
) == 0)
144 else /* real path element */
146 /* add separator if not at start of work portion of result */
150 while (*p
&& (ISDIRSEP(*p
) == 0))
152 if (q
- result
>= PATH_MAX
)
155 errno
= ENAMETOOLONG
;
167 linklen
= _path_readlink (result
, linkbuf
, PATH_MAX
);
168 if (linklen
< 0) /* if errno == EINVAL, it's not a symlink */
175 /* It's a symlink, and the value is in LINKBUF. */
177 if (nlink
> MAXSYMLINKS
)
187 return ((char *)NULL
);
190 linkbuf
[linklen
] = '\0';
192 /* If the new path length would overrun PATH_MAX, punt now. */
193 if ((strlen (p
) + linklen
+ 2) >= PATH_MAX
)
196 errno
= ENAMETOOLONG
;
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
208 strcpy (tbuf
, linkbuf
);
210 strcpy (tbuf
+ linklen
, p
);
211 strcpy (workpath
, tbuf
);
213 if (ABSPATH(linkbuf
))
216 /* Duplicating some code here... */
217 #if defined (__CYGWIN__)
218 qbase
= (ISALPHA((unsigned char)workpath
[0]) && workpath
[1] == ':') ? workpath
+ 3 : workpath
+ 1;
220 qbase
= workpath
+ 1;
222 double_slash_path
= DOUBLE_SLASH (workpath
);
223 qbase
+= double_slash_path
;
225 for (p
= workpath
; p
< qbase
; )
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)
245 if (result
[2] == '\0') /* short-circuit for bare `//' */
248 strcpy (result
, result
+ 1);
255 sh_realpath (pathname
, resolved
)
256 const char *pathname
;
261 if (pathname
== 0 || *pathname
== '\0')
263 errno
= (pathname
== 0) ? EINVAL
: ENOENT
;
264 return ((char *)NULL
);
267 if (ABSPATH (pathname
) == 0)
269 wd
= get_working_directory ("sh_realpath");
271 return ((char *)NULL
);
272 tdir
= sh_makepath (wd
, (char *)pathname
, 0);
276 tdir
= savestring (pathname
);
278 wd
= sh_physpath (tdir
, 0);
286 strncpy (resolved
, wd
, PATH_MAX
- 1);
287 resolved
[PATH_MAX
- 1] = '\0';