Update.
[platform/upstream/glibc.git] / catgets / open_catalog.c
1 /* Copyright (C) 1996-2000, 2001, 2002 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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    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 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 (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 (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       || 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 # ifndef MAP_INHERIT
216     /* Some systems might lack this; they lose.  */
217 #  define MAP_INHERIT 0
218 # endif
219   catalog->file_ptr =
220     (struct catalog_obj *) __mmap (NULL, st.st_size, PROT_READ,
221                                    MAP_FILE|MAP_COPY|MAP_INHERIT, fd, 0);
222   if (__builtin_expect (catalog->file_ptr != (struct catalog_obj *) MAP_FAILED,
223                         1))
224     /* Tell the world we managed to mmap the file.  */
225     catalog->status = mmapped;
226   else
227 #endif /* _POSIX_MAPPED_FILES */
228     {
229       /* mmap failed perhaps because the system call is not
230          implemented.  Try to load the file.  */
231       size_t todo;
232       catalog->file_ptr = malloc (st.st_size);
233       if (catalog->file_ptr == NULL)
234         goto close_unlock_return;
235
236       todo = st.st_size;
237       /* Save read, handle partial reads.  */
238       do
239         {
240           size_t now = __read (fd, (((char *) catalog->file_ptr)
241                                     + (st.st_size - todo)), todo);
242           if (now == 0 || now == (size_t) -1)
243             {
244 #ifdef EINTR
245               if (now == (size_t) -1 && errno == EINTR)
246                 continue;
247 #endif
248               free ((void *) catalog->file_ptr);
249               goto close_unlock_return;
250             }
251           todo -= now;
252         }
253       while (todo > 0);
254       catalog->status = malloced;
255     }
256
257   /* Determine whether the file is a catalog file and if yes whether
258      it is written using the correct byte order.  Else we have to swap
259      the values.  */
260   if (__builtin_expect (catalog->file_ptr->magic, CATGETS_MAGIC)
261       == CATGETS_MAGIC)
262     swapping = 0;
263   else if (catalog->file_ptr->magic == SWAPU32 (CATGETS_MAGIC))
264     swapping = 1;
265   else
266     {
267     invalid_file:
268       /* Invalid file.  Free the resources and mark catalog as not
269          usable.  */
270 #ifdef _POSIX_MAPPED_FILES
271       if (catalog->status == mmapped)
272         __munmap ((void *) catalog->file_ptr, catalog->file_size);
273       else
274 #endif  /* _POSIX_MAPPED_FILES */
275         free (catalog->file_ptr);
276       goto close_unlock_return;
277     }
278
279 #define SWAP(x) (swapping ? SWAPU32 (x) : (x))
280
281   /* Get dimensions of the used hashing table.  */
282   catalog->plane_size = SWAP (catalog->file_ptr->plane_size);
283   catalog->plane_depth = SWAP (catalog->file_ptr->plane_depth);
284
285   /* The file contains two versions of the pointer tables.  Pick the
286      right one for the local byte order.  */
287 #if __BYTE_ORDER == __LITTLE_ENDIAN
288   catalog->name_ptr = &catalog->file_ptr->name_ptr[0];
289 #elif __BYTE_ORDER == __BIG_ENDIAN
290   catalog->name_ptr = &catalog->file_ptr->name_ptr[catalog->plane_size
291                                                   * catalog->plane_depth
292                                                   * 3];
293 #else
294 # error Cannot handle __BYTE_ORDER byte order
295 #endif
296
297   /* The rest of the file contains all the strings.  They are
298      addressed relative to the position of the first string.  */
299   catalog->strings =
300     (const char *) &catalog->file_ptr->name_ptr[catalog->plane_size
301                                                * catalog->plane_depth * 3 * 2];
302
303   /* Determine the largest string offset mentioned in the table.  */
304   max_offset = 0;
305   tab_size = 3 * catalog->plane_size * catalog->plane_depth;
306   for (cnt = 2; cnt < tab_size; cnt += 3)
307     if (catalog->name_ptr[cnt] > max_offset)
308       max_offset = catalog->name_ptr[cnt];
309
310   /* Now we can check whether the file is large enough to contain the
311      tables it says it contains.  */
312   if (st.st_size <= (sizeof (struct catalog_obj) + 2 * tab_size + max_offset))
313     /* The last string is not contained in the file.  */
314     goto invalid_file;
315
316   lastp = catalog->strings + max_offset;
317   max_offset = (st.st_size
318                 - sizeof (struct catalog_obj) + 2 * tab_size + max_offset);
319   while (*lastp != '\0')
320     {
321       if (--max_offset == 0)
322         goto invalid_file;
323       ++lastp;
324     }
325
326   /* We succeeded.  */
327   result = 0;
328
329   /* Release the lock again.  */
330  close_unlock_return:
331   __close (fd);
332
333   return result;
334 }