]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gnulib/import/readlink.c
Update gnulib
[thirdparty/binutils-gdb.git] / gnulib / import / readlink.c
CommitLineData
9c9d63b1 1/* Read the contents of a symbolic link.
dc6c21da 2 Copyright (C) 2003-2007, 2009-2022 Free Software Foundation, Inc.
2196f55f 3
dc6c21da
TT
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
2196f55f 8
dc6c21da 9 This file is distributed in the hope that it will be useful,
2196f55f
YQ
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
dc6c21da 12 GNU Lesser General Public License for more details.
2196f55f 13
dc6c21da 14 You should have received a copy of the GNU Lesser General Public License
c0c3707f 15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
2196f55f
YQ
16
17#include <config.h>
18
19/* Specification. */
20#include <unistd.h>
21
22#include <errno.h>
23#include <string.h>
24#include <sys/stat.h>
25
26#if !HAVE_READLINK
27
28/* readlink() substitute for systems that don't have a readlink() function,
29 such as DJGPP 2.03 and mingw32. */
30
31ssize_t
dc6c21da
TT
32readlink (char const *file, _GL_UNUSED char *buf,
33 _GL_UNUSED size_t bufsize)
2196f55f
YQ
34{
35 struct stat statbuf;
36
37 /* In general we should use lstat() here, not stat(). But on platforms
38 without symbolic links, lstat() - if it exists - would be equivalent to
39 stat(), therefore we can use stat(). This saves us a configure check. */
9c9d63b1 40 if (stat (file, &statbuf) >= 0)
2196f55f
YQ
41 errno = EINVAL;
42 return -1;
43}
44
45#else /* HAVE_READLINK */
46
47# undef readlink
48
49/* readlink() wrapper that uses correct types, for systems like cygwin
50 1.5.x where readlink returns int, and which rejects trailing slash,
51 for Solaris 9. */
52
53ssize_t
9c9d63b1 54rpl_readlink (char const *file, char *buf, size_t bufsize)
2196f55f
YQ
55{
56# if READLINK_TRAILING_SLASH_BUG
9c9d63b1
PM
57 size_t file_len = strlen (file);
58 if (file_len && file[file_len - 1] == '/')
2196f55f 59 {
9c9d63b1 60 /* Even if FILE without the slash is a symlink to a directory,
2196f55f
YQ
61 both lstat() and stat() must resolve the trailing slash to
62 the directory rather than the symlink. We can therefore
63 safely use stat() to distinguish between EINVAL and
64 ENOTDIR/ENOENT, avoiding extra overhead of rpl_lstat(). */
65 struct stat st;
9c9d63b1 66 if (stat (file, &st) == 0 || errno == EOVERFLOW)
2196f55f
YQ
67 errno = EINVAL;
68 return -1;
69 }
70# endif /* READLINK_TRAILING_SLASH_BUG */
9c9d63b1
PM
71
72 ssize_t r = readlink (file, buf, bufsize);
73
74# if READLINK_TRUNCATE_BUG
75 if (r < 0 && errno == ERANGE)
76 {
77 /* Try again with a bigger buffer. This is just for test cases;
78 real code invariably discards short reads. */
79 char stackbuf[4032];
80 r = readlink (file, stackbuf, sizeof stackbuf);
81 if (r < 0)
82 {
83 if (errno == ERANGE)
84 {
85 /* Clear the buffer, which is good enough for real code.
86 Thankfully, no test cases try short reads of enormous
87 symlinks and what would be the point anyway? */
88 r = bufsize;
89 memset (buf, 0, r);
90 }
91 }
92 else
93 {
94 if (bufsize < r)
95 r = bufsize;
96 memcpy (buf, stackbuf, r);
97 }
98 }
99# endif
100
101 return r;
2196f55f
YQ
102}
103
104#endif /* HAVE_READLINK */