Mercurial > hg > xemacs-beta
comparison src/realpath.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 8de8e3f6228a |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
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 XEmacs. | |
7 | |
8 XEmacs is free software; you can redistribute it and/or modify it | |
9 under the terms of the GNU General Public License as published by the | |
10 Free Software Foundation; either version 2, or (at your option) any | |
11 later version. | |
12 | |
13 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with XEmacs; see the file COPYING. If not, write to | |
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
21 Boston, MA 02111-1307, USA. */ | |
22 | |
23 /* Synched up with: Not in FSF. */ | |
24 | |
25 #include <config.h> | |
26 | |
27 #include <sys/types.h> | |
28 #include <stdio.h> | |
29 #include <string.h> | |
30 #include <errno.h> | |
31 #ifdef HAVE_UNISTD_H | |
32 #include <unistd.h> | |
33 #endif | |
34 #ifdef _POSIX_VERSION | |
35 #include <limits.h> /* for PATH_MAX */ | |
36 #else | |
37 #include <sys/param.h> /* for MAXPATHLEN */ | |
38 #endif | |
39 | |
40 #ifdef WINDOWSNT | |
41 #include <direct.h> | |
42 #endif | |
43 | |
44 #include <sys/stat.h> /* for S_IFLNK */ | |
45 | |
46 #ifndef PATH_MAX | |
47 #ifdef _POSIX_VERSION | |
48 #define PATH_MAX _POSIX_PATH_MAX | |
49 #else | |
50 #ifdef MAXPATHLEN | |
51 #define PATH_MAX MAXPATHLEN | |
52 #else | |
53 #define PATH_MAX 1024 | |
54 #endif | |
55 #endif | |
56 #endif | |
57 | |
58 #define MAX_READLINKS 32 | |
59 | |
60 char * | |
61 xrealpath (const char *path, char resolved_path []) | |
62 { | |
63 char copy_path[PATH_MAX]; | |
64 char *new_path = resolved_path; | |
65 char *max_path; | |
66 #ifdef S_IFLNK | |
67 int readlinks = 0; | |
68 char link_path[PATH_MAX]; | |
69 int n; | |
70 #endif | |
71 | |
72 /* Make a copy of the source path since we may need to modify it. */ | |
73 strcpy(copy_path, path); | |
74 path = copy_path; | |
75 max_path = copy_path + PATH_MAX - 2; | |
76 #ifdef WINDOWSNT | |
77 /* | |
78 ** In NT we have two different cases: (1) the path name begins | |
79 ** with a drive letter, e.g., "C:"; and (2) the path name begins | |
80 ** with just a slash, which roots to the current drive. In the | |
81 ** first case we are going to leave things alone, in the second | |
82 ** case we will prepend the drive letter to the given path. | |
83 ** Note: So far in testing, I'm only seeing case #1, even though | |
84 ** I've tried to get the other cases to happen. | |
85 ** August Hill, 31 Aug 1997. | |
86 ** | |
87 ** Check for a driver letter...C:/... | |
88 */ | |
89 if (*(path + 1) == ':') | |
90 { | |
91 strncpy(new_path, path, 3); | |
92 new_path += 3; | |
93 path += 3; | |
94 } | |
95 | |
96 /* | |
97 ** No drive letter, but a beginning slash? Prepend the drive | |
98 ** letter... | |
99 */ | |
100 else if (*path == '/') | |
101 { | |
102 getcwd(new_path, PATH_MAX - 1); | |
103 new_path += 3; | |
104 path++; | |
105 } | |
106 | |
107 /* | |
108 ** Just a path name, prepend the current directory | |
109 */ | |
110 else | |
111 { | |
112 getcwd(new_path, PATH_MAX - 1); | |
113 new_path += strlen(new_path); | |
114 if (new_path[-1] != '/') | |
115 *new_path++ = '/'; | |
116 } | |
117 | |
118 #else | |
119 /* If it's a relative pathname use getwd for starters. */ | |
120 if (*path != '/') | |
121 { | |
122 #ifdef HAVE_GETCWD | |
123 getcwd(new_path, PATH_MAX - 1); | |
124 #else | |
125 getwd(new_path); | |
126 #endif | |
127 new_path += strlen(new_path); | |
128 if (new_path[-1] != '/') | |
129 *new_path++ = '/'; | |
130 } | |
131 else | |
132 { | |
133 *new_path++ = '/'; | |
134 path++; | |
135 } | |
136 #endif | |
137 /* Expand each slash-separated pathname component. */ | |
138 while (*path != '\0') | |
139 { | |
140 /* Ignore stray "/". */ | |
141 if (*path == '/') | |
142 { | |
143 path++; | |
144 continue; | |
145 } | |
146 | |
147 if (*path == '.') | |
148 { | |
149 /* Ignore ".". */ | |
150 if (path[1] == '\0' || path[1] == '/') | |
151 { | |
152 path++; | |
153 continue; | |
154 } | |
155 | |
156 if (path[1] == '.') | |
157 { | |
158 if (path[2] == '\0' || path[2] == '/') | |
159 { | |
160 path += 2; | |
161 | |
162 /* Ignore ".." at root. */ | |
163 if (new_path == resolved_path + 1) | |
164 continue; | |
165 | |
166 /* Handle ".." by backing up. */ | |
167 while ((--new_path)[-1] != '/') | |
168 ; | |
169 continue; | |
170 } | |
171 } | |
172 } | |
173 | |
174 /* Safely copy the next pathname component. */ | |
175 while (*path != '\0' && *path != '/') | |
176 { | |
177 if (path > max_path) | |
178 { | |
179 errno = ENAMETOOLONG; | |
180 return NULL; | |
181 } | |
182 *new_path++ = *path++; | |
183 } | |
184 | |
185 #ifdef S_IFLNK | |
186 /* See if latest pathname component is a symlink. */ | |
187 *new_path = '\0'; | |
188 n = readlink(resolved_path, link_path, PATH_MAX - 1); | |
189 | |
190 if (n < 0) | |
191 { | |
192 /* EINVAL means the file exists but isn't a symlink. */ | |
193 if (errno != EINVAL) | |
194 return NULL; | |
195 } | |
196 else | |
197 { | |
198 /* Protect against infinite loops. */ | |
199 if (readlinks++ > MAX_READLINKS) | |
200 { | |
201 errno = ELOOP; | |
202 return NULL; | |
203 } | |
204 | |
205 /* Note: readlink doesn't add the null byte. */ | |
206 link_path[n] = '\0'; | |
207 | |
208 if (*link_path == '/') | |
209 /* Start over for an absolute symlink. */ | |
210 new_path = resolved_path; | |
211 else | |
212 /* Otherwise back up over this component. */ | |
213 while (*(--new_path) != '/') | |
214 ; | |
215 | |
216 /* Safe sex check. */ | |
217 if (strlen(path) + n >= PATH_MAX) | |
218 { | |
219 errno = ENAMETOOLONG; | |
220 return NULL; | |
221 } | |
222 | |
223 /* Insert symlink contents into path. */ | |
224 strcat(link_path, path); | |
225 strcpy(copy_path, link_path); | |
226 path = copy_path; | |
227 } | |
228 #endif /* S_IFLNK */ | |
229 *new_path++ = '/'; | |
230 } | |
231 | |
232 /* Delete trailing slash but don't whomp a lone slash. */ | |
233 if (new_path != resolved_path + 1 && new_path[-1] == '/') | |
234 new_path--; | |
235 | |
236 /* Make sure it's null terminated. */ | |
237 *new_path = '\0'; | |
238 return resolved_path; | |
239 } |