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 it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with Bash; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
23 #include "bashtypes.h"
25 # include <sys/param.h>
27 #include "posixstat.h"
29 #if defined (HAVE_UNISTD_H)
42 #if !defined (MAXSYMLINKS)
43 # define MAXSYMLINKS 32
50 extern char *get_working_directory __P((char *));
53 _path_readlink (path, buf, bufsiz)
59 return readlink (path, buf, bufsiz);
66 /* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */
68 #define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')
71 * Return PATH with all symlinks expanded in newly-allocated memory.
72 * This always gets a full pathname.
76 sh_physpath (path, flags)
80 char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1];
81 char *result, *p, *q, *qsave, *qbase, *workpath;
82 int double_slash_path, linklen, nlink;
85 q = result = xmalloc (PATH_MAX + 1);
87 workpath = xmalloc (PATH_MAX + 1);
88 strcpy (workpath, path);
90 /* This always gets an absolute pathname. */
92 /* POSIX.2 says to leave a leading `//' alone. On cygwin, we skip over any
93 leading `x:' (dos drive name). */
94 #if defined (__CYGWIN__)
95 qbase = (isalpha(workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
99 double_slash_path = DOUBLE_SLASH (workpath);
100 qbase += double_slash_path;
102 for (p = workpath; p < qbase; )
108 * qbase points to the portion of the result path we want to modify
109 * p points at beginning of path element we're considering.
110 * q points just past the last path element we wrote (no slash).
112 * XXX -- need to fix error checking for too-long pathnames
117 if (ISDIRSEP(p[0])) /* null element */
119 else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
120 p += 1; /* don't count the separator in case it is nul */
121 else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
123 p += 2; /* skip `..' */
126 while (--q > qbase && ISDIRSEP(*q) == 0)
130 else /* real path element */
132 /* add separator if not at start of work portion of result */
136 while (*p && (ISDIRSEP(*p) == 0))
141 linklen = _path_readlink (result, linkbuf, PATH_MAX);
142 if (linklen < 0) /* if errno == EINVAL, it's not a symlink */
149 /* It's a symlink, and the value is in LINKBUF. */
151 if (nlink > MAXSYMLINKS)
159 return ((char *)NULL);
162 linkbuf[linklen] = '\0';
164 /* Form the new pathname by copying the link value to a temporary
165 buffer and appending the rest of `workpath'. Reset p to point
166 to the start of the rest of the path. If the link value is an
167 absolute pathname, reset p, q, and qbase. If not, reset p
169 strcpy (tbuf, linkbuf);
171 strcpy (tbuf + linklen, p);
172 strcpy (workpath, tbuf);
174 if (ABSPATH(linkbuf))
177 /* Duplicating some code here... */
178 #if defined (__CYGWIN__)
179 qbase = (isalpha(workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
181 qbase = workpath + 1;
183 double_slash_path = DOUBLE_SLASH (workpath);
184 qbase += double_slash_path;
186 for (p = workpath; p < qbase; )
201 /* If the result starts with `//', but the original path does not, we
202 can turn the // into /. Because of how we set `qbase', this should never
203 be true, but it's a sanity check. */
204 if (DOUBLE_SLASH(result) && double_slash_path == 0)
206 if (result[2] == '\0') /* short-circuit for bare `//' */
209 strcpy (result, result + 1);
216 sh_realpath (pathname, resolved)
217 const char *pathname;
222 if (pathname == 0 || *pathname == '\0')
224 errno = (pathname == 0) ? EINVAL : ENOENT;
225 return ((char *)NULL);
228 if (ABSPATH (pathname) == 0)
230 wd = get_working_directory ("sh_realpath");
232 return ((char *)NULL);
233 tdir = sh_makepath ((char *)pathname, wd, 0);
237 tdir = savestring (pathname);
239 wd = sh_physpath (tdir, 0);
247 strncpy (resolved, wd, PATH_MAX - 1);
248 resolved[PATH_MAX - 1] = '\0';