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