Mercurial > hg > xemacs-beta
comparison src/realpath.c @ 446:1ccc32a20af4 r21-2-38
Import from CVS: tag r21-2-38
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:37:21 +0200 |
parents | abe6d1db359e |
children | 0784d089fdc9 |
comparison
equal
deleted
inserted
replaced
445:34f3776fcf0e | 446:1ccc32a20af4 |
---|---|
21 Boston, MA 02111-1307, USA. */ | 21 Boston, MA 02111-1307, USA. */ |
22 | 22 |
23 /* Synched up with: Not in FSF. */ | 23 /* Synched up with: Not in FSF. */ |
24 | 24 |
25 #include <config.h> | 25 #include <config.h> |
26 | 26 #include "lisp.h" |
27 #include <sys/types.h> | |
28 #include <stdio.h> | |
29 #include <string.h> | |
30 #include <errno.h> | 27 #include <errno.h> |
31 #include <limits.h> | |
32 | 28 |
33 #ifdef HAVE_UNISTD_H | 29 #ifdef HAVE_UNISTD_H |
34 #include <unistd.h> | 30 #include <unistd.h> |
35 #endif | 31 #endif |
36 | 32 |
37 #if defined (HAVE_SYS_PARAM_H) | 33 #if defined (HAVE_SYS_PARAM_H) && !defined (WIN32_NATIVE) |
38 #include <sys/param.h> | 34 #include <sys/param.h> |
39 #endif | 35 #endif |
40 | 36 |
41 #ifdef WIN32_NATIVE | 37 #ifdef WIN32_NATIVE |
42 #include <direct.h> | 38 #include <direct.h> |
43 #endif | 39 #endif |
44 | 40 |
45 #include <sys/stat.h> /* for S_IFLNK */ | 41 #include <sys/stat.h> /* for S_IFLNK */ |
42 | |
43 /* First char after start of absolute filename. */ | |
44 #define ABS_START(name) (name + ABS_LENGTH (name)) | |
45 | |
46 #if defined (WIN32_NATIVE) | |
47 /* Length of start of absolute filename. */ | |
48 # define ABS_LENGTH(name) (win32_abs_start (name)) | |
49 static int win32_abs_start (const char * name); | |
50 /* System dependent version of readlink. */ | |
51 # define system_readlink win32_readlink | |
52 #else | |
53 # ifdef CYGWIN | |
54 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \ | |
55 (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0) | |
56 # define system_readlink cygwin_readlink | |
57 # else | |
58 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0) | |
59 # define system_readlink readlink | |
60 # endif /* CYGWIN */ | |
61 #endif /* WIN32_NATIVE */ | |
62 | |
63 #if defined (WIN32_NATIVE) || defined (CYGWIN) | |
64 #include "syswindows.h" | |
65 /* Emulate readlink on win32 - finds real name (i.e. correct case) of | |
66 a file. UNC servers and shares are lower-cased. Directories must be | |
67 given without trailing '/'. One day, this could read Win2K's | |
68 reparse points. */ | |
69 static int | |
70 win32_readlink (const char * name, char * buf, int size) | |
71 { | |
72 WIN32_FIND_DATA find_data; | |
73 HANDLE dir_handle = NULL; | |
74 int len = 0; | |
75 int err = 0; | |
76 const char* lastname; | |
77 int count = 0; | |
78 const char* tmp; | |
79 char* res = NULL; | |
80 | |
81 assert (*name); | |
82 | |
83 /* Sort of check we have a valid filename. */ | |
84 if (strpbrk (name, "*?|<>\"") || strlen (name) >= MAX_PATH) | |
85 { | |
86 errno = EIO; | |
87 return -1; | |
88 } | |
89 | |
90 /* Find start of filename */ | |
91 lastname = name + strlen (name); | |
92 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1])) | |
93 --lastname; | |
94 | |
95 /* Count slashes in unc path */ | |
96 if (ABS_LENGTH (name) == 2) | |
97 for (tmp = name; *tmp; tmp++) | |
98 if (IS_DIRECTORY_SEP (*tmp)) | |
99 count++; | |
100 | |
101 if (count >= 2 && count < 4) | |
102 { | |
103 /* UNC server or share name: just copy lowercased name. */ | |
104 res = find_data.cFileName; | |
105 for (tmp = lastname; *tmp; tmp++) | |
106 *res++ = tolower (*tmp); | |
107 *res = '\0'; | |
108 } | |
109 else | |
110 dir_handle = FindFirstFile (name, &find_data); | |
111 | |
112 if (res || dir_handle != INVALID_HANDLE_VALUE) | |
113 { | |
114 if ((len = strlen (find_data.cFileName)) < size) | |
115 { | |
116 if (strcmp (lastname, find_data.cFileName) == 0) | |
117 /* Signal that the name is already OK. */ | |
118 err = EINVAL; | |
119 else | |
120 memcpy (buf, find_data.cFileName, len + 1); | |
121 } | |
122 else | |
123 err = ENAMETOOLONG; | |
124 if (!res) FindClose (dir_handle); | |
125 } | |
126 else | |
127 err = ENOENT; | |
128 | |
129 errno = err; | |
130 return err ? -1 : len; | |
131 } | |
132 #endif /* WIN32_NATIVE || CYGWIN */ | |
133 | |
134 #ifdef CYGWIN | |
135 /* Call readlink and try to find out the correct case for the file. */ | |
136 static int | |
137 cygwin_readlink (const char * name, char * buf, int size) | |
138 { | |
139 int n = readlink (name, buf, size); | |
140 if (n < 0) | |
141 { | |
142 /* The file may exist, but isn't a symlink. Try to find the | |
143 right name. */ | |
144 char* tmp = alloca (cygwin_posix_to_win32_path_list_buf_size (name)); | |
145 cygwin_posix_to_win32_path_list (name, tmp); | |
146 n = win32_readlink (tmp, buf, size); | |
147 } | |
148 return n; | |
149 } | |
150 #endif /* CYGWIN */ | |
151 | |
152 #ifdef WIN32_NATIVE | |
153 #ifndef ELOOP | |
154 #define ELOOP 10062 /* = WSAELOOP in winsock.h */ | |
155 #endif | |
156 /* Length of start of absolute filename. */ | |
157 static int | |
158 win32_abs_start (const char * name) | |
159 { | |
160 if (isalpha (*name) && IS_DEVICE_SEP (name[1]) | |
161 && IS_DIRECTORY_SEP (name[2])) | |
162 return 3; | |
163 else if (IS_DIRECTORY_SEP (*name)) | |
164 return IS_DIRECTORY_SEP (name[1]) ? 2 : 1; | |
165 else | |
166 return 0; | |
167 } | |
168 #endif /* WIN32_NATIVE */ | |
46 | 169 |
47 #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD) | 170 #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD) |
48 #undef getcwd | 171 #undef getcwd |
49 #define getcwd(buffer, len) getwd (buffer) | 172 #define getcwd(buffer, len) getwd (buffer) |
50 #endif | 173 #endif |
66 xrealpath (const char *path, char resolved_path []) | 189 xrealpath (const char *path, char resolved_path []) |
67 { | 190 { |
68 char copy_path[PATH_MAX]; | 191 char copy_path[PATH_MAX]; |
69 char *new_path = resolved_path; | 192 char *new_path = resolved_path; |
70 char *max_path; | 193 char *max_path; |
71 #ifdef S_IFLNK | 194 #if defined (S_IFLNK) || defined (WIN32_NATIVE) |
72 int readlinks = 0; | 195 int readlinks = 0; |
73 char link_path[PATH_MAX]; | 196 char link_path[PATH_MAX]; |
74 int n; | 197 int n; |
198 int abslen = ABS_LENGTH (path); | |
75 #endif | 199 #endif |
76 | 200 |
77 /* Make a copy of the source path since we may need to modify it. */ | 201 /* Make a copy of the source path since we may need to modify it. */ |
78 strcpy (copy_path, path); | 202 strcpy (copy_path, path); |
79 path = copy_path; | 203 path = copy_path; |
80 max_path = copy_path + PATH_MAX - 2; | 204 max_path = copy_path + PATH_MAX - 2; |
205 | |
81 #ifdef WIN32_NATIVE | 206 #ifdef WIN32_NATIVE |
82 /* | 207 /* Check for c:/... or //server/... */ |
83 ** In NT we have two different cases: (1) the path name begins | 208 if (abslen == 2 || abslen == 3) |
84 ** with a drive letter, e.g., "C:"; and (2) the path name begins | 209 { |
85 ** with just a slash, which roots to the current drive. In the | 210 strncpy (new_path, path, abslen); |
86 ** first case we are going to leave things alone, in the second | 211 new_path += abslen; |
87 ** case we will prepend the drive letter to the given path. | 212 path += abslen; |
88 ** Note: So far in testing, I'm only seeing case #1, even though | 213 } |
89 ** I've tried to get the other cases to happen. | 214 /* No drive letter, but a beginning slash? Prepend drive letter. */ |
90 ** August Hill, 31 Aug 1997. | 215 else if (abslen == 1) |
91 ** | |
92 ** Check for a driver letter...C:/... | |
93 */ | |
94 if (*(path + 1) == ':') | |
95 { | |
96 strncpy(new_path, path, 3); | |
97 new_path += 3; | |
98 path += 3; | |
99 } | |
100 | |
101 /* | |
102 ** No drive letter, but a beginning slash? Prepend the drive | |
103 ** letter... | |
104 */ | |
105 else if (*path == '/') | |
106 { | 216 { |
107 getcwd (new_path, PATH_MAX - 1); | 217 getcwd (new_path, PATH_MAX - 1); |
108 new_path += 3; | 218 new_path += 3; |
109 path++; | 219 path++; |
110 } | 220 } |
111 | 221 /* Just a path name, prepend the current directory */ |
112 /* | |
113 ** Just a path name, prepend the current directory | |
114 */ | |
115 else | 222 else |
116 { | 223 { |
117 getcwd (new_path, PATH_MAX - 1); | 224 getcwd (new_path, PATH_MAX - 1); |
118 new_path += strlen(new_path); | 225 new_path += strlen (new_path); |
119 if (new_path[-1] != '/') | 226 if (!IS_DIRECTORY_SEP (new_path[-1])) |
120 *new_path++ = '/'; | 227 *new_path++ = DIRECTORY_SEP; |
121 } | 228 } |
122 | |
123 #else | 229 #else |
124 /* If it's a relative pathname use getcwd for starters. */ | 230 /* If it's a relative pathname use getcwd for starters. */ |
125 if (*path != '/') | 231 if (abslen == 0) |
126 { | 232 { |
127 getcwd (new_path, PATH_MAX - 1); | 233 getcwd (new_path, PATH_MAX - 1); |
128 new_path += strlen(new_path); | 234 new_path += strlen (new_path); |
129 if (new_path[-1] != '/') | 235 if (!IS_DIRECTORY_SEP (new_path[-1])) |
130 *new_path++ = '/'; | 236 *new_path++ = DIRECTORY_SEP; |
131 } | 237 } |
132 else | 238 else |
133 { | 239 { |
134 *new_path++ = '/'; | 240 /* Copy first directory sep. May have two on cygwin. */ |
135 path++; | 241 strncpy (new_path, path, abslen); |
242 new_path += abslen; | |
243 path += abslen; | |
136 } | 244 } |
137 #endif | 245 #endif |
138 /* Expand each slash-separated pathname component. */ | 246 /* Expand each slash-separated pathname component. */ |
139 while (*path != '\0') | 247 while (*path != '\0') |
140 { | 248 { |
141 /* Ignore stray "/". */ | 249 /* Ignore stray "/". */ |
142 if (*path == '/') | 250 if (IS_DIRECTORY_SEP (*path)) |
143 { | 251 { |
144 path++; | 252 path++; |
145 continue; | 253 continue; |
146 } | 254 } |
147 | 255 |
148 if (*path == '.') | 256 if (*path == '.') |
149 { | 257 { |
150 /* Ignore ".". */ | 258 /* Ignore ".". */ |
151 if (path[1] == '\0' || path[1] == '/') | 259 if (path[1] == '\0' || IS_DIRECTORY_SEP (path[1])) |
152 { | 260 { |
153 path++; | 261 path++; |
154 continue; | 262 continue; |
155 } | 263 } |
156 | 264 |
157 /* Handle ".." */ | 265 /* Handle ".." */ |
158 if (path[1] == '.' && | 266 if (path[1] == '.' && |
159 (path[2] == '\0' || path[2] == '/')) | 267 (path[2] == '\0' || IS_DIRECTORY_SEP (path[2]))) |
160 { | 268 { |
161 path += 2; | 269 path += 2; |
162 | 270 |
163 /* Ignore ".." at root. */ | 271 /* Ignore ".." at root. */ |
164 if (new_path == resolved_path + 1) | 272 if (new_path == ABS_START (resolved_path)) |
165 continue; | 273 continue; |
166 | 274 |
167 /* Handle ".." by backing up. */ | 275 /* Handle ".." by backing up. */ |
168 while ((--new_path)[-1] != '/') | 276 --new_path; |
169 ; | 277 while (!IS_DIRECTORY_SEP (new_path[-1])) |
278 --new_path; | |
170 continue; | 279 continue; |
171 } | 280 } |
172 } | 281 } |
173 | 282 |
174 /* Safely copy the next pathname component. */ | 283 /* Safely copy the next pathname component. */ |
175 while (*path != '\0' && *path != '/') | 284 while (*path != '\0' && !IS_DIRECTORY_SEP (*path)) |
176 { | 285 { |
177 if (path > max_path) | 286 if (path > max_path) |
178 { | 287 { |
179 errno = ENAMETOOLONG; | 288 errno = ENAMETOOLONG; |
180 return NULL; | 289 return NULL; |
181 } | 290 } |
182 *new_path++ = *path++; | 291 *new_path++ = *path++; |
183 } | 292 } |
184 | 293 |
185 #ifdef S_IFLNK | 294 #if defined (S_IFLNK) || defined (WIN32_NATIVE) |
186 /* See if latest pathname component is a symlink. */ | 295 /* See if latest pathname component is a symlink. */ |
187 *new_path = '\0'; | 296 *new_path = '\0'; |
188 n = readlink (resolved_path, link_path, PATH_MAX - 1); | 297 n = system_readlink (resolved_path, link_path, PATH_MAX - 1); |
189 | 298 |
190 if (n < 0) | 299 if (n < 0) |
191 { | 300 { |
192 /* EINVAL means the file exists but isn't a symlink. */ | 301 /* EINVAL means the file exists but isn't a symlink. */ |
193 if (errno != EINVAL) | 302 if (errno != EINVAL) |
202 return NULL; | 311 return NULL; |
203 } | 312 } |
204 | 313 |
205 /* Note: readlink doesn't add the null byte. */ | 314 /* Note: readlink doesn't add the null byte. */ |
206 link_path[n] = '\0'; | 315 link_path[n] = '\0'; |
207 | 316 |
208 if (*link_path == '/') | 317 if (ABS_LENGTH (link_path) > 0) |
209 /* Start over for an absolute symlink. */ | 318 /* Start over for an absolute symlink. */ |
210 new_path = resolved_path; | 319 new_path = resolved_path + ABS_LENGTH (link_path) - 1; |
211 else | 320 else |
212 /* Otherwise back up over this component. */ | 321 /* Otherwise back up over this component. */ |
213 while (*(--new_path) != '/') | 322 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path) |
214 ; | 323 assert (new_path > resolved_path); |
215 | 324 |
216 /* Safe sex check. */ | 325 /* Safe sex check. */ |
217 if (strlen(path) + n >= PATH_MAX) | 326 if (strlen(path) + n >= PATH_MAX) |
218 { | 327 { |
219 errno = ENAMETOOLONG; | 328 errno = ENAMETOOLONG; |
223 /* Insert symlink contents into path. */ | 332 /* Insert symlink contents into path. */ |
224 strcat(link_path, path); | 333 strcat(link_path, path); |
225 strcpy(copy_path, link_path); | 334 strcpy(copy_path, link_path); |
226 path = copy_path; | 335 path = copy_path; |
227 } | 336 } |
228 #endif /* S_IFLNK */ | 337 #endif /* S_IFLNK || WIN32_NATIVE */ |
229 *new_path++ = '/'; | 338 *new_path++ = DIRECTORY_SEP; |
230 } | 339 } |
231 | 340 |
232 /* Delete trailing slash but don't whomp a lone slash. */ | 341 /* Delete trailing slash but don't whomp a lone slash. */ |
233 if (new_path != resolved_path + 1 && new_path[-1] == '/') | 342 if (new_path != ABS_START (resolved_path) && IS_DIRECTORY_SEP (new_path[-1])) |
234 new_path--; | 343 new_path--; |
235 | 344 |
236 /* Make sure it's null terminated. */ | 345 /* Make sure it's null terminated. */ |
237 *new_path = '\0'; | 346 *new_path = '\0'; |
347 | |
348 #ifdef WIN32_NATIVE | |
349 if (ABS_LENGTH (resolved_path) == 3) | |
350 /* Lowercase drive letter. */ | |
351 *resolved_path = tolower (*resolved_path); | |
352 #endif | |
238 return resolved_path; | 353 return resolved_path; |
239 } | 354 } |