intprops: Avoid bogus "warning: division by zero is undefined" on clang.
[gnulib.git] / lib / canonicalize-lgpl.c
blob0b89d2a18426761c42a82f8fa074a79918fb948a
1 /* Return the canonical absolute name of a given file.
2 Copyright (C) 1996-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 #ifndef _LIBC
19 /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
20 optimizes away the name == NULL test below. */
21 # define _GL_ARG_NONNULL(params)
23 # define _GL_USE_STDLIB_ALLOC 1
24 # include <config.h>
25 #endif
27 #if !HAVE_CANONICALIZE_FILE_NAME || !FUNC_REALPATH_WORKS || defined _LIBC
29 /* Specification. */
30 #include <stdlib.h>
32 #include <alloca.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <limits.h>
36 #if HAVE_SYS_PARAM_H || defined _LIBC
37 # include <sys/param.h>
38 #endif
39 #include <sys/stat.h>
40 #include <errno.h>
41 #include <stddef.h>
43 #ifdef _LIBC
44 # include <shlib-compat.h>
45 #else
46 # define SHLIB_COMPAT(lib, introduced, obsoleted) 0
47 # define versioned_symbol(lib, local, symbol, version) extern int dummy
48 # define compat_symbol(lib, local, symbol, version)
49 # define weak_alias(local, symbol)
50 # define __canonicalize_file_name canonicalize_file_name
51 # define __realpath realpath
52 # include "pathmax.h"
53 # include "malloca.h"
54 # include "filename.h"
55 # if defined _WIN32 && !defined __CYGWIN__
56 # define __getcwd _getcwd
57 # elif HAVE_GETCWD
58 # if IN_RELOCWRAPPER
59 /* When building the relocatable program wrapper, use the system's getcwd
60 function, not the gnulib override, otherwise we would get a link error.
62 # undef getcwd
63 # endif
64 # if defined VMS && !defined getcwd
65 /* We want the directory in Unix syntax, not in VMS syntax.
66 The gnulib override of 'getcwd' takes 2 arguments; the original VMS
67 'getcwd' takes 3 arguments. */
68 # define __getcwd(buf, max) getcwd (buf, max, 0)
69 # else
70 # define __getcwd getcwd
71 # endif
72 # else
73 # define __getcwd(buf, max) getwd (buf)
74 # endif
75 # define __readlink readlink
76 # define __set_errno(e) errno = (e)
77 # ifndef MAXSYMLINKS
78 # ifdef SYMLOOP_MAX
79 # define MAXSYMLINKS SYMLOOP_MAX
80 # else
81 # define MAXSYMLINKS 20
82 # endif
83 # endif
84 #endif
86 #ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
87 # define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
88 #endif
90 /* Define this independently so that stdint.h is not a prerequisite. */
91 #ifndef SIZE_MAX
92 # define SIZE_MAX ((size_t) -1)
93 #endif
95 #if !FUNC_REALPATH_WORKS || defined _LIBC
97 static void
98 alloc_failed (void)
100 #if defined _WIN32 && ! defined __CYGWIN__
101 /* Avoid errno problem without using the malloc or realloc modules; see:
102 https://lists.gnu.org/r/bug-gnulib/2016-08/msg00025.html */
103 errno = ENOMEM;
104 #endif
107 /* Return the canonical absolute name of file NAME. A canonical name
108 does not contain any ".", ".." components nor any repeated path
109 separators ('/') or symlinks. All path components must exist. If
110 RESOLVED is null, the result is malloc'd; otherwise, if the
111 canonical name is PATH_MAX chars or more, returns null with 'errno'
112 set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
113 returns the name in RESOLVED. If the name cannot be resolved and
114 RESOLVED is non-NULL, it contains the path of the first component
115 that cannot be resolved. If the path can be resolved, RESOLVED
116 holds the same value as the value returned. */
118 char *
119 __realpath (const char *name, char *resolved)
121 char *rpath, *dest, *extra_buf = NULL;
122 const char *start, *end, *rpath_limit;
123 long int path_max;
124 int num_links = 0;
125 size_t prefix_len;
127 if (name == NULL)
129 /* As per Single Unix Specification V2 we must return an error if
130 either parameter is a null pointer. We extend this to allow
131 the RESOLVED parameter to be NULL in case the we are expected to
132 allocate the room for the return value. */
133 __set_errno (EINVAL);
134 return NULL;
137 if (name[0] == '\0')
139 /* As per Single Unix Specification V2 we must return an error if
140 the name argument points to an empty string. */
141 __set_errno (ENOENT);
142 return NULL;
145 #ifdef PATH_MAX
146 path_max = PATH_MAX;
147 #else
148 path_max = pathconf (name, _PC_PATH_MAX);
149 if (path_max <= 0)
150 path_max = 8192;
151 #endif
153 if (resolved == NULL)
155 rpath = malloc (path_max);
156 if (rpath == NULL)
158 alloc_failed ();
159 return NULL;
162 else
163 rpath = resolved;
164 rpath_limit = rpath + path_max;
166 /* This is always zero for Posix hosts, but can be 2 for MS-Windows
167 and MS-DOS X:/foo/bar file names. */
168 prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
170 if (!IS_ABSOLUTE_FILE_NAME (name))
172 if (!__getcwd (rpath, path_max))
174 rpath[0] = '\0';
175 goto error;
177 dest = strchr (rpath, '\0');
178 start = name;
179 prefix_len = FILE_SYSTEM_PREFIX_LEN (rpath);
181 else
183 dest = rpath;
184 if (prefix_len)
186 memcpy (rpath, name, prefix_len);
187 dest += prefix_len;
189 *dest++ = '/';
190 if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
192 if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len)
193 *dest++ = '/';
194 *dest = '\0';
196 start = name + prefix_len;
199 for (end = start; *start; start = end)
201 #ifdef _LIBC
202 struct stat64 st;
203 #else
204 struct stat st;
205 #endif
207 /* Skip sequence of multiple path-separators. */
208 while (ISSLASH (*start))
209 ++start;
211 /* Find end of path component. */
212 for (end = start; *end && !ISSLASH (*end); ++end)
213 /* Nothing. */;
215 if (end - start == 0)
216 break;
217 else if (end - start == 1 && start[0] == '.')
218 /* nothing */;
219 else if (end - start == 2 && start[0] == '.' && start[1] == '.')
221 /* Back up to previous component, ignore if at root already. */
222 if (dest > rpath + prefix_len + 1)
223 for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest)
224 continue;
225 if (DOUBLE_SLASH_IS_DISTINCT_ROOT
226 && dest == rpath + 1 && !prefix_len
227 && ISSLASH (*dest) && !ISSLASH (dest[1]))
228 dest++;
230 else
232 size_t new_size;
234 if (!ISSLASH (dest[-1]))
235 *dest++ = '/';
237 if (dest + (end - start) >= rpath_limit)
239 ptrdiff_t dest_offset = dest - rpath;
240 char *new_rpath;
242 if (resolved)
244 __set_errno (ENAMETOOLONG);
245 if (dest > rpath + prefix_len + 1)
246 dest--;
247 *dest = '\0';
248 goto error;
250 new_size = rpath_limit - rpath;
251 if (end - start + 1 > path_max)
252 new_size += end - start + 1;
253 else
254 new_size += path_max;
255 new_rpath = (char *) realloc (rpath, new_size);
256 if (new_rpath == NULL)
258 alloc_failed ();
259 goto error;
261 rpath = new_rpath;
262 rpath_limit = rpath + new_size;
264 dest = rpath + dest_offset;
267 #ifdef _LIBC
268 dest = __mempcpy (dest, start, end - start);
269 #else
270 memcpy (dest, start, end - start);
271 dest += end - start;
272 #endif
273 *dest = '\0';
275 /* FIXME: if lstat fails with errno == EOVERFLOW,
276 the entry exists. */
277 #ifdef _LIBC
278 if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
279 #else
280 if (lstat (rpath, &st) < 0)
281 #endif
282 goto error;
284 if (S_ISLNK (st.st_mode))
286 char *buf;
287 size_t len;
288 ssize_t n;
290 if (++num_links > MAXSYMLINKS)
292 __set_errno (ELOOP);
293 goto error;
296 buf = malloca (path_max);
297 if (!buf)
299 __set_errno (ENOMEM);
300 goto error;
303 n = __readlink (rpath, buf, path_max - 1);
304 if (n < 0)
306 int saved_errno = errno;
307 freea (buf);
308 __set_errno (saved_errno);
309 goto error;
311 buf[n] = '\0';
313 if (!extra_buf)
315 extra_buf = malloca (path_max);
316 if (!extra_buf)
318 freea (buf);
319 __set_errno (ENOMEM);
320 goto error;
324 len = strlen (end);
325 /* Check that n + len + 1 doesn't overflow and is <= path_max. */
326 if (n >= SIZE_MAX - len || n + len >= path_max)
328 freea (buf);
329 __set_errno (ENAMETOOLONG);
330 goto error;
333 /* Careful here, end may be a pointer into extra_buf... */
334 memmove (&extra_buf[n], end, len + 1);
335 name = end = memcpy (extra_buf, buf, n);
337 if (IS_ABSOLUTE_FILE_NAME (buf))
339 size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf);
341 if (pfxlen)
342 memcpy (rpath, buf, pfxlen);
343 dest = rpath + pfxlen;
344 *dest++ = '/'; /* It's an absolute symlink */
345 if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
347 if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen)
348 *dest++ = '/';
349 *dest = '\0';
351 /* Install the new prefix to be in effect hereafter. */
352 prefix_len = pfxlen;
354 else
356 /* Back up to previous component, ignore if at root
357 already: */
358 if (dest > rpath + prefix_len + 1)
359 for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest)
360 continue;
361 if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
362 && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len)
363 dest++;
366 else if (!S_ISDIR (st.st_mode) && *end != '\0')
368 __set_errno (ENOTDIR);
369 goto error;
373 if (dest > rpath + prefix_len + 1 && ISSLASH (dest[-1]))
374 --dest;
375 if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && !prefix_len
376 && ISSLASH (*dest) && !ISSLASH (dest[1]))
377 dest++;
378 *dest = '\0';
380 if (extra_buf)
381 freea (extra_buf);
383 return rpath;
385 error:
387 int saved_errno = errno;
388 if (extra_buf)
389 freea (extra_buf);
390 if (resolved == NULL)
391 free (rpath);
392 __set_errno (saved_errno);
394 return NULL;
396 versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
397 #endif /* !FUNC_REALPATH_WORKS || defined _LIBC */
400 #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
401 char *
402 attribute_compat_text_section
403 __old_realpath (const char *name, char *resolved)
405 if (resolved == NULL)
407 __set_errno (EINVAL);
408 return NULL;
411 return __realpath (name, resolved);
413 compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
414 #endif
417 char *
418 __canonicalize_file_name (const char *name)
420 return __realpath (name, NULL);
422 weak_alias (__canonicalize_file_name, canonicalize_file_name)
424 #else
426 /* This declaration is solely to ensure that after preprocessing
427 this file is never empty. */
428 typedef int dummy;
430 #endif