summaryrefslogtreecommitdiff
path: root/gl/lib/areadlink-with-size.c
diff options
context:
space:
mode:
Diffstat (limited to 'gl/lib/areadlink-with-size.c')
-rw-r--r--gl/lib/areadlink-with-size.c36
1 files changed, 30 insertions, 6 deletions
diff --git a/gl/lib/areadlink-with-size.c b/gl/lib/areadlink-with-size.c
index eacad3f6..95a28b31 100644
--- a/gl/lib/areadlink-with-size.c
+++ b/gl/lib/areadlink-with-size.c
@@ -26,6 +26,7 @@
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#ifndef SSIZE_MAX
@@ -60,18 +61,28 @@ areadlink_with_size (char const *file, size_t size)
? symlink_max + 1
: INITIAL_LIMIT_BOUND);
+ enum { stackbuf_size = 128 };
+
/* The initial buffer size for the link value. */
- size_t buf_size = size < initial_limit ? size + 1 : initial_limit;
+ size_t buf_size = (size == 0 ? stackbuf_size
+ : size < initial_limit ? size + 1 : initial_limit);
while (1)
{
ssize_t r;
size_t link_length;
- char *buffer = malloc (buf_size);
+ char stackbuf[stackbuf_size];
+ char *buf = stackbuf;
+ char *buffer = NULL;
+
+ if (! (size == 0 && buf_size == stackbuf_size))
+ {
+ buf = buffer = malloc (buf_size);
+ if (!buffer)
+ return NULL;
+ }
- if (buffer == NULL)
- return NULL;
- r = readlink (file, buffer, buf_size);
+ r = readlink (file, buf, buf_size);
link_length = r;
/* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
@@ -86,7 +97,20 @@ areadlink_with_size (char const *file, size_t size)
if (link_length < buf_size)
{
- buffer[link_length] = 0;
+ buf[link_length] = 0;
+ if (!buffer)
+ {
+ buffer = malloc (link_length + 1);
+ if (buffer)
+ return memcpy (buffer, buf, link_length + 1);
+ }
+ else if (link_length + 1 < buf_size)
+ {
+ /* Shrink BUFFER before returning it. */
+ char *shrinked_buffer = realloc (buffer, link_length + 1);
+ if (shrinked_buffer != NULL)
+ buffer = shrinked_buffer;
+ }
return buffer;
}