comparison src/realpath.c @ 771:943eaba38521

[xemacs-hg @ 2002-03-13 08:51:24 by ben] The big ben-mule-21-5 check-in! Various files were added and deleted. See CHANGES-ben-mule. There are still some test suite failures. No crashes, though. Many of the failures have to do with problems in the test suite itself rather than in the actual code. I'll be addressing these in the next day or so -- none of the test suite failures are at all critical. Meanwhile I'll be trying to address the biggest issues -- i.e. build or run failures, which will almost certainly happen on various platforms. All comments should be sent to ben@xemacs.org -- use a Cc: if necessary when sending to mailing lists. There will be pre- and post- tags, something like pre-ben-mule-21-5-merge-in, and post-ben-mule-21-5-merge-in.
author ben
date Wed, 13 Mar 2002 08:54:06 +0000
parents 5fd7ba8b56e7
children 6504113e7c2d
comparison
equal deleted inserted replaced
770:336a418893b5 771:943eaba38521
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 * 5 *
5 6
6 This file is part of XEmacs. 7 This file is part of XEmacs.
7 8
8 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
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */ 22 Boston, MA 02111-1307, USA. */
22 23
23 /* Synched up with: Not in FSF. */ 24 /* Synched up with: Not in FSF. */
24 25
25 #define DONT_ENCAPSULATE 26 /* This file has been Mule-ized, June 2001 by Ben Wing.
27
28 Everything in this file now works in terms of internal, not external,
29 data. This is the only way to be safe, and it makes the code cleaner. */
30
26 #include <config.h> 31 #include <config.h>
27 #include "lisp.h" 32 #include "lisp.h"
28 33
29 #include "sysfile.h" 34 #include "sysfile.h"
30 35
36 #define MAX_READLINKS 32
37
31 /* First char after start of absolute filename. */ 38 /* First char after start of absolute filename. */
32 #define ABS_START(name) (name + ABS_LENGTH (name)) 39 #define ABS_START(name) (name + ABS_LENGTH (name))
33 40
34 #if defined (WIN32_NATIVE) 41 #if defined (WIN32_NATIVE)
35 /* Length of start of absolute filename. */ 42 /* Length of start of absolute filename. */
36 # define ABS_LENGTH(name) (win32_abs_start (name)) 43 # define ABS_LENGTH(name) (mswindows_abs_start (name))
37 static int win32_abs_start (const char * name); 44 static int mswindows_abs_start (const Intbyte *name);
38 /* System dependent version of readlink. */ 45 # define readlink_and_correct_case mswindows_readlink_and_correct_case
39 # define system_readlink win32_readlink
40 #else 46 #else
41 # ifdef CYGWIN 47 # ifdef CYGWIN
42 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \ 48 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? \
43 (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0) 49 (IS_DIRECTORY_SEP (name[1]) ? 2 : 1) : 0)
44 # define system_readlink cygwin_readlink 50 # define readlink_and_correct_case cygwin_readlink_and_correct_case
45 # else 51 # else
46 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0) 52 # define ABS_LENGTH(name) (IS_DIRECTORY_SEP (*name) ? 1 : 0)
47 # define system_readlink readlink 53 # define readlink_and_correct_case qxe_readlink
48 # endif /* CYGWIN */ 54 # endif /* CYGWIN */
49 #endif /* WIN32_NATIVE */ 55 #endif /* WIN32_NATIVE */
50 56
51 #if defined (WIN32_NATIVE) || defined (CYGWIN) 57 #if defined (WIN32_NATIVE) || defined (CYGWIN)
52 #include "syswindows.h" 58 #include "syswindows.h"
53 /* Emulate readlink on win32 - finds real name (i.e. correct case) of 59 /* "Emulate" readlink on mswindows - finds real name (i.e. correct
54 a file. UNC servers and shares are lower-cased. Directories must be 60 case) of a file. (#### "readlink" is used extremely misleadingly
55 given without trailing '/'. One day, this could read Win2K's 61 here. This is much more like "truename"!) UNC servers and shares
56 reparse points. */ 62 are lower-cased. Directories must be given without trailing
63 '/'. One day, this could read Win2K's reparse points. */
57 static int 64 static int
58 win32_readlink (const char * name, char * buf, int size) 65 mswindows_readlink_and_correct_case (const Intbyte *name, Intbyte *buf,
66 int size)
59 { 67 {
60 WIN32_FIND_DATA find_data;
61 HANDLE dir_handle = NULL;
62 int len = 0; 68 int len = 0;
63 int err = 0; 69 int err = 0;
64 const char* lastname; 70 const Intbyte *lastname;
65 int count = 0; 71 int count = 0;
66 const char* tmp; 72 const Intbyte *tmp;
67 char* res = NULL; 73 DECLARE_EISTRING (result);
68 74
69 assert (*name); 75 assert (*name);
70 76
71 /* Sort of check we have a valid filename. */ 77 /* Sort of check we have a valid filename. */
72 if (strpbrk (name, "*?|<>\"") || strlen (name) >= PATH_MAX) 78 if (qxestrpbrk (name, "*?|<>\"") || qxestrlen (name) >= PATH_MAX)
73 { 79 {
74 errno = EIO; 80 errno = EIO;
75 return -1; 81 return -1;
76 } 82 }
77 83
78 /* Find start of filename */ 84 /* Find start of filename */
79 lastname = name + strlen (name); 85 lastname = name + qxestrlen (name);
80 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1])) 86 while (lastname > name && !IS_DIRECTORY_SEP (lastname[-1]))
81 --lastname; 87 --lastname;
82 88
83 /* Count slashes in unc path */ 89 /* Count slashes in unc path */
84 if (ABS_LENGTH (name) == 2) 90 if (ABS_LENGTH (name) == 2)
86 if (IS_DIRECTORY_SEP (*tmp)) 92 if (IS_DIRECTORY_SEP (*tmp))
87 count++; 93 count++;
88 94
89 if (count >= 2 && count < 4) 95 if (count >= 2 && count < 4)
90 { 96 {
91 /* UNC server or share name: just copy lowercased name. */ 97 eicpy_rawz (result, lastname);
92 res = find_data.cFileName; 98 eilwr (result);
93 for (tmp = lastname; *tmp; tmp++)
94 *res++ = tolower (*tmp);
95 *res = '\0';
96 } 99 }
97 else 100 else
98 dir_handle = FindFirstFile (name, &find_data); 101 {
99 102 WIN32_FIND_DATAW find_data;
100 if (res || dir_handle != INVALID_HANDLE_VALUE) 103 Extbyte *nameext;
101 { 104 HANDLE dir_handle;
102 if ((len = strlen (find_data.cFileName)) < size) 105
103 { 106 C_STRING_TO_TSTR (name, nameext);
104 if (strcmp (lastname, find_data.cFileName) == 0) 107 dir_handle = qxeFindFirstFile (nameext, &find_data);
105 /* Signal that the name is already OK. */ 108 if (dir_handle == INVALID_HANDLE_VALUE)
106 err = EINVAL; 109 {
107 else 110 errno = ENOENT;
108 memcpy (buf, find_data.cFileName, len + 1); 111 return -1;
109 } 112 }
113 eicpy_ext (result, (Extbyte *) find_data.cFileName, Qmswindows_tstr);
114 FindClose (dir_handle);
115 }
116
117 if ((len = eilen (result)) < size)
118 {
119 DECLARE_EISTRING (eilastname);
120
121 eicpy_rawz (eilastname, lastname);
122 if (eicmp_ei (eilastname, result) == 0)
123 /* Signal that the name is already OK. */
124 err = EINVAL;
110 else 125 else
111 err = ENAMETOOLONG; 126 memcpy (buf, eidata (result), len + 1);
112 if (!res) FindClose (dir_handle);
113 } 127 }
114 else 128 else
115 err = ENOENT; 129 err = ENAMETOOLONG;
116 130
117 errno = err; 131 errno = err;
118 return err ? -1 : len; 132 return err ? -1 : len;
119 } 133 }
120 #endif /* WIN32_NATIVE || CYGWIN */ 134 #endif /* WIN32_NATIVE || CYGWIN */
121 135
122 #ifdef CYGWIN 136 #ifdef CYGWIN
123 /* Call readlink and try to find out the correct case for the file. */ 137 /* Call readlink and try to find out the correct case for the file. */
124 static int 138 static int
125 cygwin_readlink (const char * name, char * buf, int size) 139 cygwin_readlink_and_correct_case (const Intbyte *name, Intbyte *buf,
140 int size)
126 { 141 {
127 int n = readlink (name, buf, size); 142 int n = qxe_readlink (name, buf, size);
128 if (n < 0 && errno == EINVAL) 143 if (n < 0 && errno == EINVAL)
129 { 144 {
130 /* The file may exist, but isn't a symlink. Try to find the 145 /* The file may exist, but isn't a symlink. Try to find the
131 right name. */ 146 right name. */
132 /* !!#### mule-bogosity */ 147 Intbyte *tmp =
133 char* tmp = (char *) alloca (cygwin_posix_to_win32_path_list_buf_size (name)); 148 (Intbyte *) alloca (cygwin_posix_to_win32_path_list_buf_size
134 cygwin_posix_to_win32_path_list (name, tmp); 149 ((char *) name));
135 n = win32_readlink (tmp, buf, size); 150 cygwin_posix_to_win32_path_list ((char *) name, (char *) tmp);
151 n = mswindows_readlink_and_correct_case (tmp, buf, size);
136 } 152 }
137 return n; 153 return n;
138 } 154 }
139 #endif /* CYGWIN */ 155 #endif /* CYGWIN */
140 156
142 #ifndef ELOOP 158 #ifndef ELOOP
143 #define ELOOP 10062 /* = WSAELOOP in winsock.h */ 159 #define ELOOP 10062 /* = WSAELOOP in winsock.h */
144 #endif 160 #endif
145 /* Length of start of absolute filename. */ 161 /* Length of start of absolute filename. */
146 static int 162 static int
147 win32_abs_start (const char * name) 163 mswindows_abs_start (const Intbyte *name)
148 { 164 {
149 if (isalpha (*name) && IS_DEVICE_SEP (name[1]) 165 if (isalpha (*name) && IS_DEVICE_SEP (name[1])
150 && IS_DIRECTORY_SEP (name[2])) 166 && IS_DIRECTORY_SEP (name[2]))
151 return 3; 167 return 3;
152 else if (IS_DIRECTORY_SEP (*name)) 168 else if (IS_DIRECTORY_SEP (*name))
154 else 170 else
155 return 0; 171 return 0;
156 } 172 }
157 #endif /* WIN32_NATIVE */ 173 #endif /* WIN32_NATIVE */
158 174
159 #if !defined (HAVE_GETCWD) && defined (HAVE_GETWD) 175 /* Mule Note: This function works with and returns
160 #undef getcwd 176 internally-formatted strings. */
161 #define getcwd(buffer, len) getwd (buffer) 177
162 #endif 178 Intbyte *
163 179 qxe_realpath (const Intbyte *path, Intbyte *resolved_path)
164 #ifndef PATH_MAX
165 # if defined (_POSIX_PATH_MAX)
166 # define PATH_MAX _POSIX_PATH_MAX
167 # elif defined (MAXPATHLEN)
168 # define PATH_MAX MAXPATHLEN
169 # else
170 # define PATH_MAX 1024
171 # endif
172 #endif
173
174 #define MAX_READLINKS 32
175
176 char * xrealpath (const char *path, char resolved_path []);
177 char *
178 xrealpath (const char *path, char resolved_path [])
179 { 180 {
180 char copy_path[PATH_MAX]; 181 Intbyte copy_path[PATH_MAX];
181 char *new_path = resolved_path; 182 Intbyte *new_path = resolved_path;
182 char *max_path; 183 Intbyte *max_path;
183 #if defined (S_IFLNK) || defined (WIN32_NATIVE) 184 #if defined (HAVE_READLINK) || defined (WIN32_NATIVE)
184 int readlinks = 0; 185 int readlinks = 0;
185 char link_path[PATH_MAX]; 186 Intbyte link_path[PATH_MAX];
186 int n; 187 int n;
187 int abslen = ABS_LENGTH (path); 188 int abslen = ABS_LENGTH (path);
188 #endif 189 #endif
189 190
190 /* Make a copy of the source path since we may need to modify it. */ 191 /* Make a copy of the source path since we may need to modify it. */
191 strcpy (copy_path, path); 192 qxestrcpy (copy_path, path);
192 path = copy_path; 193 path = copy_path;
193 max_path = copy_path + PATH_MAX - 2; 194 max_path = copy_path + PATH_MAX - 2;
194 195
195 #ifdef WIN32_NATIVE 196 #ifdef WIN32_NATIVE
196 /* Check for c:/... or //server/... */ 197 /* Check for c:/... or //server/... */
197 if (abslen == 2 || abslen == 3) 198 if (abslen == 2 || abslen == 3)
198 { 199 {
199 strncpy (new_path, path, abslen); 200 qxestrncpy (new_path, path, abslen);
200 /* Make sure drive letter is lowercased. */ 201 /* Make sure drive letter is lowercased. */
201 if (abslen == 3) 202 if (abslen == 3)
202 *new_path = tolower (*new_path); 203 *new_path = tolower (*new_path);
203 new_path += abslen; 204 new_path += abslen;
204 path += abslen; 205 path += abslen;
205 } 206 }
206 /* No drive letter, but a beginning slash? Prepend drive letter. */ 207 /* No drive letter, but a beginning slash? Prepend drive letter. */
207 else if (abslen == 1) 208 else if (abslen == 1)
208 { 209 {
209 getcwd (new_path, PATH_MAX - 1); 210 get_initial_directory (new_path, PATH_MAX - 1);
210 new_path += 3; 211 new_path += 3;
211 path++; 212 path++;
212 } 213 }
213 /* Just a path name, prepend the current directory */ 214 /* Just a path name, prepend the current directory */
214 else 215 else
215 { 216 {
216 getcwd (new_path, PATH_MAX - 1); 217 get_initial_directory (new_path, PATH_MAX - 1);
217 new_path += strlen (new_path); 218 new_path += qxestrlen (new_path);
218 if (!IS_DIRECTORY_SEP (new_path[-1])) 219 if (!IS_DIRECTORY_SEP (new_path[-1]))
219 *new_path++ = DIRECTORY_SEP; 220 *new_path++ = DIRECTORY_SEP;
220 } 221 }
221 #else 222 #else
222 /* If it's a relative pathname use getcwd for starters. */ 223 /* If it's a relative pathname use get_initial_directory for starters. */
223 if (abslen == 0) 224 if (abslen == 0)
224 { 225 {
225 getcwd (new_path, PATH_MAX - 1); 226 get_initial_directory (new_path, PATH_MAX - 1);
226 new_path += strlen (new_path); 227 new_path += qxestrlen (new_path);
227 if (!IS_DIRECTORY_SEP (new_path[-1])) 228 if (!IS_DIRECTORY_SEP (new_path[-1]))
228 *new_path++ = DIRECTORY_SEP; 229 *new_path++ = DIRECTORY_SEP;
229 } 230 }
230 else 231 else
231 { 232 {
232 /* Copy first directory sep. May have two on cygwin. */ 233 /* Copy first directory sep. May have two on cygwin. */
233 strncpy (new_path, path, abslen); 234 qxestrncpy (new_path, path, abslen);
234 new_path += abslen; 235 new_path += abslen;
235 path += abslen; 236 path += abslen;
236 } 237 }
237 #endif 238 #endif
238 /* Expand each slash-separated pathname component. */ 239 /* Expand each slash-separated pathname component. */
281 return NULL; 282 return NULL;
282 } 283 }
283 *new_path++ = *path++; 284 *new_path++ = *path++;
284 } 285 }
285 286
286 #if defined (S_IFLNK) || defined (WIN32_NATIVE) 287 #if defined (HAVE_READLINK) || defined (WIN32_NATIVE)
287 /* See if latest pathname component is a symlink. */ 288 /* See if latest pathname component is a symlink or needs case
289 correction. */
288 *new_path = '\0'; 290 *new_path = '\0';
289 n = system_readlink (resolved_path, link_path, PATH_MAX - 1); 291 n = readlink_and_correct_case (resolved_path, link_path, PATH_MAX - 1);
290 292
291 if (n < 0) 293 if (n < 0)
292 { 294 {
293 /* EINVAL means the file exists but isn't a symlink. */ 295 /* EINVAL means the file exists but isn't a symlink or doesn't
296 need case correction. */
294 #ifdef CYGWIN 297 #ifdef CYGWIN
295 if (errno != EINVAL && errno != ENOENT) 298 if (errno != EINVAL && errno != ENOENT)
296 #else 299 #else
297 if (errno != EINVAL) 300 if (errno != EINVAL)
298 #endif 301 #endif
317 /* Otherwise back up over this component. */ 320 /* Otherwise back up over this component. */
318 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path) 321 for (--new_path; !IS_DIRECTORY_SEP (*new_path); --new_path)
319 assert (new_path > resolved_path); 322 assert (new_path > resolved_path);
320 323
321 /* Safe sex check. */ 324 /* Safe sex check. */
322 if (strlen(path) + n >= PATH_MAX) 325 if (qxestrlen (path) + n >= PATH_MAX)
323 { 326 {
324 errno = ENAMETOOLONG; 327 errno = ENAMETOOLONG;
325 return NULL; 328 return NULL;
326 } 329 }
327 330
328 /* Insert symlink contents into path. */ 331 /* Insert symlink contents into path. */
329 strcat(link_path, path); 332 qxestrcat (link_path, path);
330 strcpy(copy_path, link_path); 333 qxestrcpy (copy_path, link_path);
331 path = copy_path; 334 path = copy_path;
332 } 335 }
333 #endif /* S_IFLNK || WIN32_NATIVE */ 336 #endif /* HAVE_READLINK || WIN32_NATIVE */
334 *new_path++ = DIRECTORY_SEP; 337 *new_path++ = DIRECTORY_SEP;
335 } 338 }
336 339
337 /* Delete trailing slash but don't whomp a lone slash. */ 340 /* Delete trailing slash but don't whomp a lone slash. */
338 if (new_path != ABS_START (resolved_path) && IS_DIRECTORY_SEP (new_path[-1])) 341 if (new_path != ABS_START (resolved_path) &&
342 IS_DIRECTORY_SEP (new_path[-1]))
339 new_path--; 343 new_path--;
340 344
341 /* Make sure it's null terminated. */ 345 /* Make sure it's null terminated. */
342 *new_path = '\0'; 346 *new_path = '\0';
343 347