Define and use $(run-built-tests).
[platform/upstream/glibc.git] / catgets / open_catalog.c
1 /* Copyright (C) 1996-2000, 2001, 2002, 2003 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Ulrich Drepper, <drepper@gnu.org>.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #include <byteswap.h>
20 #include <endian.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #ifdef _POSIX_MAPPED_FILES
27 # include <sys/mman.h>
28 #endif
29 #include <sys/stat.h>
30
31 #include "catgetsinfo.h"
32 #include <not-cancel.h>
33
34
35 #define SWAPU32(w) bswap_32 (w)
36
37
38 int
39 __open_catalog (const char *cat_name, const char *nlspath, const char *env_var,
40                 __nl_catd catalog)
41 {
42   int fd = -1;
43   struct stat64 st;
44   int swapping;
45   size_t cnt;
46   size_t max_offset;
47   size_t tab_size;
48   const char *lastp;
49   int result = -1;
50
51   if (strchr (cat_name, '/') != NULL || nlspath == NULL)
52     fd = open_not_cancel_2 (cat_name, O_RDONLY);
53   else
54     {
55       const char *run_nlspath = nlspath;
56 #define ENOUGH(n)                                                             \
57   if (__builtin_expect (bufact + (n) >= bufmax, 0))                           \
58     {                                                                         \
59       char *old_buf = buf;                                                    \
60       bufmax += 256 + (n);                                                    \
61       buf = (char *) alloca (bufmax);                                         \
62       memcpy (buf, old_buf, bufact);                                          \
63     }
64
65       /* The RUN_NLSPATH variable contains a colon separated list of
66          descriptions where we expect to find catalogs.  We have to
67          recognize certain % substitutions and stop when we found the
68          first existing file.  */
69       char *buf;
70       size_t bufact;
71       size_t bufmax;
72       size_t len;
73
74       buf = NULL;
75       bufmax = 0;
76
77       fd = -1;
78       while (*run_nlspath != '\0')
79         {
80           bufact = 0;
81
82           if (*run_nlspath == ':')
83             {
84               /* Leading colon or adjacent colons - treat same as %N.  */
85               len = strlen (cat_name);
86               ENOUGH (len);
87               memcpy (&buf[bufact], cat_name, len);
88               bufact += len;
89             }
90           else
91             while (*run_nlspath != ':' && *run_nlspath != '\0')
92               if (*run_nlspath == '%')
93                 {
94                   const char *tmp;
95
96                   ++run_nlspath;        /* We have seen the `%'.  */
97                   switch (*run_nlspath++)
98                     {
99                     case 'N':
100                       /* Use the catalog name.  */
101                       len = strlen (cat_name);
102                       ENOUGH (len);
103                       memcpy (&buf[bufact], cat_name, len);
104                       bufact += len;
105                       break;
106                     case 'L':
107                       /* Use the current locale category value.  */
108                       len = strlen (env_var);
109                       ENOUGH (len);
110                       memcpy (&buf[bufact], env_var, len);
111                       bufact += len;
112                       break;
113                     case 'l':
114                       /* Use language element of locale category value.  */
115                       tmp = env_var;
116                       do
117                         {
118                           ENOUGH (1);
119                           buf[bufact++] = *tmp++;
120                         }
121                       while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
122                       break;
123                     case 't':
124                       /* Use territory element of locale category value.  */
125                       tmp = env_var;
126                       do
127                         ++tmp;
128                       while (*tmp != '\0' && *tmp != '_' && *tmp != '.');
129                       if (*tmp == '_')
130                         {
131                           ++tmp;
132                           do
133                             {
134                               ENOUGH (1);
135                               buf[bufact++] = *tmp++;
136                             }
137                           while (*tmp != '\0' && *tmp != '.');
138                         }
139                       break;
140                     case 'c':
141                       /* Use code set element of locale category value.  */
142                       tmp = env_var;
143                       do
144                         ++tmp;
145                       while (*tmp != '\0' && *tmp != '.');
146                       if (*tmp == '.')
147                         {
148                           ++tmp;
149                           do
150                             {
151                               ENOUGH (1);
152                               buf[bufact++] = *tmp++;
153                             }
154                           while (*tmp != '\0');
155                         }
156                       break;
157                     case '%':
158                       ENOUGH (1);
159                       buf[bufact++] = '%';
160                       break;
161                     default:
162                       /* Unknown variable: ignore this path element.  */
163                       bufact = 0;
164                       while (*run_nlspath != '\0' && *run_nlspath != ':')
165                         ++run_nlspath;
166                       break;
167                     }
168                 }
169               else
170                 {
171                   ENOUGH (1);
172                   buf[bufact++] = *run_nlspath++;
173                 }
174
175           ENOUGH (1);
176           buf[bufact] = '\0';
177
178           if (bufact != 0)
179             {
180               fd = open_not_cancel_2 (buf, O_RDONLY);
181               if (fd >= 0)
182                 break;
183             }
184
185           ++run_nlspath;
186         }
187     }
188
189   /* Avoid dealing with directories and block devices */
190   if (__builtin_expect (fd, 0) < 0)
191     return -1;
192
193   if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0)
194     goto close_unlock_return;
195
196   if (__builtin_expect (!S_ISREG (st.st_mode), 0)
197       || (size_t) st.st_size < sizeof (struct catalog_obj))
198     {
199       /* `errno' is not set correctly but the file is not usable.
200          Use an reasonable error value.  */
201       __set_errno (EINVAL);
202       goto close_unlock_return;
203     }
204
205   catalog->file_size = st.st_size;
206 #ifdef _POSIX_MAPPED_FILES
207 # ifndef MAP_COPY
208     /* Linux seems to lack read-only copy-on-write.  */
209 #  define MAP_COPY MAP_PRIVATE
210 # endif
211 # ifndef MAP_FILE
212     /* Some systems do not have this flag; it is superfluous.  */
213 #  define MAP_FILE 0
214 # endif
215   catalog->file_ptr =
216     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
217                                    MAP_FILE|MAP_COPY, fd, 0);
218   if (__builtin_expect (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED,
219                         1))
220     /* Tell the world we managed to mmap the file.  */
221     catalog->status = mmapped;
222   else
223 #endif /* _POSIX_MAPPED_FILES */
224     {
225       /* mmap failed perhaps because the system call is not
226          implemented.  Try to load the file.  */
227       size_t todo;
228       catalog->file_ptr = malloc (st.st_size);
229       if (catalog->file_ptr == NULL)
230         goto close_unlock_return;
231
232       todo = st.st_size;
233       /* Save read, handle partial reads.  */
234       do
235         {
236           size_t now = read_not_cancel (fd, (((char *) catalog->file_ptr)
237                                              + (st.st_size - todo)), todo);
238           if (now == 0 || now == (size_t) -1)
239             {
240 #ifdef EINTR
241               if (now == (size_t) -1 && errno == EINTR)
242                 continue;
243 #endif
244               free ((void *) catalog->file_ptr);
245               goto close_unlock_return;
246             }
247           todo -= now;
248         }
249       while (todo > 0);
250       catalog->status = malloced;
251     }
252
253   /* Determine whether the file is a catalog file and if yes whether
254      it is written using the correct byte order.  Else we have to swap
255      the values.  */
256   if (__builtin_expect (catalog->file_ptr->magic == CATGETS_MAGIC, 1))
257     swapping = 0;
258   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
259     swapping = 1;
260   else
261     {
262     invalid_file:
263       /* Invalid file.  Free the resources and mark catalog as not
264          usable.  */
265 #ifdef _POSIX_MAPPED_FILES
266       if (catalog->status == mmapped)
267         __munmap ((void *) catalog->file_ptr, catalog->file_size);
268       else
269 #endif  /* _POSIX_MAPPED_FILES */
270         free (catalog->file_ptr);
271       goto close_unlock_return;
272     }
273
274 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
275
276   /* Get dimensions of the used hashing table.  */
277   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
278   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
279
280   /* The file contains two versions of the pointer tables.  Pick the
281      right one for the local byte order.  */
282 #if __BYTE_ORDER == __LITTLE_ENDIAN
283   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
284 #elif __BYTE_ORDER == __BIG_ENDIAN
285   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
286                                                   * catalog->plane_depth
287                                                   * 3];
288 #else
289 # error Cannot handle __BYTE_ORDER byte order
290 #endif
291
292   /* The rest of the file contains all the strings.  They are
293      addressed relative to the position of the first string.  */
294   catalog->strings =
295     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
296                                                * catalog->plane_depth * 3 * 2];
297
298   /* Determine the largest string offset mentioned in the table.  */
299   max_offset = 0;
300   tab_size = 3 * catalog->plane_size * catalog->plane_depth;
301   for (cnt = 2; cnt < tab_size; cnt += 3)
302     if (catalog->name_ptr[cnt] > max_offset)
303       max_offset = catalog->name_ptr[cnt];
304
305   /* Now we can check whether the file is large enough to contain the
306      tables it says it contains.  */
307   if ((size_t) st.st_size
308       <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
309     /* The last string is not contained in the file.  */
310     goto invalid_file;
311
312   lastp = catalog->strings + max_offset;
313   max_offset = (st.st_size
314                 - sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
315   while (*lastp != '\0')
316     {
317       if (--max_offset == 0)
318         goto invalid_file;
319       ++lastp;
320     }
321
322   /* We succeeded.  */
323   result = 0;
324
325   /* Release the lock again.  */
326  close_unlock_return:
327   close_not_cancel_no_status (fd);
328
329   return result;
330 }
331 libc_hidden_def (__open_catalog)