comparison src/realpath.c @ 1116:3bcd77d0bf93

[xemacs-hg @ 2002-11-22 12:57:09 by ben] file-truename and other crash fileio.c: Fix crashes due to incorrectly ported code from FSF. realpath.c: Don't return EIO when a filename is incorrect, since the I/O error gets passed all the way up. Clean up this file and avoid using macros as a poor-man's dispatch mechanism -- it just makes it impossible to follow the code.
author ben
date Fri, 22 Nov 2002 12:57:11 +0000
parents 5df795348f45
children e22b0213b713
comparison
equal deleted inserted replaced
1115:cb1d0fc87e10 1116:3bcd77d0bf93
1 /* 1 /*
2 * realpath.c -- canonicalize pathname by removing symlinks 2 * realpath.c -- canonicalize pathname by removing symlinks
3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com> 3 * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4 * Copyright (C) 2001 Ben Wing. 4 * Copyright (C) 2001, 2002 Ben Wing.
5 * 5 *
6 6
7 This file is part of XEmacs. 7 This file is part of XEmacs.
8 8
9 XEmacs is free software; you can redistribute it and/or modify it 9 XEmacs is free software; you can redistribute it and/or modify it
30 30
31 #include <config.h> 31 #include <config.h>
32 #include "lisp.h" 32 #include "lisp.h"
33 33
34 #include "sysfile.h" 34 #include "sysfile.h"
35 #include "sysdir.h"
35 36
36 #define MAX_READLINKS 32 37 #define MAX_READLINKS 32
37 38
38 #if defined (HAVE_SYS_PARAM_H) && !defined (WIN32_NATIVE) 39 #ifdef WIN32_ANY
39 #include <sys/param.h>
40 #endif
41
42 #ifdef WIN32_NATIVE
43 #include <direct.h>
44 #endif
45
46 #include <sys/stat.h> /* for S_IFLNK */
47
48 #if defined(WIN32_NATIVE) || defined(CYGWIN)
49 #define WIN32_FILENAMES
50 #endif
51
52 /* First char after start of absolute filename. */
53 #define ABS_START(name) (name + ABS_LENGTH (name))
54
55 #if defined (WIN32_NATIVE)
56 /* Length of start of absolute filename. */
57 # define ABS_LENGTH(name) (mswindows_abs_start (name))
58 static int mswindows_abs_start (const Ibyte *name);
59 # define readlink_and_correct_case mswindows_readlink_and_correct_case
60 #else
61 # ifdef CYGWIN
62 # ifdef WIN32_FILENAMES
63 # define ABS_LENGTH(name) (mswindows_abs_start (name))
64 static int mswindows_abs_start (const Ibyte * name);
65 # else
66 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \
67 (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0)
68 # endif
69 # define readlink_and_correct_case cygwin_readlink_and_correct_case
70 # else
71 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
72 # define readlink_and_correct_case qxe_readlink
73 # endif /* CYGWIN */
74 #endif /* WIN32_NATIVE */
75
76 #if defined (WIN32_NATIVE) || defined (CYGWIN)
77 #include "syswindows.h" 40 #include "syswindows.h"
78 /* "Emulate" readlink on mswindows - finds real name (i.e. correct
79 case) of a file. (#### "readlink" is used extremely misleadingly
80 here. This is much more like "truename"!) UNC servers and shares
81 are lower-cased. Directories must be given without trailing
82 '/'. One day, this could read Win2K's reparse points. */
83 static int
84 mswindows_readlink_and_correct_case (const Ibyte *name, Ibyte *buf,
85 int size)
86 {
87 int len = 0;
88 int err = 0;
89 const Ibyte *lastname;
90 int count = 0;
91 const Ibyte *tmp;
92 DECLARE_EISTRING (result);
93
94 assert (*name);
95
96 /* Sort of check we have a valid filename. */
97 if (qxestrpbrk (name, "*?|<>\"") || qxestrlen (name) >= PATH_MAX)
98 {
99 errno = EIO;
100 return -1;
101 }
102
103 /* Find start of filename */
104 lastname = name + qxestrlen (name);
105 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1]))
106 --lastname;
107
108 /* Count slashes in unc path */
109 if (ABS_LENGTH (name) == 2)
110 for (tmp = name; *tmp; tmp++)
111 if (IS_DIRECTORY_SEP (*tmp))
112 count++;
113
114 if (count >= 2 && count < 4)
115 {
116 eicpy_rawz (result, lastname);
117 eilwr (result);
118 }
119 else
120 {
121 WIN32_FIND_DATAW find_data;
122 Extbyte *nameext;
123 HANDLE dir_handle;
124
125 C_STRING_TO_TSTR (name, nameext);
126 dir_handle = qxeFindFirstFile (nameext, &find_data);
127 if (dir_handle == INVALID_HANDLE_VALUE)
128 {
129 errno = ENOENT;
130 return -1;
131 }
132 eicpy_ext (result, (Extbyte *) find_data.cFileName, Qmswindows_tstr);
133 FindClose (dir_handle);
134 }
135
136 if ((len = eilen (result)) < size)
137 {
138 DECLARE_EISTRING (eilastname);
139
140 eicpy_rawz (eilastname, lastname);
141 if (eicmp_ei (eilastname, result) == 0)
142 /* Signal that the name is already OK. */
143 err = EINVAL;
144 else
145 memcpy (buf, eidata (result), len + 1);
146 }
147 else
148 err = ENAMETOOLONG;
149
150 errno = err;
151 return err ? -1 : len;
152 }
153 #endif /* WIN32_NATIVE || CYGWIN */
154
155 #ifdef CYGWIN
156 /* Call readlink and try to find out the correct case for the file. */
157 static int
158 cygwin_readlink_and_correct_case (const Ibyte *name, Ibyte *buf,
159 int size)
160 {
161 int n = qxe_readlink (name, buf, size);
162 if (n < 0 && errno == EINVAL)
163 {
164 /* The file may exist, but isn't a symlink. Try to find the
165 right name. */
166 Ibyte *tmp =
167 (Ibyte *) ALLOCA (cygwin_posix_to_win32_path_list_buf_size
168 ((char *) name));
169 cygwin_posix_to_win32_path_list ((char *) name, (char *) tmp);
170 n = mswindows_readlink_and_correct_case (tmp, buf, size);
171 }
172 return n;
173 }
174 #endif /* CYGWIN */
175
176 #ifdef WIN32_FILENAMES
177 #ifndef ELOOP 41 #ifndef ELOOP
178 #define ELOOP 10062 /* = WSAELOOP in winsock.h */ 42 #define ELOOP 10062 /* = WSAELOOP in winsock.h */
179 #endif 43 #endif
44 #endif
45
180 /* Length of start of absolute filename. */ 46 /* Length of start of absolute filename. */
181 static int 47 static int
182 mswindows_abs_start (const Ibyte *name) 48 abs_start (const Ibyte *name)
183 { 49 {
50 #ifdef WIN32_ANY
184 if (isalpha (*name) && IS_DEVICE_SEP (name[1]) 51 if (isalpha (*name) && IS_DEVICE_SEP (name[1])
185 && IS_DIRECTORY_SEP (name[2])) 52 && IS_DIRECTORY_SEP (name[2]))
186 return 3; 53 return 3;
187 else if (IS_DIRECTORY_SEP (*name)) 54 else if (IS_DIRECTORY_SEP (*name))
188 return IS_DIRECTORY_SEP (name[1]) ? 2 : 1; 55 return IS_DIRECTORY_SEP (name[1]) ? 2 : 1;
189 else 56 else
190 return 0; 57 return 0;
58 #else /* not WIN32_ANY */
59 return IS_DIRECTORY_SEP (*name) ? 1 : 0;
60 #endif
191 } 61 }
192 #endif /* WIN32_NATIVE */ 62
63 /* Find real name of a file by resolving symbolic links and (under Windows)
64 looking up the correct case of the file as it appears on the file
65 system.
66
67 Under Windows, UNC servers and shares are lower-cased. Directories must
68 be given without trailing '/'. One day, this could read Win2K's reparse
69 points. */
70
71 static int
72 readlink_and_correct_case (const Ibyte *name, Ibyte *buf,
73 int size)
74 {
75 #ifndef WIN32_ANY
76 return qxe_readlink (name, buf, size);
77 #else
78 # ifdef CYGWIN
79 Ibyte *tmp;
80 int n = qxe_readlink (name, buf, size);
81 if (n >= 0 || errno != EINVAL)
82 return n;
83
84 /* The file may exist, but isn't a symlink. Try to find the
85 right name. */
86 tmp = (Ibyte *) ALLOCA (cygwin_posix_to_win32_path_list_buf_size
87 ((char *) name));
88 cygwin_posix_to_win32_path_list ((char *) name, (char *) tmp);
89 name = tmp;
90 # endif
91
92 {
93 int len = 0;
94 int err = 0;
95 const Ibyte *lastname;
96 int count = 0;
97 const Ibyte *tmp;
98 DECLARE_EISTRING (result);
99
100 assert (*name);
101
102 /* Sort of check we have a valid filename. */
103 if (qxestrpbrk (name, "*?|<>\""))
104 {
105 errno = ENOENT;
106 return -1;
107 }
108 else if (qxestrlen (name) >= PATH_MAX)
109 {
110 errno = ENAMETOOLONG;
111 return -1;
112 }
113
114 /* Find start of filename */
115 lastname = name + qxestrlen (name);
116 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1]))
117 --lastname;
118
119 /* Count slashes in unc path */
120 if (abs_start (name) == 2)
121 for (tmp = name; *tmp; tmp++)
122 if (IS_DIRECTORY_SEP (*tmp))
123 count++;
124
125 if (count >= 2 && count < 4)
126 {
127 eicpy_rawz (result, lastname);
128 eilwr (result);
129 }
130 else
131 {
132 WIN32_FIND_DATAW find_data;
133 Extbyte *nameext;
134 HANDLE dir_handle;
135
136 C_STRING_TO_TSTR (name, nameext);
137 dir_handle = qxeFindFirstFile (nameext, &find_data);
138 if (dir_handle == INVALID_HANDLE_VALUE)
139 {
140 errno = ENOENT;
141 return -1;
142 }
143 eicpy_ext (result, (Extbyte *) find_data.cFileName, Qmswindows_tstr);
144 FindClose (dir_handle);
145 }
146
147 if ((len = eilen (result)) < size)
148 {
149 DECLARE_EISTRING (eilastname);
150
151 eicpy_rawz (eilastname, lastname);
152 if (eicmp_ei (eilastname, result) == 0)
153 /* Signal that the name is already OK. */
154 err = EINVAL;
155 else
156 memcpy (buf, eidata (result), len + 1);
157 }
158 else
159 err = ENAMETOOLONG;
160
161 errno = err;
162 return err ? -1 : len;
163 }
164 #endif /* WIN32_ANY */
165 }
193 166
194 /* Mule Note: This function works with and returns 167 /* Mule Note: This function works with and returns
195 internally-formatted strings. */ 168 internally-formatted strings. */
196 169
197 Ibyte * 170 Ibyte *
198 qxe_realpath (const Ibyte *path, Ibyte *resolved_path) 171 qxe_realpath (const Ibyte *path, Ibyte *resolved_path)
199 { 172 {
200 Ibyte copy_path[PATH_MAX]; 173 Ibyte copy_path[PATH_MAX];
201 Ibyte *new_path = resolved_path; 174 Ibyte *new_path = resolved_path;
202 Ibyte *max_path; 175 Ibyte *max_path;
203 #if defined (HAVE_READLINK) || defined (WIN32_NATIVE) 176 #if defined (HAVE_READLINK) || defined (WIN32_ANY)
204 int readlinks = 0; 177 int readlinks = 0;
205 Ibyte link_path[PATH_MAX]; 178 Ibyte link_path[PATH_MAX];
206 int n; 179 int n;
207 int abslen = ABS_LENGTH (path); 180 int abslen = abs_start (path);
208 #endif 181 #endif
209 182
210 /* Make a copy of the source path since we may need to modify it. */ 183 /* Make a copy of the source path since we may need to modify it. */
211 qxestrcpy (copy_path, path); 184 qxestrcpy (copy_path, path);
212 path = copy_path; 185 path = copy_path;
213 max_path = copy_path + PATH_MAX - 2; 186 max_path = copy_path + PATH_MAX - 2;
214 187
215 if (0) 188 if (0)
216 ; 189 ;
217 #ifdef WIN32_FILENAMES 190 #ifdef WIN32_ANY
218 /* Check for c:/... or //server/... */ 191 /* Check for c:/... or //server/... */
219 else if (abslen == 3 || abslen == 2) 192 else if (abslen == 3 || abslen == 2)
220 { 193 {
221 /* Make sure drive letter is lowercased. */ 194 /* Make sure drive letter is lowercased. */
222 if (abslen == 3) { 195 if (abslen == 3)
223 *new_path = tolower (*path); 196 {
224 new_path++; 197 *new_path = tolower (*path);
225 path++; 198 new_path++;
226 abslen--; 199 path++;
227 } 200 abslen--;
201 }
228 /* Coerce directory chars. */ 202 /* Coerce directory chars. */
229 while (abslen-- > 0) { 203 while (abslen-- > 0)
230 if (IS_DIRECTORY_SEP (*path)) 204 {
231 *new_path++ = DIRECTORY_SEP; 205 if (IS_DIRECTORY_SEP (*path))
232 else 206 *new_path++ = DIRECTORY_SEP;
233 *new_path++ = *path; 207 else
234 path++; 208 *new_path++ = *path;
235 } 209 path++;
210 }
236 } 211 }
237 #endif 212 #endif
238 #ifdef WIN32_NATIVE 213 #ifdef WIN32_NATIVE
239 /* No drive letter, but a beginning slash? Prepend drive letter. */ 214 /* No drive letter, but a beginning slash? Prepend drive letter. */
240 else if (abslen == 1) 215 else if (abslen == 1)
242 get_initial_directory (new_path, PATH_MAX - 1); 217 get_initial_directory (new_path, PATH_MAX - 1);
243 new_path += 3; 218 new_path += 3;
244 path++; 219 path++;
245 } 220 }
246 /* Just a path name, prepend the current directory */ 221 /* Just a path name, prepend the current directory */
247 else if (1) 222 else
248 { 223 {
249 get_initial_directory (new_path, PATH_MAX - 1); 224 get_initial_directory (new_path, PATH_MAX - 1);
250 new_path += qxestrlen (new_path); 225 new_path += qxestrlen (new_path);
251 if (!IS_DIRECTORY_SEP (new_path[-1])) 226 if (!IS_DIRECTORY_SEP (new_path[-1]))
252 *new_path++ = DIRECTORY_SEP; 227 *new_path++ = DIRECTORY_SEP;
292 (path[2] == '\0' || IS_DIRECTORY_SEP (path[2]))) 267 (path[2] == '\0' || IS_DIRECTORY_SEP (path[2])))
293 { 268 {
294 path += 2; 269 path += 2;
295 270
296 /* Ignore ".." at root. */ 271 /* Ignore ".." at root. */
297 if (new_path == ABS_START (resolved_path)) 272 if (new_path == resolved_path + abs_start (resolved_path))
298 continue; 273 continue;
299 274
300 /* Handle ".." by backing up. */ 275 /* Handle ".." by backing up. */
301 --new_path; 276 --new_path;
302 while (!IS_DIRECTORY_SEP (new_path[-1])) 277 while (!IS_DIRECTORY_SEP (new_path[-1]))
314 return NULL; 289 return NULL;
315 } 290 }
316 *new_path++ = *path++; 291 *new_path++ = *path++;
317 } 292 }
318 293
319 #if defined (HAVE_READLINK) || defined (WIN32_NATIVE) 294 #if defined (HAVE_READLINK) || defined (WIN32_ANY)
320 /* See if latest pathname component is a symlink or needs case 295 /* See if latest pathname component is a symlink or needs case
321 correction. */ 296 correction. */
322 *new_path = '\0'; 297 *new_path = '\0';
323 n = readlink_and_correct_case (resolved_path, link_path, PATH_MAX - 1); 298 n = readlink_and_correct_case (resolved_path, link_path, PATH_MAX - 1);
324 299
325 if (n < 0) 300 if (n < 0)
326 { 301 {
327 /* EINVAL means the file exists but isn't a symlink or doesn't 302 /* EINVAL means the file exists but isn't a symlink or doesn't
328 need case correction. */ 303 need case correction. */
329 #ifdef CYGWIN 304 #ifdef WIN32_ANY
330 if (errno != EINVAL && errno != ENOENT) 305 if (errno != EINVAL && errno != ENOENT)
331 #else 306 #else
332 if (errno != EINVAL) 307 if (errno != EINVAL)
333 #endif 308 #endif
334 return NULL; 309 return NULL;
343 } 318 }
344 319
345 /* Note: readlink doesn't add the null byte. */ 320 /* Note: readlink doesn't add the null byte. */
346 link_path[n] = '\0'; 321 link_path[n] = '\0';
347 322
348 if (ABS_LENGTH (link_path) > 0) 323 if (abs_start (link_path) > 0)
349 /* Start over for an absolute symlink. */ 324 /* Start over for an absolute symlink. */
350 new_path = resolved_path + ABS_LENGTH (link_path) - 1; 325 new_path = resolved_path + abs_start (link_path) - 1;
351 else 326 else
352 /* Otherwise back up over this component. */ 327 /* Otherwise back up over this component. */
353 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path) 328 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path)
354 assert (new_path > resolved_path); 329 assert (new_path > resolved_path);
355 330
363 /* Insert symlink contents into path. */ 338 /* Insert symlink contents into path. */
364 qxestrcat (link_path, path); 339 qxestrcat (link_path, path);
365 qxestrcpy (copy_path, link_path); 340 qxestrcpy (copy_path, link_path);
366 path = copy_path; 341 path = copy_path;
367 } 342 }
368 #endif /* HAVE_READLINK || WIN32_NATIVE */ 343 #endif /* HAVE_READLINK || WIN32_ANY */
369 *new_path++ = DIRECTORY_SEP; 344 *new_path++ = DIRECTORY_SEP;
370 } 345 }
371 346
372 /* Delete trailing slash but don't whomp a lone slash. */ 347 /* Delete trailing slash but don't whomp a lone slash. */
373 if (new_path != ABS_START (resolved_path) && 348 if (new_path != resolved_path + abs_start (resolved_path) &&
374 IS_DIRECTORY_SEP (new_path[-1])) 349 IS_DIRECTORY_SEP (new_path[-1]))
375 new_path--; 350 new_path--;
376 351
377 /* Make sure it's null terminated. */ 352 /* Make sure it's null terminated. */
378 *new_path = '\0'; 353 *new_path = '\0';