2 * realpath.c -- canonicalize pathname by removing symlinks
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
6 This file is part of SXEmacs
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 /* Synched up with: Not in FSF. */
32 #if defined (HAVE_SYS_PARAM_H)
33 #include <sys/param.h>
36 #include <sys/stat.h> /* for S_IFLNK */
38 /* First char after start of absolute filename. */
39 #define ABS_START(name) (name + ABS_LENGTH (name))
41 #define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
42 #define system_readlink readlink
45 #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD)
47 #define getcwd(buffer, len) getwd (buffer)
51 # if defined (_POSIX_PATH_MAX)
52 # define PATH_MAX _POSIX_PATH_MAX
53 # elif defined (MAXPATHLEN)
54 # define PATH_MAX MAXPATHLEN
56 # define PATH_MAX 1024
60 #define MAX_READLINKS 32
62 char *xrealpath(const char *path, char *restrict resolved_path);
63 char *xrealpath(const char *path, char *restrict resolved_path)
65 char copy_path[PATH_MAX];
66 char *new_path = resolved_path;
70 char link_path[PATH_MAX];
72 int abslen = ABS_LENGTH(path);
75 /* Make a copy of the source path since we may need to modify it. */
76 strncpy(copy_path, path, sizeof(copy_path)-1);
77 copy_path[sizeof(copy_path)-1]='\0';
79 max_path = copy_path + PATH_MAX - 2;
81 /* If it's a relative pathname use getcwd for starters. */
83 void *ok = getcwd(new_path, PATH_MAX - 1);
85 new_path += strlen(new_path);
86 if (!IS_DIRECTORY_SEP(new_path[-1]))
87 *new_path++ = DIRECTORY_SEP;
92 /* Copy first directory sep. */
93 strncpy(new_path, path, abslen);
98 /* Expand each slash-separated pathname component. */
99 while (*path != '\0') {
100 /* Ignore stray "/". */
101 if (IS_DIRECTORY_SEP(*path)) {
108 if (path[1] == '\0' || IS_DIRECTORY_SEP(path[1])) {
114 if (path[1] == '.' &&
115 (path[2] == '\0' || IS_DIRECTORY_SEP(path[2]))) {
118 /* Ignore ".." at root. */
119 if (new_path == ABS_START(resolved_path))
122 /* Handle ".." by backing up. */
124 while (!IS_DIRECTORY_SEP(new_path[-1]))
130 /* Safely copy the next pathname component. */
131 while (*path != '\0' && !IS_DIRECTORY_SEP(*path)) {
132 if (path > max_path) {
133 errno = ENAMETOOLONG;
136 *new_path++ = *path++;
139 #if defined (S_IFLNK)
140 /* See if latest pathname component is a symlink. */
142 n = system_readlink(resolved_path, link_path, PATH_MAX - 1);
145 /* EINVAL means the file exists but isn't a symlink. */
149 /* Protect against infinite loops. */
150 if (readlinks++ > MAX_READLINKS) {
155 /* Note: readlink doesn't add the null byte. */
158 if (ABS_LENGTH(link_path) > 0)
159 /* Start over for an absolute symlink. */
161 resolved_path + ABS_LENGTH(link_path) - 1;
163 /* Otherwise back up over this component. */
164 for (--new_path; !IS_DIRECTORY_SEP(*new_path);
166 assert(new_path > resolved_path);
168 /* Safe sex check. */
169 if (strlen(path) + n >= PATH_MAX) {
170 errno = ENAMETOOLONG;
174 /* Insert symlink contents into path. */
175 strcat(link_path, path);
176 strcpy(copy_path, link_path);
180 *new_path++ = DIRECTORY_SEP;
183 /* Delete trailing slash but don't whomp a lone slash. */
184 if (new_path != ABS_START(resolved_path)
185 && IS_DIRECTORY_SEP(new_path[-1]))
188 /* Make sure it's null terminated. */
191 return resolved_path;