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 getcwd(new_path, PATH_MAX - 1);
84 new_path += strlen(new_path);
85 if (!IS_DIRECTORY_SEP(new_path[-1]))
86 *new_path++ = DIRECTORY_SEP;
88 /* Copy first directory sep. */
89 strncpy(new_path, path, abslen);
94 /* Expand each slash-separated pathname component. */
95 while (*path != '\0') {
96 /* Ignore stray "/". */
97 if (IS_DIRECTORY_SEP(*path)) {
104 if (path[1] == '\0' || IS_DIRECTORY_SEP(path[1])) {
110 if (path[1] == '.' &&
111 (path[2] == '\0' || IS_DIRECTORY_SEP(path[2]))) {
114 /* Ignore ".." at root. */
115 if (new_path == ABS_START(resolved_path))
118 /* Handle ".." by backing up. */
120 while (!IS_DIRECTORY_SEP(new_path[-1]))
126 /* Safely copy the next pathname component. */
127 while (*path != '\0' && !IS_DIRECTORY_SEP(*path)) {
128 if (path > max_path) {
129 errno = ENAMETOOLONG;
132 *new_path++ = *path++;
135 #if defined (S_IFLNK)
136 /* See if latest pathname component is a symlink. */
138 n = system_readlink(resolved_path, link_path, PATH_MAX - 1);
141 /* EINVAL means the file exists but isn't a symlink. */
145 /* Protect against infinite loops. */
146 if (readlinks++ > MAX_READLINKS) {
151 /* Note: readlink doesn't add the null byte. */
154 if (ABS_LENGTH(link_path) > 0)
155 /* Start over for an absolute symlink. */
157 resolved_path + ABS_LENGTH(link_path) - 1;
159 /* Otherwise back up over this component. */
160 for (--new_path; !IS_DIRECTORY_SEP(*new_path);
162 assert(new_path > resolved_path);
164 /* Safe sex check. */
165 if (strlen(path) + n >= PATH_MAX) {
166 errno = ENAMETOOLONG;
170 /* Insert symlink contents into path. */
171 strcat(link_path, path);
172 strcpy(copy_path, link_path);
176 *new_path++ = DIRECTORY_SEP;
179 /* Delete trailing slash but don't whomp a lone slash. */
180 if (new_path != ABS_START(resolved_path)
181 && IS_DIRECTORY_SEP(new_path[-1]))
184 /* Make sure it's null terminated. */
187 return resolved_path;