Mercurial > hg > octave-nkf > gnulib-hg
comparison lib/areadlinkat.c @ 12136:e81e96b1161c
areadlinkat: new module
* lib/at-func.c (FUNC_FAIL): New define.
(AT_FUNC_NAME, VALIDATE_FLAG): Use it rather than raw -1.
* modules/areadlinkat: New module.
* lib/linkat.c (areadlinkat): Move...
* lib/areadlinkat.c (areadlinkat): ...to new file.
* lib/areadlink.h (areadlinkat): Declare it.
* modules/linkat (Depends-on): Add areadlinkat.
* MODULES.html.sh (File system functions): Mention it.
* modules/areadlinkat-tests: New test.
* tests/test-areadlinkat.c: New file.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Wed, 07 Oct 2009 10:15:33 -0600 |
parents | |
children | 66ce99c59da1 |
comparison
equal
deleted
inserted
replaced
12135:1e701cd8a52d | 12136:e81e96b1161c |
---|---|
1 /* areadlink.c -- readlink wrapper to return the link name in malloc'd storage | |
2 Unlike xreadlinkat, only call exit on failure to change directory. | |
3 | |
4 Copyright (C) 2001, 2003-2007, 2009 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 and Eric Blake <ebb9@byu.net>. */ | |
22 | |
23 #include <config.h> | |
24 | |
25 /* Specification. */ | |
26 #include "areadlink.h" | |
27 | |
28 #include <string.h> | |
29 #include <errno.h> | |
30 #include <limits.h> | |
31 #include <sys/types.h> | |
32 #include <stdlib.h> | |
33 #include <unistd.h> | |
34 | |
35 #ifndef SSIZE_MAX | |
36 # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) | |
37 #endif | |
38 | |
39 #if HAVE_READLINKAT | |
40 | |
41 /* Call readlinkat to get the symbolic link value of FILENAME relative to FD. | |
42 Return a pointer to that NUL-terminated string in malloc'd storage. | |
43 If readlinkat fails, return NULL and set errno (although failure to | |
44 change directory will issue a diagnostic and exit). | |
45 If realloc fails, or if the link value is longer than SIZE_MAX :-), | |
46 return NULL and set errno to ENOMEM. */ | |
47 | |
48 char * | |
49 areadlinkat (int fd, char const *filename) | |
50 { | |
51 /* The initial buffer size for the link value. A power of 2 | |
52 detects arithmetic overflow earlier, but is not required. */ | |
53 # define INITIAL_BUF_SIZE 1024 | |
54 | |
55 /* Allocate the initial buffer on the stack. This way, in the common | |
56 case of a symlink of small size, we get away with a single small malloc() | |
57 instead of a big malloc() followed by a shrinking realloc(). */ | |
58 char initial_buf[INITIAL_BUF_SIZE]; | |
59 | |
60 char *buffer = initial_buf; | |
61 size_t buf_size = sizeof (initial_buf); | |
62 | |
63 while (1) | |
64 { | |
65 /* Attempt to read the link into the current buffer. */ | |
66 ssize_t link_length = readlinkat (fd, filename, buffer, buf_size); | |
67 | |
68 /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1 | |
69 with errno == ERANGE if the buffer is too small. */ | |
70 if (link_length < 0 && errno != ERANGE) | |
71 { | |
72 if (buffer != initial_buf) | |
73 { | |
74 int saved_errno = errno; | |
75 free (buffer); | |
76 errno = saved_errno; | |
77 } | |
78 return NULL; | |
79 } | |
80 | |
81 if ((size_t) link_length < buf_size) | |
82 { | |
83 buffer[link_length++] = '\0'; | |
84 | |
85 /* Return it in a chunk of memory as small as possible. */ | |
86 if (buffer == initial_buf) | |
87 { | |
88 buffer = (char *) malloc (link_length); | |
89 if (buffer == NULL) | |
90 /* errno is ENOMEM. */ | |
91 return NULL; | |
92 memcpy (buffer, initial_buf, link_length); | |
93 } | |
94 else | |
95 { | |
96 /* Shrink buffer before returning it. */ | |
97 if ((size_t) link_length < buf_size) | |
98 { | |
99 char *smaller_buffer = (char *) realloc (buffer, link_length); | |
100 | |
101 if (smaller_buffer != NULL) | |
102 buffer = smaller_buffer; | |
103 } | |
104 } | |
105 return buffer; | |
106 } | |
107 | |
108 if (buffer != initial_buf) | |
109 free (buffer); | |
110 buf_size *= 2; | |
111 if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0)) | |
112 { | |
113 errno = ENOMEM; | |
114 return NULL; | |
115 } | |
116 buffer = (char *) malloc (buf_size); | |
117 if (buffer == NULL) | |
118 /* errno is ENOMEM. */ | |
119 return NULL; | |
120 } | |
121 } | |
122 | |
123 #else /* !HAVE_READLINKAT */ | |
124 | |
125 /* It is more efficient to change directories only once and call | |
126 areadlink, rather than repeatedly call the replacement | |
127 readlinkat. */ | |
128 | |
129 # define AT_FUNC_NAME areadlinkat | |
130 # define AT_FUNC_F1 areadlink | |
131 # define AT_FUNC_POST_FILE_PARAM_DECLS /* empty */ | |
132 # define AT_FUNC_POST_FILE_ARGS /* empty */ | |
133 # define AT_FUNC_RESULT char * | |
134 # define AT_FUNC_FAIL NULL | |
135 # include "at-func.c" | |
136 # undef AT_FUNC_NAME | |
137 # undef AT_FUNC_F1 | |
138 # undef AT_FUNC_POST_FILE_PARAM_DECLS | |
139 # undef AT_FUNC_POST_FILE_ARGS | |
140 # undef AT_FUNC_RESULT | |
141 # undef AT_FUNC_FAIL | |
142 | |
143 #endif /* !HAVE_READLINKAT */ |