Merge branch 'upstream' into stable
[gnulib.git] / lib / areadlink.c
1 /* areadlink.c -- readlink wrapper to return the link name in malloc'd storage
2    Unlike xreadlink and xreadlink_with_size, don't ever call exit.
3
4    Copyright (C) 2001, 2003-2007, 2009-2011 Free Software Foundation, Inc.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 /* Written by Jim Meyering <jim@meyering.net>
20    and Bruno Haible <bruno@clisp.org>.  */
21
22 #include <config.h>
23
24 /* Specification.  */
25 #include "areadlink.h"
26
27 #include <errno.h>
28 #include <limits.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #ifndef SSIZE_MAX
35 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
36 #endif
37
38 /* Use the system functions, not the gnulib overrides in this file.  */
39 #undef malloc
40 #undef realloc
41
42 /* The initial buffer size for the link value.  A power of 2
43    detects arithmetic overflow earlier, but is not required.  */
44 enum {
45   INITIAL_BUF_SIZE = 1024
46 };
47
48 /* Call readlink to get the symbolic link value of FILENAME.
49    Return a pointer to that NUL-terminated string in malloc'd storage.
50    If readlink fails, return NULL and set errno.
51    If realloc fails, or if the link value is longer than SIZE_MAX :-),
52    return NULL and set errno to ENOMEM.  */
53
54 char *
55 areadlink (char const *filename)
56 {
57   /* Allocate the initial buffer on the stack.  This way, in the common
58      case of a symlink of small size, we get away with a single small malloc()
59      instead of a big malloc() followed by a shrinking realloc().  */
60   char initial_buf[INITIAL_BUF_SIZE];
61
62   char *buffer = initial_buf;
63   size_t buf_size = sizeof initial_buf;
64
65   while (1)
66     {
67       /* Attempt to read the link into the current buffer.  */
68       ssize_t link_length = readlink (filename, buffer, buf_size);
69
70       /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
71          with errno == ERANGE if the buffer is too small.  */
72       if (link_length < 0 && errno != ERANGE)
73         {
74           if (buffer != initial_buf)
75             {
76               int saved_errno = errno;
77               free (buffer);
78               errno = saved_errno;
79             }
80           return NULL;
81         }
82
83       if ((size_t) link_length < buf_size)
84         {
85           buffer[link_length++] = '\0';
86
87           /* Return it in a chunk of memory as small as possible.  */
88           if (buffer == initial_buf)
89             {
90               buffer = (char *) malloc (link_length);
91               if (buffer == NULL)
92                 {
93                   /* It's easier to set errno to ENOMEM than to rely on the
94                      'malloc-posix' gnulib module.  */
95                   errno = ENOMEM;
96                   return NULL;
97                 }
98               memcpy (buffer, initial_buf, link_length);
99             }
100           else
101             {
102               /* Shrink buffer before returning it.  */
103               if ((size_t) link_length < buf_size)
104                 {
105                   char *smaller_buffer = (char *) realloc (buffer, link_length);
106
107                   if (smaller_buffer != NULL)
108                     buffer = smaller_buffer;
109                 }
110             }
111           return buffer;
112         }
113
114       if (buffer != initial_buf)
115         free (buffer);
116       buf_size *= 2;
117       if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))
118         {
119           errno = ENOMEM;
120           return NULL;
121         }
122       buffer = (char *) malloc (buf_size);
123       if (buffer == NULL)
124         {
125           /* It's easier to set errno to ENOMEM than to rely on the
126              'malloc-posix' gnulib module.  */
127           errno = ENOMEM;
128           return NULL;
129         }
130     }
131 }