Compiler & warning related updates/fixes from Nelson
[sxemacs] / src / realpath.c
1 /*
2  * realpath.c -- canonicalize pathname by removing symlinks
3  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4  *
5
6 This file is part of SXEmacs
7
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.
12
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.
17
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/>. */
20
21
22 /* Synched up with: Not in FSF. */
23
24 #include <config.h>
25 #include "lisp.h"
26 #include <errno.h>
27
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #if defined (HAVE_SYS_PARAM_H)
33 #include <sys/param.h>
34 #endif
35
36 #include <sys/stat.h>           /* for S_IFLNK */
37
38 /* First char after start of absolute filename. */
39 #define ABS_START(name) (name + ABS_LENGTH (name))
40
41 #define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
42 #define system_readlink readlink
43
44
45 #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD)
46 #undef getcwd
47 #define getcwd(buffer, len) getwd (buffer)
48 #endif
49
50 #ifndef PATH_MAX
51 # if defined (_POSIX_PATH_MAX)
52 #  define PATH_MAX _POSIX_PATH_MAX
53 # elif defined (MAXPATHLEN)
54 #  define PATH_MAX MAXPATHLEN
55 # else
56 #  define PATH_MAX 1024
57 # endif
58 #endif
59
60 #define MAX_READLINKS 32
61
62 char *xrealpath(const char *path, char *restrict resolved_path);
63 char *xrealpath(const char *path, char *restrict resolved_path)
64 {
65         char copy_path[PATH_MAX];
66         char *new_path = resolved_path;
67         char *max_path;
68 #if defined (S_IFLNK)
69         int readlinks = 0;
70         char link_path[PATH_MAX];
71         int n;
72         int abslen = ABS_LENGTH(path);
73 #endif
74
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';
78         path = copy_path;
79         max_path = copy_path + PATH_MAX - 2;
80
81         /* If it's a relative pathname use getcwd for starters. */
82         if (abslen == 0) {
83                 void *ok = getcwd(new_path, PATH_MAX - 1);
84                 if (ok) {
85                         new_path += strlen(new_path);
86                         if (!IS_DIRECTORY_SEP(new_path[-1]))
87                                 *new_path++ = DIRECTORY_SEP;
88                 } else {
89                         return NULL;
90                 }
91         } else {
92                 /* Copy first directory sep. */
93                 strncpy(new_path, path, abslen);
94                 new_path += abslen;
95                 path += abslen;
96         }
97
98         /* Expand each slash-separated pathname component. */
99         while (*path != '\0') {
100                 /* Ignore stray "/". */
101                 if (IS_DIRECTORY_SEP(*path)) {
102                         path++;
103                         continue;
104                 }
105
106                 if (*path == '.') {
107                         /* Ignore ".". */
108                         if (path[1] == '\0' || IS_DIRECTORY_SEP(path[1])) {
109                                 path++;
110                                 continue;
111                         }
112
113                         /* Handle ".." */
114                         if (path[1] == '.' &&
115                             (path[2] == '\0' || IS_DIRECTORY_SEP(path[2]))) {
116                                 path += 2;
117
118                                 /* Ignore ".." at root. */
119                                 if (new_path == ABS_START(resolved_path))
120                                         continue;
121
122                                 /* Handle ".." by backing up. */
123                                 --new_path;
124                                 while (!IS_DIRECTORY_SEP(new_path[-1]))
125                                         --new_path;
126                                 continue;
127                         }
128                 }
129
130                 /* Safely copy the next pathname component. */
131                 while (*path != '\0' && !IS_DIRECTORY_SEP(*path)) {
132                         if (path > max_path) {
133                                 errno = ENAMETOOLONG;
134                                 return NULL;
135                         }
136                         *new_path++ = *path++;
137                 }
138
139 #if defined (S_IFLNK)
140                 /* See if latest pathname component is a symlink. */
141                 *new_path = '\0';
142                 n = system_readlink(resolved_path, link_path, PATH_MAX - 1);
143
144                 if (n < 0) {
145                         /* EINVAL means the file exists but isn't a symlink. */
146                         if (errno != EINVAL)
147                                 return NULL;
148                 } else {
149                         /* Protect against infinite loops. */
150                         if (readlinks++ > MAX_READLINKS) {
151                                 errno = ELOOP;
152                                 return NULL;
153                         }
154
155                         /* Note: readlink doesn't add the null byte. */
156                         link_path[n] = '\0';
157
158                         if (ABS_LENGTH(link_path) > 0)
159                                 /* Start over for an absolute symlink. */
160                                 new_path =
161                                     resolved_path + ABS_LENGTH(link_path) - 1;
162                         else
163                                 /* Otherwise back up over this component. */
164                                 for (--new_path; !IS_DIRECTORY_SEP(*new_path);
165                                      --new_path)
166                                         assert(new_path > resolved_path);
167
168                         /* Safe sex check. */
169                         if (strlen(path) + n >= PATH_MAX) {
170                                 errno = ENAMETOOLONG;
171                                 return NULL;
172                         }
173
174                         /* Insert symlink contents into path. */
175                         strcat(link_path, path);
176                         strcpy(copy_path, link_path);
177                         path = copy_path;
178                 }
179 #endif                          /* S_IFLNK */
180                 *new_path++ = DIRECTORY_SEP;
181         }
182
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]))
186                 new_path--;
187
188         /* Make sure it's null terminated. */
189         *new_path = '\0';
190
191         return resolved_path;
192 }