1 /* Integration test to ensure we issue a FILE * leak diagnostic for
2 this particular non-trivial case.
3 Adapted from intl/localealias.c, with all #includes removed. */
4
5 /* { dg-do "compile" } */
6 /* { dg-additional-options "-Wno-analyzer-too-complex" } */
7 /* TODO: remove the need for this option. */
8 /* { dg-require-effective-target alloca } */
9
10 /* Handle aliases for locale names.
11 Copyright (C) 1995-1999, 2000-2001, 2003 Free Software Foundation, Inc.
12
13 This program is free software; you can redistribute it and/or modify it
14 under the terms of the GNU Library General Public License as published
15 by the Free Software Foundation; either version 2, or (at your option)
16 any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Library General Public License for more details.
22
23 You should have received a copy of the GNU Library General Public
24 License along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
26 USA. */
27
28 /* Minimal version of system headers. */
29
30 typedef __SIZE_TYPE__ size_t;
31 #define NULL ((void *) 0)
32
33 #define PATH_SEPARATOR ':'
34 typedef struct _IO_FILE FILE;
35 extern FILE *fopen(const char *__restrict __filename,
36 const char *__restrict __modes);
37 extern int feof_unlocked(FILE *__stream) __attribute__((__nothrow__, __leaf__));
38 extern char *fgets_unlocked(char *__restrict __s, int __n,
39 FILE *__restrict __stream);
40 extern int fclose(FILE *__stream);
41
42 #define alloca __builtin_alloca
43
44 extern char *strchr(const char *__s, int __c)
45 __attribute__((__nothrow__, __leaf__)) __attribute__((__pure__))
46 __attribute__((__nonnull__(1)));
47 extern void *memcpy(void *__restrict __dest, const void *__restrict __src,
48 size_t __n) __attribute__((__nothrow__, __leaf__))
49 __attribute__((__nonnull__(1, 2)));
50 extern void *mempcpy(void *__restrict __dest, const void *__restrict __src,
51 size_t __n) __attribute__((__nothrow__, __leaf__))
52 __attribute__((__nonnull__(1, 2)));
53 #define HAVE_MEMPCPY 1
54 extern size_t strlen(const char *__s) __attribute__((__nothrow__, __leaf__))
55 __attribute__((__pure__)) __attribute__((__nonnull__(1)));
56
57 extern int strcasecmp(const char *__s1, const char *__s2)
58 __attribute__((__nothrow__, __leaf__)) __attribute__((__pure__))
59 __attribute__((__nonnull__(1, 2)));
60
61 extern int isspace(int) __attribute__((__nothrow__, __leaf__));
62
63 extern void *realloc(void *__ptr, size_t __size)
64 __attribute__((__nothrow__, __leaf__))
65 __attribute__((__warn_unused_result__));
66
67 typedef int (*__compar_fn_t)(const void *, const void *);
68 extern void *bsearch(const void *__key, const void *__base, size_t __nmemb,
69 size_t __size, __compar_fn_t __compar)
70 __attribute__((__nonnull__(1, 2, 5)));
71
72 extern __inline __attribute__((__gnu_inline__)) void *
bsearch(const void * __key,const void * __base,size_t __nmemb,size_t __size,__compar_fn_t __compar)73 bsearch(const void *__key, const void *__base, size_t __nmemb, size_t __size,
74 __compar_fn_t __compar) {
75 size_t __l, __u, __idx;
76 const void *__p;
77 int __comparison;
78
79 __l = 0;
80 __u = __nmemb;
81 while (__l < __u) {
82 __idx = (__l + __u) / 2;
83 __p = (void *)(((const char *)__base) + (__idx * __size));
84 __comparison = (*__compar)(__key, __p);
85 if (__comparison < 0)
86 __u = __idx;
87 else if (__comparison > 0)
88 __l = __idx + 1;
89 else
90 return (void *)__p;
91 }
92
93 return ((void *)0);
94 }
95
96 extern void qsort(void *__base, size_t __nmemb, size_t __size,
97 __compar_fn_t __compar) __attribute__((__nonnull__(1, 4)));
98
99 /* Minimal version of intl headers. */
100
101 #define PARAMS(args) args
102
103 #define relocate libintl_relocate
104 extern const char *libintl_relocate(const char *pathname);
105
106 #define LOCALE_ALIAS_PATH "value for LOCALE_ALIAS_PATH"
107
108 /* Cleaned-up body of localealias.c follows. */
109
110 #ifndef internal_function
111 # define internal_function
112 #endif
113
114 /* Some optimizations for glibc. */
115 # define FEOF(fp) feof_unlocked (fp)
116 # define FGETS(buf, n, fp) fgets_unlocked (buf, n, fp)
117
118 /* For those losing systems which don't have `alloca' we have to add
119 some additional code emulating it. */
120 # define freea(p) /* nothing */
121
122 struct alias_map
123 {
124 const char *alias;
125 const char *value;
126 };
127
128 # define libc_freeres_ptr(decl) decl
129
130 libc_freeres_ptr (static char *string_space);
131 static size_t string_space_act;
132 static size_t string_space_max;
133 libc_freeres_ptr (static struct alias_map *map);
134 static size_t nmap;
135 static size_t maxmap;
136
137
138 /* Prototypes for local functions. */
139 static size_t read_alias_file PARAMS ((const char *fname, int fname_len))
140 internal_function;
141 static int extend_alias_table PARAMS ((void));
142 static int alias_compare PARAMS ((const struct alias_map *map1,
143 const struct alias_map *map2));
144
145
146 const char *
_nl_expand_alias(name)147 _nl_expand_alias (name)
148 const char *name;
149 {
150 static const char *locale_alias_path;
151 struct alias_map *retval;
152 const char *result = NULL;
153 size_t added;
154
155 #ifdef _LIBC
156 __libc_lock_lock (lock);
157 #endif
158
159 if (locale_alias_path == NULL)
160 locale_alias_path = LOCALE_ALIAS_PATH;
161
162 do
163 {
164 struct alias_map item;
165
166 item.alias = name;
167
168 if (nmap > 0)
169 retval = (struct alias_map *) bsearch (&item, map, nmap,
170 sizeof (struct alias_map),
171 (int (*) PARAMS ((const void *,
172 const void *))
173 ) alias_compare);
174 else
175 retval = NULL;
176
177 /* We really found an alias. Return the value. */
178 if (retval != NULL)
179 {
180 result = retval->value;
181 break;
182 }
183
184 /* Perhaps we can find another alias file. */
185 added = 0;
186 while (added == 0 && locale_alias_path[0] != '\0')
187 {
188 const char *start;
189
190 while (locale_alias_path[0] == PATH_SEPARATOR)
191 ++locale_alias_path;
192 start = locale_alias_path;
193
194 while (locale_alias_path[0] != '\0'
195 && locale_alias_path[0] != PATH_SEPARATOR)
196 ++locale_alias_path;
197
198 if (start < locale_alias_path)
199 added = read_alias_file (start, locale_alias_path - start);
200 }
201 }
202 while (added != 0);
203
204 #ifdef _LIBC
205 __libc_lock_unlock (lock);
206 #endif
207
208 return result;
209 }
210
211
212 static size_t
213 internal_function
read_alias_file(fname,fname_len)214 read_alias_file (fname, fname_len)
215 const char *fname;
216 int fname_len;
217 {
218 FILE *fp;
219 char *full_fname;
220 size_t added;
221 static const char aliasfile[] = "/locale.alias";
222
223 full_fname = (char *) alloca (fname_len + sizeof aliasfile);
224 #ifdef HAVE_MEMPCPY
225 mempcpy (mempcpy (full_fname, fname, fname_len),
226 aliasfile, sizeof aliasfile);
227 #else
228 memcpy (full_fname, fname, fname_len);
229 memcpy (&full_fname[fname_len], aliasfile, sizeof aliasfile);
230 #endif
231
232 fp = fopen (relocate (full_fname), "r"); /* { dg-message "opened here" } */
233 freea (full_fname);
234 if (fp == NULL)
235 return 0;
236
237 #ifdef HAVE___FSETLOCKING
238 /* No threads present. */
239 __fsetlocking (fp, FSETLOCKING_BYCALLER);
240 #endif
241
242 added = 0;
243 while (!FEOF (fp))
244 {
245 /* It is a reasonable approach to use a fix buffer here because
246 a) we are only interested in the first two fields
247 b) these fields must be usable as file names and so must not
248 be that long
249 We avoid a multi-kilobyte buffer here since this would use up
250 stack space which we might not have if the program ran out of
251 memory. */
252 char buf[400];
253 char *alias;
254 char *value;
255 char *cp;
256
257 if (FGETS (buf, sizeof buf, fp) == NULL)
258 /* EOF reached. */
259 break;
260
261 cp = buf;
262 /* Ignore leading white space. */
263 while (isspace ((unsigned char) cp[0]))
264 ++cp;
265
266 /* A leading '#' signals a comment line. */
267 if (cp[0] != '\0' && cp[0] != '#')
268 {
269 alias = cp++;
270 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
271 ++cp;
272 /* Terminate alias name. */
273 if (cp[0] != '\0')
274 *cp++ = '\0';
275
276 /* Now look for the beginning of the value. */
277 while (isspace ((unsigned char) cp[0]))
278 ++cp;
279
280 if (cp[0] != '\0')
281 {
282 size_t alias_len;
283 size_t value_len;
284
285 value = cp++;
286 while (cp[0] != '\0' && !isspace ((unsigned char) cp[0]))
287 ++cp;
288 /* Terminate value. */
289 if (cp[0] == '\n')
290 {
291 /* This has to be done to make the following test
292 for the end of line possible. We are looking for
293 the terminating '\n' which do not overwrite here. */
294 *cp++ = '\0';
295 *cp = '\n';
296 }
297 else if (cp[0] != '\0')
298 *cp++ = '\0';
299
300 if (nmap >= maxmap)
301 if (__builtin_expect (extend_alias_table (), 0))
302 return added; /* { dg-warning "leak of FILE 'fp'" } */
303
304 alias_len = strlen (alias) + 1;
305 value_len = strlen (value) + 1;
306
307 if (string_space_act + alias_len + value_len > string_space_max)
308 {
309 /* Increase size of memory pool. */
310 size_t new_size = (string_space_max
311 + (alias_len + value_len > 1024
312 ? alias_len + value_len : 1024));
313 char *new_pool = (char *) realloc (string_space, new_size);
314 if (new_pool == NULL)
315 return added;
316
317 if (__builtin_expect (string_space != new_pool, 0))
318 {
319 size_t i;
320
321 for (i = 0; i < nmap; i++)
322 {
323 map[i].alias += new_pool - string_space;
324 map[i].value += new_pool - string_space;
325 }
326 }
327
328 string_space = new_pool;
329 string_space_max = new_size;
330 }
331
332 map[nmap].alias = memcpy (&string_space[string_space_act],
333 alias, alias_len);
334 string_space_act += alias_len;
335
336 map[nmap].value = memcpy (&string_space[string_space_act],
337 value, value_len);
338 string_space_act += value_len;
339
340 ++nmap;
341 ++added;
342 }
343 }
344
345 /* Possibly not the whole line fits into the buffer. Ignore
346 the rest of the line. */
347 while (strchr (buf, '\n') == NULL)
348 if (FGETS (buf, sizeof buf, fp) == NULL)
349 /* Make sure the inner loop will be left. The outer loop
350 will exit at the `feof' test. */
351 break;
352 }
353
354 /* Should we test for ferror()? I think we have to silently ignore
355 errors. --drepper */
356 fclose (fp);
357
358 if (added > 0)
359 qsort (map, nmap, sizeof (struct alias_map),
360 (int (*) PARAMS ((const void *, const void *))) alias_compare);
361
362 return added;
363 }
364
365
366 static int
extend_alias_table()367 extend_alias_table ()
368 {
369 size_t new_size;
370 struct alias_map *new_map;
371
372 new_size = maxmap == 0 ? 100 : 2 * maxmap;
373 new_map = (struct alias_map *) realloc (map, (new_size
374 * sizeof (struct alias_map)));
375 if (new_map == NULL)
376 /* Simply don't extend: we don't have any more core. */
377 return -1;
378
379 map = new_map;
380 maxmap = new_size;
381 return 0;
382 }
383
384
385 static int
alias_compare(map1,map2)386 alias_compare (map1, map2)
387 const struct alias_map *map1;
388 const struct alias_map *map2;
389 {
390 return strcasecmp (map1->alias, map2->alias);
391 }
392